All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itextpdf.signatures.cms.CMSContainer Maven / Gradle / Ivy

There is a newer version: 9.0.0
Show newest version
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2024 Apryse Group NV
    Authors: Apryse Software.

    This program is offered under a commercial and under the AGPL license.
    For commercial licensing, contact us at https://itextpdf.com/sales.  For AGPL licensing, see below.

    AGPL licensing:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see .
 */
package com.itextpdf.signatures.cms;

import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator;
import com.itextpdf.commons.bouncycastle.IBouncyCastleFactory;
import com.itextpdf.commons.bouncycastle.asn1.IASN1Encodable;
import com.itextpdf.commons.bouncycastle.asn1.IASN1EncodableVector;
import com.itextpdf.commons.bouncycastle.asn1.IASN1InputStream;
import com.itextpdf.commons.bouncycastle.asn1.IASN1Sequence;
import com.itextpdf.commons.bouncycastle.asn1.IASN1Set;
import com.itextpdf.commons.bouncycastle.asn1.IDERSet;
import com.itextpdf.commons.bouncycastle.asn1.IASN1TaggedObject;
import com.itextpdf.commons.bouncycastle.asn1.ocsp.IBasicOCSPResponse;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.signatures.CertificateUtil;
import com.itextpdf.signatures.SecurityIDs;
import com.itextpdf.signatures.exceptions.SignExceptionMessageConstant;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CRL;
import java.security.cert.CRLException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

/**
 * The CMS container which represents SignedData structure from
 * rfc5652 Cryptographic Message Syntax (CMS)
 */
public class CMSContainer {

    private static final IBouncyCastleFactory BC_FACTORY = BouncyCastleFactoryCreator.getFactory();

    /**
     * Collection to store revocation info other than OCSP and CRL responses, e.g. SCVP Request and Response.
     */
    final Collection otherRevocationInfo = new ArrayList<>();

