Java tutorial
/* * 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.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERObjectIdentifier; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERSet; import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.cms.Attribute; import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.asn1.cms.ContentInfo; import org.bouncycastle.asn1.cms.SignedData; import org.bouncycastle.asn1.cms.SignerIdentifier; import org.bouncycastle.asn1.cms.SignerInfo; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.SignerInformation; import eu.europa.ec.markt.dss.DSSASN1Utils; import eu.europa.ec.markt.dss.DSSUtils; import eu.europa.ec.markt.dss.DigestAlgorithm; import eu.europa.ec.markt.dss.OID; import eu.europa.ec.markt.dss.exception.DSSException; import eu.europa.ec.markt.dss.validation102853.CertificateToken; import eu.europa.ec.markt.dss.validation102853.TimestampToken; import eu.europa.ec.markt.dss.validation102853.cades.CAdESSignature; /** * Extracts the necessary information to compute the CAdES Archive Timestamp V3. * <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 CadesLevelBaselineLTATimestampExtractor { private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory .getLogger(CadesLevelBaselineLTATimestampExtractor.class); public static final DigestAlgorithm DEFAULT_ARCHIVE_TIMESTAMP_HASH_ALGO = DigestAlgorithm.SHA256; /** * If the algorithm identifier in ATSHashIndex as the default value (DEFAULT_ARCHIVE_TIMESTAMP_HASH_ALGO), it can be omited. */ private static final boolean OMIT_ALGORITHM_IDENTIFIER_IF_DEFAULT = true; /** * The field hashIndAlgorithm contains an identifier of the hash algorithm used to compute the hash values * contained in certificatesHashIndex, crlsHashIndex, and unsignedAttrsHashIndex. This algorithm * shall be the same as the hash algorithm used for computing the archive time-stamps message imprint. * <p/> * hashIndAlgorithm AlgorithmIdentifier DEFAULT {algorithm id-sha256}, */ private DigestAlgorithm hashIndexDigestAlgorithm; private final Set<ASN1ObjectIdentifier> excludedAttributesFromAtsHashIndex = new HashSet<ASN1ObjectIdentifier>(); public CadesLevelBaselineLTATimestampExtractor() { /* these attribute are validated elsewhere */ excludedAttributesFromAtsHashIndex.add(PKCSObjectIdentifiers.id_aa_ets_certValues); excludedAttributesFromAtsHashIndex.add(PKCSObjectIdentifiers.id_aa_ets_revocationValues); } /** * The ats-hash-index unsigned attribute provides an unambiguous imprint of the essential components of a CAdES * signature for use in the archive time-stamp (see 6.4.3). These essential components are elements of the following ASN.1 * SET OF structures: unsignedAttrs, SignedData.certificates, and SignedData.crls. * <p/> * The ats-hash-index attribute value has the ASN.1 syntax ATSHashIndex: * ATSHashIndex ::= SEQUENCE { * hashIndAlgorithm AlgorithmIdentifier DEFAULT {algorithm id-sha256}, * certificatesHashIndex SEQUENCE OF OCTET STRING, * crlsHashIndex SEQUENCE OF OCTET STRING, * * @param signerInformation * @param cAdESSignature * @return */ public Attribute getAtsHashIndex(SignerInformation signerInformation, DigestAlgorithm hashIndexDigestAlgorithm, CAdESSignature cAdESSignature) throws DSSException { this.hashIndexDigestAlgorithm = hashIndexDigestAlgorithm; final AlgorithmIdentifier algorithmIdentifier = getHashIndexDigestAlgorithmIdentifier(); final ASN1Sequence certificatesHashIndex = getCertificatesHashIndex(cAdESSignature); final ASN1Sequence crLsHashIndex = getCRLsHashIndex(cAdESSignature); final ASN1Sequence unsignedAttributesHashIndex = getUnsignedAttributesHashIndex(signerInformation); return getComposedAtsHashIndex(algorithmIdentifier, certificatesHashIndex, crLsHashIndex, unsignedAttributesHashIndex); } /** * get the atsHash index for verification of the provided token. * * @param signerInformation * @param cAdESSignature * @param timestampToken @return */ public Attribute getVerifiedAtsHashIndex(SignerInformation signerInformation, CAdESSignature cAdESSignature, TimestampToken timestampToken) throws DSSException { final AlgorithmIdentifier derObjectAlgorithmIdentifier = getAlgorithmIdentifier(timestampToken); final ASN1Sequence certificatesHashIndex = getVerifiedCertificatesHashIndex(cAdESSignature, timestampToken); final ASN1Sequence crLsHashIndex = getVerifiedCRLsHashIndex(cAdESSignature, timestampToken); final ASN1Sequence unsignedAttributesHashIndex = getVerifiedUnsignedAttributesHashIndex(signerInformation, timestampToken); return getComposedAtsHashIndex(derObjectAlgorithmIdentifier, certificatesHashIndex, crLsHashIndex, unsignedAttributesHashIndex); } private Attribute getComposedAtsHashIndex(AlgorithmIdentifier algorithmIdentifiers, ASN1Sequence certificatesHashIndex, ASN1Sequence crLsHashIndex, ASN1Sequence unsignedAttributesHashIndex) { final ASN1EncodableVector vector = new ASN1EncodableVector(); if (algorithmIdentifiers != null) { vector.add(algorithmIdentifiers); } vector.add(certificatesHashIndex); vector.add(crLsHashIndex); vector.add(unsignedAttributesHashIndex); final ASN1Sequence derSequence = new DERSequence(vector); return new Attribute(OID.id_aa_ATSHashIndex, new DERSet(derSequence)); } /** * The field certificatesHashIndex is a sequence of octet strings. Each one contains the hash value of one * instance of CertificateChoices within certificates field of the root SignedData. A hash value for * every instance of CertificateChoices, as present at the time when the corresponding archive time-stamp is * requested, shall be included in certificatesHashIndex. No other hash value shall be included in this field. * * @param cAdESSignature * @return * @throws eu.europa.ec.markt.dss.exception.DSSException */ private ASN1Sequence getCertificatesHashIndex(CAdESSignature cAdESSignature) throws DSSException { final ASN1EncodableVector certificatesHashIndexVector = new ASN1EncodableVector(); final List<CertificateToken> certificateTokens = cAdESSignature .getCertificatesWithinSignatureAndTimestamps(); for (final CertificateToken certificateToken : certificateTokens) { final byte[] encodedCertificate = certificateToken.getEncoded(); final byte[] digest = DSSUtils.digest(hashIndexDigestAlgorithm, encodedCertificate); if (LOG.isDebugEnabled()) { LOG.debug("Adding to CertificatesHashIndex DSS-Identifier: {} with hash {}", certificateToken.getDSSId(), DSSUtils.encodeHexString(digest)); } final DEROctetString derOctetStringDigest = new DEROctetString(digest); certificatesHashIndexVector.add(derOctetStringDigest); } return new DERSequence(certificatesHashIndexVector); } /** * The field certificatesHashIndex is a sequence of octet strings. Each one contains the hash value of one * instance of CertificateChoices within certificates field of the root SignedData. A hash value for * every instance of CertificateChoices, as present at the time when the corresponding archive time-stamp is * requested, shall be included in certificatesHashIndex. No other hash value shall be included in this field. * * @param cAdESSignature * @return * @throws eu.europa.ec.markt.dss.exception.DSSException */ @SuppressWarnings("unchecked") private ASN1Sequence getVerifiedCertificatesHashIndex(CAdESSignature cAdESSignature, TimestampToken timestampToken) throws DSSException { final ASN1Sequence certHashes = getCertificatesHashIndex(timestampToken); final ArrayList<DEROctetString> certHashesList = Collections.list(certHashes.getObjects()); final List<CertificateToken> certificates = cAdESSignature.getCertificatesWithinSignatureAndTimestamps(); for (final CertificateToken certificateToken : certificates) { final byte[] encodedCertificate = certificateToken.getEncoded(); final byte[] digest = DSSUtils.digest(hashIndexDigestAlgorithm, encodedCertificate); final DEROctetString derOctetStringDigest = new DEROctetString(digest); if (certHashesList.remove(derOctetStringDigest)) { // attribute present in signature and in timestamp LOG.debug("Cert {} present in timestamp", certificateToken.getAbbreviation()); } else { LOG.debug("Cert {} not present in timestamp", certificateToken.getAbbreviation()); } } if (!certHashesList.isEmpty()) { LOG.error("{} attribute hash in Cert Hashes have not been found in document attributes: {}", certHashesList.size(), certHashesList); // return a empty DERSequence to screw up the hash return new DERSequence(); } return certHashes; } /** * The field crlsHashIndex is a sequence of octet strings. Each one contains the hash value of one instance of * RevocationInfoChoice within crls field of the root SignedData. A hash value for every instance of * RevocationInfoChoice, as present at the time when the corresponding archive time-stamp is requested, shall be * included in crlsHashIndex. No other hash values shall be included in this field. * * @param cAdESSignature * @return * @throws eu.europa.ec.markt.dss.exception.DSSException */ @SuppressWarnings("unchecked") private ASN1Sequence getCRLsHashIndex(CAdESSignature cAdESSignature) throws DSSException { final ASN1EncodableVector crlsHashIndex = new ASN1EncodableVector(); final SignedData signedData = SignedData .getInstance(cAdESSignature.getCmsSignedData().toASN1Structure().getContent()); final ASN1Set signedDataCRLs = signedData.getCRLs(); if (signedDataCRLs != null) { final Enumeration<ASN1Encodable> crLs = signedDataCRLs.getObjects(); if (crLs != null) { while (crLs.hasMoreElements()) { final ASN1Encodable asn1Encodable = crLs.nextElement(); digestAndAddToList(crlsHashIndex, DSSASN1Utils.getDEREncoded(asn1Encodable)); } } } return new DERSequence(crlsHashIndex); } private void digestAndAddToList(ASN1EncodableVector crlsHashIndex, byte[] encoded) { final byte[] digest = DSSUtils.digest(hashIndexDigestAlgorithm, encoded); if (LOG.isDebugEnabled()) { LOG.debug("Adding to crlsHashIndex with hash {}", DSSUtils.encodeHexString(digest)); } final DEROctetString derOctetStringDigest = new DEROctetString(digest); crlsHashIndex.add(derOctetStringDigest); } /** * The field crlsHashIndex is a sequence of octet strings. Each one contains the hash value of one instance of * RevocationInfoChoice within crls field of the root SignedData. A hash value for every instance of * RevocationInfoChoice, as present at the time when the corresponding archive time-stamp is requested, shall be * included in crlsHashIndex. No other hash values shall be included in this field. * * @param cAdESSignature * @return * @throws eu.europa.ec.markt.dss.exception.DSSException */ @SuppressWarnings("unchecked") private ASN1Sequence getVerifiedCRLsHashIndex(CAdESSignature cAdESSignature, TimestampToken timestampToken) throws DSSException { final ASN1Sequence crlHashes = getCRLHashIndex(timestampToken); final ArrayList<DEROctetString> crlHashesList = Collections.list(crlHashes.getObjects()); final SignedData signedData = SignedData .getInstance(cAdESSignature.getCmsSignedData().toASN1Structure().getContent()); final ASN1Set signedDataCRLs = signedData.getCRLs(); if (signedDataCRLs != null) { final Enumeration<ASN1Encodable> crLs = signedDataCRLs.getObjects(); if (crLs != null) { while (crLs.hasMoreElements()) { final ASN1Encodable asn1Encodable = crLs.nextElement(); handleRevocationEncoded(crlHashesList, DSSASN1Utils.getDEREncoded(asn1Encodable)); } } } if (!crlHashesList.isEmpty()) { LOG.error("{} attribute hash in CRL Hashes have not been found in document attributes: {}", crlHashesList.size(), crlHashesList); // return a empty DERSequence to screw up the hash return new DERSequence(); } return crlHashes; } private void handleRevocationEncoded(ArrayList<DEROctetString> crlHashesList, byte[] ocspHolderEncoded) { final byte[] digest = DSSUtils.digest(hashIndexDigestAlgorithm, ocspHolderEncoded); final DEROctetString derOctetStringDigest = new DEROctetString(digest); if (crlHashesList.remove(derOctetStringDigest)) { // attribute present in signature and in timestamp if (LOG.isDebugEnabled()) { LOG.debug("CRL/OCSP present in timestamp {}", DSSUtils.toHex(derOctetStringDigest.getOctets())); } } else { if (LOG.isDebugEnabled()) { LOG.debug("CRL/OCSP not present in timestamp {}", DSSUtils.toHex(derOctetStringDigest.getOctets())); } } } private boolean handleCrlEncoded(ArrayList<DEROctetString> crlHashesList, byte[] crlHolderEncoded) { final byte[] digest = DSSUtils.digest(hashIndexDigestAlgorithm, crlHolderEncoded); final DEROctetString derOctetStringDigest = new DEROctetString(digest); return crlHashesList.remove(derOctetStringDigest); } /** * The field unsignedAttrsHashIndex is a sequence of octet strings. Each one contains the hash value of one * instance of Attribute within unsignedAttrs field of the SignerInfo. A hash value for every instance of * Attribute, as present at the time when the corresponding archive time-stamp is requested, shall be included in * unsignedAttrsHashIndex. No other hash values shall be included in this field. * * @param signerInformation * @return */ @SuppressWarnings("unchecked") private ASN1Sequence getUnsignedAttributesHashIndex(SignerInformation signerInformation) throws DSSException { final ASN1EncodableVector unsignedAttributesHashIndex = new ASN1EncodableVector(); AttributeTable unsignedAttributes = signerInformation.getUnsignedAttributes(); final ASN1EncodableVector asn1EncodableVector = unsignedAttributes.toASN1EncodableVector(); for (int i = 0; i < asn1EncodableVector.size(); i++) { final Attribute attribute = (Attribute) asn1EncodableVector.get(i); if (!excludedAttributesFromAtsHashIndex.contains(attribute.getAttrType())) { final DEROctetString derOctetStringDigest = getAttributeDerOctetStringHash(attribute); unsignedAttributesHashIndex.add(derOctetStringDigest); } } return new DERSequence(unsignedAttributesHashIndex); } /** * The field unsignedAttrsHashIndex is a sequence of octet strings. Each one contains the hash value of one * instance of Attribute within unsignedAttrs field of the SignerInfo. A hash value for every instance of * Attribute, as present at the time when the corresponding archive time-stamp is requested, shall be included in * unsignedAttrsHashIndex. No other hash values shall be included in this field. * <p/> * We check that every hash attribute found in the timestamp token is found if the signerInformation. * <p/> * If there is more unsigned attributes in the signerInformation than present in the hash attributes list * (and there is at least the archiveTimestampAttributeV3), we don't report any error nor which attributes are signed by the timestamp. * If there is some attributes that are not present or altered in the signerInformation, we just return some empty sequence to make * sure that the timestamped data will not match. We do not report which attributes hash are present if any. * <p/> * If there is not attribute at all in the archive timestamp hash index, that would means we didn't check anything. * * @param signerInformation * @param timestampToken * @return */ @SuppressWarnings("unchecked") private ASN1Sequence getVerifiedUnsignedAttributesHashIndex(SignerInformation signerInformation, TimestampToken timestampToken) throws DSSException { final ASN1Sequence unsignedAttributesHashes = getUnsignedAttributesHashIndex(timestampToken); final ArrayList<DEROctetString> timestampUnsignedAttributesHashesList = Collections .list(unsignedAttributesHashes.getObjects()); AttributeTable unsignedAttributes = CAdESSignature.getUnsignedAttributes(signerInformation); final ASN1EncodableVector asn1EncodableVector = unsignedAttributes.toASN1EncodableVector(); for (int i = 0; i < asn1EncodableVector.size(); i++) { final Attribute attribute = (Attribute) asn1EncodableVector.get(i); final DEROctetString derOctetStringDigest = getAttributeDerOctetStringHash(attribute); final ASN1ObjectIdentifier attrType = attribute.getAttrType(); if (timestampUnsignedAttributesHashesList.remove(derOctetStringDigest)) { // attribute present in signature and in timestamp LOG.debug("Attribute {} present in timestamp", attrType.getId()); } else { LOG.debug("Attribute {} not present in timestamp", attrType.getId()); } } if (!timestampUnsignedAttributesHashesList.isEmpty()) { LOG.error("{} attribute hash in Timestamp have not been found in document attributes: {}", timestampUnsignedAttributesHashesList.size(), timestampUnsignedAttributesHashesList); // return a empty DERSequence to screw up the hash return new DERSequence(); } // return the original DERSequence return unsignedAttributesHashes; } private DEROctetString getAttributeDerOctetStringHash(Attribute attribute) throws DSSException { final byte[] attributeEncoded = DSSASN1Utils.getDEREncoded(attribute); final byte[] digest = DSSUtils.digest(hashIndexDigestAlgorithm, attributeEncoded); return new DEROctetString(digest); } /** * Extract the Unsigned Attribute Archive Timestamp Attribute Hash Index from a timestampToken * * @param timestampToken * @return */ private ASN1Sequence getUnsignedAttributesHashIndex(TimestampToken timestampToken) { final ASN1Sequence timestampAttributeAtsHashIndexValue = getAtsHashIndex(timestampToken); int UNSIGNED_ATTRIBUTES_INDEX = 2; if (timestampAttributeAtsHashIndexValue.size() > 3) { UNSIGNED_ATTRIBUTES_INDEX++; } return (ASN1Sequence) timestampAttributeAtsHashIndexValue.getObjectAt(UNSIGNED_ATTRIBUTES_INDEX) .toASN1Primitive(); } /** * Extract the Unsigned Attribute Archive Timestamp Crl Hash Index from a timestampToken * * @param timestampToken * @return */ private ASN1Sequence getCRLHashIndex(TimestampToken timestampToken) { final ASN1Sequence timestampAttributeAtsHashIndexValue = getAtsHashIndex(timestampToken); int CRL_INDEX = 1; if (timestampAttributeAtsHashIndexValue.size() > 3) { CRL_INDEX++; } return (ASN1Sequence) timestampAttributeAtsHashIndexValue.getObjectAt(CRL_INDEX).toASN1Primitive(); } /** * Extract the Unsigned Attribute Archive Timestamp Cert Hash Index from a timestampToken * * @param timestampToken * @return */ private ASN1Sequence getCertificatesHashIndex(TimestampToken timestampToken) { final ASN1Sequence timestampAttributeAtsHashIndexValue = getAtsHashIndex(timestampToken); int CERT_INDEX = 0; if (timestampAttributeAtsHashIndexValue.size() > 3) { CERT_INDEX++; } return (ASN1Sequence) timestampAttributeAtsHashIndexValue.getObjectAt(CERT_INDEX).toASN1Primitive(); } /** * Extract the Unsigned Attribute Archive Timestamp Cert Hash Index from a timestampToken * * @param timestampToken * @return */ private AlgorithmIdentifier getAlgorithmIdentifier(TimestampToken timestampToken) { final ASN1Sequence timestampAttributeAtsHashIndexValue = getAtsHashIndex(timestampToken); if (timestampAttributeAtsHashIndexValue.size() > 3) { final int ALGO_INDEX = 0; final ASN1Encodable derEncodable = timestampAttributeAtsHashIndexValue.getObjectAt(ALGO_INDEX); if (derEncodable instanceof ASN1Sequence) { final ASN1Sequence derSequence = (ASN1Sequence) derEncodable; hashIndexDigestAlgorithm = DigestAlgorithm .forOID(((DERObjectIdentifier) derSequence.getObjectAt(0)).getId()); return AlgorithmIdentifier.getInstance(derSequence); } else if (derEncodable instanceof DERObjectIdentifier) { ASN1ObjectIdentifier derObjectIdentifier = ASN1ObjectIdentifier.getInstance(derEncodable); hashIndexDigestAlgorithm = DigestAlgorithm.forOID(derObjectIdentifier.getId()); return new AlgorithmIdentifier(derObjectIdentifier); } } hashIndexDigestAlgorithm = DEFAULT_ARCHIVE_TIMESTAMP_HASH_ALGO; return null; } /** * @param timestampToken * @return the content of SignedAttribute: ATS-hash-index unsigned attribute {itu-t(0) identified-organization(4) etsi(0) electronic-signature-standard(1733) attributes(2) 5} */ private ASN1Sequence getAtsHashIndex(TimestampToken timestampToken) { final AttributeTable timestampTokenUnsignedAttributes = timestampToken.getUnsignedAttributes(); final Attribute atsHashIndexAttribute = timestampTokenUnsignedAttributes.get(OID.id_aa_ATSHashIndex); final ASN1Set attrValues = atsHashIndexAttribute.getAttrValues(); return (ASN1Sequence) attrValues.getObjectAt(0).toASN1Primitive(); } private AlgorithmIdentifier getHashIndexDigestAlgorithmIdentifier() { if (OMIT_ALGORITHM_IDENTIFIER_IF_DEFAULT && hashIndexDigestAlgorithm.getOid().equals(DEFAULT_ARCHIVE_TIMESTAMP_HASH_ALGO.getOid())) { return null; } else { return hashIndexDigestAlgorithm.getAlgorithmIdentifier(); } } public byte[] getArchiveTimestampDataV3(CAdESSignature cadesSignature, SignerInformation signerInformation, Attribute atsHashIndexAttribute, byte[] originalDocument, DigestAlgorithm digestAlgorithm) throws DSSException { final CMSSignedData cmsSignedData = cadesSignature.getCmsSignedData(); final byte[] encodedContentType = getEncodedContentType(cmsSignedData); final byte[] signedDataDigest = DSSUtils.digest(digestAlgorithm, originalDocument); final byte[] encodedFields = geSignedFields(signerInformation); final byte[] encodedAtsHashIndex = DSSASN1Utils .getDEREncoded(atsHashIndexAttribute.getAttrValues().getObjectAt(0)); final byte[] dataToTimestamp = concatenateArrays(encodedContentType, signedDataDigest, encodedFields, encodedAtsHashIndex); if (LOG.isDebugEnabled()) { LOG.debug("eContentType={}", DSSUtils.encodeHexString(encodedContentType)); LOG.debug("signedDataDigest={}", DSSUtils.encodeHexString(signedDataDigest)); LOG.debug("encodedFields={}", DSSUtils.encodeHexString(encodedFields)); LOG.debug("encodedAtsHashIndex={}", DSSUtils.encodeHexString(encodedAtsHashIndex)); LOG.debug("Archive Timestamp Data v3 is: {}", DSSUtils.encodeHexString(dataToTimestamp)); } return dataToTimestamp; } /** * The input for the archive-time-stamp-v3s message imprint computation shall be the concatenation (in the * order shown by the list below) of the signed data hash (see bullet 2 below) and certain fields in their binary encoded * form without any modification and including the tag, length and value octets: * * @param byteArrays * @return * @throws eu.europa.ec.markt.dss.exception.DSSException */ private byte[] concatenateArrays(byte[]... byteArrays) throws DSSException { try { ByteArrayOutputStream concatenationResult = new ByteArrayOutputStream(); for (final byte[] byteArray : byteArrays) { concatenationResult.write(byteArray); } return concatenationResult.toByteArray(); } catch (IOException e) { throw new DSSException(e); } } /** * 1) The SignedData.encapContentInfo.eContentType. * * @param cmsSignedData * @return */ private byte[] getEncodedContentType(CMSSignedData cmsSignedData) { ContentInfo contentInfo = cmsSignedData.toASN1Structure(); SignedData signedData = SignedData.getInstance(contentInfo.getContent()); try { return signedData.getEncapContentInfo().getContentType().getEncoded(ASN1Encoding.DER); } catch (IOException e) { throw new DSSException(e); } } /** * 3) Fields version, sid, digestAlgorithm, signedAttrs, signatureAlgorithm, and * signature within the SignedData.signerInfoss item corresponding to the signature being archive * time-stamped, in their order of appearance. * * @param signerInformation * @return */ private byte[] geSignedFields(SignerInformation signerInformation) { final SignerInfo signerInfo = signerInformation.toASN1Structure(); final ASN1Integer version = signerInfo.getVersion(); final SignerIdentifier sid = signerInfo.getSID(); final AlgorithmIdentifier digestAlgorithm = signerInfo.getDigestAlgorithm(); final ASN1TaggedObject signedAttributes = new DERTaggedObject(false, 0, new DERSequence(signerInfo.getAuthenticatedAttributes().toArray())); final AlgorithmIdentifier digestEncryptionAlgorithm = signerInfo.getDigestEncryptionAlgorithm(); final ASN1OctetString encryptedDigest = signerInfo.getEncryptedDigest(); final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try { final byte[] derEncodedVersion = DSSASN1Utils.getDEREncoded(version); final byte[] derEncodedSid = DSSASN1Utils.getDEREncoded(sid); final byte[] derEncodedDigestAlgo = DSSASN1Utils.getDEREncoded(digestAlgorithm); final byte[] derEncodedSignedAttributes = DSSASN1Utils.getDEREncoded(signedAttributes); final byte[] derEncodedDigestEncryptionAlgo = DSSASN1Utils.getDEREncoded(digestEncryptionAlgorithm); final byte[] derEncodedEncryptedDigest = DSSASN1Utils.getDEREncoded(encryptedDigest); if (LOG.isDebugEnabled()) { LOG.debug("getSignedFields Version={}", DSSUtils.encodeHexString(derEncodedVersion)); LOG.debug("getSignedFields Sid={}", DSSUtils.encodeHexString(derEncodedSid)); LOG.debug("getSignedFields DigestAlgo={}", DSSUtils.encodeHexString(derEncodedDigestAlgo)); LOG.debug("getSignedFields SignedAttributes={}", DSSUtils.encodeHexString(derEncodedSignedAttributes)); // bad LOG.debug("getSignedFields DigestEncryptionAlgo={}", DSSUtils.encodeHexString(derEncodedDigestEncryptionAlgo)); LOG.debug("getSignedFields EncryptedDigest={}", DSSUtils.encodeHexString(derEncodedEncryptedDigest)); } byteArrayOutputStream.write(derEncodedVersion); byteArrayOutputStream.write(derEncodedSid); byteArrayOutputStream.write(derEncodedDigestAlgo); byteArrayOutputStream.write(derEncodedSignedAttributes); byteArrayOutputStream.write(derEncodedDigestEncryptionAlgo); byteArrayOutputStream.write(derEncodedEncryptedDigest); return byteArrayOutputStream.toByteArray(); } catch (IOException e) { throw new DSSException(e); } } }