]> source.dussan.org Git - poi.git/commitdiff
more flexible signer verification through Iterable-Interface
authorAndreas Beeker <kiwiwings@apache.org>
Wed, 24 Sep 2014 22:54:21 +0000 (22:54 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Wed, 24 Sep 2014 22:54:21 +0000 (22:54 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/xml_signature@1627434 13f79535-47bb-0310-9956-ffa450edef68

src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java
src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java

index cd01d60c0554038f966c2a9f4be27a161f39c502..6ebb78439252a23ff127df368c0ce883dbdf2cbb 100644 (file)
@@ -44,8 +44,10 @@ import java.security.cert.X509Certificate;
 import java.util.ArrayList;\r
 import java.util.Collections;\r
 import java.util.HashMap;\r
+import java.util.Iterator;\r
 import java.util.List;\r
 import java.util.Map;\r
+import java.util.NoSuchElementException;\r
 \r
 import javax.crypto.Cipher;\r
 import javax.xml.crypto.MarshalException;\r
@@ -142,6 +144,58 @@ public class SignatureInfo implements SignatureConfigurable {
 \r
     public static final byte[] RIPEMD256_DIGEST_INFO_PREFIX = new byte[]\r
         { 0x30, 0x2b, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x03, 0x04, 0x20 };\r
+\r
+    private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);\r
+    private static boolean isInitialized = false;\r
+    \r
+    private SignatureConfig signatureConfig;\r
+\r
+    public class SignaturePart {\r
+        private final PackagePart signaturePart;\r
+        private X509Certificate signer;\r
+        \r
+        private SignaturePart(PackagePart signaturePart) {\r
+            this.signaturePart = signaturePart;\r
+        }\r
+        \r
+        public PackagePart getPackagePart() {\r
+            return signaturePart;\r
+        }\r
+        \r
+        public X509Certificate getSigner() {\r
+            return signer;\r
+        }\r
+        \r
+        public SignatureDocument getSignatureDocument() throws IOException, XmlException {\r
+            // TODO: check for XXE\r
+            return SignatureDocument.Factory.parse(signaturePart.getInputStream());\r
+        }\r
+        \r
+        public boolean validate() {\r
+            KeyInfoKeySelector keySelector = new KeyInfoKeySelector();\r
+            try {\r
+                Document doc = DocumentHelper.readDocument(signaturePart.getInputStream());\r
+                registerIds(doc);\r
+                \r
+                DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc);\r
+                domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE);\r
+                domValidateContext.setURIDereferencer(signatureConfig.getUriDereferencer());\r
+    \r
+                XMLSignatureFactory xmlSignatureFactory = getSignatureFactory();\r
+                XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext);\r
+                boolean valid = xmlSignature.validate(domValidateContext);\r
+\r
+                if (valid) {\r
+                    signer = keySelector.getCertificate();\r
+                }\r
+                \r
+                return valid;\r
+            } catch (Exception e) {\r
+                LOG.log(POILogger.ERROR, "error in marshalling and validating the signature", e);\r
+                return false;\r
+            }\r
+        }\r
+    }\r
     \r
     protected static class SignCreationListener implements EventListener, SignatureConfigurable {\r
         ThreadLocal<EventTarget> target = new ThreadLocal<EventTarget>();\r
@@ -168,11 +222,10 @@ public class SignatureInfo implements SignatureConfigurable {
     }\r
     \r
     \r
-    private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);\r
-    private static boolean isInitialized = false;\r
+    public SignatureInfo() {\r
+        initXmlProvider();        \r
+    }\r
     \r
-    private SignatureConfig signatureConfig;\r
-\r
     public SignatureConfig getSignatureConfig() {\r
         return signatureConfig;\r
     }\r
@@ -182,10 +235,12 @@ public class SignatureInfo implements SignatureConfigurable {
     }\r
 \r
     public boolean verifySignature() {\r
-        initXmlProvider();\r
         // http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html\r
-        List<X509Certificate> signers = new ArrayList<X509Certificate>();\r
-        return getSignersAndValidate(signers, true);\r
+        for (SignaturePart sp : getSignatureParts()){\r
+            // only validate first part\r
+            return sp.validate();\r
+        }\r
+        return false;\r
     }\r
 \r
     public void confirmSignature()\r
@@ -218,77 +273,50 @@ public class SignatureInfo implements SignatureConfigurable {
         }\r
     }\r
     \r
