All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.europa.esig.dss.spi.x509.tsp.KeyEntityTSPSource Maven / Gradle / Ivy

/**
 * DSS - Digital Signature Services
 * Copyright (C) 2015 European Commission, provided under the CEF programme
 * 

* This file is part of the "DSS - Digital Signature Services" project. *

* This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. *

* This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. *

* You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package eu.europa.esig.dss.spi.x509.tsp; import eu.europa.esig.dss.enumerations.DigestAlgorithm; import eu.europa.esig.dss.enumerations.EncryptionAlgorithm; import eu.europa.esig.dss.enumerations.SignatureAlgorithm; import eu.europa.esig.dss.model.DSSException; import eu.europa.esig.dss.model.TimestampBinary; import eu.europa.esig.dss.model.x509.CertificateToken; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.DERSet; import org.bouncycastle.asn1.cms.Attribute; import org.bouncycastle.asn1.cms.CMSAttributes; import org.bouncycastle.asn1.cms.Time; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaCertStore; import org.bouncycastle.cms.CMSAttributeTableGenerator; import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator; import org.bouncycastle.cms.SignerInfoGenerator; import org.bouncycastle.cms.SignerInfoGeneratorBuilder; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.DigestCalculator; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.bc.BcDigestCalculatorProvider; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.tsp.TSPException; import org.bouncycastle.tsp.TimeStampRequest; import org.bouncycastle.tsp.TimeStampRequestGenerator; import org.bouncycastle.tsp.TimeStampResponse; import org.bouncycastle.tsp.TimeStampResponseGenerator; import org.bouncycastle.tsp.TimeStampToken; import org.bouncycastle.tsp.TimeStampTokenGenerator; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.nio.file.Files; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.SecureRandom; import java.security.UnrecoverableEntryException; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; /** * TSPSource implementation allowing to configure issuance of a time-stamp using a local {@code KeyStore} */ public class KeyEntityTSPSource implements TSPSource { private static final long serialVersionUID = -5082887845359355029L; /** * The private key to be used to sign a time-stamp token */ private PrivateKey privateKey; /** * The certificate representing a time-stamp issuer */ private X509Certificate certificate; /** * Certificate chain associated with the {@code certificate} */ private List certificateChain; /** * SecureRandom used to calculate a serial number for a timestamp */ private final SecureRandom secureRandom = new SecureRandom(); /** * Collection of digest algorithms accepted by the current TSP source in the request */ private Collection acceptedDigestAlgorithms = Arrays.asList(DigestAlgorithm.SHA224, DigestAlgorithm.SHA256, DigestAlgorithm.SHA384, DigestAlgorithm.SHA512); /** * The TSA policy */ private String tsaPolicy; /** * The static production date of the timestamp */ protected Date productionTime; /** * The Digest Algorithm of the signature of the created time-stamp token */ private DigestAlgorithm digestAlgorithm = DigestAlgorithm.SHA512; /** * Encryption algorithm of the signature of the OCSP response */ private EncryptionAlgorithm encryptionAlgorithm; /** * Default constructor instantiating empty configuration of the KeyEntityTSPSource */ protected KeyEntityTSPSource() { // empty } /** * Constructor instantiating the key store content and key entry data * * @param ksContent byte array representing the key store content * @param ksType {@link String} representing the type of the key store * @param ksPassword char array representing a password from the key store * @param alias {@link String} alias of the key entry to be used for timestamp signing * @param keyEntryPassword char array representing a password from the key entry */ public KeyEntityTSPSource(byte[] ksContent, String ksType, char[] ksPassword, String alias, char[] keyEntryPassword) { this(loadKeyStore(new ByteArrayInputStream(ksContent), ksType, ksPassword), alias, keyEntryPassword); } /** * Constructor instantiating the key store path location and key entry data * * @param ksPath {@link String} representing the path to the key store * @param ksType {@link String} representing the type of the key store * @param ksPassword char array representing a password from the key store * @param alias {@link String} alias of the key entry to be used for timestamp signing * @param keyEntryPassword char array representing a password from the key entry * @throws IOException if not able to load the key store file */ public KeyEntityTSPSource(String ksPath, String ksType, char[] ksPassword, String alias, char[] keyEntryPassword) throws IOException { this(new File(ksPath), ksType, ksPassword, alias, keyEntryPassword); } /** * Constructor instantiating the key store File and key entry data * * @param ksFile {@link File} key store file * @param ksType {@link String} representing the type of the key store * @param ksPassword char array representing a password from the key store * @param alias {@link String} alias of the key entry to be used for timestamp signing * @param keyEntryPassword char array representing a password from the key entry * @throws IOException if not able to load the key store file */ public KeyEntityTSPSource(File ksFile, String ksType, char[] ksPassword, String alias, char[] keyEntryPassword) throws IOException { this(Files.newInputStream(ksFile.toPath()), ksType, ksPassword, alias, keyEntryPassword); } /** * Constructor instantiating the key store InputStream and key entry data * * @param ksIs {@link InputStream} representing the key store content * @param ksType {@link String} representing the type of the key store * @param ksPassword char array representing a password from the key store * @param alias {@link String} alias of the key entry to be used for timestamp signing * @param keyEntryPassword char array representing a password from the key entry */ public KeyEntityTSPSource(InputStream ksIs, String ksType, char[] ksPassword, String alias, char[] keyEntryPassword) { this(loadKeyStore(ksIs, ksType, ksPassword), alias, keyEntryPassword); } private static KeyStore loadKeyStore(InputStream keyStoreIs, String ksType, char[] keyStorePassword) { try (InputStream is = keyStoreIs) { KeyStore keyStore = KeyStore.getInstance(ksType); keyStore.load(is, keyStorePassword); return keyStore; } catch (Exception e) { throw new DSSException("Unable to instantiate KeyStore", e); } } /** * Constructor instantiating the key store and key entry data * * @param keyStore {@link KeyStore} * @param alias {@link String} alias of the key entry to be used for timestamp signing * @param keyEntryPassword char array representing a password from the key entry */ public KeyEntityTSPSource(KeyStore keyStore, String alias, char[] keyEntryPassword) { Objects.requireNonNull(keyStore,"KeyStore is not defined!"); Objects.requireNonNull(alias,"Alias is not defined!"); Objects.requireNonNull(keyEntryPassword,"KeyEntry Password is not defined!"); KeyStore.PrivateKeyEntry privateKeyEntry = getPrivateKeyEntry(keyStore, alias, keyEntryPassword); this.privateKey = privateKeyEntry.getPrivateKey(); this.certificate = (X509Certificate) privateKeyEntry.getCertificate(); this.certificateChain = Arrays.stream(privateKeyEntry.getCertificateChain()).map(c -> (X509Certificate) c).collect(Collectors.toList()); } private static KeyStore.PrivateKeyEntry getPrivateKeyEntry(KeyStore keyStore, String alias, char[] keyEntryPassword) { try { if (!keyStore.isKeyEntry(alias)) { throw new IllegalArgumentException(String.format("No related/supported key entry found for alias '%s'!", alias)); } if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) { throw new IllegalArgumentException(String.format("No key entry found for alias '%s' is not instance of a PrivateKeyEntry!", alias)); } return (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, new KeyStore.PasswordProtection(keyEntryPassword)); } catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException e) { throw new DSSException(String.format("Unable to recover the key entry with alias '%s'. Reason : %s", alias, e.getMessage()), e); } } /** * Constructor to instantiate KeyEntityTSPSource with the given {@code PrivateKey} and * the corresponding {@code certificateToken} and {@code certificateChain} * * @param privateKey {@link PrivateKey} representing a key t be used to sing the time-stamp token * @param certificateToken {@link CertificateToken} representing a time-stamp issuer certificate associated * with the {@code privateKey} * @param certificateChain a list of {@link CertificateToken}s representing a certificate chain * for {@code certificateToken} to be added within the time-stamp */ public KeyEntityTSPSource(PrivateKey privateKey, CertificateToken certificateToken, List certificateChain) { this(privateKey, certificateToken.getCertificate(), certificateChain.stream().map(CertificateToken::getCertificate).collect(Collectors.toList())); } /** * Constructor to instantiate KeyEntityTSPSource with the given {@code PrivateKey} and * the corresponding {@code certificate} and {@code certificateChain} * * @param privateKey {@link PrivateKey} representing a key t be used to sing the time-stamp token * @param certificate {@link X509Certificate} representing a time-stamp issuer certificate associated * with the {@code privateKey} * @param certificateChain a list of {@link X509Certificate}s representing a certificate chain * for {@code certificateToken} to be added within the time-stamp */ public KeyEntityTSPSource(PrivateKey privateKey, X509Certificate certificate, List certificateChain) { Objects.requireNonNull(privateKey,"PrivateKey is not defined!"); Objects.requireNonNull(certificate,"Certificate is not defined!"); Objects.requireNonNull(certificateChain,"Certificate chain is not defined!"); this.privateKey = privateKey; this.certificate = certificate; this.certificateChain = certificateChain; } /** * Sets the private key used to sign the time-stamp token * * @param privateKey {@link PrivateKey} */ public void setPrivateKey(PrivateKey privateKey) { this.privateKey = privateKey; } /** * Sets a time-stamp issuer certificate * * @param certificate {@link X509Certificate} */ public void setCertificate(X509Certificate certificate) { this.certificate = certificate; } /** * Sets a certificate chain to be embedded within the time-stamp token * * @param certificateChain a list of {@link CertificateToken}s */ public void setCertificateChain(List certificateChain) { this.certificateChain = certificateChain; } /** * Sets the TSA policy * NOTE: The property is mandatory for TimeStampToken generation. * * @param tsaPolicy {@link String} */ public void setTsaPolicy(String tsaPolicy) { this.tsaPolicy = tsaPolicy; } /** * Sets a collection of digest algorithms to be accepted within timestamp request * Default: SHA-224, SHA-256, SHA-384, SHA-512 * * @param digestAlgorithms a collection of {@link DigestAlgorithm}s */ public void setAcceptedDigestAlgorithms(Collection digestAlgorithms) { this.acceptedDigestAlgorithms = digestAlgorithms; } /** * Gets the production time of the time-stamp * * @return {@link Date} */ protected Date getProductionTime() { if (productionTime == null) { return new Date(); } return productionTime; } /** * Sets a production time of the timestamp. * NOTE: if not defined, the current time will be used. * * @param productionTime {@link Date} */ public void setProductionTime(Date productionTime) { this.productionTime = productionTime; } /** * Sets the digest algorithm of the signature of the generated time-stamp token * Default: DigestAlgorithm.SHA256 * * @param digestAlgorithm {@link DigestAlgorithm} */ public void setDigestAlgorithm(DigestAlgorithm digestAlgorithm) { this.digestAlgorithm = digestAlgorithm; } /** * Sets the encryption algorithm to be used on time-stamp's signature generation. * NOTE: the encryptionAlgorithm, when defined, shall be compatible with the encryption algorithm used by the target key! * * @param encryptionAlgorithm {@link EncryptionAlgorithm} */ public void setEncryptionAlgorithm(EncryptionAlgorithm encryptionAlgorithm) { this.encryptionAlgorithm = encryptionAlgorithm; } @Override public TimestampBinary getTimeStampResponse(DigestAlgorithm digestAlgorithm, byte[] digest) { Objects.requireNonNull(privateKey, "PrivateKey is not defined! Use #setPrivateKey method."); Objects.requireNonNull(certificate, "Certificate is not defined! Use #setCertificate method."); Objects.requireNonNull(certificateChain, "Certificate chain is not defined! Use #setCertificateChain method."); Objects.requireNonNull(digestAlgorithm, "DigestAlgorithm is not defined!"); Objects.requireNonNull(digest, "digest is not defined!"); Objects.requireNonNull(tsaPolicy, "TSAPolicy OID is not defined! Use #setTsaPolicy method."); if (!acceptedDigestAlgorithms.contains(digestAlgorithm)) { throw new DSSException(String.format("DigestAlgorithm '%s' is not supported by the KeyEntityTSPSource implementation!", digestAlgorithm)); } try { TimeStampRequest request = createRequest(digestAlgorithm, digest); TimeStampResponse response = generateResponse(request, digestAlgorithm); return getTimestampBinary(response); } catch (IOException | TSPException e) { throw new DSSException(String.format("Unable to generate a timestamp. Reason : %s", e.getMessage()), e); } } /** * Creates a request for a time-stamp token generation * * @param digestAlgorithm {@link DigestAlgorithm} to be used to compute hash to be time-stamped * @param digest byte array representing hash to be time-stamped * @return {@link TimeStampRequest} */ protected TimeStampRequest createRequest(DigestAlgorithm digestAlgorithm, byte[] digest) { final TimeStampRequestGenerator requestGenerator = new TimeStampRequestGenerator(); requestGenerator.setCertReq(true); return requestGenerator.generate(getASN1ObjectIdentifier(digestAlgorithm), digest); } private Set getAcceptedDigestAlgorithmIdentifiers() { Set result = new HashSet<>(); for (DigestAlgorithm acceptedDigestAlgorithm : acceptedDigestAlgorithms) { result.add(getASN1ObjectIdentifier(acceptedDigestAlgorithm)); } return result; } private ASN1ObjectIdentifier getASN1ObjectIdentifier(DigestAlgorithm digestAlgorithm) { return getASN1ObjectIdentifier(digestAlgorithm.getOid()); } private ASN1ObjectIdentifier getASN1ObjectIdentifier(String oid) { return new ASN1ObjectIdentifier(oid); } /** * Returns the target signature algorithm to be used to time-stamp generation * * @return {@link String} signature algorithm name */ protected SignatureAlgorithm getSignatureAlgorithm() { EncryptionAlgorithm keyAlgorithm = EncryptionAlgorithm.forKey(privateKey); if (this.encryptionAlgorithm != null) { if (!this.encryptionAlgorithm.isEquivalent(keyAlgorithm)) { throw new IllegalArgumentException(String.format( "Defined EncryptionAlgorithm '%s' is not equivalent to the one returned by time-stamp issuer '%s'", encryptionAlgorithm, keyAlgorithm)); } keyAlgorithm = encryptionAlgorithm; } return SignatureAlgorithm.getAlgorithm(keyAlgorithm, digestAlgorithm); } /** * This method generates a timestamp response * * @param request {@link TimeStampRequest} * @param digestAlgorithm {@link DigestAlgorithm} used to generate the time-stamp * @return {@link TimeStampResponse} * @throws TSPException if an error occurs during the timestamp response generation */ protected TimeStampResponse generateResponse(TimeStampRequest request, DigestAlgorithm digestAlgorithm) throws TSPException { final Date genTime = getProductionTime(); TimeStampResponseGenerator responseGenerator = initResponseGenerator(digestAlgorithm, genTime); BigInteger timeStampSerialNumber = getTimeStampSerialNumber(); return buildResponse(responseGenerator, request, timeStampSerialNumber, genTime); } /** * This method initializes the {@code TimeStampResponseGenerator} * * @param digestAlgorithm {@link DigestAlgorithm} used to generate the message-imprint * @param getTime {@link Date} production time of the time-stamp * @return {@link TimeStampResponseGenerator} */ protected TimeStampResponseGenerator initResponseGenerator(DigestAlgorithm digestAlgorithm, Date getTime) { try { SignatureAlgorithm signatureAlgorithm = getSignatureAlgorithm(); ContentSigner signer = new JcaContentSignerBuilder(signatureAlgorithm.getJCEId()).build(privateKey); X509CertificateHolder certificateHolder = new X509CertificateHolder(certificate.getEncoded()); SignerInfoGenerator infoGenerator = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider()) .setSignedAttributeGenerator(getSignedAttributeGenerator(getTime)).build(signer, certificateHolder); AlgorithmIdentifier digestAlgorithmIdentifier = new AlgorithmIdentifier(getASN1ObjectIdentifier(digestAlgorithm)); DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder().build().get(digestAlgorithmIdentifier); TimeStampTokenGenerator tokenGenerator = new TimeStampTokenGenerator(infoGenerator, digestCalculator, getASN1ObjectIdentifier(tsaPolicy)); tokenGenerator.addCertificates(new JcaCertStore(certificateChain)); return new TimeStampResponseGenerator(tokenGenerator, getAcceptedDigestAlgorithmIdentifiers()); } catch (CertificateEncodingException | OperatorCreationException | TSPException e) { throw new DSSException(String.format("Unable to generate a timestamp. Reason : %s", e.getMessage()), e); } catch (IOException e) { throw new DSSException(String.format("An error occurred on timestamp response generation: %s", e.getMessage()), e); } } /** * Returns generator for signed attributes of a time-stamp * * @param getTime {@link Date} production time of the time-stamp * @return {@link CMSAttributeTableGenerator} */ @SuppressWarnings({"unchecked", "rawtypes"}) protected CMSAttributeTableGenerator getSignedAttributeGenerator(Date getTime) { return new DefaultSignedAttributeTableGenerator() { @Override protected Hashtable createStandardAttributeTable(Map map) { Hashtable hashtable = super.createStandardAttributeTable(map); // Ensure the same productionTime is used if (getTime != null) { Attribute attr = new Attribute(CMSAttributes.signingTime, new DERSet(new Time(getTime))); hashtable.put(CMSAttributes.signingTime, attr); } return hashtable; } }; } /** * Generates a time-stamp response * * @param responseGenerator {@link TimeStampResponseGenerator} * @param request {@link TimeStampRequest} * @param timeStampSerialNumber {@link BigInteger} * @param productionTime {@link Date} representing a time-stamp's generation time * @return {@link TimeStampResponse} * @throws TSPException if an error occurs on time-stamp generation */ protected TimeStampResponse buildResponse(TimeStampResponseGenerator responseGenerator, TimeStampRequest request, BigInteger timeStampSerialNumber, Date productionTime) throws TSPException { return responseGenerator.generate(request, timeStampSerialNumber, productionTime); } /** * Generates a serial number of the produced timestamp token * * @return {@link BigInteger} serial number */ protected BigInteger getTimeStampSerialNumber() { return new BigInteger(128, secureRandom); } /** * Returns time-stamp binary from the obtained {@code response} * * @param response {@link TimeStampResponse} * @return {@link TimestampBinary} * @throws IOException if en error occurs on time-stamp binaries extraction */ protected TimestampBinary getTimestampBinary(TimeStampResponse response) throws IOException { TimeStampToken timeStampToken = response.getTimeStampToken(); if (timeStampToken != null) { return new TimestampBinary(timeStampToken.getEncoded()); } else if (response.getStatusString() != null) { throw new DSSException(String.format("Unable to generate a timestamp. Reason : %s", response.getStatusString())); } else { throw new DSSException("Unable to generate a timestamp. Response returned an empty time-stamp token."); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy