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

se.idsec.sigval.xml.svt.XMLSVTSigValClaimsIssuer Maven / Gradle / Ivy

/*
 * Copyright (c) 2020. 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.sigval.xml.svt;

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

import org.bouncycastle.util.encoders.Base64;

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

import lombok.Setter;
import se.idsec.sigval.commons.algorithms.DigestAlgorithmRegistry;
import se.idsec.sigval.commons.svt.AbstractSVTSigValClaimsIssuer;
import se.idsec.sigval.svt.claims.PolicyValidationClaims;
import se.idsec.sigval.svt.claims.SVTProfile;
import se.idsec.sigval.svt.claims.SigReferenceClaims;
import se.idsec.sigval.svt.claims.SignatureClaims;
import se.idsec.sigval.svt.claims.SignedDataClaims;
import se.idsec.sigval.svt.claims.ValidationConclusion;
import se.idsec.sigval.xml.data.ExtendedXmlSigvalResult;
import se.idsec.sigval.xml.verify.XMLSignatureElementValidator;
import se.idsec.sigval.xml.xmlstruct.SignatureData;

/**
 * Implementation of the {@link AbstractSVTSigValClaimsIssuer} class for collecting XML claims data from an XML signature
 *
 * @author Martin Lindström ([email protected])
 * @author Stefan Santesson ([email protected])
 */
public class XMLSVTSigValClaimsIssuer extends AbstractSVTSigValClaimsIssuer {

  /** Signature verifier used to validate XML signatures to determine signature validity */
  private final XMLSignatureElementValidator signatureVerifier;

  /** If this is true and signature validation did not provide any policy validation conclusion, then set basic validation level */
  @Setter private boolean defaultBasicValidation = false;

  /**
   * Constructor.
   * 
   * @param algorithm    the algorithm used to sign the SVT as well as selecting the Hash algorithm used to generate SVT hash values
   * @param privateKey   private key used to sign the SVT
   * @param certificates certificates supporting the SVT signature
   * @param signatureVerifier verifier of XML signature elements
   * @throws NoSuchAlgorithmException unsupported algorithm
   * @throws JOSEException JOSE exception
   */
  public XMLSVTSigValClaimsIssuer(JWSAlgorithm algorithm, Object privateKey,
    List certificates, XMLSignatureElementValidator signatureVerifier) throws NoSuchAlgorithmException, JOSEException {
    super(algorithm, privateKey, certificates);
    this.signatureVerifier = signatureVerifier;
  }

  /** {@inheritDoc} */
  @Override protected List verify(XMLSigValInput sigValInput, String hashAlgoUri) throws Exception {

    SignatureData signatureData = sigValInput.getSignatureData();
    Map refDataMap = signatureData.getRefDataMap();
    ExtendedXmlSigvalResult sigResult = signatureVerifier.validateSignature(sigValInput.getSignatureElement(),
      signatureData);

    if (isIssueSVT(sigResult)) {
      SignatureClaims claimsData = SignatureClaims.builder()
        .sig_ref(getSigRefData(signatureData, hashAlgoUri))
        .sig_val(getSignaturePolicyValidations(sigResult))
        .sig_data_ref(getDocRefHashes(refDataMap, hashAlgoUri))
        .time_val(
          sigResult.getTimeValidationResults().stream()
            .map(pdfTimeValidationResult -> pdfTimeValidationResult.getTimeValidationClaims())
            .filter(timeValidationClaims -> isVerifiedTime(timeValidationClaims))
            .collect(Collectors.toList())
        )
        .signer_cert_ref(getCertRef(sigResult, hashAlgoUri))
        .build();

      return Arrays.asList(claimsData);
    }
    // This signature should not be extended with a new SVT token.
    return null;
  }

  /**
   * Test if a new SVT should be issued for a particular signature
   * @param sigResult The result of validating this signature.
   * @return true if an SVT should be issued (if possible) and false if a null result should be enforced.
   */
  private boolean isIssueSVT(ExtendedXmlSigvalResult sigResult) {

    boolean validationPolicyPassed = false;
    List validationPolicyResultList = sigResult.getValidationPolicyResultList();
    if (validationPolicyResultList != null){
      validationPolicyPassed = validationPolicyResultList.stream()
        .filter(policyValidationClaims -> policyValidationClaims.getRes().equals(ValidationConclusion.PASSED))
        .findFirst()
        .isPresent();
    }

    // We only issue SVT tokes if signature validation passed.
    return validationPolicyPassed;
  }

  /**
   * Obtain the document reference hash values
   * @param refDataMap a data map of signed bytes, keyed by the reference URLs pointing to each XML fragment
   * @param hashAlgoUri algorithm URI for the selected hash algorithm
   * @return List of {@link SignedDataClaims}
   * @throws IOException on error parsing data
   * @throws NoSuchAlgorithmException on unrecognized hash algorithm
   */
  private List getDocRefHashes(Map refDataMap, String hashAlgoUri)
    throws IOException, NoSuchAlgorithmException {

    // Go through all XML references and locate the bytes that were hashed by each reference
    // Throw exception if the reference data cannot be located. This implementation only supports internal references
    List signedDataClaimsList = new ArrayList<>();
    Set refSet = refDataMap.keySet();
    for(String ref : refSet){
      byte[] signedBytes = refDataMap.get(ref);
      if (signedBytes == null){
        throw new IOException("Missing referenced data in signed document. Unable to collect signed data references for SVT");
      }
      MessageDigest digest = DigestAlgorithmRegistry.get(hashAlgoUri).getInstance();
      signedDataClaimsList.add(SignedDataClaims.builder()
        .ref(ref)
        .hash(Base64.toBase64String(digest.digest(signedBytes)))
        .build());
    }

    // Idea - Fix function in signature context to make it able to extract all referenced data (as opposed to now)
    // Return a map of signed data, mapped by reference URI. as well as the URI representing the root node in SignatureData.

    return signedDataClaimsList;
  }

  /**
   * Obtain signature reference data, uniquely identifies the target XML signature
   * @param signatureData signature data collected for this signature
   * @param hashAlgoUri algorithm URI for the hash algorithm
   * @return {@link SigReferenceClaims}
   * @throws IOException on error parsing data
   * @throws NoSuchAlgorithmException on unrecognized hash algorithm
   */
  private SigReferenceClaims getSigRefData(SignatureData signatureData, String hashAlgoUri) throws IOException, NoSuchAlgorithmException{
    byte[] signatureBytes = signatureData.getSignatureBytes();
    byte[] signedInfoBytes = signatureData.getSignedInfoBytes();
    if (signatureBytes == null || signedInfoBytes == null) throw new IOException("No signature or signed document bytes available");
    MessageDigest digest = DigestAlgorithmRegistry.get(hashAlgoUri).getInstance();
    return SigReferenceClaims.builder()
      .id(signatureData.getSignature().getId())
      .sb_hash(Base64.toBase64String(digest.digest(signedInfoBytes)))
      .sig_hash(Base64.toBase64String(digest.digest(signatureData.getSignatureBytes())))
      .build();
  }

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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy