eu.europa.esig.dss.asic.signature.ASiCService.java Source code

Java tutorial

Introduction

Here is the source code for eu.europa.esig.dss.asic.signature.ASiCService.java

Source

/**
 * DSS - Digital Signature Services
 * Copyright (C) 2015 European Commission, provided under the CEF programme
 *
 * This file is part of the "DSS - Digital Signature Services" project.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package eu.europa.esig.dss.asic.signature;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

import eu.europa.esig.dss.ASiCNamespaces;
import eu.europa.esig.dss.AbstractSignatureParameters;
import eu.europa.esig.dss.DSSDocument;
import eu.europa.esig.dss.DSSException;
import eu.europa.esig.dss.DSSUtils;
import eu.europa.esig.dss.DSSXMLUtils;
import eu.europa.esig.dss.DigestAlgorithm;
import eu.europa.esig.dss.InMemoryDocument;
import eu.europa.esig.dss.MimeType;
import eu.europa.esig.dss.SignatureForm;
import eu.europa.esig.dss.SignatureLevel;
import eu.europa.esig.dss.SignaturePackaging;
import eu.europa.esig.dss.SignatureValue;
import eu.europa.esig.dss.SigningOperation;
import eu.europa.esig.dss.ToBeSigned;
import eu.europa.esig.dss.asic.ASiCParameters;
import eu.europa.esig.dss.asic.ASiCSignatureParameters;
import eu.europa.esig.dss.asic.validation.ASiCCMSDocumentValidator;
import eu.europa.esig.dss.asic.validation.ASiCContainerValidator;
import eu.europa.esig.dss.cades.CAdESSignatureParameters;
import eu.europa.esig.dss.cades.signature.CAdESService;
import eu.europa.esig.dss.signature.AbstractSignatureService;
import eu.europa.esig.dss.signature.DocumentSignatureService;
import eu.europa.esig.dss.validation.CertificateVerifier;
import eu.europa.esig.dss.validation.DocumentValidator;
import eu.europa.esig.dss.validation.SignedDocumentValidator;
import eu.europa.esig.dss.xades.XAdESSignatureParameters;
import eu.europa.esig.dss.xades.signature.XAdESService;

/**
 * Implementation of {@code DocumentSignatureService} for ASiC-S and -E containers. It allows the creation of containers based on XAdES or CAdES standard.
 *
 */
public class ASiCService extends AbstractSignatureService<ASiCSignatureParameters> {

    private static final Logger LOG = LoggerFactory.getLogger(ASiCService.class);

    private final static String ZIP_ENTRY_DETACHED_FILE = "detached-file";
    private final static String ZIP_ENTRY_MIMETYPE = "mimetype";
    private final static String META_INF = "META-INF/";
    private final static String ZIP_ENTRY_ASICS_METAINF_XADES_SIGNATURE = META_INF + "signatures.xml";
    private final static String ZIP_ENTRY_ASICE_METAINF_XADES_SIGNATURE = META_INF + "signatures001.xml";
    private final static String ZIP_ENTRY_ASICS_METAINF_CADES_SIGNATURE = META_INF + "signature.p7s";
    private final static String ZIP_ENTRY_ASICE_METAINF_CADES_SIGNATURE = META_INF + "signature001.p7s";

    public final static String ASICS_NS = "asic:XAdESSignatures";

    private DocumentSignatureService underlyingASiCService;

    /**
     * This is the constructor to create an instance of the {@code ASiCService}. A certificate verifier must be provided.
     *
     * @param certificateVerifier {@code CertificateVerifier} provides information on the sources to be used in the validation process in the context of a signature.
     */
    public ASiCService(final CertificateVerifier certificateVerifier) {

        super(certificateVerifier);
        LOG.debug("+ ASiCService created");
    }

    @Override
    public ToBeSigned getDataToSign(final DSSDocument toSignDocument, final ASiCSignatureParameters parameters)
            throws DSSException {

        final ASiCParameters asicParameters = parameters.aSiC();

        // toSignDocument can be a simple file or an ASiC container
        final DSSDocument contextToSignDocument = prepare(toSignDocument, parameters);
        parameters.aSiC().setEnclosedSignature(asicParameters.getEnclosedSignature());

        final AbstractSignatureParameters underlyingParameters = getParameters(parameters);
        final DocumentSignatureService underlyingService = getSpecificService(
                parameters.aSiC().getUnderlyingForm());
        return underlyingService.getDataToSign(contextToSignDocument, underlyingParameters);
    }

    /**
     * ETSI TS 102 918 v1.2.1 (2012-02) <br />
     *
     * Contents of Container ( 6.2.2 )
     * </p>
     * <ul>
     * <li>The file extension ".asics" should be used .</li>
     * <li>The root element of each signature content shall be either &lt;asic:XadESSignatures&gt; as specified in clause
     * A.5. Its the recommended format</li>
     * <li>The comment field in the ZIP header may be used to identify the type of the data object within the container.
     * <br />
     * If this field is present, it should be set with "mimetype=" followed by the mime type of the data object held in
     * the signed data object</li>
     * <li>The mimetype file can be used to support operating systems that rely on some content in specific positions in
     * a file.<br />
     * <ul>
     * <li>It has to be the first entry in the archive.</li>
     * <li>It cannot contain "Extra fields".</li>
     * <li>It cannot be compressed or encrypted inside the ZIP file</li>
     * </ul>
     * </li>
     * </ul>
     */
    @Override
    public DSSDocument signDocument(final DSSDocument toSignDocument, final ASiCSignatureParameters parameters,
            SignatureValue signatureValue) throws DSSException {
        try {
            assertSigningDateInCertificateValidityRange(parameters);

            // Signs the toSignDocument first
            final ASiCParameters asicParameters = parameters.aSiC();

            DSSDocument contextToSignDocument = prepare(toSignDocument, parameters);
            parameters.aSiC().setEnclosedSignature(asicParameters.getEnclosedSignature());

            AbstractSignatureParameters underlyingParameters = getParameters(parameters);
            final DocumentSignatureService underlyingService = getSpecificService(
                    parameters.aSiC().getUnderlyingForm());
            final DSSDocument signature = underlyingService.signDocument(contextToSignDocument,
                    underlyingParameters, signatureValue);

            underlyingParameters = getParameters(parameters);
            DSSDocument asicContainer = null;
            final boolean signingContainer = asicParameters.getEnclosedSignature() != null;
            if (signingContainer) {
                asicContainer = toSignDocument;
            }
            if (isAsice(asicParameters) && isCAdESForm(asicParameters)) {
                if (!signingContainer) {
                    contextToSignDocument = toSignDocument;
                } else {
                    contextToSignDocument = parameters.getDetachedContent();
                }
            }
            final InMemoryDocument asicSignature = buildASiCContainer(contextToSignDocument, asicContainer,
                    parameters, signature);
            asicSignature.setName(DSSUtils.getFinalFileName(toSignDocument, SigningOperation.SIGN,
                    parameters.getSignatureLevel()));
            parameters.reinitDeterministicId();
            return asicSignature;

        } catch (IOException e) {
            throw new DSSException(e);
        }

    }

    @Override
    public DSSDocument extendDocument(final DSSDocument toExtendDocument, final ASiCSignatureParameters parameters)
            throws DSSException {
        try {
            final DocumentValidator validator = SignedDocumentValidator.fromDocument(toExtendDocument);
            final DocumentValidator subordinatedValidator = validator.getSubordinatedValidator();
            final DocumentSignatureService specificService = getSpecificService(
                    parameters.aSiC().getUnderlyingForm());
            specificService.setTspSource(tspSource);

            final AbstractSignatureParameters underlyingParameters = getParameters(parameters);
            final DSSDocument detachedContent = parameters.getDetachedContent();
            final DSSDocument detachedContents = getDetachedContents(subordinatedValidator, detachedContent);
            underlyingParameters.setDetachedContent(detachedContents);
            final DSSDocument signature = subordinatedValidator.getDocument();
            final DSSDocument signedDocument = specificService.extendDocument(signature, underlyingParameters);

            final ByteArrayOutputStream output = new ByteArrayOutputStream();
            final ZipOutputStream zipOutputStream = new ZipOutputStream(output);
            final ZipInputStream zipInputStream = new ZipInputStream(toExtendDocument.openStream());
            ZipEntry entry;
            while ((entry = getNextZipEntry(zipInputStream)) != null) {

                final String name = entry.getName();
                final ZipEntry newEntry = new ZipEntry(name);
                if (ASiCContainerValidator.isMimetype(name)) {

                    storeMimetype(parameters.aSiC(), zipOutputStream);
                } else if (ASiCContainerValidator.isXAdES(name) || ASiCContainerValidator.isCAdES(name)) {

                    createZipEntry(zipOutputStream, newEntry);
                    final InputStream inputStream = signedDocument.openStream();
                    IOUtils.copy(inputStream, zipOutputStream);
                    IOUtils.closeQuietly(inputStream);
                } else {

                    createZipEntry(zipOutputStream, newEntry);
                    IOUtils.copy(zipInputStream, zipOutputStream);
                }
            }
            IOUtils.closeQuietly(zipInputStream);
            IOUtils.closeQuietly(zipOutputStream);
            DSSDocument asicSignature = new InMemoryDocument(output.toByteArray(), null,
                    getMimeType(parameters.aSiC().getContainerForm()));
            asicSignature.setName(DSSUtils.getFinalFileName(toExtendDocument, SigningOperation.EXTEND,
                    parameters.getSignatureLevel()));
            return asicSignature;
        } catch (IOException e) {
            throw new DSSException(e);
        }
    }

    private DSSDocument copyDetachedContent(final AbstractSignatureParameters underlyingParameters,
            final DocumentValidator subordinatedValidator) {

        DSSDocument contextToSignDocument = null;
        DSSDocument currentDetachedDocument = null;
        final List<DSSDocument> detachedContents = subordinatedValidator.getDetachedContents();
        for (final DSSDocument detachedDocument : detachedContents) {

            if (contextToSignDocument == null) {
                contextToSignDocument = detachedDocument;
            } else {
                currentDetachedDocument.setNextDocument(detachedDocument);
            }
            currentDetachedDocument = detachedDocument;
        }
        underlyingParameters.setDetachedContent(contextToSignDocument);
        return contextToSignDocument;
    }

    private DocumentValidator getAsicValidator(final DSSDocument toSignDocument) {

        // Check if this is an existing container
        try {

            final DocumentValidator validator = SignedDocumentValidator.fromDocument(toSignDocument);
            if (isAsicValidator(validator)) {

                return validator;
            }
        } catch (Exception e) {
            // do nothing
        }
        return null;
    }

    private InMemoryDocument buildASiCContainer(final DSSDocument toSignDocument, DSSDocument signDocument,
            final ASiCSignatureParameters parameters, final DSSDocument signature) throws IOException {

        ASiCParameters asicParameters = parameters.aSiC();

        final boolean asice = isAsice(asicParameters);
        final boolean cadesForm = isCAdESForm(asicParameters);

        final String toSignDocumentName = toSignDocument.getName();

        final ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
        ZipOutputStream zipOutputStream = new ZipOutputStream(outBytes);
        if (asice && (signDocument != null)) {

            copyZipContent(signDocument, zipOutputStream);
        } else {

            storeZipComment(asicParameters, zipOutputStream, toSignDocumentName);

            storeMimetype(asicParameters, zipOutputStream);
        }
        storeSignedFiles(toSignDocument, zipOutputStream);

        storesSignature(asicParameters, signature, zipOutputStream);

        if (asice && cadesForm) {
            storeAsicManifest(parameters, toSignDocument, zipOutputStream);
        }
        DSSUtils.close(zipOutputStream);

        final InMemoryDocument asicContainer = createASiCContainer(asicParameters, outBytes);
        return asicContainer;
    }

    private void copyZipContent(DSSDocument toSignAsicContainer, ZipOutputStream zipOutputStream)
            throws IOException {

        final InputStream inputStream = toSignAsicContainer.openStream();
        final ZipInputStream zipInputStream = new ZipInputStream(inputStream);
        for (ZipEntry entry = getNextZipEntry(zipInputStream); entry != null; entry = getNextZipEntry(
                zipInputStream)) {

            createZipEntry(zipOutputStream, entry);
            IOUtils.copy(zipInputStream, zipOutputStream);
        }
        IOUtils.closeQuietly(zipInputStream);
    }

    private void storeAsicManifest(ASiCSignatureParameters parameters, final DSSDocument detachedDocument,
            final ZipOutputStream outZip) {

        ASiCParameters asicParameters = parameters.aSiC();

        final String signatureName = getSignatureFileName(asicParameters);
        final int indexOfSignature = signatureName.indexOf("signature");
        String suffix = signatureName.substring(indexOfSignature + "signature".length());
        final int lastIndexOf = suffix.lastIndexOf(".");
        suffix = suffix.substring(0, lastIndexOf);
        final String asicManifestZipEntryName = META_INF + "ASiCManifest" + suffix + ".xml";
        final ZipEntry entrySignature = new ZipEntry(asicManifestZipEntryName);
        createZipEntry(outZip, entrySignature);

        buildAsicManifest(parameters, detachedDocument, outZip);
    }

    private void buildAsicManifest(final ASiCSignatureParameters underlyingParameters,
            final DSSDocument detachedDocument, final OutputStream outputStream) {

        ASiCParameters asicParameters = underlyingParameters.aSiC();

        final Document documentDom = DSSXMLUtils.buildDOM();
        final Element asicManifestDom = documentDom.createElementNS(ASiCNamespaces.ASiC, "asic:ASiCManifest");
        documentDom.appendChild(asicManifestDom);

        final Element sigReferenceDom = DSSXMLUtils.addElement(documentDom, asicManifestDom, ASiCNamespaces.ASiC,
                "asic:SigReference");
        final String signatureName = getSignatureFileName(asicParameters);
        sigReferenceDom.setAttribute("URI", signatureName);
        sigReferenceDom.setAttribute("MimeType", MimeType.PKCS7.getMimeTypeString()); // only CAdES form

        DSSDocument currentDetachedDocument = detachedDocument;
        do {

            final String detachedDocumentName = currentDetachedDocument.getName();
            final Element dataObjectReferenceDom = DSSXMLUtils.addElement(documentDom, sigReferenceDom,
                    ASiCNamespaces.ASiC, "asic:DataObjectReference");
            dataObjectReferenceDom.setAttribute("URI", detachedDocumentName);

            final Element digestMethodDom = DSSXMLUtils.addElement(documentDom, dataObjectReferenceDom,
                    XMLSignature.XMLNS, "DigestMethod");
            final DigestAlgorithm digestAlgorithm = underlyingParameters.getDigestAlgorithm();
            digestMethodDom.setAttribute("Algorithm", digestAlgorithm.getXmlId());

            final Element digestValueDom = DSSXMLUtils.addElement(documentDom, dataObjectReferenceDom,
                    XMLSignature.XMLNS, "DigestValue");
            final byte[] digest = DSSUtils.digest(digestAlgorithm, currentDetachedDocument.getBytes());
            final String base64Encoded = Base64.encodeBase64String(digest);
            final Text textNode = documentDom.createTextNode(base64Encoded);
            digestValueDom.appendChild(textNode);

            currentDetachedDocument = currentDetachedDocument.getNextDocument();
        } while (currentDetachedDocument != null);

        storeXmlDom(outputStream, documentDom);
    }

    private void createZipEntry(final ZipOutputStream outZip, final ZipEntry entrySignature) throws DSSException {
        try {
            outZip.putNextEntry(entrySignature);
        } catch (IOException e) {
            throw new DSSException(e);
        }
    }

    private InMemoryDocument createASiCContainer(final ASiCParameters asicParameters,
            final ByteArrayOutputStream outBytes) {
        SignatureForm containerForm = asicParameters.getContainerForm();
        return new InMemoryDocument(outBytes.toByteArray(), null, getMimeType(containerForm));
    }

    private MimeType getMimeType(SignatureForm containerForm) {
        boolean asics = SignatureForm.ASiC_S.equals(containerForm);
        return asics ? MimeType.ASICS : MimeType.ASICE;
    }

    private void storesSignature(final ASiCParameters asicParameters, final DSSDocument signature,
            final ZipOutputStream outZip) {
        if (isXAdESForm(asicParameters)) {
            buildXAdES(asicParameters, signature, outZip);
        } else if (isCAdESForm(asicParameters)) {
            buildCAdES(asicParameters, signature, outZip);
        } else {
            throw new DSSException("ASiC signature form must be XAdES or CAdES!");
        }
    }

    private boolean isCAdESForm(final ASiCParameters asicParameters) {
        final SignatureForm underlyingForm = asicParameters.getUnderlyingForm();
        return SignatureForm.CAdES.equals(underlyingForm);
    }

    private boolean isXAdESForm(final ASiCParameters asicParameters) {
        final SignatureForm underlyingForm = asicParameters.getUnderlyingForm();
        return SignatureForm.XAdES.equals(underlyingForm);
    }

    private void storeZipComment(final ASiCParameters asicParameters, final ZipOutputStream outZip,
            final String toSignDocumentName) {
        if (asicParameters.isZipComment() && StringUtils.isNotEmpty(toSignDocumentName)) {
            outZip.setComment("mimetype=" + getMimeTypeBytes(asicParameters));
        }
    }

    private DSSDocument prepare(final DSSDocument detachedDocument,
            final ASiCSignatureParameters underlyingParameter) {

        // detachedDocument can be a simple file or an ASiC container
        DSSDocument contextToSignDocument = detachedDocument;
        ASiCParameters asicParameters = underlyingParameter.aSiC();
        final boolean asice = isAsice(asicParameters);
        final boolean cadesForm = isCAdESForm(asicParameters);
        final DocumentValidator validator = getAsicValidator(detachedDocument);
        if (isAsicValidator(validator)) {

            // This is already an existing ASiC container; a new signature should be added.
            final DocumentValidator subordinatedValidator = validator.getSubordinatedValidator();
            final DSSDocument contextSignature = subordinatedValidator.getDocument();
            asicParameters.setEnclosedSignature(contextSignature);
            if (asice) {
                if (cadesForm) {
                    final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                    buildAsicManifest(underlyingParameter, underlyingParameter.getDetachedContent(), outputStream);
                    contextToSignDocument = new InMemoryDocument(outputStream.toByteArray(), "AsicManifestXXX.xml",
                            MimeType.XML);
                    underlyingParameter.setDetachedContent(null);
                } else {
                    contextToSignDocument = underlyingParameter.getDetachedContent();
                }
            } else {
                contextToSignDocument = copyDetachedContent(underlyingParameter, subordinatedValidator);
            }
            if (!asice && (subordinatedValidator instanceof ASiCCMSDocumentValidator)) {
                contextToSignDocument = contextSignature;
            }
        } else {
            if (asice && cadesForm) {
                final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                buildAsicManifest(underlyingParameter, detachedDocument, outputStream);
                contextToSignDocument = new InMemoryDocument(outputStream.toByteArray(), "AsicManifestXXX.xml",
                        MimeType.XML);
            } else {
                underlyingParameter.setDetachedContent(contextToSignDocument);
            }
        }
        return contextToSignDocument;
    }

    private boolean isAsicValidator(final DocumentValidator documentValidator) {
        final boolean result = (documentValidator != null) && (documentValidator instanceof ASiCContainerValidator);
        return result;
    }

    private ZipEntry getNextZipEntry(final ZipInputStream zipInputStream) throws DSSException {
        try {
            return zipInputStream.getNextEntry();
        } catch (IOException e) {
            throw new DSSException(e);
        }
    }

    private DSSDocument getDetachedContents(final DocumentValidator subordinatedValidator,
            DSSDocument originalDocument) {

        final List<DSSDocument> detachedContents = subordinatedValidator.getDetachedContents();
        if ((detachedContents == null) || (detachedContents.size() == 0)) {

            final List<DSSDocument> detachedContentsList = new ArrayList<DSSDocument>();
            DSSDocument currentDocument = originalDocument;
            do {
                detachedContentsList.add(currentDocument);
                subordinatedValidator.setDetachedContents(detachedContentsList);
                currentDocument = currentDocument.getNextDocument();
            } while (currentDocument != null);
        } else {

            originalDocument = null;
            DSSDocument lastDocument = null;
            for (final DSSDocument currentDocument : detachedContents) {

                if (originalDocument == null) {
                    originalDocument = currentDocument;
                } else {
                    lastDocument.setNextDocument(currentDocument);
                }
                lastDocument = currentDocument;
            }
        }
        return originalDocument;
    }

    /**
     * Creates a specific XAdES/CAdES signature parameters on the base of the provided parameters. Forces the signature packaging to
     * DETACHED
     *
     * @param parameters must provide signingToken, PrivateKeyEntry and date
     * @return new specific instance for XAdES or CAdES
     */
    private AbstractSignatureParameters getParameters(final ASiCSignatureParameters parameters) {
        SignatureForm asicSignatureForm = parameters.aSiC().getUnderlyingForm();
        if (SignatureForm.CAdES == asicSignatureForm) {
            return initCAdESSignatureParameters(parameters);
        } else if (SignatureForm.XAdES == asicSignatureForm) {
            return initXAdESSignatureParameters(parameters);
        } else {
            throw new DSSException("Unsupported form : " + asicSignatureForm);
        }
    }

    private XAdESSignatureParameters initXAdESSignatureParameters(ASiCSignatureParameters parameters) {
        XAdESSignatureParameters xadesParameters = new XAdESSignatureParameters();
        initCommonFields(parameters, xadesParameters);
        initXAdESDocumentRoot(xadesParameters, parameters.aSiC());
        SignatureLevel asicProfile = parameters.getSignatureLevel();
        SignatureLevel underlyingLevel;
        switch (asicProfile) {
        case ASiC_S_BASELINE_B:
        case ASiC_E_BASELINE_B:
            underlyingLevel = SignatureLevel.XAdES_BASELINE_B;
            break;
        case ASiC_S_BASELINE_T:
        case ASiC_E_BASELINE_T:
            underlyingLevel = SignatureLevel.XAdES_BASELINE_T;
            break;
        case ASiC_S_BASELINE_LT:
        case ASiC_E_BASELINE_LT:
            underlyingLevel = SignatureLevel.XAdES_BASELINE_LT;
            break;
        case ASiC_S_BASELINE_LTA:
        case ASiC_E_BASELINE_LTA:
            underlyingLevel = SignatureLevel.XAdES_BASELINE_LTA;
            break;
        default:
            throw new DSSException("Unsupported format: " + asicProfile.name());
        }
        xadesParameters.setSignatureLevel(underlyingLevel);
        xadesParameters.setSignaturePackaging(SignaturePackaging.DETACHED);
        xadesParameters.setSignedInfoCanonicalizationMethod(parameters.getSignedInfoCanonicalizationMethod());
        xadesParameters
                .setSignedPropertiesCanonicalizationMethod(parameters.getSignedPropertiesCanonicalizationMethod());
        return xadesParameters;
    }

    private CAdESSignatureParameters initCAdESSignatureParameters(ASiCSignatureParameters parameters) {
        CAdESSignatureParameters cadesParameters = new CAdESSignatureParameters();
        initCommonFields(parameters, cadesParameters);
        SignatureLevel asicProfile = parameters.getSignatureLevel();
        SignatureLevel underlyingLevel;
        switch (asicProfile) {
        case ASiC_S_BASELINE_B:
        case ASiC_E_BASELINE_B:
            underlyingLevel = SignatureLevel.CAdES_BASELINE_B;
            break;
        case ASiC_S_BASELINE_T:
        case ASiC_E_BASELINE_T:
            underlyingLevel = SignatureLevel.CAdES_BASELINE_T;
            break;
        case ASiC_S_BASELINE_LT:
        case ASiC_E_BASELINE_LT:
            underlyingLevel = SignatureLevel.CAdES_BASELINE_LT;
            break;
        case ASiC_S_BASELINE_LTA:
        case ASiC_E_BASELINE_LTA:
            underlyingLevel = SignatureLevel.CAdES_BASELINE_LTA;
            break;
        default:
            throw new DSSException("Unsupported format: " + asicProfile.name());
        }
        cadesParameters.setSignatureLevel(underlyingLevel);
        cadesParameters.setSignaturePackaging(SignaturePackaging.DETACHED);
        return cadesParameters;
    }

    private void initCommonFields(AbstractSignatureParameters originalParameters,
            AbstractSignatureParameters parameters) {
        if (originalParameters.getSigningCertificate() != null) { // extends no need certificate
            parameters.setSigningCertificate(originalParameters.getSigningCertificate());
            parameters.setCertificateChain(originalParameters.getCertificateChain());
        }
        parameters.setSignWithExpiredCertificate(originalParameters.isSignWithExpiredCertificate());
        parameters.setDetachedContent(originalParameters.getDetachedContent());
        parameters.setBLevelParams(originalParameters.bLevel());
        parameters.setDigestAlgorithm(originalParameters.getDigestAlgorithm());
        parameters.setEncryptionAlgorithm(originalParameters.getEncryptionAlgorithm());
        parameters.setContentTimestampParameters(originalParameters.getContentTimestampParameters());
        parameters.setContentTimestamps(originalParameters.getContentTimestamps());
        parameters.setSignatureTimestampParameters(originalParameters.getSignatureTimestampParameters());
        parameters.setArchiveTimestampParameters(originalParameters.getArchiveTimestampParameters());
    }

    private void buildCAdES(final ASiCParameters asicParameters, final DSSDocument signature,
            final ZipOutputStream outZip) throws DSSException {
        final String signatureZipEntryName = getSignatureFileName(asicParameters);
        final ZipEntry entrySignature = new ZipEntry(signatureZipEntryName);
        createZipEntry(outZip, entrySignature);
        zipWriteBytes(outZip, signature.getBytes());
    }

    private void zipWriteBytes(final ZipOutputStream outZip, final byte[] bytes) throws DSSException {
        try {
            outZip.write(bytes);
        } catch (IOException e) {
            throw new DSSException(e);
        }
    }

    private String getSignatureFileName(final ASiCParameters asicParameters) {
        if (StringUtils.isNotBlank(asicParameters.getSignatureFileName())) {
            return META_INF + asicParameters.getSignatureFileName();
        }
        final boolean asice = isAsice(asicParameters);
        if (isXAdESForm(asicParameters)) {
            return asice ? ZIP_ENTRY_ASICE_METAINF_XADES_SIGNATURE : ZIP_ENTRY_ASICS_METAINF_XADES_SIGNATURE;
        } else if (isCAdESForm(asicParameters)) {
            return asice ? ZIP_ENTRY_ASICE_METAINF_CADES_SIGNATURE : ZIP_ENTRY_ASICS_METAINF_CADES_SIGNATURE;
        } else {
            throw new DSSException("ASiC signature form must be XAdES or CAdES!");
        }
    }

    private void storeMimetype(final ASiCParameters asicParameters, final ZipOutputStream outZip)
            throws DSSException {
        final byte[] mimeTypeBytes = getMimeTypeBytes(asicParameters).getBytes();
        final ZipEntry entryMimetype = getZipEntryMimeType(mimeTypeBytes);

        writeZipEntry(outZip, mimeTypeBytes, entryMimetype);
    }

    private void writeZipEntry(final ZipOutputStream outZip, final byte[] mimeTypeBytes,
            final ZipEntry entryMimetype) throws DSSException {
        createZipEntry(outZip, entryMimetype);
        zipWriteBytes(outZip, mimeTypeBytes);
    }

    private void storeSignedFiles(final DSSDocument detachedDocument, final ZipOutputStream outZip)
            throws IOException {
        DSSDocument currentDetachedDocument = detachedDocument;
        do {

            final String detachedDocumentName = currentDetachedDocument.getName();
            final String name = detachedDocumentName != null ? detachedDocumentName : ZIP_ENTRY_DETACHED_FILE;
            final ZipEntry entryDocument = new ZipEntry(name);
            outZip.setLevel(ZipEntry.DEFLATED);
            try {
                createZipEntry(outZip, entryDocument);
                final InputStream inputStream = currentDetachedDocument.openStream();
                IOUtils.copy(inputStream, outZip);
                IOUtils.closeQuietly(inputStream);
            } catch (DSSException e) {
                if (!((e.getCause() instanceof ZipException)
                        && e.getCause().getMessage().startsWith("duplicate entry:"))) {
                    throw e;
                }
            }
            currentDetachedDocument = currentDetachedDocument.getNextDocument();
        } while (currentDetachedDocument != null);
    }

    private String getMimeTypeBytes(final ASiCParameters asicParameters) {
        final String asicParameterMimeType = asicParameters.getMimeType();
        String mimeTypeBytes;
        if (StringUtils.isBlank(asicParameterMimeType)) {

            if (isAsice(asicParameters)) {
                mimeTypeBytes = MimeType.ASICE.getMimeTypeString();
            } else {
                mimeTypeBytes = MimeType.ASICS.getMimeTypeString();
            }
        } else {
            mimeTypeBytes = asicParameterMimeType;
        }
        return mimeTypeBytes;
    }

    private ZipEntry getZipEntryMimeType(final byte[] mimeTypeBytes) {

        final ZipEntry entryMimetype = new ZipEntry(ZIP_ENTRY_MIMETYPE);
        entryMimetype.setMethod(ZipEntry.STORED);
        entryMimetype.setSize(mimeTypeBytes.length);
        entryMimetype.setCompressedSize(mimeTypeBytes.length);
        final CRC32 crc = new CRC32();
        crc.update(mimeTypeBytes);
        entryMimetype.setCrc(crc.getValue());
        return entryMimetype;
    }

    private void initXAdESDocumentRoot(XAdESSignatureParameters xadesParameters, ASiCParameters asicParameters) {
        DSSDocument enclosedSignature = asicParameters.getEnclosedSignature();
        Document rootDocument;
        if ((enclosedSignature != null) && isAsics(asicParameters)) {
            rootDocument = DSSXMLUtils.buildDOM(enclosedSignature);
        } else {
            rootDocument = DSSXMLUtils.createDocument(ASiCNamespaces.ASiC, ASICS_NS);
        }
        xadesParameters.setRootDocument(rootDocument);
    }

    /**
     * This method creates a XAdES signature. When adding a new signature,  this one is appended to the already present signatures.
     *
     * @param asicParameters ASiC parameters
     * @param signature      signature being created
     * @param outZip         destination {@code ZipOutputStream}
     * @throws eu.europa.esig.dss.DSSException
     */
    private void buildXAdES(final ASiCParameters asicParameters, final DSSDocument signature,
            final ZipOutputStream outZip) throws DSSException {
        final String signatureZipEntryName = getSignatureFileName(asicParameters);
        final ZipEntry entrySignature = new ZipEntry(signatureZipEntryName);
        createZipEntry(outZip, entrySignature);
        Document xmlSignatureDoc = DSSXMLUtils.buildDOM(signature);
        storeXmlDom(outZip, xmlSignatureDoc);
    }

    private void storeXmlDom(final OutputStream outZip, final Document xml) throws DSSException {
        try {
            final DOMSource xmlSource = new DOMSource(xml);
            final StreamResult outputTarget = new StreamResult(outZip);
            TransformerFactory transformerFactory = DSSXMLUtils.getSecureTransformerFactory();
            transformerFactory.newTransformer().transform(xmlSource, outputTarget);
        } catch (Exception e) {
            throw new DSSException(e);
        }
    }

    private boolean isAsics(final ASiCParameters asicParameters) {
        return SignatureForm.ASiC_S.equals(asicParameters.getContainerForm());
    }

    private boolean isAsice(final ASiCParameters asicParameters) {
        return SignatureForm.ASiC_E.equals(asicParameters.getContainerForm());
    }

    /**
     * This method returns the specific service associated with the container: XAdES or CAdES.
     *
     * @param specificParameters {@code DocumentSignatureService}
     * @return
     */
    protected DocumentSignatureService getSpecificService(SignatureForm signatureForm) {
        if (underlyingASiCService == null) {
            if (signatureForm == SignatureForm.XAdES) {
                underlyingASiCService = new XAdESService(certificateVerifier);
            } else if (signatureForm == SignatureForm.CAdES) {
                underlyingASiCService = new CAdESService(certificateVerifier);
            } else {
                throw new DSSException("Unsupported parameter value: only XAdES and CAdES forms are acceptable!");
            }
            underlyingASiCService.setTspSource(tspSource);
        }
        return underlyingASiCService;
    }

}