package com.opentrust.spi.cms;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CRL;
import java.security.cert.CRLException;
import java.security.cert.CertStoreException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Hashtable;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.cert.jcajce.JcaCRLStore;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSAbsentContent;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSSignedDataStreamGenerator;
import org.bouncycastle.cms.CMSSignedGenerator;
import org.bouncycastle.cms.SignerInfoGenerator;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.io.Streams;
import com.keynectis.sequoia.ca.crypto.utils.OIDUtils;
import com.keynectis.sequoia.ca.crypto.utils.PKCS12File;
import com.opentrust.spi.cms.helpers.ContentSignerWithProvidedSignatureValue;
import com.opentrust.spi.cms.helpers.DefaultSignedAttributeTableGeneratorWithoutDefaultSigningTime;
import com.opentrust.spi.cms.helpers.SignatureHelper;
import com.opentrust.spi.cms.helpers.SignedAttributesHelper;
public class CMSGenerator {
static {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
// Performs CMS signing on provided content
public static byte[] sign(String documentFileName, String keyStoreFileName,
String password) throws IOException, CMSException, OperatorCreationException, URISyntaxException, GeneralSecurityException {
/*
KeyStore keyStore = KeyStoreHelper.load(keyStoreFileName, password);
String alias = KeyStoreHelper.getDefaultAlias(keyStore);
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());
Certificate certificate = keyStore.getCertificate(alias);
*/
PKCS12File p12 = new PKCS12File(keyStoreFileName, password);
byte[] signatureBytes = null;
InputStream inputStream = new FileInputStream(documentFileName);
try {
signatureBytes = signContent(BouncyCastleProvider.PROVIDER_NAME, inputStream, p12.mCertificate, p12.mPrivateKey,
null, null, OIDUtils.getOID("SHA-1").getId(), null, false);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// ignore it
}
}
}
return signatureBytes;
}
// Performs CMS signing on provided content
// optionally, content can be encapsulated in CMS
public static byte[] signContent(String provider, InputStream inputStream,
Certificate certificate, PrivateKey privateKey,
Collection<Certificate> certStore, Date signingTime,
String digestAlgorithm, Collection<CRL> unsignedCrls,
boolean encapsulate) throws InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException,
CertStoreException, CMSException, IOException, CRLException, OperatorCreationException, CertificateEncodingException, URISyntaxException, SignatureException {
CMSGenerator cmsGenerator = new CMSGenerator(provider, certificate, digestAlgorithm);
cmsGenerator.privateKey = privateKey;
cmsGenerator.certStore = certStore;
cmsGenerator.signingTime = signingTime;
cmsGenerator.unsignedCrls = unsignedCrls;
return cmsGenerator.signContent(inputStream, encapsulate);
}
public byte[] signContent(InputStream inputStream,
boolean encapsulate) throws InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException,
CertStoreException, CMSException, IOException, CRLException, OperatorCreationException, CertificateEncodingException, URISyntaxException, SignatureException {
CMSSignedDataStreamGenerator signGen = (CMSSignedDataStreamGenerator) buildCMSSignedGenerator(true);
byte[] signature = null;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
OutputStream outputStream = null;
try {
outputStream = signGen.open(byteArrayOutputStream, encapsulate);
Streams.pipeAll(inputStream, outputStream);
} finally {// close the streams
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
// ignore it
}
}
}
if (byteArrayOutputStream != null) {
signature = byteArrayOutputStream.toByteArray();
try {
byteArrayOutputStream.close();
} catch (IOException e) {
// ignore it
}
}
return signature;
}
// Performs CMS signing on pre-digested content
public static byte[] signReference(byte[] digest, String keyStoreFileName,
String password) throws IOException, CMSException, OperatorCreationException, URISyntaxException, GeneralSecurityException {
PKCS12File p12 = new PKCS12File(keyStoreFileName, password);
/*
KeyStore keyStore = KeyStoreHelper.load(keyStoreFileName, password);
String alias = KeyStoreHelper.getDefaultAlias(keyStore);
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());
Certificate certificate = keyStore.getCertificate(alias);
*/
return signReference(BouncyCastleProvider.PROVIDER_NAME, digest, p12.mCertificate,
p12.mPrivateKey, null, null, OIDUtils.getOID("SHA-1").getId(), null);
}
// Performs CMS signing on pre-digested content
public static byte[] signReference(String provider, byte[] digest, Certificate certificate,
PrivateKey privateKey, Collection<Certificate> certStore,
Date signingTime, String digestAlgorithm, Collection<CRL> unsignedCrls)
throws InvalidAlgorithmParameterException,
NoSuchAlgorithmException, NoSuchProviderException,
CertStoreException, CMSException, IOException,
CRLException, OperatorCreationException, CertificateEncodingException, URISyntaxException, SignatureException {
CMSGenerator cmsGenerator = new CMSGenerator(provider, certificate, digestAlgorithm);
cmsGenerator.privateKey = privateKey;
cmsGenerator.certStore = certStore;
cmsGenerator.signingTime = signingTime;
cmsGenerator.unsignedCrls = unsignedCrls;
return cmsGenerator.signReference(digest);
}
public byte[] signReference(byte[] digest)
throws InvalidAlgorithmParameterException,
NoSuchAlgorithmException, NoSuchProviderException,
CertStoreException, CMSException, IOException,
CRLException, OperatorCreationException, CertificateEncodingException, URISyntaxException, SignatureException {
SignedAttributesHelper.addMessageDigestAttribute(signedAttributesHashtable, digest);
CMSSignedDataGenerator signGen = (CMSSignedDataGenerator) buildCMSSignedGenerator(false);
CMSSignedData signedData = signGen.generate(new CMSAbsentContent(), false);
return signedData.getEncoded();
}
protected String provider = BouncyCastleProvider.PROVIDER_NAME;
protected Certificate certificate;
protected Collection<Certificate> certStore;
protected Hashtable<DERObjectIdentifier, Attribute> signedAttributesHashtable = new Hashtable<DERObjectIdentifier, Attribute>();
protected Date signingTime;
protected String digestAlg;
protected Collection<CRL> unsignedCrls;
protected PrivateKey privateKey;
protected ContentSignerWithProvidedSignatureValue contentSigner;
protected CMSGenerator(String provider, Certificate certificate, String digestAlgorithm) throws NoSuchAlgorithmException {
if(provider!=null) this.provider = provider; // otherwise we keep default = BouncyCastle
this.certificate = certificate;
this.digestAlg = digestAlgorithm;
if(digestAlg==null) throw new NoSuchAlgorithmException("Could not map " + digestAlgorithm + " to a known algorithm");
}
protected CMSSignedGenerator buildCMSSignedGenerator(boolean isStream) throws SignatureException,
OperatorCreationException, CertificateEncodingException, CMSException, CRLException, IOException,
NoSuchAlgorithmException, NoSuchProviderException {
populateSignedAttributesHashtable();
AttributeTable signedAttributesTable = new AttributeTable(signedAttributesHashtable);
String signatureAlgorithm = null;
if (privateKey != null)
signatureAlgorithm = SignatureHelper.getSignatureAlgoFromDigestAndKeyAlgo(digestAlg,
privateKey.getAlgorithm());
else if (contentSigner != null)
signatureAlgorithm = contentSigner.getAlgorithm();
if (signatureAlgorithm == null)
throw new SignatureException("Could not find/build signatureAlgorithm");
ContentSigner usedContentSigner = contentSigner;
if (usedContentSigner == null)
{
//usedContentSigner = new JcaContentSignerBuilder(signatureAlgorithm).setProvider(provider).build(privateKey);
usedContentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(privateKey);
}
CMSSignedGenerator signGen = null;
if (isStream)
signGen = new CMSSignedDataStreamGenerator();
else
signGen = new CMSSignedDataGenerator();
JcaSignerInfoGeneratorBuilder sigBuilder = new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider(provider).build());
sigBuilder.setSignedAttributeGenerator(new DefaultSignedAttributeTableGeneratorWithoutDefaultSigningTime(
signedAttributesTable));
SignerInfoGenerator signerInfoGen = sigBuilder.build(usedContentSigner, (X509Certificate) certificate);
signGen.addSignerInfoGenerator(signerInfoGen);
if (certStore == null)
certStore = new ArrayList<Certificate>();
if (!certStore.contains(certificate))
certStore.add(certificate);
signGen.addCertificates(new JcaCertStore(certStore));
if (unsignedCrls != null)
signGen.addCRLs(new JcaCRLStore(unsignedCrls));
return signGen;
}
protected void populateSignedAttributesHashtable() throws CRLException, IOException, CertificateEncodingException, NoSuchAlgorithmException, NoSuchProviderException {
if(signingTime==null) signingTime = new Date();
SignedAttributesHelper.addSigningTimeAttribute(signedAttributesHashtable, signingTime);
}
}