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

se.idsec.signservice.security.sign.pdf.utils.PDFBoxSignatureUtils Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2019-2024 IDsec Solutions AB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package se.idsec.signservice.security.sign.pdf.utils;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OutputStream;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERPrintableString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DERUTF8String;
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.SignerInfo;
import org.bouncycastle.asn1.ess.ESSCertID;
import org.bouncycastle.asn1.ess.ESSCertIDv2;
import org.bouncycastle.asn1.ess.SigningCertificate;
import org.bouncycastle.asn1.ess.SigningCertificateV2;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.IssuerSerial;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import se.idsec.signservice.security.certificate.CertificateUtils;
import se.idsec.signservice.security.sign.pdf.configuration.PDFObjectIdentifiers;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

/**
 * Static utilities for signed PDF documents.
 *
 * @author Martin Lindström ([email protected])
 * @author Stefan Santesson ([email protected])
 */
public class PDFBoxSignatureUtils {

  /**
   * This method extracts signed attribute data from a CMS signature
   *
   * @param signedData CMSSignedData object holding signature data
   * @return The signed attributes of a PDF signature
   * @throws CMSException If the provided input has no signed attribute data
   */
  public static byte[] getCmsSignedAttributes(final CMSSignedData signedData) throws CMSException {
    byte[] cmsSignedAttributes = null;
    try {
      if (signedData.getSignerInfos() != null || signedData.getSignerInfos().size() > 0) {
        cmsSignedAttributes = signedData.getSignerInfos().iterator().next().getEncodedSignedAttributes();
      }
    }
    catch (final IOException e) {
      throw new CMSException("No CMS signed attributes are available", e);
    }
    if (cmsSignedAttributes != null) {
      return cmsSignedAttributes;
    }
    else {
      throw new CMSException("No CMS signed attributes are available");
    }
  }

  /**
   * This method extracts signed attribute data from a CMS signature.
   *
   * @param contentInfoBytes the CMS Content info bytes holding CMS SignedData content
   * @return The signed attributes of a PDF signature
   * @throws CMSException If the provided input has no signed attribute data
   */
  public static byte[] getCmsSignedAttributes(final byte[] contentInfoBytes) throws CMSException {
    try {
      final ContentInfo contentInfo = ContentInfo.getInstance(contentInfoBytes);
      final ASN1ObjectIdentifier contentType = contentInfo.getContentType();
      if (!contentType.getId().equals(PDFObjectIdentifiers.ID_PKCS7_SIGNED_DATA)) {
        throw new IOException("No SignedData present in input");
      }
      final SignedData signedData = SignedData.getInstance(contentInfo.getContent());
      final SignerInfo signerInfo = SignerInfo.getInstance(signedData.getSignerInfos().getObjectAt(0));
      return signerInfo.getAuthenticatedAttributes().getEncoded("DER");
    }
    catch (final IllegalArgumentException | NullPointerException | IOException e) {
      throw new CMSException("No CMS signed attributes are available", e);
    }
  }

  /**
   * A method that updates the PDF SignedData object (Actually a CMS ContentInfo) with a new signature, certificates and
   * SignedAttributes obtained from an external signing service.
   *
   * @param cmsSignedData Input CMS SignedData
   * @param newTbsBytes The new signed attributes bytes signed by the new signature
   * @param newSigValue The new signature value
   * @param chain The new certificate chain
   * @return The bytes of an updated PDF signature (Encoded Content info)
   * @throws CMSException for errors
   */
  public static byte[] updatePdfPKCS7(final byte[] cmsSignedData, final byte[] newTbsBytes,
      final byte[] newSigValue, final List chain) throws CMSException {

    try {
      //
      // Basic checks to make sure it's a PKCS#7 SignedData Object
      //
      final ASN1Primitive pkcs7;
      try (final ASN1InputStream din = new ASN1InputStream(new ByteArrayInputStream(cmsSignedData))) {
        pkcs7 = din.readObject();
      }
      catch (final IOException e) {
        throw new CMSException("Illegal PKCS7");
      }

      if (!(pkcs7 instanceof final ASN1Sequence signedData)) {
        throw new CMSException("Illegal PKCS7");
      }
      final ASN1ObjectIdentifier objId = (ASN1ObjectIdentifier) signedData.getObjectAt(0);
      if (!PDFObjectIdentifiers.ID_PKCS7_SIGNED_DATA.equals(objId.getId())) {
        throw new CMSException("No SignedData available");
      }

      // Add Signed data content type to new PKCS7
      final ASN1EncodableVector npkcs7 = new ASN1EncodableVector();
      npkcs7.add(objId);

      /*
       * SignedData ::= SEQUENCE { version CMSVersion, digestAlgorithms DigestAlgorithmIdentifiers, encapContentInfo
       * EncapsulatedContentInfo, certificates [0] IMPLICIT CertificateSet OPTIONAL, crls [1] IMPLICIT
       * RevocationInfoChoices OPTIONAL, signerInfos SignerInfos }
       */

      // Get the SignedData sequence
      final ASN1Sequence signedDataSeq = (ASN1Sequence) ((ASN1TaggedObject) signedData.getObjectAt(1)).getBaseObject();
      int sdObjCount = 0;

      final ASN1EncodableVector nsd = new ASN1EncodableVector();

      // The version
      nsd.add(signedDataSeq.getObjectAt(sdObjCount++));

      // The digestAlgorithms
      nsd.add(signedDataSeq.getObjectAt(sdObjCount++));

      // The possible ecapsulated content info
      nsd.add(signedDataSeq.getObjectAt(sdObjCount++));

      // The certificates. The certs are taken from the input parameters to the method
      final ASN1Encodable[] newCerts = new ASN1Encodable[chain.size()];
      for (int i = 0; i < chain.size(); i++) {
        try (final ASN1InputStream cin = new ASN1InputStream(new ByteArrayInputStream(chain.get(i).getEncoded()))) {
          newCerts[i] = cin.readObject();
        }
      }
      nsd.add(new DERTaggedObject(false, 0, new DERSet(newCerts)));

      // Step counter past tagged objects
      while (signedDataSeq.getObjectAt(sdObjCount) instanceof ASN1TaggedObject) {
        ++sdObjCount;
      }

      // SignerInfos is the next object in the sequence of Signed Data (first untagged after certs)
      final ASN1Set signerInfos = (ASN1Set) signedDataSeq.getObjectAt(sdObjCount);
      if (signerInfos.size() != 1) {
        throw new CMSException("Unsupported multiple signer infos");
      }
      final ASN1Sequence signerInfo = (ASN1Sequence) signerInfos.getObjectAt(0);
      int siCounter = 0;

      // SignerInfo sequence
      //
      // 0 - CMSVersion
      // 1 - SignerIdentifier (CHOICE IssuerAndSerialNumber SEQUENCE)
      // 2 - DigestAglorithmIdentifier
      // 3 - [0] IMPLICIT SignedAttributes SET
      // 3 - Signature AlgorithmIdentifier
      // 4 - Signature Value OCTET STRING
      // 5 - [1] IMPLICIT UnsignedAttributes
      //

      final ASN1EncodableVector nsi = new ASN1EncodableVector();

      // version
      nsi.add(signerInfo.getObjectAt(siCounter++));

      // signing certificate issuer and serial number
      final Certificate sigCert = chain.get(0);
      final ASN1EncodableVector issuerAndSerial = PDFBoxSignatureUtils.getIssuerAndSerial(sigCert);
      nsi.add(new DERSequence(issuerAndSerial));
      siCounter++;

      // Digest AlgorithmIdentifier
      nsi.add(signerInfo.getObjectAt(siCounter++));

      // Add signed attributes from signature service
      try (final ASN1InputStream sigAttrIs = new ASN1InputStream(newTbsBytes)) {
        nsi.add(new DERTaggedObject(false, 0, sigAttrIs.readObject()));
      }

      // Step counter past tagged objects (because signedAttrs i optional in the input data)
      while (signerInfo.getObjectAt(siCounter) instanceof ASN1TaggedObject) {
        siCounter++;
      }

      // Signature Alg identifier
      nsi.add(signerInfo.getObjectAt(siCounter++));

      // Add new signature value from signing service
      nsi.add(new DEROctetString(newSigValue));
      siCounter++;

      // Add unsigned Attributes if present
      if (signerInfo.size() > siCounter && signerInfo.getObjectAt(siCounter) instanceof ASN1TaggedObject) {
        nsi.add(signerInfo.getObjectAt(siCounter));
      }

      /*
       * Final Assembly
       */
      // Add the SignerInfo sequence to the SignerInfos set and add this to the SignedData sequence
      nsd.add(new DERSet(new DERSequence(nsi)));
      // Add the SignedData sequence as an eplicitly tagged object to the pkcs7 object
      npkcs7.add(new DERTaggedObject(true, 0, new DERSequence(nsd)));

      byte[] pkcs7Bytes;
      try (final ByteArrayOutputStream bout = new ByteArrayOutputStream()) {
        final ASN1OutputStream dout = ASN1OutputStream.create(bout, ASN1Encoding.DER);
        try {
          dout.writeObject(new DERSequence(npkcs7));
          pkcs7Bytes = bout.toByteArray();
        }
        finally {
          dout.close();
        }
      }

      return pkcs7Bytes;
    }
    catch (final IOException | CertificateEncodingException | NullPointerException | IllegalArgumentException e) {
      throw new CMSException("Failed to update PKCS7 - " + e.getMessage(), e);
    }
  }

  /**
   * Internal helper method that constructs an IssuerAndSerial object for SignerInfo based on a signer certificate
   *
   * @param sigCert the certificate
   * @return an ASN1EncodableVector holding the IssuerAndSerial ASN.1 sequence.
   * @throws CertificateEncodingException for errors encoding the certificate
   * @throws IOException for Bouncy castle errors
   */
  private static ASN1EncodableVector getIssuerAndSerial(final Certificate sigCert)
      throws CertificateEncodingException, IOException {

    final ASN1Sequence certSeq;
    try (final ASN1InputStream ain = new ASN1InputStream(sigCert.getEncoded())) {
      certSeq = (ASN1Sequence) ain.readObject();
    }

    final ASN1Sequence tbsSeq = (ASN1Sequence) certSeq.getObjectAt(0);

    int counter = 0;
    while (tbsSeq.getObjectAt(counter) instanceof ASN1TaggedObject) {
      counter++;
    }

    // Get serial and issuer DN
    final ASN1Integer serial = (ASN1Integer) tbsSeq.getObjectAt(counter);
    counter += 2;
    final ASN1Sequence issuerDn = (ASN1Sequence) tbsSeq.getObjectAt(counter);

    // Return the issuer field
    final ASN1EncodableVector issuerAndSerial = new ASN1EncodableVector();
    issuerAndSerial.add(issuerDn);
    issuerAndSerial.add(serial);

    return issuerAndSerial;
  }

  /**
   * Sets the signer name and location from the signer certificate subject DN.
   *
   * @param signature the signature object to be updated
   * @param sigCert the certificate being source of data
   * @throws IOException for errors getting the subject attributes from the certificate
   */
  public static void setSubjectNameAndLocality(final PDSignature signature, final Certificate sigCert)
      throws IOException {

    final Map subjectDnAttributeMap = PDFBoxSignatureUtils.getSubjectAttributes(sigCert);
    signature.setName(PDFBoxSignatureUtils.getName(subjectDnAttributeMap));
    signature.setLocation(PDFBoxSignatureUtils.getLocation(subjectDnAttributeMap));
  }

