package eu.europa.esig.dss.asic.signature;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import eu.europa.esig.dss.DSSDocument;
import eu.europa.esig.dss.DSSException;
import eu.europa.esig.dss.DSSUtils;
import eu.europa.esig.dss.DomUtils;
import eu.europa.esig.dss.InMemoryDocument;
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.ASiCNamespace;
import eu.europa.esig.dss.asic.ASiCParameters;
import eu.europa.esig.dss.asic.ASiCUtils;
import eu.europa.esig.dss.asic.ASiCWithXAdESContainerExtractor;
import eu.europa.esig.dss.asic.ASiCWithXAdESSignatureParameters;
import eu.europa.esig.dss.asic.AbstractASiCContainerExtractor;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.validation.CertificateVerifier;
import eu.europa.esig.dss.xades.XAdESSignatureParameters;
import eu.europa.esig.dss.xades.signature.XAdESService;
public class ASiCWithXAdESService extends AbstractASiCSignatureService<ASiCWithXAdESSignatureParameters> {
private static final Logger LOG = LoggerFactory.getLogger(ASiCWithXAdESService.class);
static {
DomUtils.registerNamespace("asic", ASiCNamespace.NS);
}
public ASiCWithXAdESService(CertificateVerifier certificateVerifier) {
super(certificateVerifier);
LOG.debug("+ ASiCService with XAdES created");
}
@Override
public ToBeSigned getDataToSign(List<DSSDocument> toSignDocuments, ASiCWithXAdESSignatureParameters parameters) throws DSSException {
final ASiCParameters asicParameters = parameters.aSiC();
assertCanBeSign(toSignDocuments, asicParameters);
GetDataToSignASiCWithXAdESHelper dataToSignHelper = ASiCWithXAdESDataToSignHelperBuilder.getGetDataToSignHelper(toSignDocuments, parameters);
XAdESSignatureParameters xadesParameters = getXAdESParameters(parameters, dataToSignHelper.getExistingSignature());
return getXAdESService().getDataToSign(dataToSignHelper.getToBeSigned(), xadesParameters);
}
@Override
public DSSDocument signDocument(List<DSSDocument> toSignDocuments, ASiCWithXAdESSignatureParameters parameters, SignatureValue signatureValue)
throws DSSException {
final ASiCParameters asicParameters = parameters.aSiC();
assertCanBeSign(toSignDocuments, asicParameters);
assertSigningDateInCertificateValidityRange(parameters);
GetDataToSignASiCWithXAdESHelper dataToSignHelper = ASiCWithXAdESDataToSignHelperBuilder.getGetDataToSignHelper(toSignDocuments, parameters);
List<DSSDocument> signatures = dataToSignHelper.getSignatures();
List<DSSDocument> manifestFiles = dataToSignHelper.getManifestFiles();
List<DSSDocument> signedDocuments = dataToSignHelper.getSignedDocuments();
XAdESSignatureParameters xadesParameters = getXAdESParameters(parameters, dataToSignHelper.getExistingSignature());
final DSSDocument newSignature = getXAdESService().signDocument(dataToSignHelper.getToBeSigned(), xadesParameters, signatureValue);
String newSignatureFilename = dataToSignHelper.getSignatureFilename();
newSignature.setName(newSignatureFilename);
if (ASiCUtils.isASiCS(asicParameters)) {
Iterator<DSSDocument> iterator = signatures.iterator();
while (iterator.hasNext()) {
if (Utils.areStringsEqual(newSignatureFilename, iterator.next().getName())) {
iterator.remove(); // remove existing file to be replaced
}
}
}
signatures.add(newSignature);
final DSSDocument asicSignature = buildASiCContainer(signedDocuments, signatures, manifestFiles, asicParameters);
asicSignature
.setName(DSSUtils.getFinalFileName(asicSignature, SigningOperation.SIGN, parameters.getSignatureLevel(), parameters.aSiC().getContainerType()));
parameters.reinitDeterministicId();
return asicSignature;
}
@Override
public DSSDocument extendDocument(DSSDocument toExtendDocument, ASiCWithXAdESSignatureParameters parameters) throws DSSException {
if (!ASiCUtils.isASiCContainer(toExtendDocument) || !ASiCUtils.isArchiveContainsCorrectSignatureExtension(toExtendDocument, ".xml")) {
throw new DSSException("Unsupported file type");
}
extractCurrentArchive(toExtendDocument);
List<DSSDocument> signedDocuments = getEmbeddedSignedDocuments();
List<DSSDocument> signatureDocuments = getEmbeddedSignatures();
List<DSSDocument> extendedDocuments = new ArrayList<DSSDocument>();
for (DSSDocument signature : signatureDocuments) {
XAdESSignatureParameters xadesParameters = getXAdESParameters(parameters, null);
xadesParameters.setDetachedContents(signedDocuments);
DSSDocument extendDocument = getXAdESService().extendDocument(signature, xadesParameters);
extendedDocuments.add(extendDocument);
}
ByteArrayOutputStream baos = null;
try {
baos = new ByteArrayOutputStream();
copyExistingArchiveWithSignatureList(toExtendDocument, extendedDocuments, baos);
} finally {
Utils.closeQuietly(baos);
}
DSSDocument asicSignature = new InMemoryDocument(baos.toByteArray(), null, toExtendDocument.getMimeType());
asicSignature.setName(
DSSUtils.getFinalFileName(toExtendDocument, SigningOperation.EXTEND, parameters.getSignatureLevel(), parameters.aSiC().getContainerType()));
return asicSignature;
}
@Override
void storeSignatures(List<DSSDocument> signatures, ZipOutputStream zos) throws IOException {
for (DSSDocument dssDocument : signatures) {
ZipEntry entrySignature = new ZipEntry(dssDocument.getName());
zos.putNextEntry(entrySignature);
Document xmlSignatureDoc = DomUtils.buildDOM(dssDocument);
DomUtils.writeDocumentTo(xmlSignatureDoc, zos);
}
}
@Override
boolean isSignatureFilename(String name) {
return ASiCUtils.isXAdES(name);
}
private XAdESService getXAdESService() {
XAdESService xadesService = new XAdESService(certificateVerifier);
xadesService.setTspSource(tspSource);
return xadesService;
}
private XAdESSignatureParameters getXAdESParameters(ASiCWithXAdESSignatureParameters parameters, DSSDocument existingXAdESSignatureASiCS) {
XAdESSignatureParameters xadesParameters = parameters;
xadesParameters.setSignaturePackaging(SignaturePackaging.DETACHED);
Document rootDocument = null;
// If ASiC-S + already existing signature file, we re-use the same signature file
if (existingXAdESSignatureASiCS != null) {
rootDocument = DomUtils.buildDOM(existingXAdESSignatureASiCS);
} else {
rootDocument = DomUtils.createDocument(ASiCNamespace.NS, ASiCNamespace.XADES_SIGNATURES);
}
xadesParameters.setRootDocument(rootDocument);
return xadesParameters;
}
@Override
AbstractASiCContainerExtractor getArchiveExtractor(DSSDocument archive) {
return new ASiCWithXAdESContainerExtractor(archive);
}
@Override
boolean canBeSigned(List<DSSDocument> documents, ASiCParameters asicParameters) {
boolean isMimetypeCorrect = true;
boolean isSignatureTypeCorrect = true;
if (ASiCUtils.isArchive(documents)) {
DSSDocument archive = documents.get(0);
String expectedMimeType = archive.getMimeType().getMimeTypeString();
String mimeTypeFromParameter = ASiCUtils.getMimeTypeString(asicParameters);
isMimetypeCorrect = Utils.areStringsEqualIgnoreCase(expectedMimeType, mimeTypeFromParameter);
if (isMimetypeCorrect) {
isSignatureTypeCorrect = ASiCUtils.isArchiveContainsCorrectSignatureExtension(archive, ".xml");
}
}
return isMimetypeCorrect && isSignatureTypeCorrect;
}
}