org.digidoc4j.impl.bdoc.BDocSignatureBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.digidoc4j.impl.bdoc.BDocSignatureBuilder.java

Source

/* DigiDoc4J library
*
* This software is released under either the GNU Library General Public
* License (see LICENSE.LGPL).
*
* Note that the only valid version of the LGPL license as far as this
* project is concerned is the original GNU Library General Public License
* Version 2.1, February 1999
*/

package org.digidoc4j.impl.bdoc;

import static eu.europa.esig.dss.DigestAlgorithm.SHA256;
import static eu.europa.esig.dss.SignatureLevel.XAdES_BASELINE_B;
import static eu.europa.esig.dss.SignatureLevel.XAdES_BASELINE_LT;
import static eu.europa.esig.dss.SignatureLevel.XAdES_BASELINE_LTA;
import static org.apache.commons.codec.binary.Base64.decodeBase64;
import static org.apache.commons.lang.StringUtils.isEmpty;
import static org.digidoc4j.impl.bdoc.ocsp.OcspSourceBuilder.anOcspSource;

import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.digidoc4j.Configuration;
import org.digidoc4j.DataFile;
import org.digidoc4j.DataToSign;
import org.digidoc4j.DigestAlgorithm;
import org.digidoc4j.EncryptionAlgorithm;
import org.digidoc4j.Signature;
import org.digidoc4j.SignatureBuilder;
import org.digidoc4j.SignatureProfile;
import org.digidoc4j.exceptions.ContainerWithoutFilesException;
import org.digidoc4j.exceptions.InvalidSignatureException;
import org.digidoc4j.exceptions.OCSPRequestFailedException;
import org.digidoc4j.exceptions.SignerCertificateRequiredException;
import org.digidoc4j.impl.SignatureFinalizer;
import org.digidoc4j.impl.bdoc.asic.DetachedContentCreator;
import org.digidoc4j.impl.bdoc.ocsp.SKOnlineOCSPSource;
import org.digidoc4j.impl.bdoc.xades.XadesSignature;
import org.digidoc4j.impl.bdoc.xades.XadesSigningDssFacade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import eu.europa.esig.dss.DSSDocument;
import eu.europa.esig.dss.DSSUtils;
import eu.europa.esig.dss.InMemoryDocument;
import eu.europa.esig.dss.Policy;
import eu.europa.esig.dss.SignerLocation;
import eu.europa.esig.dss.client.tsp.OnlineTSPSource;

public class BDocSignatureBuilder extends SignatureBuilder implements SignatureFinalizer {

    private final static Logger logger = LoggerFactory.getLogger(BDocSignatureBuilder.class);
    private static final SignatureProfile DEFAULT_SIGNATURE_PROFILE = SignatureProfile.LT;
    private transient XadesSigningDssFacade facade;
    private Date signingDate;

    @Override
    protected Signature invokeSigningProcess() {
        logger.info("Signing BDoc container");
        signatureParameters.setSigningCertificate(signatureToken.getCertificate());
        byte[] dataToSign = getDataToBeSigned();
        byte[] signatureValue = signatureToken.sign(signatureParameters.getDigestAlgorithm(), dataToSign);
        return finalizeSignature(signatureValue);
    }

    @Override
    public DataToSign buildDataToSign() throws SignerCertificateRequiredException, ContainerWithoutFilesException {
        byte[] dataToSign = getDataToBeSigned();
        byte[] digestToSign = calculateDigestToSign(dataToSign);
        return new DataToSign(digestToSign, signatureParameters, this);
    }

    @Override
    public Signature openAdESSignature(byte[] signatureDocument) {
        if (signatureDocument == null) {
            logger.error("Signature cannot be empty");
            throw new InvalidSignatureException();
        }
        InMemoryDocument document = new InMemoryDocument(signatureDocument);
        return createSignature(document);
    }

    @Override
    public Signature finalizeSignature(byte[] signatureValueBytes) {
        logger.info("Finalizing BDoc signature");
        populateParametersForFinalizingSignature(signatureValueBytes);
        Collection<DataFile> dataFilesToSign = getDataFiles();
        validateDataFilesToSign(dataFilesToSign);
        DSSDocument signedDocument = facade.signDocument(signatureValueBytes, dataFilesToSign);
        return createSignature(signedDocument);
    }