-    public List<X509Certificate> getSigners() {\r
-        initXmlProvider();\r
-        List<X509Certificate> signers = new ArrayList<X509Certificate>();\r
-        getSignersAndValidate(signers, false);\r
-        return signers;\r
-    }\r
-    \r
-    protected boolean getSignersAndValidate(List<X509Certificate> signers, boolean onlyFirst) {\r
-        signatureConfig.init(true);\r
-        \r
-        boolean allValid = true;\r
-        List<PackagePart> signatureParts = getSignatureParts(onlyFirst);\r
-        if (signatureParts.isEmpty()) {\r
-            LOG.log(POILogger.DEBUG, "no signature resources");\r
-            allValid = false;\r
-        }\r
-\r
-        for (PackagePart signaturePart : signatureParts) {\r
-            KeyInfoKeySelector keySelector = new KeyInfoKeySelector();\r
-\r
-            try {\r
-                Document doc = DocumentHelper.readDocument(signaturePart.getInputStream());\r
-                registerIds(doc);\r
-                \r
-                DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc);\r
-                domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE);\r
-                domValidateContext.setURIDereferencer(signatureConfig.getUriDereferencer());\r
-    \r
-                XMLSignatureFactory xmlSignatureFactory = getSignatureFactory();\r
-                XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext);\r
-                boolean validity = xmlSignature.validate(domValidateContext);\r
-                allValid &= validity;\r
-                if (!validity) continue;\r
-                // TODO: check what has been signed.\r
-            } catch (Exception e) {\r
-                LOG.log(POILogger.ERROR, "error in marshalling and validating the signature", e);\r
-                continue;\r
-            }\r
-\r
-            X509Certificate signer = keySelector.getCertificate();\r
-            signers.add(signer);\r
-        }\r
-        \r
-        return allValid;\r
-    }\r
-\r
-    protected List<PackagePart> getSignatureParts(boolean onlyFirst) {\r
-        List<PackagePart> packageParts = new ArrayList<PackagePart>();\r
-        OPCPackage pkg = signatureConfig.getOpcPackage();\r
-        \r
-        PackageRelationshipCollection sigOrigRels = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);\r
-        for (PackageRelationship rel : sigOrigRels) {\r
-            PackagePart sigPart = pkg.getPart(rel);\r
-            LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart);\r
-\r
-            try {\r
-                PackageRelationshipCollection sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE);\r
-                for (PackageRelationship sigRel : sigRels) {\r
-                    PackagePart sigRelPart = sigPart.getRelatedPart(sigRel); \r
-                    LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart);\r
-                    packageParts.add(sigRelPart);\r
-                    if (onlyFirst) break;\r
-                }\r
-            } catch (InvalidFormatException e) {\r
-                LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);\r
+    public Iterable<SignaturePart> getSignatureParts() {\r
+        return new Iterable<SignaturePart>() {\r
+            public Iterator<SignaturePart> iterator() {\r
+                return new Iterator<SignaturePart>() {\r
+                    OPCPackage pkg = signatureConfig.getOpcPackage();\r
+                    Iterator<PackageRelationship> sigOrigRels = \r
+                        pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN).iterator();\r
+                    Iterator<PackageRelationship> sigRels = null;\r
+                    PackagePart sigPart = null;\r
+                    \r
+                    public boolean hasNext() {\r
+                        while (sigRels == null || !sigRels.hasNext()) {\r
+                            if (!sigOrigRels.hasNext()) return false;\r
+                            sigPart = pkg.getPart(sigOrigRels.next());\r
+                            LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart);\r
+                            try {\r
+                                sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE).iterator();\r
+                            } catch (InvalidFormatException e) {\r
+                                LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);\r
+                            }\r
+                        }\r
+                        return true;\r
+                    }\r
+                    \r
+                    public SignaturePart next() {\r
+                        PackagePart sigRelPart = null;\r
+                        do {\r
+                            try {\r
+                                if (!hasNext()) throw new NoSuchElementException();\r
+                                sigRelPart = sigPart.getRelatedPart(sigRels.next()); \r
+                                LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart);\r
+                            } catch (InvalidFormatException e) {\r
+                                LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);\r
+                            }\r
+                        } while (sigPart == null);\r
+                        return new SignaturePart(sigRelPart);\r
+                    }\r
+                    \r
+                    public void remove() {\r
+                        throw new UnsupportedOperationException();\r
+                    }\r
+                };\r
             }\r
-            \r
-            if (onlyFirst && !packageParts.isEmpty()) break;\r
-        }\r
-\r
-        return packageParts;\r
+        };\r
     }\r
     \r
     public static XMLSignatureFactory getSignatureFactory() {\r
index 4eca018a5881966f2343c5f5a2988d0639a99cc4..77299c937d589d13b232c5d5e74448ad0d361707 100644 (file)
@@ -45,19 +45,16 @@ import java.util.ArrayList;
 import java.util.Calendar;\r
 import java.util.Collections;\r
 import java.util.Date;\r
+import java.util.Iterator;\r
 import java.util.List;\r
 import java.util.TimeZone;\r
 \r
-import javax.xml.crypto.KeySelector;\r
-import javax.xml.crypto.dsig.XMLSignature;\r
-import javax.xml.crypto.dsig.XMLSignatureFactory;\r
-import javax.xml.crypto.dsig.dom.DOMValidateContext;\r
-\r
 import org.apache.poi.POIDataSamples;\r
 import org.apache.poi.openxml4j.opc.OPCPackage;\r
 import org.apache.poi.openxml4j.opc.PackageAccess;\r
 import org.apache.poi.poifs.crypt.dsig.SignatureConfig;\r
 import org.apache.poi.poifs.crypt.dsig.SignatureInfo;\r
+import org.apache.poi.poifs.crypt.dsig.SignatureInfo.SignaturePart;\r
 import org.apache.poi.poifs.crypt.dsig.facets.EnvelopedSignatureFacet;\r
 import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet;\r
 import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet;\r
@@ -78,6 +75,7 @@ import org.etsi.uri.x01903.v13.DigestAlgAndValueType;
 import org.etsi.uri.x01903.v13.QualifyingPropertiesType;\r
 import org.junit.BeforeClass;\r
 import org.junit.Test;\r
+import org.w3.x2000.x09.xmldsig.ReferenceType;\r
 import org.w3.x2000.x09.xmldsig.SignatureDocument;\r
 import org.w3c.dom.Document;\r
 \r
@@ -122,7 +120,12 @@ public class TestSignatureInfo {
             sic.setOpcPackage(pkg);\r
             SignatureInfo si = new SignatureInfo();\r
             si.setSignatureConfig(sic);\r
-            List<X509Certificate> result = si.getSigners();\r
+            List<X509Certificate> result = new ArrayList<X509Certificate>();\r
+            for (SignaturePart sp : si.getSignatureParts()) {\r
+                if (sp.validate()) {\r
+                    result.add(sp.getSigner());\r
+                }\r
+            }\r
             pkg.revert();\r
             pkg.close();\r
             assertNotNull(result);\r
@@ -151,7 +154,12 @@ public class TestSignatureInfo {
             sic.setOpcPackage(pkg);\r
             SignatureInfo si = new SignatureInfo();\r
             si.setSignatureConfig(sic);\r
-            List<X509Certificate> result = si.getSigners();\r
+            List<X509Certificate> result = new ArrayList<X509Certificate>();\r
+            for (SignaturePart sp : si.getSignatureParts()) {\r
+                if (sp.validate()) {\r
+                    result.add(sp.getSigner());\r
+                }\r
+            }\r
 \r
             assertNotNull(result);\r
             assertEquals("test-file: "+testFile, 1, result.size());\r
@@ -172,7 +180,12 @@ public class TestSignatureInfo {
         sic.setOpcPackage(pkg);\r
         SignatureInfo si = new SignatureInfo();\r
         si.setSignatureConfig(sic);\r
-        List<X509Certificate> result = si.getSigners();\r
+        List<X509Certificate> result = new ArrayList<X509Certificate>();\r
+        for (SignaturePart sp : si.getSignatureParts()) {\r
+            if (sp.validate()) {\r
+                result.add(sp.getSigner());\r
+            }\r
+        }\r
 \r
         assertNotNull(result);\r
         assertEquals("test-file: "+testFile, 2, result.size());\r
@@ -207,12 +220,16 @@ public class TestSignatureInfo {
         si.setSignatureConfig(sic);\r
         // hash > sha1 doesn't work in excel viewer ...\r
         si.confirmSignature();\r
-        List<X509Certificate> signer = si.getSigners();\r
-        assertEquals(1, signer.size());\r
+        List<X509Certificate> result = new ArrayList<X509Certificate>();\r
+        for (SignaturePart sp : si.getSignatureParts()) {\r
+            if (sp.validate()) {\r
+                result.add(sp.getSigner());\r
+            }\r
+        }\r
+        assertEquals(1, result.size());\r
         pkg.close();\r
     }\r
 \r
-    @SuppressWarnings("unused")\r
     @Test\r
     public void testSignEnvelopingDocument() throws Exception {\r
         String testFile = "hello-world-unsigned.xlsx";\r
@@ -283,60 +300,45 @@ public class TestSignatureInfo {
         };\r
         signatureConfig.setRevocationDataService(revocationDataService);\r
 \r
+        // operate\r
         SignatureInfo si = new SignatureInfo();\r
         si.setSignatureConfig(signatureConfig);\r
+        si.confirmSignature();\r
         \r
-        Document document = DocumentHelper.createDocument();\r
-        \r
-        // operate\r
-        DigestInfo digestInfo = si.preSign(document, null);\r
-\r
         // verify\r
-        assertNotNull(digestInfo);\r
-        assertEquals(HashAlgorithm.sha1, digestInfo.hashAlgo);\r
-        assertNotNull(digestInfo.digestValue);\r
+        Iterator<SignaturePart> spIter = si.getSignatureParts().iterator();\r
+        assertTrue(spIter.hasNext());\r
+        SignaturePart sp = spIter.next();\r
+        boolean valid = sp.validate();\r
+        assertTrue(valid);\r
         \r
-        SignatureDocument sigDoc = SignatureDocument.Factory.parse(document);\r
-        String certDigestXQuery =\r
-                "declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; "\r
-              + "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; "\r
-              + "$this/ds:Signature/ds:Object/xades:QualifyingProperties/xades:SignedProperties/xades:SignedSignatureProperties/xades:SigningCertificate/xades:Cert/xades:CertDigest";\r
+        SignatureDocument sigDoc = sp.getSignatureDocument();\r
+        String declareNS = \r
+            "declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; "\r
+          + "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; ";\r
+        \r
+        String digestValXQuery = declareNS +\r
+            "$this/ds:Signature/ds:SignedInfo/ds:Reference";\r
+        for (ReferenceType rt : (ReferenceType[])sigDoc.selectPath(digestValXQuery)) {\r
+            assertNotNull(rt.getDigestValue());\r
+            assertEquals(HashAlgorithm.sha1.xmlSignUri, rt.getDigestMethod().getAlgorithm());\r
+        }\r
+\r
+        String certDigestXQuery = declareNS +\r
+            "$this//xades:SigningCertificate/xades:Cert/xades:CertDigest";\r
         XmlObject xoList[] = sigDoc.selectPath(certDigestXQuery);\r
         assertEquals(xoList.length, 1);\r
         DigestAlgAndValueType certDigest = (DigestAlgAndValueType)xoList[0];\r
         assertNotNull(certDigest.getDigestValue());\r
 \r
-        // Sign the received XML signature digest value.\r
-        byte[] signatureValue = si.signDigest(digestInfo.digestValue);\r
-\r
-        // Operate: postSign\r
-        si.postSign(document, signatureValue);\r
-        \r
-        DOMValidateContext domValidateContext = new DOMValidateContext(\r
-                KeySelector.singletonKeySelector(keyPair.getPublic()),\r
-                document);\r
-        XMLSignatureFactory xmlSignatureFactory = SignatureInfo.getSignatureFactory();\r
-        XMLSignature xmlSignature = xmlSignatureFactory\r
-                .unmarshalXMLSignature(domValidateContext);\r
-        boolean validity = xmlSignature.validate(domValidateContext);\r
-        assertTrue(validity);\r
-\r
-        sigDoc = SignatureDocument.Factory.parse(document);\r
-        xoList = sigDoc.selectPath(certDigestXQuery);\r
-        assertEquals(xoList.length, 1);\r
-        certDigest = (DigestAlgAndValueType)xoList[0];\r
-        assertNotNull(certDigest.getDigestValue());\r
-        \r
-        String qualPropXQuery =\r
-                "declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; "\r
-              + "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; "\r
-              + "$this/ds:Signature/ds:Object/xades:QualifyingProperties";\r
+        String qualPropXQuery = declareNS +\r
+            "$this/ds:Signature/ds:Object/xades:QualifyingProperties";\r
         xoList = sigDoc.selectPath(qualPropXQuery);\r
         assertEquals(xoList.length, 1);\r
         QualifyingPropertiesType qualProp = (QualifyingPropertiesType)xoList[0];\r
         boolean qualPropXsdOk = qualProp.validate();\r
         assertTrue(qualPropXsdOk);\r
-        \r
+\r
         pkg.close();\r
     }\r
     \r
@@ -374,8 +376,13 @@ public class TestSignatureInfo {
 \r
         // verify: signature\r
         si.getSignatureConfig().setOpcPackage(pkgCopy);\r
-        List<X509Certificate> signers = si.getSigners();\r
-        assertEquals(signerCount, signers.size());\r
+        List<X509Certificate> result = new ArrayList<X509Certificate>();\r
+        for (SignaturePart sp : si.getSignatureParts()) {\r
+            if (sp.validate()) {\r
+                result.add(sp.getSigner());\r
+            }\r
+        }\r
+        assertEquals(signerCount, result.size());\r
 \r
         return pkgCopy;\r
     }\r