/**
* Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2015-2019)
* <p>
* contact.vitam@culture.gouv.fr
* <p>
* This software is a computer program whose purpose is to implement a digital archiving back-office system managing
* high volumetry securely and efficiently.
* <p>
* This software is governed by the CeCILL 2.1 license under French law and abiding by the rules of distribution of free
* software. You can use, modify and/ or redistribute the software under the terms of the CeCILL 2.1 license as
* circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
* <p>
* As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license,
* users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the
* successive licensors have only limited liability.
* <p>
* In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or
* developing or reproducing the software by the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it is reserved for developers and
* experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling the security of their systems and/or data
* to be ensured and, more generally, to use and operate it in the same conditions as regards security.
* <p>
* The fact that you are presently reading this means that you have had knowledge of the CeCILL 2.1 license and that you
* accept its terms.
*/
package fr.gouv.vitam.common.timestamp;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cms.SignerInfoGenerator;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
import org.bouncycastle.tsp.TSPAlgorithms;
import org.bouncycastle.tsp.TSPException;
import org.bouncycastle.tsp.TimeStampRequest;
import org.bouncycastle.tsp.TimeStampResponse;
import org.bouncycastle.tsp.TimeStampResponseGenerator;
import org.bouncycastle.tsp.TimeStampTokenGenerator;
import org.bouncycastle.util.CollectionStore;
import fr.gouv.vitam.common.LocalDateUtil;
import fr.gouv.vitam.common.VitamConfiguration;
import fr.gouv.vitam.common.digest.DigestType;
/**
* Generate a time stamp token with a local PKCS12 keystore.
*/
public class TimeStampSignatureWithKeystore implements TimeStampSignature {
private final DigestCalculatorProvider digestCalculatorProvider;
private PrivateKey key;
private final String tspPolicy;
private Certificate[] certificateChain;
/**
* @param pkcs12Path file link to pkcs12 keystore
* @param keystorePassword
* @throws KeyStoreException
* @throws CertificateException
* @throws NoSuchAlgorithmException
* @throws IOException
* @throws UnrecoverableKeyException
*/
public TimeStampSignatureWithKeystore(File pkcs12Path, char[] keystorePassword)
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException,
UnrecoverableKeyException {
digestCalculatorProvider = new BcDigestCalculatorProvider();
final KeyStore keyStore = KeyStore.getInstance("PKCS12");
try (FileInputStream fileInputStream = new FileInputStream(pkcs12Path)) {
final String alias = loadKeystoreAndfindUniqueAlias(keystorePassword, keyStore, fileInputStream);
key = (PrivateKey) keyStore.getKey(alias, keystorePassword);
certificateChain = keyStore.getCertificateChain(alias);
}
tspPolicy = "1.1";
}
private String loadKeystoreAndfindUniqueAlias(char[] keystorePassword, KeyStore keyStore,
FileInputStream fileInputStream)
throws IOException, NoSuchAlgorithmException, CertificateException, KeyStoreException {
keyStore.load(fileInputStream, keystorePassword);
final Enumeration<String> aliases = keyStore.aliases();
final String alias = aliases.nextElement();
if (aliases.hasMoreElements()) {
throw new IllegalArgumentException("Keystore has many key");
}
return alias;
}
/**
* @param request time stamp request
* @return time stamp response
* @throws OperatorCreationException
* @throws TSPException
* @throws CertificateEncodingException
*/
@Override
public TimeStampResponse sign(TimeStampRequest request)
throws OperatorCreationException, TSPException, CertificateEncodingException {
final DigestCalculator digestCalculator =
digestCalculatorProvider.get(new AlgorithmIdentifier(request.getMessageImprintAlgOID()));
final String tspAlgorithm = computeTspAlgorithm(key, VitamConfiguration.getDefaultTimestampDigestType());
final SignerInfoGenerator signerInfoGen =
new JcaSimpleSignerInfoGeneratorBuilder().build(tspAlgorithm, key, (X509Certificate) certificateChain[0]);
final ASN1ObjectIdentifier tsaPolicy = new ASN1ObjectIdentifier(tspPolicy);
final TimeStampTokenGenerator tokenGen =
new TimeStampTokenGenerator(signerInfoGen, digestCalculator, tsaPolicy);
tokenGen.addCertificates(new CollectionStore<>(Arrays.asList(certificateChain)));
final TimeStampResponseGenerator timeStampResponseGenerator =
new TimeStampResponseGenerator(tokenGen, TSPAlgorithms.ALLOWED);
final Date currentDate = LocalDateUtil.getDate(LocalDateUtil.now());
return timeStampResponseGenerator.generate(request, BigInteger.ONE, currentDate);
}
private String computeTspAlgorithm(PrivateKey privateKey, DigestType digestType) {
return String.format("%sWith%s", digestType.name(), privateKey.getAlgorithm());
}
}