aboutsummaryrefslogtreecommitdiffstats
path: root/poi-ooxml
diff options
context:
space:
mode:
authorAndreas Beeker <kiwiwings@apache.org>2021-10-08 23:08:51 +0000
committerAndreas Beeker <kiwiwings@apache.org>2021-10-08 23:08:51 +0000
commit60e9de813e655a82bb01d69f91072f72be7c025a (patch)
tree70309ac461f98323718b08c00a4075c0a90fab6d /poi-ooxml
parent91f423e33cb258c67414b7739149be5c16581285 (diff)
downloadpoi-60e9de813e655a82bb01d69f91072f72be7c025a.tar.gz
poi-60e9de813e655a82bb01d69f91072f72be7c025a.zip
#65623 - Create XAdES-T signature with XAdESXLSignatureFacet
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1894049 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'poi-ooxml')
-rw-r--r--poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java137
-rw-r--r--poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java91
2 files changed, 167 insertions, 61 deletions
diff --git a/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java b/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java
index d500c6a8c7..ad2e8c41b5 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java
@@ -24,6 +24,7 @@
package org.apache.poi.poifs.crypt.dsig.facets;
+import static java.util.Optional.ofNullable;
import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
import static org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet.insertXChild;
@@ -52,6 +53,7 @@ import org.apache.logging.log4j.Logger;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig;
import org.apache.poi.poifs.crypt.dsig.SignatureInfo;
import org.apache.poi.poifs.crypt.dsig.services.RevocationData;
+import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xmlbeans.XmlException;
import org.bouncycastle.asn1.ASN1InputStream;
@@ -74,14 +76,12 @@ import org.w3c.dom.NodeList;
/**
* XAdES-X-L v1.4.1 signature facet. This signature facet implementation will
* upgrade a given XAdES-BES/EPES signature to XAdES-X-L.
+ * If no revocation data service is set, only a XAdES-T signature is created.
*
* We don't inherit from XAdESSignatureFacet as we also want to be able to use
* this facet out of the context of a signature creation. This signature facet
* assumes that the signature is already XAdES-BES/EPES compliant.
*
- * This implementation has been tested against an implementation that
- * participated multiple ETSI XAdES plugtests.
- *
* @see XAdESSignatureFacet
*/
public class XAdESXLSignatureFacet implements SignatureFacet {
@@ -104,35 +104,63 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
SignatureConfig signatureConfig = signatureInfo.getSignatureConfig();
- QualifyingPropertiesDocument qualDoc;
- QualifyingPropertiesType qualProps;
-
// check for XAdES-BES
NodeList qualNl = document.getElementsByTagNameNS(XADES_132_NS, "QualifyingProperties");
- if (qualNl.getLength() == 1) {
- try {
- qualDoc = QualifyingPropertiesDocument.Factory.parse(qualNl.item(0), DEFAULT_XML_OPTIONS);
- } catch (XmlException e) {
- throw new MarshalException(e);
- }
- qualProps = qualDoc.getQualifyingProperties();
- } else {
- throw new MarshalException("no XAdES-BES extension present");
- }
+ QualifyingPropertiesType qualProps = getQualProps(qualNl);
// create basic XML container structure
- UnsignedPropertiesType unsignedProps = qualProps.getUnsignedProperties();
- if (unsignedProps == null) {
- unsignedProps = qualProps.addNewUnsignedProperties();
+ UnsignedPropertiesType unsignedProps =
+ ofNullable(qualProps.getUnsignedProperties()).orElseGet(qualProps::addNewUnsignedProperties);
+ UnsignedSignaturePropertiesType unsignedSigProps =
+ ofNullable(unsignedProps.getUnsignedSignatureProperties()).orElseGet(unsignedProps::addNewUnsignedSignatureProperties);
+
+ // create the XAdES-T time-stamp
+ NodeList nlSigVal = document.getElementsByTagNameNS(XML_DIGSIG_NS, "SignatureValue");
+ XAdESTimeStampType signatureTimeStamp = addTimestamp(nlSigVal, signatureInfo, unsignedSigProps);
+
+ // Without revocation data service we cannot construct the XAdES-C extension.
+ RevocationDataService revDataSvc = signatureConfig.getRevocationDataService();
+ if (revDataSvc != null) {
+ // XAdES-C: complete certificate refs
+ CompleteCertificateRefsType completeCertificateRefs = completeCertificateRefs(unsignedSigProps, signatureConfig);
+
+ // XAdES-C: complete revocation refs
+ RevocationData revocationData = revDataSvc.getRevocationData(signatureConfig.getSigningCertificateChain());
+ CompleteRevocationRefsType completeRevocationRefs = unsignedSigProps.addNewCompleteRevocationRefs();
+ addRevocationCRL(completeRevocationRefs, signatureConfig, revocationData);
+ addRevocationOCSP(completeRevocationRefs, signatureConfig, revocationData);
+
+ // XAdES-X Type 1 timestamp
+ addTimestampX(unsignedSigProps, signatureInfo, nlSigVal, signatureTimeStamp, completeCertificateRefs, completeRevocationRefs);
+
+ // XAdES-X-L
+ addCertificateValues(unsignedSigProps, signatureConfig);
+
+ RevocationValuesType revocationValues = unsignedSigProps.addNewRevocationValues();
+ createRevocationValues(revocationValues, revocationData);
}
- UnsignedSignaturePropertiesType unsignedSigProps = unsignedProps.getUnsignedSignatureProperties();
- if (unsignedSigProps == null) {
- unsignedSigProps = unsignedProps.addNewUnsignedSignatureProperties();
+
+ // marshal XAdES-X-L
+ Node n = document.importNode(qualProps.getDomNode(), true);
+ qualNl.item(0).getParentNode().replaceChild(n, qualNl.item(0));
+ }
+
+ private QualifyingPropertiesType getQualProps(NodeList qualNl) throws MarshalException {
+ // check for XAdES-BES
+ if (qualNl.getLength() != 1) {
+ throw new MarshalException("no XAdES-BES extension present");
}
+ try {
+ Node first = qualNl.item(0);
+ QualifyingPropertiesDocument qualDoc = QualifyingPropertiesDocument.Factory.parse(first, DEFAULT_XML_OPTIONS);
+ return qualDoc.getQualifyingProperties();
+ } catch (XmlException e) {
+ throw new MarshalException(e);
+ }
+ }
- // create the XAdES-T time-stamp
- NodeList nlSigVal = document.getElementsByTagNameNS(XML_DIGSIG_NS, "SignatureValue");
+ private XAdESTimeStampType addTimestamp(NodeList nlSigVal, SignatureInfo signatureInfo, UnsignedSignaturePropertiesType unsignedSigProps) {
if (nlSigVal.getLength() != 1) {
throw new IllegalArgumentException("SignatureValue is not set.");
}
@@ -151,17 +179,11 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
insertXChild(unsignedSigProps, validationData);
}
- if (signatureConfig.getRevocationDataService() == null) {
- /*
- * Without revocation data service we cannot construct the XAdES-C
- * extension.
- */
- return;
- }
+ return signatureTimeStamp;
+ }
- // XAdES-C: complete certificate refs
- CompleteCertificateRefsType completeCertificateRefs =
- unsignedSigProps.addNewCompleteCertificateRefs();
+ private CompleteCertificateRefsType completeCertificateRefs(UnsignedSignaturePropertiesType unsignedSigProps, SignatureConfig signatureConfig) {
+ CompleteCertificateRefsType completeCertificateRefs = unsignedSigProps.addNewCompleteCertificateRefs();
CertIDListType certIdList = completeCertificateRefs.addNewCertRefs();
/*
@@ -169,19 +191,14 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
* 4.4.3.2 of the XAdES 1.4.1 specification.
*/
List<X509Certificate> certChain = signatureConfig.getSigningCertificateChain();
- int chainSize = certChain.size();
- if (chainSize > 1) {
- for (X509Certificate cert : certChain.subList(1, chainSize)) {
- CertIDType certId = certIdList.addNewCert();
- XAdESSignatureFacet.setCertID(certId, signatureConfig, false, cert);
- }
- }
+ certChain.stream().skip(1).forEachOrdered(cert ->
+ XAdESSignatureFacet.setCertID(certIdList.addNewCert(), signatureConfig, false, cert)
+ );
+
+ return completeCertificateRefs;
+ }
- // XAdES-C: complete revocation refs
- CompleteRevocationRefsType completeRevocationRefs =
- unsignedSigProps.addNewCompleteRevocationRefs();
- RevocationData revocationData = signatureConfig.getRevocationDataService()
- .getRevocationData(certChain);
+ private void addRevocationCRL(CompleteRevocationRefsType completeRevocationRefs, SignatureConfig signatureConfig, RevocationData revocationData) {
if (revocationData.hasCRLs()) {
CRLRefsType crlRefs = completeRevocationRefs.addNewCRLRefs();
completeRevocationRefs.setCRLRefs(crlRefs);
@@ -191,10 +208,9 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
X509CRL crl;
try {
crl = (X509CRL) this.certificateFactory
- .generateCRL(new UnsynchronizedByteArrayInputStream(encodedCrl));
+ .generateCRL(new UnsynchronizedByteArrayInputStream(encodedCrl));
} catch (CRLException e) {
- throw new RuntimeException("CRL parse error: "
- + e.getMessage(), e);
+ throw new RuntimeException("CRL parse error: " + e.getMessage(), e);
}
CRLIdentifierType crlIdentifier = crlRef.addNewCRLIdentifier();
@@ -209,6 +225,9 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
XAdESSignatureFacet.setDigestAlgAndValue(digestAlgAndValue, encodedCrl, signatureConfig.getDigestAlgo());
}
}
+ }
+
+ private void addRevocationOCSP(CompleteRevocationRefsType completeRevocationRefs, SignatureConfig signatureConfig, RevocationData revocationData) {
if (revocationData.hasOCSPs()) {
OCSPRefsType ocspRefs = completeRevocationRefs.addNewOCSPRefs();
for (byte[] ocsp : revocationData.getOCSPs()) {
@@ -247,10 +266,11 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
}
}
}
+ }
- // marshal XAdES-C
+ private void addTimestampX(UnsignedSignaturePropertiesType unsignedSigProps, SignatureInfo signatureInfo, NodeList nlSigVal, XAdESTimeStampType signatureTimeStamp,
+ CompleteCertificateRefsType completeCertificateRefs, CompleteRevocationRefsType completeRevocationRefs) {
- // XAdES-X Type 1 timestamp
List<Node> timeStampNodesXadesX1 = new ArrayList<>();
timeStampNodesXadesX1.add(nlSigVal.item(0));
timeStampNodesXadesX1.add(signatureTimeStamp.getDomNode());
@@ -269,9 +289,11 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
// marshal XAdES-X
unsignedSigProps.addNewSigAndRefsTimeStamp().set(timeStampXadesX1);
- // XAdES-X-L
+ }
+
+ private void addCertificateValues(UnsignedSignaturePropertiesType unsignedSigProps, SignatureConfig signatureConfig) {
CertificateValuesType certificateValues = unsignedSigProps.addNewCertificateValues();
- for (X509Certificate certificate : certChain) {
+ for (X509Certificate certificate : signatureConfig.getSigningCertificateChain()) {
EncapsulatedPKIDataType encapsulatedPKIDataType = certificateValues.addNewEncapsulatedX509Certificate();
try {
encapsulatedPKIDataType.setByteArrayValue(certificate.getEncoded());
@@ -279,16 +301,9 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
throw new RuntimeException("certificate encoding error: " + e.getMessage(), e);
}
}
-
- RevocationValuesType revocationValues = unsignedSigProps.addNewRevocationValues();
- createRevocationValues(revocationValues, revocationData);
-
- // marshal XAdES-X-L
- Node n = document.importNode(qualProps.getDomNode(), true);
- qualNl.item(0).getParentNode().replaceChild(n, qualNl.item(0));
}
- public static byte[] getC14nValue(List<Node> nodeList, String c14nAlgoId) {
+ private static byte[] getC14nValue(List<Node> nodeList, String c14nAlgoId) {
try (UnsynchronizedByteArrayOutputStream c14nValue = new UnsynchronizedByteArrayOutputStream()) {
for (Node node : nodeList) {
/*
diff --git a/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java b/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java
index 502a125613..9d240ef4be 100644
--- a/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java
+++ b/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java
@@ -94,6 +94,8 @@ import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.crypt.dsig.facets.EnvelopedSignatureFacet;
import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet;
import org.apache.poi.poifs.crypt.dsig.facets.OOXMLSignatureFacet;
+import org.apache.poi.poifs.crypt.dsig.facets.Office2010SignatureFacet;
+import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet;
import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet;
import org.apache.poi.poifs.crypt.dsig.facets.XAdESXLSignatureFacet;
import org.apache.poi.poifs.crypt.dsig.services.RevocationData;
@@ -116,6 +118,7 @@ import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFHyperlinkRun;
import org.apache.poi.xwpf.usermodel.XWPFSignatureLine;
import org.apache.xmlbeans.SystemProperties;
+import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.bouncycastle.asn1.DEROctetString;
@@ -156,7 +159,11 @@ import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.etsi.uri.x01903.v13.DigestAlgAndValueType;
+import org.etsi.uri.x01903.v13.EncapsulatedPKIDataType;
import org.etsi.uri.x01903.v13.QualifyingPropertiesType;
+import org.etsi.uri.x01903.v13.UnsignedPropertiesType;
+import org.etsi.uri.x01903.v13.UnsignedSignaturePropertiesType;
+import org.etsi.uri.x01903.v13.XAdESTimeStampType;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
@@ -164,6 +171,7 @@ import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
+import org.w3.x2000.x09.xmldsig.ObjectType;
import org.w3.x2000.x09.xmldsig.ReferenceType;
import org.w3.x2000.x09.xmldsig.SignatureDocument;
import org.w3c.dom.Document;
@@ -924,6 +932,89 @@ class TestSignatureInfo {
}
@Test
+ void createXAdES_T_65623() throws Exception {
+ initKeyPair();
+
+ UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream();
+ try (XSSFWorkbook wb = new XSSFWorkbook()) {
+ wb.createSheet().createRow(0).createCell(0).setCellValue("Test");
+ wb.write(bos);
+ }
+
+ SignatureConfig signatureConfig = new SignatureConfig();
+ signatureConfig.setDigestAlgo(HashAlgorithm.sha256);
+ signatureConfig.setKey(keyPair.getPrivate());
+ signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
+
+ // mock tsp
+ // signatureConfig.setTspUrl("http://timestamp.digicert.com");
+ final X509CRL crl = generateCrl(x509, keyPair.getPrivate());
+ TimeStampService tspService = (signatureInfo, data, revocationData) -> {
+ revocationData.addCRL(crl);
+ return "time-stamp-token".getBytes(LocaleUtil.CHARSET_1252);
+ };
+ signatureConfig.setTspService(tspService);
+
+ signatureConfig.setTspRequestPolicy(null); // comodoca request fails, if default policy is set ...
+ signatureConfig.setTspOldProtocol(false);
+
+ signatureConfig.setXadesDigestAlgo(HashAlgorithm.sha512);
+ signatureConfig.setXadesRole("Xades Reviewer");
+ signatureConfig.setSignatureDescription("test xades signature");
+
+ signatureConfig.setSignatureFacets(Arrays.asList(
+ new OOXMLSignatureFacet(),
+ new KeyInfoSignatureFacet(),
+ new XAdESSignatureFacet(),
+ new Office2010SignatureFacet(),
+ new XAdESXLSignatureFacet()
+ ));
+
+ // create signature
+ try (OPCPackage pkg = OPCPackage.open(bos.toInputStream())) {
+ SignatureInfo si = new SignatureInfo();
+ si.setOpcPackage(pkg);
+ si.setSignatureConfig(signatureConfig);
+ si.confirmSignature();
+
+ bos.reset();
+ pkg.save(bos);
+ } catch (EncryptedDocumentException e) {
+ assumeTrue(e.getMessage().startsWith("Export Restrictions"));
+ }
+
+ // check if timestamp node is filled
+ try (OPCPackage pkg = OPCPackage.open(bos.toInputStream())) {
+ SignatureInfo si = new SignatureInfo();
+ si.setOpcPackage(pkg);
+ si.setSignatureConfig(signatureConfig);
+ assertTrue(si.verifySignature());
+ boolean found = false;
+ for (SignaturePart sp : si.getSignatureParts()) {
+ for (ObjectType ot : sp.getSignatureDocument().getSignature().getObjectArray()) {
+ XmlCursor xc = ot.newCursor();
+ if (xc.toChild(SignatureFacet.XADES_132_NS, "QualifyingProperties")) {
+ QualifyingPropertiesType qpt = (QualifyingPropertiesType) xc.getObject();
+ assertTrue(qpt.isSetUnsignedProperties());
+ UnsignedPropertiesType up = qpt.getUnsignedProperties();
+ assertTrue(up.isSetUnsignedSignatureProperties());
+ UnsignedSignaturePropertiesType ups = up.getUnsignedSignatureProperties();
+ assertEquals(1, ups.sizeOfSignatureTimeStampArray());
+ XAdESTimeStampType ts = ups.getSignatureTimeStampArray(0);
+ assertEquals(1, ts.sizeOfEncapsulatedTimeStampArray());
+ EncapsulatedPKIDataType ets = ts.getEncapsulatedTimeStampArray(0);
+ assertFalse(ets.getStringValue().isEmpty());
+ found = true;
+ }
+ xc.dispose();
+ }
+ }
+ assertTrue(found);
+ }
+ }
+
+
+ @Test
@DisabledOnJreEx("1.8.0_292")
@Tag("scratchpad.ignore")
void testSignatureImage() throws Exception {