    private Signature createSignature(DSSDocument signedDocument) {
        logger.debug("Opening signed document validator");
        Configuration configuration = getConfiguration();
        DetachedContentCreator detachedContentCreator = new DetachedContentCreator().populate(getDataFiles());
        List<DSSDocument> detachedContents = detachedContentCreator.getDetachedContentList();
        BDocSignatureOpener signatureOpener = new BDocSignatureOpener(detachedContents, configuration);
        List<BDocSignature> signatureList = signatureOpener.parse(signedDocument);
        BDocSignature signature = signatureList.get(0); //Only one signature was created
        validateOcspResponse(signature.getOrigin());
        logger.info("Signing BDoc successfully completed");
        return signature;
    }

    private byte[] getDataToBeSigned() {
        logger.info("Getting data to sign");
        initSigningFacade();
        populateSignatureParameters();
        Collection<DataFile> dataFilesToSign = getDataFiles();
        validateDataFilesToSign(dataFilesToSign);
        byte[] dataToSign = facade.getDataToSign(dataFilesToSign);
        String signatureId = facade.getSignatureId();
        signatureParameters.setSignatureId(signatureId);
        return dataToSign;
    }

    private void populateSignatureParameters() {
        setDigestAlgorithm();
        setEncryptionAlgorithm();
        setSignatureProfile();
        setSignerInformation();
        setSignatureId();
        setSignaturePolicy();
        setSigningCertificate();
        setSigningDate();
        setTimeStampProviderSource();
    }

    private void populateParametersForFinalizingSignature(byte[] signatureValueBytes) {
        if (facade == null) {
            initSigningFacade();
            populateSignatureParameters();
        }
        Configuration configuration = getConfiguration();
        facade.setCertificateSource(configuration.getTSL());
        setOcspSource(signatureValueBytes);
    }

    private void initSigningFacade() {
        if (facade == null) {
            facade = new XadesSigningDssFacade();
        }
    }

    private byte[] calculateDigestToSign(byte[] dataToDigest) {
        DigestAlgorithm digestAlgorithm = signatureParameters.getDigestAlgorithm();
        return DSSUtils.digest(digestAlgorithm.getDssDigestAlgorithm(), dataToDigest);
    }

    private Configuration getConfiguration() {
        return ((BDocContainer) container).getConfiguration();
    }

    private List<DataFile> getDataFiles() {
        return container.getDataFiles();
    }

    private void validateOcspResponse(XadesSignature xadesSignature) {
        if (isBaselineSignatureProfile()) {
            return;
        }
        List<BasicOCSPResp> ocspResponses = xadesSignature.getOcspResponses();
        if (ocspResponses == null || ocspResponses.isEmpty()) {
            logger.error("Signature does not contain OCSP response");
            throw new OCSPRequestFailedException();
        }
    }

    private boolean isBaselineSignatureProfile() {
        return signatureParameters.getSignatureProfile() != null
                && (SignatureProfile.B_BES == signatureParameters.getSignatureProfile()
                        || SignatureProfile.B_EPES == signatureParameters.getSignatureProfile());
    }

    private void setOcspSource(byte[] signatureValueBytes) {
        SKOnlineOCSPSource ocspSource = anOcspSource()
                .withSignatureProfile(signatureParameters.getSignatureProfile())
                .withSignatureValue(signatureValueBytes).withConfiguration(getConfiguration()).build();
        facade.setOcspSource(ocspSource);
    }

    private void setTimeStampProviderSource() {
        Configuration configuration = getConfiguration();
        OnlineTSPSource tspSource = new OnlineTSPSource(configuration.getTspSource());
        SkDataLoader dataLoader = SkDataLoader.createTimestampDataLoader(configuration);
        dataLoader.setUserAgentSignatureProfile(signatureParameters.getSignatureProfile());
        tspSource.setDataLoader(dataLoader);
        facade.setTspSource(tspSource);
    }

    private void setDigestAlgorithm() {
        if (signatureParameters.getDigestAlgorithm() == null) {
            signatureParameters.setDigestAlgorithm(DigestAlgorithm.SHA256);
        }
        facade.setSignatureDigestAlgorithm(signatureParameters.getDigestAlgorithm());
    }

    private void setEncryptionAlgorithm() {
        if (signatureParameters.getEncryptionAlgorithm() == EncryptionAlgorithm.ECDSA || isEcdsaCertificate()) {
            logger.debug("Using ECDSA encryption algorithm");
            facade.setEncryptionAlgorithm(eu.europa.esig.dss.EncryptionAlgorithm.ECDSA);
        }
    }

