/* * DSS - Digital Signature Services * * Copyright (C) 2013 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel * * Developed by: 2013 ARHS Developments S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-developments.com * * This file is part of the "DSS - Digital Signature Services" project. * * "DSS - Digital Signature Services" 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. * * DSS 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 * "DSS - Digital Signature Services". If not, see <http://www.gnu.org/licenses/>. */ package eu.europa.ec.markt.dss.signature.cades; import java.io.InputStream; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.security.auth.x500.X500Principal; import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaCertStore; import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSSignedDataGenerator; import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator; import org.bouncycastle.cms.SignerInfoGenerator; import org.bouncycastle.cms.SignerInfoGeneratorBuilder; import org.bouncycastle.cms.SimpleAttributeTableGenerator; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.DigestCalculatorProvider; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.bc.BcDigestCalculatorProvider; import org.bouncycastle.util.Store; import eu.europa.ec.markt.dss.DSSUtils; import eu.europa.ec.markt.dss.exception.DSSException; import eu.europa.ec.markt.dss.parameter.ChainCertificate; import eu.europa.ec.markt.dss.parameter.SignatureParameters; import eu.europa.ec.markt.dss.validation102853.CertificateVerifier; import eu.europa.ec.markt.dss.validation102853.TrustedCertificateSource; import static org.bouncycastle.asn1.cms.CMSObjectIdentifiers.id_ri_ocsp_response; import static org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers.id_pkix_ocsp_basic; /** * <p/> * <p/> * DISCLAIMER: Project owner DG-MARKT. * * @author <a href="mailto:dgmarkt.Project-DSS@arhs-developments.com">ARHS Developments</a> * @version $Revision: 1016 $ - $Date: 2011-06-17 15:30:45 +0200 (Fri, 17 Jun 2011) $ */ public class CMSSignedDataBuilder { private CertificateVerifier certificateVerifier; /** * This is the default constructor for {@code CMSSignedDataGeneratorBuilder}. The {@code CertificateVerifier} is used to find the trusted certificates. * * @param certificateVerifier {@code CertificateVerifier} provides information on the sources to be used in the validation process in the context of a signature. */ public CMSSignedDataBuilder(final CertificateVerifier certificateVerifier) { this.certificateVerifier = certificateVerifier; } /** * Note: * Section 5.1 of RFC 3852 [4] requires that, the CMS SignedData version be set to 3 if certificates from * SignedData is present AND (any version 1 attribute certificates are present OR any SignerInfo structures * are version 3 OR eContentType from encapContentInfo is other than id-data). Otherwise, the CMS * SignedData version is required to be set to 1. * ---> CMS SignedData Version is handled automatically by BouncyCastle. * * @param parameters set of the driving signing parameters * @param contentSigner the contentSigned to get the hash of the data to be signed * @param signerInfoGeneratorBuilder true if the unsigned attributes must be included * @param originalSignedData the original signed data if extending an existing signature. null otherwise. * @return the bouncycastle signed data generator which signs the document and adds the required signed and unsigned CMS attributes * @throws eu.europa.ec.markt.dss.exception.DSSException */ protected CMSSignedDataGenerator createCMSSignedDataGenerator(final SignatureParameters parameters, final ContentSigner contentSigner, final SignerInfoGeneratorBuilder signerInfoGeneratorBuilder, final CMSSignedData originalSignedData) throws DSSException { try { final X509Certificate signingCertificate = parameters.getSigningCertificate(); final CMSSignedDataGenerator generator = new CMSSignedDataGenerator(); final X509CertificateHolder certHolder = DSSUtils.getX509CertificateHolder(signingCertificate); final SignerInfoGenerator signerInfoGenerator = signerInfoGeneratorBuilder.build(contentSigner, certHolder); generator.addSignerInfoGenerator(signerInfoGenerator); final Set<X509Certificate> newCertificateChain = new HashSet<X509Certificate>(); if (originalSignedData != null) { generator.addSigners(originalSignedData.getSignerInfos()); generator.addAttributeCertificates(originalSignedData.getAttributeCertificates()); generator.addCRLs(originalSignedData.getCRLs()); generator.addOtherRevocationInfo(id_pkix_ocsp_basic, originalSignedData.getOtherRevocationInfo(id_pkix_ocsp_basic)); generator.addOtherRevocationInfo(id_ri_ocsp_response, originalSignedData.getOtherRevocationInfo(id_ri_ocsp_response)); final Store certificates = originalSignedData.getCertificates(); final Collection<X509CertificateHolder> certificatesMatches = certificates.getMatches(null); for (final X509CertificateHolder certificatesMatch : certificatesMatches) { final X509Certificate x509Certificate = DSSUtils.getCertificate(certificatesMatch); newCertificateChain.add(x509Certificate); } } final List<ChainCertificate> certificateChain = parameters.getCertificateChain(); for (final ChainCertificate chainCertificate : certificateChain) { final X509Certificate x509Certificate = chainCertificate.getX509Certificate(); newCertificateChain.add(x509Certificate); } final boolean trustAnchorBPPolicy = parameters.bLevel().isTrustAnchorBPPolicy(); final Store jcaCertStore = getJcaCertStore(newCertificateChain, trustAnchorBPPolicy); generator.addCertificates(jcaCertStore); return generator; } catch (CMSException e) { throw new DSSException(e); } catch (OperatorCreationException e) { throw new DSSException(e); } } /** * @param parameters the parameters of the signature containing values for the attributes * @param includeUnsignedAttributes true if the unsigned attributes must be included * @return a SignerInfoGeneratorBuilder that generate the signed and unsigned attributes according to the CAdESLevelBaselineB */ SignerInfoGeneratorBuilder getSignerInfoGeneratorBuilder(final SignatureParameters parameters, final boolean includeUnsignedAttributes) { final CAdESLevelBaselineB cadesProfile = new CAdESLevelBaselineB(); final AttributeTable signedAttributes = cadesProfile.getSignedAttributes(parameters); AttributeTable unsignedAttributes = null; if (includeUnsignedAttributes) { unsignedAttributes = cadesProfile.getUnsignedAttributes(); } return getSignerInfoGeneratorBuilder(signedAttributes, unsignedAttributes); } /** * @param signedAttributes the signedAttributes * @param unsignedAttributes the unsignedAttributes * @return a SignerInfoGeneratorBuilder that generate the signed and unsigned attributes according to the parameters */ private SignerInfoGeneratorBuilder getSignerInfoGeneratorBuilder(AttributeTable signedAttributes, AttributeTable unsignedAttributes) { if (signedAttributes != null && signedAttributes.size() == 0) { signedAttributes = null; } final DefaultSignedAttributeTableGenerator signedAttributeGenerator = new DefaultSignedAttributeTableGenerator(signedAttributes); if (unsignedAttributes != null && unsignedAttributes.size() == 0) { unsignedAttributes = null; } final SimpleAttributeTableGenerator unsignedAttributeGenerator = new SimpleAttributeTableGenerator(unsignedAttributes); return getSignerInfoGeneratorBuilder(signedAttributeGenerator, unsignedAttributeGenerator); } /** * @param signedAttributeGenerator the signedAttribute generator * @param unsignedAttributeGenerator the unsignedAttribute generator * @return a SignerInfoGeneratorBuilder that generate the signed and unsigned attributes according to the parameters */ private SignerInfoGeneratorBuilder getSignerInfoGeneratorBuilder(DefaultSignedAttributeTableGenerator signedAttributeGenerator, SimpleAttributeTableGenerator unsignedAttributeGenerator) { final DigestCalculatorProvider digestCalculatorProvider = new BcDigestCalculatorProvider(); SignerInfoGeneratorBuilder sigInfoGeneratorBuilder = new SignerInfoGeneratorBuilder(digestCalculatorProvider); sigInfoGeneratorBuilder.setSignedAttributeGenerator(signedAttributeGenerator); sigInfoGeneratorBuilder.setUnsignedAttributeGenerator(unsignedAttributeGenerator); return sigInfoGeneratorBuilder; } /** * The order of the certificates is important, the fist one must be the signing certificate. * * @return a store with the certificate chain of the signing certificate. The {@code Collection} is unique. * @throws CertificateEncodingException */ private JcaCertStore getJcaCertStore(final Collection<X509Certificate> certificateChain, boolean trustAnchorBPPolicy) { try { final Collection<X509Certificate> certs = new ArrayList<X509Certificate>(); for (final X509Certificate certificateInChain : certificateChain) { // CAdES-Baseline-B: do not include certificates found in the trusted list if (trustAnchorBPPolicy) { final X500Principal subjectX500Principal = certificateInChain.getSubjectX500Principal(); final TrustedCertificateSource trustedCertSource = certificateVerifier.getTrustedCertSource(); if (trustedCertSource != null) { if (!trustedCertSource.get(subjectX500Principal).isEmpty()) { continue; } } } certs.add(certificateInChain); } return new JcaCertStore(certs); } catch (CertificateEncodingException e) { throw new DSSException(e); } } protected CMSSignedData regenerateCMSSignedData(CMSSignedData cmsSignedData, SignatureParameters parameters, Store certificatesStore, Store attributeCertificatesStore, Store crlsStore, Store otherRevocationInfoFormatStoreBasic, Store otherRevocationInfoFormatStoreOcsp) { try { final CMSSignedDataGenerator cmsSignedDataGenerator = new CMSSignedDataGenerator(); cmsSignedDataGenerator.addSigners(cmsSignedData.getSignerInfos()); cmsSignedDataGenerator.addAttributeCertificates(attributeCertificatesStore); cmsSignedDataGenerator.addCertificates(certificatesStore); cmsSignedDataGenerator.addCRLs(crlsStore); cmsSignedDataGenerator.addOtherRevocationInfo(id_pkix_ocsp_basic, otherRevocationInfoFormatStoreBasic); cmsSignedDataGenerator.addOtherRevocationInfo(id_ri_ocsp_response, otherRevocationInfoFormatStoreOcsp); final boolean encapsulate = cmsSignedData.getSignedContent() != null; if (!encapsulate) { final InputStream inputStream = parameters.getDetachedContent().openStream(); final CMSProcessableByteArray content = new CMSProcessableByteArray(DSSUtils.toByteArray(inputStream)); cmsSignedData = cmsSignedDataGenerator.generate(content, encapsulate); } else { cmsSignedData = cmsSignedDataGenerator.generate(cmsSignedData.getSignedContent(), encapsulate); } return cmsSignedData; } catch (CMSException e) { throw new DSSException(e); } } //TODO Vincent: regeneration of SignedData -> Content-TS }