  /**
   * Gets a map of recognized subject DN attributes.
   *
   * @param cert X.509 certificate
   * @return subject DN attribute map
   * @throws IOException for errors getting the subject attributes from the certificate
   */
  public static Map getSubjectAttributes(final Certificate cert) throws IOException {

    try {
      final ASN1Sequence certSeq;
      try (final ASN1InputStream ain = new ASN1InputStream(cert.getEncoded())) {
        certSeq = (ASN1Sequence) ain.readObject();
      }
      final ASN1Sequence tbsSeq = (ASN1Sequence) certSeq.getObjectAt(0);

      int counter = 0;
      while (tbsSeq.getObjectAt(counter) instanceof ASN1TaggedObject) {
        counter++;
      }
      // Get subject
      final ASN1Sequence subjectDn = (ASN1Sequence) tbsSeq.getObjectAt(counter + 4);

      return PDFBoxSignatureUtils.getSubjectAttributes(subjectDn);
    }
    catch (final CertificateEncodingException e) {
      throw new IOException("Failed to get subject attributes from certificate - " + e.getMessage(), e);
    }
  }

  /**
   * Gets a map of recognized subject DN attributes.
   *
   * @param subjectDn subject DN
   * @return subject DN attribute map
   */
  public static Map getSubjectAttributes(final ASN1Sequence subjectDn) {
    final Map subjectDnAttributeMap = new EnumMap<>(SubjectDnAttribute.class);

    for (final ASN1Encodable asn1Encodable : subjectDn) {
      final ASN1Set rdnSet = (ASN1Set) asn1Encodable;
      for (final ASN1Encodable encodable : rdnSet) {
        final ASN1Sequence rdnSeq = (ASN1Sequence) encodable;
        final ASN1ObjectIdentifier rdnOid = (ASN1ObjectIdentifier) rdnSeq.getObjectAt(0);
        final String oidStr = rdnOid.getId();
        final ASN1Encodable rdnVal = rdnSeq.getObjectAt(1);
        final String rdnValStr = PDFBoxSignatureUtils.getStringValue(rdnVal);
        final SubjectDnAttribute subjectDnAttr = SubjectDnAttribute.getSubjectDnFromOid(oidStr);
        if (subjectDnAttr != SubjectDnAttribute.unknown) {
          subjectDnAttributeMap.put(subjectDnAttr, rdnValStr);
        }
      }
    }

    return subjectDnAttributeMap;
  }

  /**
   * Gets the RSA PKCS#10 digest info.
   *
   * @param digestAlgo digest algorithm
   * @param hashValue the hash value
   * @return the digest info
   * @throws IOException for errors
   */
  public static byte[] getRSAPkcs1DigestInfo(final AlgorithmIdentifier digestAlgo, final byte[] hashValue)
      throws IOException {
    final ASN1EncodableVector digestInfoSeq = new ASN1EncodableVector();
    digestInfoSeq.add(digestAlgo);
    digestInfoSeq.add(new DEROctetString(hashValue));

    try (final ByteArrayOutputStream bout = new ByteArrayOutputStream()) {
      final ASN1OutputStream dout = ASN1OutputStream.create(bout, ASN1Encoding.DER);
      try {
        dout.writeObject(new DERSequence(digestInfoSeq));
        return bout.toByteArray();
      }
      finally {
        dout.close();
      }
    }
  }

  private static String getStringValue(final ASN1Encodable rdnVal) {
    if (rdnVal instanceof final DERUTF8String utf8Str) {
      return utf8Str.getString();
    }
    if (rdnVal instanceof final DERPrintableString str) {
      return str.getString();
    }
    return rdnVal.toString();
  }

  private static String getName(final Map subjectDnAttributeMap) {

    final String commonName = subjectDnAttributeMap.getOrDefault(SubjectDnAttribute.cn, null);

    if (commonName != null) {
      return commonName;
    }

    final String surname = subjectDnAttributeMap.getOrDefault(SubjectDnAttribute.surname, null);

    final String givenName = subjectDnAttributeMap.getOrDefault(SubjectDnAttribute.givenName, null);

    if (surname != null && givenName != null) {
      return givenName + " " + surname;
    }

    if (givenName != null) {
      return givenName;
    }

    if (surname != null) {
      return surname;
    }

    return "unknown";
  }

