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

se.swedenconnect.sigval.pdf.svt.PDFSVTSigValClaimsIssuer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2020-2022. Sweden Connect
 *
 * 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.swedenconnect.sigval.pdf.svt;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.codec.binary.Base64;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.cms.SignedData;
import org.bouncycastle.asn1.cms.SignerInfo;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;

import se.swedenconnect.sigval.commons.data.SignedDocumentValidationResult;
import se.swedenconnect.sigval.commons.svt.AbstractSVTSigValClaimsIssuer;
import se.swedenconnect.sigval.pdf.data.ExtendedPdfSigValResult;
import se.swedenconnect.sigval.pdf.verify.ExtendedPDFSignatureValidator;
import se.swedenconnect.sigval.svt.algorithms.SVTAlgoRegistry;
import se.swedenconnect.sigval.svt.claims.SVTProfile;
import se.swedenconnect.sigval.svt.claims.SigReferenceClaims;
import se.swedenconnect.sigval.svt.claims.SignatureClaims;
import se.swedenconnect.sigval.svt.claims.SignedDataClaims;

/**
 * Representation of a SVT claims issuer.
 *
 * @author Martin Lindström ([email protected])
 * @author Stefan Santesson ([email protected])
 */
public class PDFSVTSigValClaimsIssuer extends AbstractSVTSigValClaimsIssuer {

  private final ExtendedPDFSignatureValidator signatureVerifier;

  /**
   * Constructor for the PDF SVT claims issuer. This class is not thread safe and an instance of this issuer must be
   * created for each instance of SVT creation.
   *
   * @param algorithm
   *          the algorithm used to sign the SVT
   * @param privateKey
   *          the private key used to sign the SVT
   * @param certificates
   *          certificates for validating the signature on the SVT
   * @param signatureVerifier
   *          signature verifier used to validate the signature on the PDF document
   * @throws JOSEException
   *           on JWT errors
   * @throws NoSuchAlgorithmException
   *           if the algorithm is not supported
   */
  public PDFSVTSigValClaimsIssuer(JWSAlgorithm algorithm, Object privateKey, List certificates,
      ExtendedPDFSignatureValidator signatureVerifier)
      throws JOSEException, NoSuchAlgorithmException {
    super(algorithm, privateKey, certificates);
    this.signatureVerifier = signatureVerifier;
  }

  /**
   * This method is called from within the SVT Issuer to perform signature validation and to collect the signature
   * validation claims as a result of this validation process
   *
   * @param signedDocument
   *          the signed document to validate
   * @throws IOException
   *           on error
   */
  @Override
  protected List verify(byte[] signedDocument, String hashAlgoUri) throws IOException, SignatureException {
    SignedDocumentValidationResult pdfSigVerifyResultDocument = signatureVerifier.extendedResultValidation(
      signedDocument);

    if (pdfSigVerifyResultDocument.getSignatureCount() < 1) {
      return new ArrayList<>();
    }
    // Loop through the signature validation results
    List resultList = pdfSigVerifyResultDocument.getSignatureValidationResults();
    List claimsResultsList = new ArrayList<>();

    for (ExtendedPdfSigValResult sigResult : resultList) {
      try {
        SignatureClaims claimsData = SignatureClaims.builder()
          .sig_ref(getSigRefData(sigResult.getSignedData(), hashAlgoUri))
          .sig_val(getSignaturePolicyValidations(sigResult))
          .sig_data_ref(getDocRefHashes(sigResult, signedDocument, hashAlgoUri))
          .time_val(
            sigResult.getTimeValidationResults()
              .stream()
              .map(timeValidationResult -> extractTimeValClaims(timeValidationResult, hashAlgoUri))
              .filter(timeValidationClaims -> isVerifiedTime(timeValidationClaims))
              .collect(Collectors.toList()))
          .signer_cert_ref(getCertRef(sigResult, hashAlgoUri))
          .build();
        claimsResultsList.add(claimsData);
      }
      catch (Exception e) {
        e.printStackTrace();
      }
    }
    return claimsResultsList;
  }

  /** {@inheritDoc} */
  @Override
  protected SVTProfile getSvtProfile() {
    return SVTProfile.PDF;
  }

  /**
   * Extract signed attributes and signature data and provide hash of both in a signature reference for an SVA claims
   * set.
   *
   * @param contentInfoBytes
   *          CMS Content info from PDF signature (The bytes provided as signature data in a PDF signature dictionary)
   * @return SignatureReference
   * @throws IOException
   *           if the content is not legal data
   */
  private SigReferenceClaims getSigRefData(byte[] contentInfoBytes, String hashAlgoUri) throws IOException, NoSuchAlgorithmException {
    ASN1InputStream asn1Stream = null;
    try {
      asn1Stream = new ASN1InputStream(contentInfoBytes);
      ContentInfo contentInfo = ContentInfo.getInstance(asn1Stream.readObject());
      if (!contentInfo.getContentType().equals(PKCSObjectIdentifiers.signedData)) {
        throw new IOException("Illegal content for PDF signature. Must contain SignedData");
      }
      SignedData signedData = SignedData.getInstance(contentInfo.getContent());
      SignerInfo signerInfo = SignerInfo.getInstance(signedData.getSignerInfos().getObjectAt(0));
      byte[] sigAttrsEncBytes = signerInfo.getAuthenticatedAttributes().getEncoded("DER");
      byte[] signatureBytes = signerInfo.getEncryptedDigest().getOctets();

      return SigReferenceClaims.builder()
        .sig_hash(getB64Hash(signatureBytes, hashAlgoUri))
        .sb_hash(getB64Hash(sigAttrsEncBytes, hashAlgoUri))
        .build();
    }
    finally {
      if (asn1Stream != null) {
        try {
          asn1Stream.close();
        }
        catch (IOException e) {
        }
      }
    }
  }

  private List getDocRefHashes(ExtendedPdfSigValResult sigVerifyResult, byte[] signedDocument, String hashAlgoUri)
      throws IOException, NoSuchAlgorithmException {
    return Arrays.asList(calculateDocRefHash(sigVerifyResult.getPdfSignature(), signedDocument, hashAlgoUri));
  }

  /**
   * Performs the basic calculation of the hash of signed data in a PDF document, signed by a particular signature
   *
   * @param sig
   *          Signature
   * @param signedDocument
   *          signed document
   * @param hashAlgoUri
   *          hash algorithm URI identifier
   * @return Signed document data hashes
   * @throws IOException
   *           parsing errors
   * @throws NoSuchAlgorithmException
   *           unsupported algorithm
   */
  protected SignedDataClaims calculateDocRefHash(PDSignature sig, byte[] signedDocument, String hashAlgoUri) throws IOException,
      NoSuchAlgorithmException {
    MessageDigest md = SVTAlgoRegistry.getMessageDigestInstance(hashAlgoUri);
    byte[] digest = md.digest(sig.getSignedContent(signedDocument));
    int[] byteRange = sig.getByteRange();
    String ref = byteRange[0] + " " + byteRange[1] + " " + byteRange[2] + " " + byteRange[3];

    return SignedDataClaims.builder()
      .ref(ref)
      .hash(Base64.encodeBase64String(digest))
      .build();
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy