You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2021/10/08 23:08:51 UTC

svn commit: r1894049 - in /poi: site/src/documentation/content/xdocs/ trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/ trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/

Author: kiwiwings
Date: Fri Oct  8 23:08:51 2021
New Revision: 1894049

URL: http://svn.apache.org/viewvc?rev=1894049&view=rev
Log:
#65623 - Create XAdES-T signature with XAdESXLSignatureFacet

Modified:
    poi/site/src/documentation/content/xdocs/changes.xml
    poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java
    poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java

Modified: poi/site/src/documentation/content/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/changes.xml?rev=1894049&r1=1894048&r2=1894049&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/changes.xml (original)
+++ poi/site/src/documentation/content/xdocs/changes.xml Fri Oct  8 23:08:51 2021
@@ -115,6 +115,7 @@
             <action type="fix" fixes-bug="65490" context="XSSF">Better support for shared hyperlinks</action>
             <action type="fix" fixes-bug="65372" context="OPC">Allow ZipSecureFile.setMaxEntrySize to accept sizes above 4Gb</action>
             <action type="fix" fixes-bug="65331" context="XWPF">Fix issue in XWPFTable.setTableAlignment(TableRowAlign tra)</action>
+            <action type="fix" fixes-bug="65623" context="OPC">Create XAdES-T signature with XAdESXLSignatureFacet</action>
         </actions>
     </release>
 

Modified: poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java?rev=1894049&r1=1894048&r2=1894049&view=diff
==============================================================================
--- poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java (original)
+++ poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java Fri Oct  8 23:08:51 2021
@@ -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 imple
 
         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 imple
             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 imple
          * 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 imple
                 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 imple
                 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 imple
                 }
             }
         }
+    }
 
-        // 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 imple
         // 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 imple
                 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) {
                 /*

Modified: poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java?rev=1894049&r1=1894048&r2=1894049&view=diff
==============================================================================
--- poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java (original)
+++ poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java Fri Oct  8 23:08:51 2021
@@ -94,6 +94,8 @@ import org.apache.poi.poifs.crypt.HashAl
 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.XWP
 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.Operato
 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 {



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org