  private static String getLocation(final Map subjectDnAttributeMap) {

    final String country = subjectDnAttributeMap.getOrDefault(SubjectDnAttribute.country, null);

    final String locality = subjectDnAttributeMap.getOrDefault(SubjectDnAttribute.locality, null);

    if (country != null && locality != null) {
      return locality + ", " + country;
    }
    if (country != null) {
      return country;
    }
    if (locality != null) {
      return locality;
    }

    return "unknown";
  }

  public static DefaultSignedAttributeTableGenerator getPadesSignerInfoGenerator(
      final Certificate signerCert, final ASN1ObjectIdentifier digestAlgo, final boolean includeIssuerSerial)
      throws CertificateException, NoSuchAlgorithmException {

    final ASN1EncodableVector signedCertAttr = PDFBoxSignatureUtils.getSignedCertAttr(
        digestAlgo, CertificateUtils.decodeCertificate(signerCert.getEncoded()), includeIssuerSerial);
    final ASN1EncodableVector v = new ASN1EncodableVector();
    v.add(new DERSequence(signedCertAttr));

    return new DefaultSignedAttributeTableGenerator(new AttributeTable(v));
  }

  public static ASN1EncodableVector getSignedCertAttr(
      final ASN1ObjectIdentifier digestAlgo, final X509Certificate certificate, final boolean includeIssuerSerial)
      throws NoSuchAlgorithmException, CertificateException {

    try {
      final GeneralNames generalNames = new GeneralNames(
          new GeneralName(new X509CertificateHolder(certificate.getEncoded()).getIssuer()));
      final BigInteger serialNumber = certificate.getSerialNumber();
      final IssuerSerial issuerSerial = new IssuerSerial(generalNames, serialNumber);

      final ASN1EncodableVector signedCert = new ASN1EncodableVector();

      final boolean essSigCertV2;
      final ASN1ObjectIdentifier signedCertOid;

      if (digestAlgo.equals(CMSAlgorithm.SHA1)) {
        signedCertOid = new ASN1ObjectIdentifier(PDFObjectIdentifiers.ID_AA_SIGNING_CERTIFICATE_V1);
        essSigCertV2 = false;
      }
      else {
        signedCertOid = new ASN1ObjectIdentifier(PDFObjectIdentifiers.ID_AA_SIGNING_CERTIFICATE_V2);
        essSigCertV2 = true;
      }

      final MessageDigest md = MessageDigest.getInstance(digestAlgo.getId(), BouncyCastleProvider.PROVIDER_NAME);
      md.update(certificate.getEncoded());
      final byte[] certHash = md.digest();

      signedCert.add(signedCertOid);

      final ASN1EncodableVector attrValSet = new ASN1EncodableVector();
      final ASN1EncodableVector signingCertObjSeq = new ASN1EncodableVector();
      final ASN1EncodableVector essCertV2Seq = new ASN1EncodableVector();
      final ASN1EncodableVector certSeq = new ASN1EncodableVector();
      final ASN1EncodableVector algoSeq = new ASN1EncodableVector();
      algoSeq.add(digestAlgo);
      algoSeq.add(DERNull.INSTANCE);
      if (essSigCertV2) {
        certSeq.add(new DERSequence(algoSeq));
      }
      // Add cert hash
      certSeq.add(new DEROctetString(certHash));
      if (includeIssuerSerial) {
        certSeq.add(issuerSerial);
      }

      // Finalize assembly
      essCertV2Seq.add(new DERSequence(certSeq));
      signingCertObjSeq.add(new DERSequence(essCertV2Seq));
      attrValSet.add(new DERSequence(signingCertObjSeq));
      signedCert.add(new DERSet(attrValSet));

      return signedCert;
    }
    catch (final NoSuchProviderException e) {
      throw new SecurityException("The BC provider is not installed", e);
    }
    catch (final IOException e) {
      throw new CertificateException("Failed to encode certificate - " + e.getMessage(), e);
    }
  }

