/************************************************************************* * * * SignServer: The OpenSource Automated Signing Server * * * * This software 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 any later version. * * * * See terms of license at gnu.org. * * * *************************************************************************/ package org.signserver.module.cmssigner; import java.io.IOException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.security.interfaces.DSAPublicKey; import java.security.interfaces.ECPublicKey; import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.List; import javax.persistence.EntityManager; import org.apache.log4j.Logger; import org.bouncycastle.cert.jcajce.JcaCertStore; import org.bouncycastle.cms.*; import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.signserver.common.*; import org.signserver.common.util.*; import org.signserver.server.WorkerContext; import org.signserver.server.archive.Archivable; import org.signserver.server.archive.DefaultArchivable; import org.signserver.server.cryptotokens.ICryptoToken; import org.signserver.server.signers.BaseSigner; public class CMSSigner extends BaseSigner { /** Logger for this class. */ private static final Logger LOG = Logger.getLogger(CMSSigner.class); /** Content-type for the produced data. */ private static final String CONTENT_TYPE = "application/pkcs7-signature"; // Property constants public static final String SIGNATUREALGORITHM = "SIGNATUREALGORITHM"; private String signatureAlgorithm; private String WORKERNAME = "CMSSigner"; private String ResponseMessage = Defines.ERROR_INTERNALSYSTEM; private int ResponseCode = Defines.CODE_INTERNALSYSTEM; @Override public void init(final int workerId, final WorkerConfig config, final WorkerContext workerContext, final EntityManager workerEM) { super.init(workerId, config, workerContext, workerEM); // Get the signature algorithm signatureAlgorithm = config.getProperty(SIGNATUREALGORITHM); } public ProcessResponse processData(final ProcessRequest signRequest, final RequestContext requestContext) throws IllegalRequestException, CryptoTokenOfflineException, SignServerException { ProcessResponse signResponse; ISignRequest sReq = (ISignRequest) signRequest; // Check that the request contains a valid GenericSignRequest object // with a byte[]. //final String userContract = RequestMetadata.getInstance(requestContext).get("UsernameContract"); if (!(signRequest instanceof GenericSignRequest)) { //DBConnector.getInstances().writeLogToDataBaseOutside(WORKERNAME, userContract, "[IllegalRequestException] Server: Recieved request wasn't a expected GenericSignRequest.", 201); throw new IllegalRequestException( "Recieved request wasn't a expected GenericSignRequest."); } if (!(sReq.getRequestData() instanceof byte[])) { //DBConnector.getInstances().writeLogToDataBaseOutside(WORKERNAME, userContract, "[IllegalRequestException] Server: Recieved request data wasn't a expected byte[].", 202); throw new IllegalRequestException( "Recieved request data wasn't a expected byte[]."); } byte[] data = (byte[]) sReq.getRequestData(); final String archiveId = createArchiveId(data, (String) requestContext.get(RequestContext.TRANSACTION_ID)); // check license for CMSSigner LOG.info("Checking license for CMSSigner."); License licInfo = License.getInstance(); if(licInfo.getStatusCode() != 0) { return new GenericSignResponse(sReq.getRequestID(), archiveId, Defines.CODE_INFO_LICENSE, licInfo.getStatusDescription()); } else { if(!licInfo.checkWorker(WORKERNAME)) { return new GenericSignResponse(sReq.getRequestID(), archiveId , Defines.CODE_INFO_LICENSE_NOTSUPPORT, Defines.ERROR_INFO_LICENSE_NOTSUPPORT); } } // Get certificate chain and signer certificate Collection certs = this.getSigningCertificateChain(); if (certs == null) { return new GenericSignResponse(sReq.getRequestID(), archiveId, Defines.CODE_NOCERTCHAIN, Defines.ERROR_NOCERTCHAIN); //throw new IllegalArgumentException("Null certificate chain. This signer needs a certificate."); } List x509CertChain = new LinkedList(); for (Certificate cert : certs) { if (cert instanceof X509Certificate) { x509CertChain.add((X509Certificate) cert); LOG.debug("Adding to chain: " + ((X509Certificate) cert).getSubjectDN()); } } Certificate cert = this.getSigningCertificate(); LOG.debug("SigningCert: " + ((X509Certificate) cert).getSubjectDN()); // Private key PrivateKey privKey = getCryptoToken().getPrivateKey(ICryptoToken.PURPOSE_SIGN); try { final CMSSignedDataGenerator generator = new CMSSignedDataGenerator(); final String sigAlg = signatureAlgorithm == null ? getDefaultSignatureAlgorithm(cert.getPublicKey()) : signatureAlgorithm; final ContentSigner contentSigner = new JcaContentSignerBuilder(sigAlg).setProvider(getCryptoToken().getProvider(ICryptoToken.PROVIDERUSAGE_SIGN)).build(privKey); generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder( new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()) .build(contentSigner, (X509Certificate) cert)); generator.addCertificates(new JcaCertStore(certs)); final CMSTypedData content = new CMSProcessableByteArray(data); final CMSSignedData signedData = generator.generate(content, false); final byte[] signedbytes = signedData.getEncoded(); final Collection archivables = Arrays.asList(new DefaultArchivable(Archivable.TYPE_RESPONSE, CONTENT_TYPE, signedbytes, archiveId)); if (signRequest instanceof GenericServletRequest) { signResponse = new GenericServletResponse(sReq.getRequestID(), signedbytes, getSigningCertificate(), archiveId, archivables, CONTENT_TYPE); } else { ResponseCode = Defines.CODE_SUCCESS; ResponseMessage = Defines.SUCCESS; signResponse = new GenericSignResponse(sReq.getRequestID(), signedbytes, getSigningCertificate(), null, archiveId, archivables, ResponseCode, ResponseMessage); } // Suggest new file name final Object fileNameOriginal = requestContext.get(RequestContext.FILENAME); if (fileNameOriginal instanceof String) { requestContext.put(RequestContext.RESPONSE_FILENAME, fileNameOriginal + ".p7s"); } return signResponse; } catch (OperatorCreationException ex) { LOG.error("Error initializing signer", ex); return new GenericSignResponse(sReq.getRequestID(), archiveId, Defines.CODE_INITSIGNER, Defines.ERROR_INITSIGNER); //throw new SignServerException("Error initializing signer", ex); } catch (CertificateEncodingException ex) { LOG.error("Error constructing cert store", ex); return new GenericSignResponse(sReq.getRequestID(), archiveId, Defines.CODE_CERTSTORE, Defines.ERROR_CERTSTORE); //throw new SignServerException("Error constructing cert store", ex); } catch (CMSException ex) { LOG.error("Error constructing CMS", ex); return new GenericSignResponse(sReq.getRequestID(), archiveId, Defines.CODE_CONSTRUCTCMS, Defines.ERROR_CONSTRUCTCMS); //throw new SignServerException("Error constructing CMS", ex); } catch (IOException ex) { LOG.error("Error constructing CMS", ex); return new GenericSignResponse(sReq.getRequestID(), archiveId, Defines.CODE_CONSTRUCTCMS, Defines.ERROR_CONSTRUCTCMS); //throw new SignServerException("Error constructing CMS", ex); } } private String getDefaultSignatureAlgorithm(final PublicKey publicKey) { final String result; if (publicKey instanceof ECPublicKey) { result = "SHA1withECDSA"; } else if (publicKey instanceof DSAPublicKey) { result = "SHA1withDSA"; } else { result = "SHA1withRSA"; } return result; } }