    private boolean isEcdsaCertificate() {
        X509Certificate certificate = signatureParameters.getSigningCertificate();
        String algorithm = certificate.getPublicKey().getAlgorithm();
        return algorithm.equals("EC") || algorithm.equals("ECC");
    }

    private void setSignatureProfile() {
        if (signatureParameters.getSignatureProfile() != null) {
            setSignatureProfile(signatureParameters.getSignatureProfile());
        } else {
            setSignatureProfile(DEFAULT_SIGNATURE_PROFILE);
            signatureParameters.setSignatureProfile(DEFAULT_SIGNATURE_PROFILE);
        }
    }

    private void setSignatureProfile(SignatureProfile profile) {
        switch (profile) {
        case B_BES:
            facade.setSignatureLevel(XAdES_BASELINE_B);
            break;
        case B_EPES:
            facade.setSignatureLevel(XAdES_BASELINE_B);
            break;
        case LTA:
            facade.setSignatureLevel(XAdES_BASELINE_LTA);
            break;
        default:
            facade.setSignatureLevel(XAdES_BASELINE_LT);
        }
    }

    private void setSignaturePolicy() {
        if (isTimeMarkProfile() || isEpesProfile()) {
            Policy signaturePolicy = createBDocSignaturePolicy();
            facade.setSignaturePolicy(signaturePolicy);
        }
    }

    public static Policy createBDocSignaturePolicy() {
        Policy signaturePolicy = new Policy();
        signaturePolicy.setId("urn:oid:1.3.6.1.4.1.10015.1000.3.2.1");
        signaturePolicy.setDigestValue(decodeBase64("3Tl1oILSvOAWomdI9VeWV6IA/32eSXRUri9kPEz1IVs="));
        signaturePolicy.setDigestAlgorithm(SHA256);
        signaturePolicy.setSpuri("https://www.sk.ee/repository/bdoc-spec21.pdf");
        return signaturePolicy;
    }

    private void setSignatureId() {
        if (StringUtils.isNotBlank(signatureParameters.getSignatureId())) {
            facade.setSignatureId(signatureParameters.getSignatureId());
        }
    }

    private void setSignerInformation() {
        logger.debug("Adding signer information");
        List<String> signerRoles = signatureParameters.getRoles();
        if (!(isEmpty(signatureParameters.getCity()) && isEmpty(signatureParameters.getStateOrProvince())
                && isEmpty(signatureParameters.getPostalCode()) && isEmpty(signatureParameters.getCountry()))) {

            SignerLocation signerLocation = new SignerLocation();

            if (!isEmpty(signatureParameters.getCity()))
                signerLocation.setLocality(signatureParameters.getCity());
            if (!isEmpty(signatureParameters.getStateOrProvince()))
                signerLocation.setStateOrProvince(signatureParameters.getStateOrProvince());
            if (!isEmpty(signatureParameters.getPostalCode()))
                signerLocation.setPostalCode(signatureParameters.getPostalCode());
            if (!isEmpty(signatureParameters.getCountry()))
                signerLocation.setCountry(signatureParameters.getCountry());
            facade.setSignerLocation(signerLocation);
        }
        facade.setSignerRoles(signerRoles);
    }

    private void setSigningCertificate() {
        X509Certificate signingCert = signatureParameters.getSigningCertificate();
        facade.setSigningCertificate(signingCert);
    }

    private void setSigningDate() {
        if (signingDate == null) {
            signingDate = new Date();
        }
        facade.setSigningDate(signingDate);
        logger.debug("Signing date is going to be " + signingDate);
    }

    private void validateDataFilesToSign(Collection<DataFile> dataFilesToSign) {
        if (dataFilesToSign.isEmpty()) {
            logger.error("Container does not contain any data files");
            throw new ContainerWithoutFilesException();
        }
    }

    private boolean isTimeMarkProfile() {
        if (signatureParameters.getSignatureProfile() == null) {
            return false;
        }
        return signatureParameters.getSignatureProfile() == SignatureProfile.LT_TM;
    }

    private boolean isEpesProfile() {
        if (signatureParameters.getSignatureProfile() != null) {
            return signatureParameters.getSignatureProfile() == SignatureProfile.B_EPES;
        }
        return false;
    }
}