  public static byte[] removeSignedAttr(final byte[] signedAttrBytes, final ASN1ObjectIdentifier[] attrOid)
      throws IOException {

    final ASN1Set inAttrSet;
    try (final ASN1InputStream ais = new ASN1InputStream(signedAttrBytes)) {
      inAttrSet = ASN1Set.getInstance(ais.readObject());
    }

    final ASN1EncodableVector newSigAttrSet = new ASN1EncodableVector();
    final List attrOidList = Arrays.asList(attrOid);

    for (int i = 0; i < inAttrSet.size(); i++) {
      final Attribute attr = Attribute.getInstance(inAttrSet.getObjectAt(i));
      if (!attrOidList.contains(attr.getAttrType())) {
        newSigAttrSet.add(attr);
      }
    }

    // Der encode the new signed attributes set
    try (final ByteArrayOutputStream bout = new ByteArrayOutputStream()) {
      final ASN1OutputStream dout = ASN1OutputStream.create(bout, ASN1Encoding.DER);
      try {
        dout.writeObject(new DERSet(newSigAttrSet));
        return bout.toByteArray();
      }
      finally {
        dout.close();
      }
    }
  }

  public static SignedCertRef getSignedCertRefAttribute(final byte[] signedAttrBytes) throws IOException {

    final ASN1Set inAttrSet;
    try (final ASN1InputStream ais = new ASN1InputStream(signedAttrBytes)) {
      inAttrSet = ASN1Set.getInstance(ais.readObject());
    }

    for (int i = 0; i < inAttrSet.size(); i++) {
      final Attribute attr = Attribute.getInstance(inAttrSet.getObjectAt(i));

      if (attr.getAttrType().equals(new ASN1ObjectIdentifier(PDFObjectIdentifiers.ID_AA_SIGNING_CERTIFICATE_V2))) {
        final ASN1Encodable[] attributeValues = attr.getAttributeValues();
        final SigningCertificateV2 signingCertificateV2 = SigningCertificateV2.getInstance(attributeValues[0]);
        final ESSCertIDv2[] certsRefs = signingCertificateV2.getCerts();
        final ESSCertIDv2 certsRef = certsRefs[0];
        final AlgorithmIdentifier hashAlgorithm = certsRef.getHashAlgorithm();
        // According to CMS, the hash algorithm is optional and defaults to SHA256
        final ASN1ObjectIdentifier hashAlgoOid =
            hashAlgorithm == null ? NISTObjectIdentifiers.id_sha256 : hashAlgorithm.getAlgorithm();

        return SignedCertRef.builder()
            .hashAlgorithm(hashAlgoOid)
            .signedCertHash(certsRef.getCertHash())
            .build();
      }
      if (attr.getAttrType().equals(new ASN1ObjectIdentifier(PDFObjectIdentifiers.ID_AA_SIGNING_CERTIFICATE_V1))) {
        final ASN1Encodable[] attributeValues = attr.getAttributeValues();
        final SigningCertificate signingCertificate = SigningCertificate.getInstance(attributeValues[0]);
        final ESSCertID[] certsRefs = signingCertificate.getCerts();
        final ESSCertID certsRef = certsRefs[0];
        return SignedCertRef.builder()
            .hashAlgorithm(OIWObjectIdentifiers.idSHA1)
            .signedCertHash(certsRef.getCertHash())
            .build();
      }
    }
    return null;
  }

  @Data
  @AllArgsConstructor
  @NoArgsConstructor
  @Builder
  public static class SignedCertRef {
    private byte[] signedCertHash;
    private ASN1ObjectIdentifier hashAlgorithm;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy