/* * DSS - Digital Signature Services * * Copyright (C) 2013 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel * * Developed by: 2013 ARHS Developments S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-developments.com * * This file is part of the "DSS - Digital Signature Services" project. * * "DSS - Digital Signature Services" 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. * * DSS 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 * "DSS - Digital Signature Services". If not, see <http://www.gnu.org/licenses/>. */ /* * Project: Digital Signature Services (DSS) * Contractor: ARHS-Developments * * $HeadURL: http://forge.aris-lux.lan/svn/dgmarktdss/trunk/buildtools/src/main/resources/eclipse/dss-java-code-template.xml $ * $Revision: 672 $ * $Date: 2011-05-12 11:59:21 +0200 (Thu, 12 May 2011) $ * $Author: hiedelch $ */ package eu.europa.ec.markt.dss.signature.asic; 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.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; 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.ec.markt.dss.ASiCNamespaces; import eu.europa.ec.markt.dss.DSSUtils; import eu.europa.ec.markt.dss.DSSXMLUtils; import eu.europa.ec.markt.dss.DigestAlgorithm; import eu.europa.ec.markt.dss.exception.DSSException; import eu.europa.ec.markt.dss.exception.DSSNullException; import eu.europa.ec.markt.dss.parameter.ASiCParameters; import eu.europa.ec.markt.dss.parameter.SignatureParameters; import eu.europa.ec.markt.dss.signature.AbstractSignatureService; import eu.europa.ec.markt.dss.signature.DSSDocument; import eu.europa.ec.markt.dss.signature.DocumentSignatureService; import eu.europa.ec.markt.dss.signature.InMemoryDocument; import eu.europa.ec.markt.dss.signature.MimeType; import eu.europa.ec.markt.dss.signature.SignatureLevel; import eu.europa.ec.markt.dss.signature.SignaturePackaging; import eu.europa.ec.markt.dss.signature.token.DSSPrivateKeyEntry; import eu.europa.ec.markt.dss.signature.token.SignatureTokenConnection; import eu.europa.ec.markt.dss.validation102853.CertificateVerifier; import eu.europa.ec.markt.dss.validation102853.DocumentValidator; import eu.europa.ec.markt.dss.validation102853.SignatureForm; import eu.europa.ec.markt.dss.validation102853.SignedDocumentValidator; import eu.europa.ec.markt.dss.validation102853.asic.ASiCCMSDocumentValidator; import eu.europa.ec.markt.dss.validation102853.asic.ASiCContainerValidator; /** * Implementation of {@code DocumentSignatureService} for ASiC-S and -E containers. It allows the creation of containers based on XAdES or CAdES standard. * <p/> * DISCLAIMER: Project owner DG-MARKT. * * @author <a href="mailto:dgmarkt.Project-DSS@arhs-developments.com">ARHS Developments</a> * @version $Revision: 672 $ - $Date: 2011-05-12 11:59:21 +0200 (Thu, 12 May 2011) $ */ public class ASiCService extends AbstractSignatureService { 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"; private final static String ASICS_EXTENSION = ".asics"; // can be ".scs" private final static String ASICE_EXTENSION = ".asice"; // can be ".sce" public final static String ASICS_NS = "asic:XAdESSignatures"; /** * 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 byte[] getDataToSign(final DSSDocument toSignDocument, final SignatureParameters parameters) throws DSSException { final SignatureParameters underlyingParameters = getParameters(parameters); // toSignDocument can be a simple file or an ASiC container final DSSDocument contextToSignDocument = prepare(toSignDocument, underlyingParameters); final ASiCParameters asicParameters = underlyingParameters.aSiC(); parameters.aSiC().setEnclosedSignature(asicParameters.getEnclosedSignature()); final DocumentSignatureService underlyingService = getSpecificService(underlyingParameters); return underlyingService.getDataToSign(contextToSignDocument, underlyingParameters); } /** * ETSI TS 102 918 v1.2.1 (2012-02) <br /> * <p> * 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 <asic:XadESSignatures> 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 SignatureParameters parameters, final byte[] signatureValue) throws DSSException { assertSigningDateInCertificateValidityRange(parameters); // Signs the toSignDocument first SignatureParameters underlyingParameters = getParameters(parameters); DSSDocument contextToSignDocument = prepare(toSignDocument, underlyingParameters); final ASiCParameters asicParameters = underlyingParameters.aSiC(); parameters.aSiC().setEnclosedSignature(asicParameters.getEnclosedSignature()); final DocumentSignatureService underlyingService = getSpecificService(underlyingParameters); 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, underlyingParameters, signature); parameters.setDeterministicId(null); return asicSignature; } @Override public DSSDocument signDocument(final DSSDocument toSignDocument, final SignatureParameters parameters) throws DSSException { final byte[] dataToSign = getDataToSign(toSignDocument, parameters); final SignatureTokenConnection signingToken = parameters.getSigningToken(); if (signingToken == null) { throw new DSSNullException(SignatureTokenConnection.class); } final DigestAlgorithm digestAlgorithm = parameters.getDigestAlgorithm(); final DSSPrivateKeyEntry privateKeyEntry = parameters.getPrivateKeyEntry(); final byte[] signatureValue = signingToken.sign(dataToSign, digestAlgorithm, privateKeyEntry); final DSSDocument dssDocument = signDocument(toSignDocument, parameters, signatureValue); return dssDocument; } @Override public DSSDocument extendDocument(final DSSDocument toExtendDocument, final SignatureParameters parameters) throws DSSException { final DocumentValidator validator = SignedDocumentValidator.fromDocument(toExtendDocument); final DocumentValidator subordinatedValidator = validator.getSubordinatedValidator(); final DocumentSignatureService specificService = getSpecificService(parameters); specificService.setTspSource(tspSource); final SignatureParameters xadesParameters = getParameters(parameters); final DSSDocument detachedContent = parameters.getDetachedContent(); final DSSDocument detachedContents = getDetachedContents(subordinatedValidator, detachedContent); xadesParameters.setDetachedContent(detachedContents); final DSSDocument signature = subordinatedValidator.getDocument(); final DSSDocument signedDocument = specificService.extendDocument(signature, xadesParameters); 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.isXAdES(name) || ASiCContainerValidator.isCAdES(name)) { createZipEntry(zipOutputStream, newEntry); final InputStream inputStream = signedDocument.openStream(); DSSUtils.copy(inputStream, zipOutputStream); DSSUtils.closeQuietly(inputStream); } else { createZipEntry(zipOutputStream, newEntry); DSSUtils.copy(zipInputStream, zipOutputStream); } } DSSUtils.closeQuietly(zipInputStream); DSSUtils.closeQuietly(zipOutputStream); return new InMemoryDocument(output.toByteArray()); } private DSSDocument copyDetachedContent(final SignatureParameters 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 SignatureParameters underlyingParameters, final DSSDocument signature) { final ASiCParameters asicParameters = underlyingParameters.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(underlyingParameters, toSignDocument, zipOutputStream); } DSSUtils.close(zipOutputStream); final InMemoryDocument asicContainer = createASiCContainer(asicParameters, outBytes, toSignDocumentName); return asicContainer; } private void copyZipContent(DSSDocument toSignAsicContainer, ZipOutputStream zipOutputStream) { final InputStream inputStream = toSignAsicContainer.openStream(); final ZipInputStream zipInputStream = new ZipInputStream(inputStream); for (ZipEntry entry = getNextZipEntry(zipInputStream); entry != null; entry = getNextZipEntry(zipInputStream)) { createZipEntry(zipOutputStream, entry); DSSUtils.copy(zipInputStream, zipOutputStream); } DSSUtils.closeQuietly(zipInputStream); } private void storeAsicManifest(final SignatureParameters underlyingParameters, final DSSDocument detachedDocument, final ZipOutputStream outZip) { final String signatureName = getSignatureFileName(underlyingParameters.aSiC()); 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(underlyingParameters, detachedDocument, outZip); } private void buildAsicManifest(final SignatureParameters underlyingParameters, final DSSDocument detachedDocument, final OutputStream outputStream) { final 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); final String signatureMimeType = getSignatureMimeType(asicParameters); sigReferenceDom.setAttribute("MimeType", signatureMimeType); 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 = DSSUtils.base64Encode(digest); final Text textNode = documentDom.createTextNode(base64Encoded); digestValueDom.appendChild(textNode); currentDetachedDocument = currentDetachedDocument.getNextDocument(); } while (currentDetachedDocument != null); storeXmlDom(outputStream, documentDom); } private static 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, final String toSignDocumentName) { final byte[] documentBytes = outBytes.toByteArray(); final SignatureForm containerForm = asicParameters.getContainerForm(); final boolean asics = SignatureForm.ASiC_S.equals(containerForm); final String extension = asics ? ASICS_EXTENSION : ASICE_EXTENSION; final String name = toSignDocumentName != null ? toSignDocumentName + extension : null; final MimeType mimeType = asics ? MimeType.ASICS : MimeType.ASICE; return new InMemoryDocument(documentBytes, name, mimeType); } 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() && DSSUtils.isNotEmpty(toSignDocumentName)) { outZip.setComment("mimetype=" + getMimeTypeBytes(asicParameters)); } } private DSSDocument prepare(final DSSDocument detachedDocument, final SignatureParameters underlyingParameters) { // detachedDocument can be a simple file or an ASiC container DSSDocument contextToSignDocument = detachedDocument; final ASiCParameters asicParameters = underlyingParameters.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(); underlyingParameters.aSiC().setEnclosedSignature(contextSignature); if (asice) { if (cadesForm) { final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); buildAsicManifest(underlyingParameters, underlyingParameters.getDetachedContent(), outputStream); contextToSignDocument = new InMemoryDocument(outputStream.toByteArray(), "AsicManifestXXX.xml", MimeType.XML); underlyingParameters.setDetachedContent(null); } else { contextToSignDocument = underlyingParameters.getDetachedContent(); } } else { contextToSignDocument = copyDetachedContent(underlyingParameters, subordinatedValidator); } if (!asice && subordinatedValidator instanceof ASiCCMSDocumentValidator) { contextToSignDocument = contextSignature; } } else { if (asice && cadesForm) { final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); buildAsicManifest(underlyingParameters, detachedDocument, outputStream); contextToSignDocument = new InMemoryDocument(outputStream.toByteArray(), "AsicManifestXXX.xml", MimeType.XML); } else { underlyingParameters.setDetachedContent(contextToSignDocument); } } return contextToSignDocument; } private boolean isAsicValidator(final DocumentValidator documentValidator) { final boolean result = documentValidator != null && (documentValidator instanceof ASiCContainerValidator); return result; } private static 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 SignatureParameters getParameters(final SignatureParameters parameters) { final SignatureParameters underlyingParameters = new SignatureParameters(parameters); final SignatureLevel asicProfile = parameters.getSignatureLevel(); final SignatureForm asicSignatureForm = parameters.aSiC().getUnderlyingForm(); final SignatureLevel underlyingLevel; final boolean xades = asicSignatureForm == SignatureForm.XAdES; switch (asicProfile) { case ASiC_S_BASELINE_B: case ASiC_E_BASELINE_B: underlyingLevel = xades ? SignatureLevel.XAdES_BASELINE_B : SignatureLevel.CAdES_BASELINE_B; break; case ASiC_S_BASELINE_T: case ASiC_E_BASELINE_T: underlyingLevel = xades ? SignatureLevel.XAdES_BASELINE_T : SignatureLevel.CAdES_BASELINE_T; break; case ASiC_S_BASELINE_LT: case ASiC_E_BASELINE_LT: underlyingLevel = xades ? SignatureLevel.XAdES_BASELINE_LT : SignatureLevel.CAdES_BASELINE_LT; break; case ASiC_S_BASELINE_LTA: case ASiC_E_BASELINE_LTA: underlyingLevel = xades ? SignatureLevel.XAdES_BASELINE_LTA : SignatureLevel.CAdES_BASELINE_LTA; break; default: throw new DSSException("Unsupported format: " + asicProfile.name()); } underlyingParameters.setSignatureLevel(underlyingLevel); underlyingParameters.setSignaturePackaging(SignaturePackaging.DETACHED); return underlyingParameters; } 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 static 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) { final boolean asice = isAsice(asicParameters); final DSSDocument enclosedSignature = asicParameters.getEnclosedSignature(); if (isXAdESForm(asicParameters)) { if (asice && enclosedSignature != null) { return META_INF + asicParameters.getSignatureFileName(); } else { return asice ? ZIP_ENTRY_ASICE_METAINF_XADES_SIGNATURE : ZIP_ENTRY_ASICS_METAINF_XADES_SIGNATURE; } } else if (isCAdESForm(asicParameters)) { if (asice && enclosedSignature != null) { return META_INF + asicParameters.getSignatureFileName(); } else { 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 String getSignatureMimeType(final ASiCParameters asicParameters) { if (isXAdESForm(asicParameters)) { return MimeType.PKCS7.getMimeTypeString(); } else if (isCAdESForm(asicParameters)) { return MimeType.PKCS7.getMimeTypeString(); } 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 DSSException { 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(); DSSUtils.copy(inputStream, outZip); DSSUtils.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 (DSSUtils.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; } /** * This method creates a XAdES signature. When adding a new signature, this one is appended to the already present signatures. * * @param asicParameters already present signatures * @param signature signature being created * @param outZip destination {@code ZipOutputStream} * @throws eu.europa.ec.markt.dss.exception.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); // Creates the XAdES signature final Document xmlSignatureDoc = DSSXMLUtils.buildDOM(signature); final Element documentElement = xmlSignatureDoc.getDocumentElement(); final Element xmlSignatureElement = (Element) xmlSignatureDoc.removeChild(documentElement); final Document xmlXAdESDoc; final DSSDocument enclosedSignature = asicParameters.getEnclosedSignature(); if (enclosedSignature != null && isAsics(asicParameters)) { final Document contextXmlSignatureDoc = DSSXMLUtils.buildDOM(enclosedSignature); final Element contextDocumentElement = contextXmlSignatureDoc.getDocumentElement(); contextXmlSignatureDoc.adoptNode(xmlSignatureElement); contextDocumentElement.appendChild(xmlSignatureElement); xmlXAdESDoc = contextXmlSignatureDoc; } else { xmlXAdESDoc = DSSXMLUtils.createDocument(ASiCNamespaces.ASiC, ASICS_NS, xmlSignatureElement); } storeXmlDom(outZip, xmlXAdESDoc); } 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.newInstance().newTransformer().transform(xmlSource, outputTarget); } catch (TransformerException e) { throw new DSSException(e); } catch (TransformerFactoryConfigurationError transformerFactoryConfigurationError) { transformerFactoryConfigurationError.printStackTrace(); } } 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(final SignatureParameters specificParameters) { final SignatureForm asicSignatureForm = specificParameters.aSiC().getUnderlyingForm(); final DocumentSignatureService underlyingASiCService = specificParameters.getContext().getUnderlyingASiCService(certificateVerifier, asicSignatureForm); underlyingASiCService.setTspSource(tspSource); return underlyingASiCService; } }