Java tutorial
/* * DSS - Digital Signature Services * * Copyright (C) 2011 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel * * Developed by: 2011 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 eu.europa.ec.markt.dss.signature.Document; import eu.europa.ec.markt.dss.signature.SignatureParameters; import eu.europa.ec.markt.dss.validation.CertificateVerifier; import eu.europa.ec.markt.dss.validation.ValidationContext; import eu.europa.ec.markt.dss.validation.cades.CAdESSignature; import eu.europa.ec.markt.dss.validation.certificate.CertificateAndContext; import eu.europa.ec.markt.dss.validation.certificate.CertificateSource; import eu.europa.ec.markt.dss.validation.certificate.CompositeCertificateSource; import eu.europa.ec.markt.dss.validation.certificate.ListCertificateSource; import java.io.IOException; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.CRL; import java.security.cert.CRLException; import java.security.cert.CertificateEncodingException; import java.security.cert.X509CRL; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Date; import java.util.Hashtable; import java.util.logging.Logger; import org.apache.commons.codec.binary.Hex; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.DERGeneralizedTime; import org.bouncycastle.asn1.DERInteger; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERSet; import org.bouncycastle.asn1.DERUTCTime; import org.bouncycastle.asn1.cms.Attribute; import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.asn1.esf.CrlIdentifier; import org.bouncycastle.asn1.esf.CrlListID; import org.bouncycastle.asn1.esf.CrlOcspRef; import org.bouncycastle.asn1.esf.CrlValidatedID; import org.bouncycastle.asn1.esf.OcspIdentifier; import org.bouncycastle.asn1.esf.OcspListID; import org.bouncycastle.asn1.esf.OcspResponsesID; import org.bouncycastle.asn1.esf.OtherHash; import org.bouncycastle.asn1.ess.OtherCertID; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.ocsp.BasicOCSPResp; import org.bouncycastle.ocsp.OCSPException; /** * This class holds the CAdES-C signature profile; it supports the inclusion of the mandatory unsigned * id-aa-ets-certificateRefs and id-aa-ets-revocationRefs attributes as specified in ETSI TS 101 733 V1.8.1, clauses * 6.2.1 & 6.2.2. * * * @version $Revision: 1867 $ - $Date: 2013-04-08 13:44:56 +0200 (Mon, 08 Apr 2013) $ */ public class CAdESProfileC extends CAdESProfileT { private static final Logger LOG = Logger.getLogger(CAdESProfileC.class.getName()); protected CertificateVerifier certificateVerifier; /** * @param certificateVerifier the certificateVerifier to set */ public void setCertificateVerifier(CertificateVerifier certificateVerifier) { this.certificateVerifier = certificateVerifier; } /** * Create a reference to a X509Certificate * * @param cert * @return * @throws NoSuchAlgorithmException * @throws CertificateEncodingException */ private OtherCertID makeOtherCertID(X509Certificate cert) throws NoSuchAlgorithmException, CertificateEncodingException { MessageDigest sha1digest = MessageDigest.getInstance(X509ObjectIdentifiers.id_SHA1.getId(), new BouncyCastleProvider()); byte[] d = sha1digest.digest(cert.getEncoded()); LOG.info(new DEROctetString(d).getDERObject().toString()); OtherHash hash = new OtherHash(sha1digest.digest(cert.getEncoded())); OtherCertID othercertid = new OtherCertID(new DERSequence(hash.getDERObject())); return othercertid; } /** * Create a reference to a X509CRL * * @param crl * @return * @throws NoSuchAlgorithmException * @throws CRLException */ private CrlValidatedID makeCrlValidatedID(X509CRL crl) throws NoSuchAlgorithmException, CRLException { MessageDigest sha1digest = MessageDigest.getInstance(X509ObjectIdentifiers.id_SHA1.getId(), new BouncyCastleProvider()); OtherHash hash = new OtherHash(sha1digest.digest(crl.getEncoded())); BigInteger crlnumber; CrlIdentifier crlid; if (crl.getExtensionValue("2.5.29.20") != null) { crlnumber = new DERInteger(crl.getExtensionValue("2.5.29.20")).getPositiveValue(); crlid = new CrlIdentifier(new X500Name(crl.getIssuerX500Principal().getName()), new DERUTCTime(crl.getThisUpdate()), crlnumber); } else { crlid = new CrlIdentifier(new X500Name(crl.getIssuerX500Principal().getName()), new DERUTCTime(crl.getThisUpdate())); } CrlValidatedID crlvid = new CrlValidatedID(hash, crlid); return crlvid; } /** * Create a reference on a OCSPResp * * @param ocspResp * @return * @throws NoSuchAlgorithmException * @throws OCSPException * @throws IOException */ private OcspResponsesID makeOcspResponsesID(BasicOCSPResp ocspResp) throws NoSuchAlgorithmException, OCSPException, IOException { /* * We hash the complete response, this is not clear in the TS but the issue was addressed here: * http://lists.iaik.tugraz.at/pipermail/jce-general/2007-January/005914.html */ MessageDigest sha1digest = MessageDigest.getInstance(X509ObjectIdentifiers.id_SHA1.getId(), new BouncyCastleProvider()); byte[] digestValue = sha1digest.digest(ocspResp.getEncoded()); OtherHash hash = new OtherHash(digestValue); OcspResponsesID ocsprespid = new OcspResponsesID(new OcspIdentifier( ocspResp.getResponderId().toASN1Object(), new DERGeneralizedTime(ocspResp.getProducedAt())), hash); LOG.info("Incorporate OcspResponseId[hash=" + Hex.encodeHexString(digestValue) + ",producedAt=" + ocspResp.getProducedAt()); return ocsprespid; } private Hashtable<ASN1ObjectIdentifier, ASN1Encodable> extendUnsignedAttributes( Hashtable<ASN1ObjectIdentifier, ASN1Encodable> unsignedAttrs, X509Certificate signingCertificate, SignatureParameters parameters, Date signingTime, CertificateSource optionalCertificateSource) throws IOException { ValidationContext validationContext = certificateVerifier.validateCertificate(signingCertificate, signingTime, new CompositeCertificateSource(new ListCertificateSource(parameters.getCertificateChain()), optionalCertificateSource), null, null); try { ArrayList<OtherCertID> completeCertificateRefs = new ArrayList<OtherCertID>(); ArrayList<CrlOcspRef> completeRevocationRefs = new ArrayList<CrlOcspRef>(); /* * The ETSI TS 101 733 stipulates (6.2.1): "It references the full set of CA certificates that have been * used to validate an ES with Complete validation data up to (but not including) the signer's certificate. * [...] NOTE 1: The signer's certificate is referenced in the signing certificate attribute (see clause * 5.7.3)." (6.2.1) * * "The second and subsequent CrlOcspRef fields shall be in the same order as the OtherCertID to which they * relate." (6.2.2) * * Also, no mention of the way to order those second and subsequent fields, so we add the certificates as * provided by the context. */ /* The SignedCertificate is in validationContext.getCertificate() */ for (CertificateAndContext c : validationContext.getNeededCertificates()) { /* * Add every certificate except the signing certificate */ if (!c.equals(signingCertificate)) { completeCertificateRefs.add(makeOtherCertID(c.getCertificate())); // certificateValues.add(new X509CertificateStructure((ASN1Sequence) ASN1Object.fromByteArray(c // .getCertificate().getEncoded()))); } ArrayList<CrlValidatedID> crlListIdValues = new ArrayList<CrlValidatedID>(); ArrayList<OcspResponsesID> ocspListIDValues = new ArrayList<OcspResponsesID>(); /* * Record each CRL and OCSP with a reference to the corresponding certificate */ for (CRL relatedcrl : validationContext.getRelatedCRLs(c)) { crlListIdValues.add(makeCrlValidatedID((X509CRL) relatedcrl)); } for (BasicOCSPResp relatedocspresp : validationContext.getRelatedOCSPResp(c)) { ocspListIDValues.add(makeOcspResponsesID(relatedocspresp)); } CrlValidatedID[] crlListIdArray = new CrlValidatedID[crlListIdValues.size()]; OcspResponsesID[] ocspListIDArray = new OcspResponsesID[ocspListIDValues.size()]; completeRevocationRefs.add(new CrlOcspRef(new CrlListID(crlListIdValues.toArray(crlListIdArray)), new OcspListID(ocspListIDValues.toArray(ocspListIDArray)), null)); } OtherCertID[] otherCertIDArray = new OtherCertID[completeCertificateRefs.size()]; CrlOcspRef[] crlOcspRefArray = new CrlOcspRef[completeRevocationRefs.size()]; unsignedAttrs.put(PKCSObjectIdentifiers.id_aa_ets_certificateRefs, new Attribute(PKCSObjectIdentifiers.id_aa_ets_certificateRefs, new DERSet(new DERSequence(completeCertificateRefs.toArray(otherCertIDArray))))); unsignedAttrs.put(PKCSObjectIdentifiers.id_aa_ets_revocationRefs, new Attribute(PKCSObjectIdentifiers.id_aa_ets_revocationRefs, new DERSet(new DERSequence(completeRevocationRefs.toArray(crlOcspRefArray))))); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } catch (CertificateEncodingException e) { throw new RuntimeException(e); } catch (OCSPException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } catch (CRLException e) { throw new RuntimeException(e); } return unsignedAttrs; } @SuppressWarnings("unchecked") @Override protected SignerInformation extendCMSSignature(CMSSignedData signedData, SignerInformation si, SignatureParameters parameters, Document originalData) throws IOException { /* Get parent unsigned attributes */ SignerInformation newSi = super.extendCMSSignature(signedData, si, parameters, originalData); Hashtable<ASN1ObjectIdentifier, ASN1Encodable> unsignedAttrs = newSi.getUnsignedAttributes().toHashtable(); /* Extends unsigned attributes */ CAdESSignature signature = new CAdESSignature(signedData, si.getSID()); unsignedAttrs = extendUnsignedAttributes(unsignedAttrs, signature.getSigningCertificate(), parameters, signature.getSigningTime(), signature.getCertificateSource()); /* Return new SignerInformation */ return SignerInformation.replaceUnsignedAttributes(newSi, new AttributeTable(unsignedAttrs)); } }