    /**
     * Optional.
     *
     * 

* It is a collection of CRL revocation status information. */ private final Collection crls = new ArrayList<>(); /** * Optional. * *

* It is a collection of CRL revocation status information. */ private final Collection ocsps = new ArrayList<>(); /** * This represents the signed content. * In the case of a signed PDF document this will of type data with no content. */ private EncapsulatedContentInfo encapContentInfo = new EncapsulatedContentInfo(); /** * Optional. * *

* It is intended to add all certificates to be able to validate the entire chain. */ private Collection certificates = new ArrayList<>(); /** * This class only supports one signer per signature field. */ private SignerInfo signerInfo = new SignerInfo(); /** * Creates an empty SignedData structure. */ public CMSContainer() { // Empty constructor. } /** * Creates a SignedData structure from a serialized ASN1 structure. * * @param encodedCMSdata the serialized CMS container * * @throws IOException if issues occur during ASN1 objects creation. * @throws CertificateException if issues occur processing the embedded certificates. * @throws CRLException if CRL encoding error occurs. */ public CMSContainer(byte[] encodedCMSdata) throws IOException, CertificateException, CRLException { try (IASN1InputStream is = BC_FACTORY.createASN1InputStream(new ByteArrayInputStream(encodedCMSdata))) { IASN1Sequence contentInfo = BC_FACTORY.createASN1Sequence(is.readObject()); IASN1Sequence signedData = BC_FACTORY.createASN1Sequence( BC_FACTORY.createASN1TaggedObject(contentInfo.getObjectAt(1)).getObject()); // The digest algorithm is retrieved from SignerInfo later on, here we just validate // that there is exactly 1 digest algorithm. IASN1Set digestAlgorithms = BC_FACTORY.createASN1Set(signedData.getObjectAt(1)); if (digestAlgorithms.size() > 1) { throw new PdfException(SignExceptionMessageConstant.CMS_ONLY_ONE_SIGNER_ALLOWED); } IASN1Sequence lencapContentInfo = BC_FACTORY.createASN1Sequence(signedData.getObjectAt(2)); encapContentInfo = new EncapsulatedContentInfo(lencapContentInfo); processCertificates(signedData); int next = 4; IASN1TaggedObject taggedObj = BC_FACTORY.createASN1TaggedObject(signedData.getObjectAt(next)); if (taggedObj != null) { ++next; CertificateUtil.retrieveRevocationInfoFromSignedData(taggedObj, this.crls, this.ocsps, this.otherRevocationInfo); } IASN1Set signerInfosS = BC_FACTORY.createASN1Set(signedData.getObjectAt(next)); if (signerInfosS.size() != 1) { throw new PdfException(SignExceptionMessageConstant.CMS_ONLY_ONE_SIGNER_ALLOWED); } signerInfo = new SignerInfo(signerInfosS.getObjectAt(0), certificates); } catch (NullPointerException npe) { throw new PdfException(SignExceptionMessageConstant.CMS_INVALID_CONTAINER_STRUCTURE, npe); } } /** * This class only supports one signer per signature field. * * @param signerInfo the singerInfo */ public void setSignerInfo(SignerInfo signerInfo) { this.signerInfo = signerInfo; } /** * This class only supports one signer per signature field. * * @return the singerInfo */ public SignerInfo getSignerInfo() { return signerInfo; } /** * When all fields except for signer.signedAttributes.digest and signer.signature are completed * it is possible to calculate the eventual size of the signature by serializing except for the signature * (that depends on the digest and cypher but is set at 1024 bytes) and later added unsigned attributes like * timestamps. * * @return the estimated size of the complete CMS container before signature is added, size for the signature is * added, size for other attributes like timestamps is not. * * @throws CertificateEncodingException if an encoding error occurs in {@link X509Certificate}. * @throws IOException if an I/O error occurs. * @throws CRLException if CRL encoding error occurs. */ public long getSizeEstimation() throws CertificateEncodingException, IOException, CRLException { byte[] result = serialize(true); return result.length; } /** * Only version 1 is supported by this class. * * @return 1 as CMSversion */ public int getCmsVersion() { return 1; } /** * The digest algorithm OID and parameters used by the signer. * This class only supports one signer for use in pdf signatures, so only one digest algorithm is supported. * *

* This field is set when adding the signerInfo. * * @return {@link AlgorithmIdentifier} digest algorithm. */ public AlgorithmIdentifier getDigestAlgorithm() { if (signerInfo == null) { return null; } return signerInfo.getDigestAlgorithm(); } /** * This represents the signed content. * In the case of a signed PDF document this will be of type data with no content. * * @return a representation of the data to be signed. */ public EncapsulatedContentInfo getEncapContentInfo() { return encapContentInfo; } /** * This represents the signed content. * In the case of a signed PDF document this will be of type data with no content. * Defaults to 1.2.840.113549.1.7.1 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-7(7) id-data(1)} * * @param encapContentInfo a representation of the data to be signed. */ public void setEncapContentInfo(EncapsulatedContentInfo encapContentInfo) { this.encapContentInfo = encapContentInfo; } /** * Adds a certificate. * * @param cert the certificate to be added */ public void addCertificate(X509Certificate cert) { certificates.add(cert); } /** * Adds a set of certificates. * * @param certs the certificates to be added */ public void addCertificates(X509Certificate[] certs) { certificates = Arrays.asList(certs); } /** * Retrieves a copy of the list of certificates. * * @return the list of certificates to be used for signing and certificate validation */ public Collection getCertificates() { return Collections.unmodifiableCollection(certificates); } /** * Retrieves a copy of the list of CRLs. * * @return the list of CRL revocation info. */ public Collection getCrls() { return Collections.unmodifiableCollection(crls); } /** * Adds a CRL response to the CMS container. * * @param crl the CRL response to be added. */ public void addCrl(CRL crl) { crls.add(crl); } /** * Retrieves a copy of the list of OCSPs. * * @return the list of OCSP revocation info. */ public Collection getOcsps() { return Collections.unmodifiableCollection(ocsps); } /** * Adds an OCSP response to the CMS container. * * @param ocspResponse the OCSP response to be added. */ public void addOcsp(IBasicOCSPResponse ocspResponse) { ocsps.add(ocspResponse); } /** * Sets the Signed Attributes of the signer info to this serialized version. * The signed attributes will become read-only. * * @param signedAttributesData the serialized Signed Attributes */ public void setSerializedSignedAttributes(byte[] signedAttributesData) { signerInfo.setSerializedSignedAttributes(signedAttributesData); } /** * Retrieves the encoded signed attributes of the signer info. * This makes the signed attributes read only. * * @return the encoded signed attributes of the signer info. * * @throws IOException if issues occur during ASN1 objects creation. */ public byte[] getSerializedSignedAttributes() throws IOException { if (signerInfo == null) { throw new IllegalStateException(SignExceptionMessageConstant.CMS_SIGNERINFO_NOT_INITIALIZED); } return signerInfo.serializeSignedAttributes(); } /** * Serializes the SignedData structure and makes the signer infos signed attributes read only. * * @return the encoded DignedData structure. * * @throws CertificateEncodingException if errors occur during certificate processing. * @throws IOException if issues occur during ASN1 objects creation. * @throws CRLException if CRL encoding error occurs. */ public byte[] serialize() throws CertificateEncodingException, IOException, CRLException { return serialize(false); } private byte[] serialize(boolean forEstimation) throws CertificateEncodingException, IOException, CRLException { /* ContentInfo SEQUENCE ContentType OBJECT IDENTIFIER (1.2.840.113549.1.7.2) Content [0] SEQUENCE SignedData SEQUENCE version INTEGER digestAlgorithms SET DigestAlgorithmIdentifier SEQUENCE algorithm OBJECT IDENTIFIER parameters ANY encapContentInfo EncapsulatedContentInfo SEQUENCE eContentType ContentType OBJECT IDENTIFIER (1.2.840.113549.1.7.1 data) certificates CertificateSet [0] SET CertificateChoices SEQUENCE tbsCertificate TBSCertificate SEQUENCE crls RevocationInfoChoices [1] SET RevocationInfoChoice CHOICE { crl CertificateList SEQUENCE, other OtherRevocationInfoFormat SEQUENCE otherRevInfoFormat OBJECT IDENTIFIER, otherRevInfo ANY DEFINED BY otherRevInfoFormat (SEQUENCE for OCSP) } signerInfos SignerInfos SET */ IASN1EncodableVector contentInfoV = BC_FACTORY.createASN1EncodableVector(); contentInfoV.add(BC_FACTORY.createASN1ObjectIdentifier(SecurityIDs.ID_PKCS7_SIGNED_DATA)); IASN1EncodableVector singedDataV = BC_FACTORY.createASN1EncodableVector(); singedDataV.add(BC_FACTORY.createASN1Integer(getCmsVersion())); // version IASN1EncodableVector digestAlgorithmsV = BC_FACTORY.createASN1EncodableVector(); digestAlgorithmsV.add(getDigestAlgorithm().getAsASN1Sequence()); singedDataV.add(BC_FACTORY.createDERSet(digestAlgorithmsV)); IASN1EncodableVector encapContentInfoV = BC_FACTORY.createASN1EncodableVector(); encapContentInfoV.add(BC_FACTORY.createASN1ObjectIdentifier(encapContentInfo.getContentType())); if (encapContentInfo.getContent() != null) { encapContentInfoV.add(encapContentInfo.getContent()); } singedDataV.add(BC_FACTORY.createDERSequence(encapContentInfoV)); IASN1EncodableVector certificateSetV = BC_FACTORY.createASN1EncodableVector(); for (X509Certificate cert : certificates) { certificateSetV.add(BC_FACTORY.createASN1Primitive(cert.getEncoded())); } singedDataV.add(BC_FACTORY.createDERTaggedObject(false, 0, BC_FACTORY.createDERSet(certificateSetV))); IDERSet revInfoChoices = CertificateUtil.createRevocationInfoChoices(this.crls, this.ocsps, this.otherRevocationInfo); if (revInfoChoices != null) { singedDataV.add(BC_FACTORY.createDERTaggedObject(false, 1, revInfoChoices)); } IASN1EncodableVector signerInfosV = BC_FACTORY.createASN1EncodableVector(); signerInfosV.add(signerInfo.getAsDerSequence(forEstimation)); singedDataV.add(BC_FACTORY.createDERSet(signerInfosV)); contentInfoV.add(BC_FACTORY.createDERTaggedObject(0, BC_FACTORY.createDERSequence(singedDataV))); return BC_FACTORY.createDERSequence(contentInfoV).getEncoded(); } private void processCertificates(IASN1Sequence signedData) throws CertificateException, IOException { // Certificates are optional according to the specs, but we do require at least the signing certificate. IASN1TaggedObject taggedCertificatesSet = BC_FACTORY.createASN1TaggedObject(signedData.getObjectAt(3)); if (taggedCertificatesSet == null) { throw new PdfException(SignExceptionMessageConstant.CMS_MISSING_CERTIFICATES); } IASN1Set certificatesSet = BC_FACTORY.createASN1Set(taggedCertificatesSet, false); if (certificatesSet.isNull() || certificatesSet.size() == 0) { throw new PdfException(SignExceptionMessageConstant.CMS_MISSING_CERTIFICATES); } for (IASN1Encodable certObj : certificatesSet.toArray()) { try (InputStream cis = new ByteArrayInputStream(certObj.toASN1Primitive(). getEncoded(BC_FACTORY.createASN1Encoding().getDer()))) { certificates.add((X509Certificate) CertificateUtil.generateCertificate(cis)); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy