You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@santuario.apache.org by co...@apache.org on 2016/09/20 13:54:14 UTC

svn commit: r1761578 - in /santuario/xml-security-java/trunk/src: main/java/org/apache/xml/security/signature/ test/java/org/apache/xml/security/test/dom/signature/ test/resources/org/apache/xml/security/samples/input/

Author: coheigea
Date: Tue Sep 20 13:54:14 2016
New Revision: 1761578

URL: http://svn.apache.org/viewvc?rev=1761578&view=rev
Log:
SANTUARIO-451 Creating and validating signatures using pre-calculated external reference digest

Added:
    santuario/xml-security-java/trunk/src/test/java/org/apache/xml/security/test/dom/signature/PreCalculatedDigestSignatureTest.java
    santuario/xml-security-java/trunk/src/test/resources/org/apache/xml/security/samples/input/signatureWithExternalReference.xml
Modified:
    santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/signature/Reference.java
    santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/signature/XMLSignatureInput.java

Modified: santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/signature/Reference.java
URL: http://svn.apache.org/viewvc/santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/signature/Reference.java?rev=1761578&r1=1761577&r2=1761578&view=diff
==============================================================================
--- santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/signature/Reference.java (original)
+++ santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/signature/Reference.java Tue Sep 20 13:54:14 2016
@@ -708,6 +708,10 @@ public class Reference extends Signature
      */
     private byte[] calculateDigest(boolean validating)
         throws ReferenceNotInitializedException, XMLSignatureException {
+        XMLSignatureInput input = this.getContentsBeforeTransformation();
+        if(input.isPreCalculatedDigest()) {
+            return getPreCalculatedDigest(input);
+        }
         OutputStream os = null;
         try {
             MessageDigestAlgorithm mda = this.getMessageDigestAlgorithm();
@@ -755,6 +759,26 @@ public class Reference extends Signature
         }
     }
 
+    /**
+     * Get the pre-calculated digest value from the XMLSignatureInput.
+     *
+     * @param input XMLSignature
+     * @return a pre-calculated digest value.
+     * @throws ReferenceNotInitializedException if there is an error decoding digest value
+     * in Base64. Properly encoded pre-calculated digest value must be set.
+     */
+    private byte[] getPreCalculatedDigest(XMLSignatureInput input)
+            throws ReferenceNotInitializedException {
+        try {
+        log.debug("Verifying element with pre-calculated digest");
+        String preCalculatedDigest = input.getPreCalculatedDigest();
+            return Base64.decode(preCalculatedDigest);
+        } catch (Base64DecodingException e) {
+            log.error("Failed to decode pre-calculated digest in base64: " + e.getMessage());
+            throw new ReferenceNotInitializedException(e);
+        }
+    }
+
     /**
      * Returns the digest value.
      *

Modified: santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/signature/XMLSignatureInput.java
URL: http://svn.apache.org/viewvc/santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/signature/XMLSignatureInput.java?rev=1761578&r1=1761577&r2=1761578&view=diff
==============================================================================
--- santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/signature/XMLSignatureInput.java (original)
+++ santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/signature/XMLSignatureInput.java Tue Sep 20 13:54:14 2016
@@ -112,6 +112,11 @@ public class XMLSignatureInput {
     private OutputStream outputStream = null;
 
     /**
+     * Pre-calculated digest value of the object in base64.
+     */
+    private String preCalculatedDigest;
+
+    /**
      * Construct a XMLSignatureInput from an octet array.
      * <p>
      * This is a comfort method, which internally converts the byte[] array into
@@ -154,6 +159,15 @@ public class XMLSignatureInput {
     }
 
     /**
+     * Construct a <code>XMLSignatureInput</code> from a known digest value in Base64.
+     * This makes it possible to compare the element digest with the provided digest value.
+     * @param preCalculatedDigest digest value in base64.
+     */
+    public XMLSignatureInput(String preCalculatedDigest) {
+        this.preCalculatedDigest = preCalculatedDigest;
+    }
+
+    /**
      * Check if the structure needs to be expanded.
      * @return true if so.
      */
@@ -323,6 +337,14 @@ public class XMLSignatureInput {
     }
 
     /**
+     * Determines if the object has been set up with a pre-calculated digest.
+     * @return
+     */
+    public boolean isPreCalculatedDigest() {
+        return preCalculatedDigest != null;
+    }
+
+    /**
      * Is the object correctly set up?
      *
      * @return true if the object has been set up correctly
@@ -584,4 +606,7 @@ public class XMLSignatureInput {
         this.secureValidation = secureValidation;
     }
 
+    public String getPreCalculatedDigest() {
+        return preCalculatedDigest;
+    }
 }

Added: santuario/xml-security-java/trunk/src/test/java/org/apache/xml/security/test/dom/signature/PreCalculatedDigestSignatureTest.java
URL: http://svn.apache.org/viewvc/santuario/xml-security-java/trunk/src/test/java/org/apache/xml/security/test/dom/signature/PreCalculatedDigestSignatureTest.java?rev=1761578&view=auto
==============================================================================
--- santuario/xml-security-java/trunk/src/test/java/org/apache/xml/security/test/dom/signature/PreCalculatedDigestSignatureTest.java (added)
+++ santuario/xml-security-java/trunk/src/test/java/org/apache/xml/security/test/dom/signature/PreCalculatedDigestSignatureTest.java Tue Sep 20 13:54:14 2016
@@ -0,0 +1,246 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.xml.security.test.dom.signature;
+
+import org.apache.xml.security.exceptions.XMLSecurityException;
+import org.apache.xml.security.signature.XMLSignature;
+import org.apache.xml.security.signature.XMLSignatureInput;
+import org.apache.xml.security.transforms.TransformationException;
+import org.apache.xml.security.transforms.Transforms;
+import org.apache.xml.security.utils.Constants;
+import org.apache.xml.security.utils.XMLUtils;
+import org.apache.xml.security.utils.resolver.ResourceResolverContext;
+import org.apache.xml.security.utils.resolver.ResourceResolverException;
+import org.apache.xml.security.utils.resolver.ResourceResolverSpi;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.*;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class PreCalculatedDigestSignatureTest {
+
+    private static Logger log = LoggerFactory.getLogger(PreCalculatedDigestSignatureTest.class);
+
+    /**
+     * External resource name to be signed
+     */
+    private static final String EXTERNAL_DOCUMENT_URI = "test.txt";
+
+    /**
+     * External resource pre-calculated digest value in base64
+     */
+    private static final String PRE_CALCULATED_DIGEST = "tYpuWTmktpzSwRM8cxRlZfY4aw4wqr4vkXKPs9lwxP4=";
+
+    private static final char[] PASSWORD = "changeit".toCharArray();
+    private static final String ALIAS = "mullan";
+    private String signatureFilePath;
+
+    @Rule
+    public TemporaryFolder testFolder = new TemporaryFolder();
+    private PrivateKey privateKey;
+    private X509Certificate signingCert;
+
+    @Before
+    public void setUp() throws Exception {
+        org.apache.xml.security.Init.init();
+        signatureFilePath = getAbsolutePath("src/test/resources/org/apache/xml/security/samples/input/signatureWithExternalReference.xml");
+        KeyStore keyStore = openKeyStore();
+        privateKey = (PrivateKey) keyStore.getKey(ALIAS, PASSWORD);
+        signingCert = (X509Certificate) keyStore.getCertificate(ALIAS);
+    }
+
+    @Test
+    public void validateSignatureWithCorrectDigestShouldBeValid() throws Exception {
+        XMLSignature signature = openSignature(signatureFilePath);
+        //Add resource resolver for the external document (test.txt) with the pre-calculated digest (valid for this test)
+        ExternalResourceResolver resolver = new ExternalResourceResolver(EXTERNAL_DOCUMENT_URI, PRE_CALCULATED_DIGEST);
+        signature.addResourceResolver(resolver);
+        boolean validSignature = validateSignature(signature);
+        assertTrue(validSignature);
+    }
+
+    @Test
+    public void validateSignatureWithWrongDigestShouldBeInvalid() throws Exception {
+        XMLSignature signature = openSignature(signatureFilePath);
+        //Add resource resolver for the external document (test.txt) with the pre-calculated digest (invalid for this test)
+        ExternalResourceResolver resolver = new ExternalResourceResolver(EXTERNAL_DOCUMENT_URI, "BjVs1oFu54LZwQuUA+kHgZApH0pIc8PGOoo0YrLrNUI=");
+        signature.addResourceResolver(resolver);
+        boolean validSignature = validateSignature(signature);
+        assertFalse(validSignature);
+    }
+
+    @Test
+    public void createSignatureWithPreCalculatedDigestShouldBeValid() throws Exception {
+        XMLSignature signature = createXmlSignature();
+
+        //Add external URI. This is a detached Reference.
+        signature.addDocument(EXTERNAL_DOCUMENT_URI, null, "http://www.w3.org/2001/04/xmlenc#sha256");
+        //Add resource resolver for the external document with pre-calculated digest
+        signature.addResourceResolver(new ExternalResourceResolver(EXTERNAL_DOCUMENT_URI, PRE_CALCULATED_DIGEST));
+
+        signature.addKeyInfo(signingCert);
+        signature.sign(privateKey);
+
+        writeSignature(signature.getDocument());
+        assertTrue(signature.checkSignatureValue(signingCert));
+    }
+
+    private XMLSignature openSignature(String signatureFile) throws ParserConfigurationException, SAXException, IOException, XMLSecurityException {
+        DocumentBuilder builder = createDocumentBuilder();
+        Document document = builder.parse(new File(signatureFile));
+        Element root = document.getDocumentElement();
+        Element signatureDocument = (Element) root.getFirstChild();
+        String baseURI = "";
+        XMLSignature signature = new XMLSignature(signatureDocument, baseURI);
+        return signature;
+    }
+
+    private boolean validateSignature(XMLSignature signature) throws XMLSecurityException {
+        PublicKey publicKey = signature.getKeyInfo().getPublicKey();
+        boolean validSignature = signature.checkSignatureValue(publicKey);
+        log.debug("Is signature valid: " + validSignature);
+        return validSignature;
+    }
+
+    private XMLSignature createXmlSignature() throws ParserConfigurationException, XMLSecurityException {
+        DocumentBuilder documentBuilder = createDocumentBuilder();
+        Document signatureDocument = documentBuilder.newDocument();
+        Element root = createSignatureRoot(signatureDocument);
+
+        String baseURI = "";
+        XMLSignature signature = new XMLSignature(signatureDocument, baseURI, XMLSignature.ALGO_ID_SIGNATURE_DSA);
+        root.appendChild(signature.getElement());
+
+        Transforms transforms = createTransformsForSignature(signatureDocument);
+        signature.addDocument("", transforms, "http://www.w3.org/2001/04/xmlenc#sha256");
+        return signature;
+    }
+
+    private DocumentBuilder createDocumentBuilder() throws ParserConfigurationException {
+        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+        documentBuilderFactory.setNamespaceAware(true);
+        return documentBuilderFactory.newDocumentBuilder();
+    }
+
+    private Transforms createTransformsForSignature(Document signatureDocument) throws TransformationException {
+        Transforms transforms = new Transforms(signatureDocument);
+        transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
+        transforms.addTransform(Transforms.TRANSFORM_C14N_OMIT_COMMENTS);
+        return transforms;
+    }
+
+    private Element createSignatureRoot(Document signatureDocument) {
+        Element root = signatureDocument.createElementNS("http://www.apache.org/ns/#app1", "apache:RootElement");
+        root.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:apache", "http://www.apache.org/ns/#app1");
+        signatureDocument.appendChild(root);
+        return root;
+    }
+
+    private void writeSignature(Document doc) throws IOException {
+        String signatureFilePath = testFolder.newFile("signature.xml").getPath();
+        FileOutputStream fileOutputStream = null;
+        try {
+            fileOutputStream = new FileOutputStream(signatureFilePath);
+            XMLUtils.outputDOMc14nWithComments(doc, fileOutputStream);
+            log.debug("Wrote signature to " + signatureFilePath);
+        } finally {
+            fileOutputStream.close();
+        }
+    }
+
+    private KeyStore openKeyStore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
+        FileInputStream fileInputStream = null;
+        try {
+            KeyStore keyStore = KeyStore.getInstance("JKS");
+            fileInputStream = new FileInputStream(getAbsolutePath("src/test/resources/test.jks"));
+            keyStore.load(fileInputStream, PASSWORD);
+            return keyStore;
+        } finally {
+            fileInputStream.close();
+        }
+    }
+
+    private String getAbsolutePath(String path) {
+        String basedir = System.getProperty("basedir");
+        if (basedir != null && !"".equals(basedir)) {
+            path = basedir + "/" + path;
+        }
+        return path;
+    }
+
+    /**
+     * Resolves external resources with pre-calculated digest.
+     */
+    public static class ExternalResourceResolver extends ResourceResolverSpi {
+
+        private final String externalDocumentUri;
+        private String preCalculatedDigest;
+
+        /**
+         * Constructor for resolving external resources with pre-calculated digest.
+         *
+         * @param externalDocumentUri external resource uri.
+         * @param preCalculatedDigest pre-calculated digest of the external resource.
+         */
+        public ExternalResourceResolver(String externalDocumentUri, String preCalculatedDigest) {
+            this.preCalculatedDigest = preCalculatedDigest;
+            this.externalDocumentUri = externalDocumentUri;
+        }
+
+        @Override
+        public XMLSignatureInput engineResolveURI(ResourceResolverContext context) throws ResourceResolverException {
+            String documentUri = extractDocumentUri(context);
+            XMLSignatureInput result = new XMLSignatureInput(preCalculatedDigest);
+            result.setSourceURI(documentUri);
+            result.setMIMEType("text/plain");
+            return result;
+        }
+
+        @Override
+        public boolean engineCanResolveURI(ResourceResolverContext context) {
+            String documentUri = extractDocumentUri(context);
+            return documentUri.equals(externalDocumentUri);
+        }
+
+        private String extractDocumentUri(ResourceResolverContext context) {
+            Attr uriAttr = context.attr;
+            return uriAttr.getNodeValue();
+        }
+    }
+}

Added: santuario/xml-security-java/trunk/src/test/resources/org/apache/xml/security/samples/input/signatureWithExternalReference.xml
URL: http://svn.apache.org/viewvc/santuario/xml-security-java/trunk/src/test/resources/org/apache/xml/security/samples/input/signatureWithExternalReference.xml?rev=1761578&view=auto
==============================================================================
--- santuario/xml-security-java/trunk/src/test/resources/org/apache/xml/security/samples/input/signatureWithExternalReference.xml (added)
+++ santuario/xml-security-java/trunk/src/test/resources/org/apache/xml/security/samples/input/signatureWithExternalReference.xml Tue Sep 20 13:54:14 2016
@@ -0,0 +1,38 @@
+<apache:RootElement xmlns:apache="http://www.apache.org/ns/#app1"><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+<ds:SignedInfo>
+<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:CanonicalizationMethod>
+<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"></ds:SignatureMethod>
+<ds:Reference URI="">
+<ds:Transforms>
+<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform>
+<ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:Transform>
+</ds:Transforms>
+<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod>
+<ds:DigestValue>OSxNn3inwJJE8LA+p5X2yuLE7nU1QVM8daQ/V6XJeCg=</ds:DigestValue>
+</ds:Reference>
+<ds:Reference URI="test.txt">
+<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod>
+<ds:DigestValue>tYpuWTmktpzSwRM8cxRlZfY4aw4wqr4vkXKPs9lwxP4=</ds:DigestValue>
+</ds:Reference>
+</ds:SignedInfo>
+<ds:SignatureValue>ItYAMa+fP9Te9p1s0SJsnhXTSxM+RpQr43p7NZwEjg0jTzqjiUnx8w==</ds:SignatureValue>
+<ds:KeyInfo>
+<ds:X509Data>
+<ds:X509Certificate>
+MIICtTCCAnMCBEfNt3IwCwYHKoZIzjgEAwUAMEAxEzARBgoJkiaJk/IsZAEZFgNjb20xEzARBgoJ&#xD;
+kiaJk/IsZAEZFgNzdW4xFDASBgNVBAMTC1NlYW4gTXVsbGFuMB4XDTA4MDMwNDIwNTYxOFoXDTA4&#xD;
+MDYwMjIwNTYxOFowQDETMBEGCgmSJomT8ixkARkWA2NvbTETMBEGCgmSJomT8ixkARkWA3N1bjEU&#xD;
+MBIGA1UEAxMLU2VhbiBNdWxsYW4wggG4MIIBLAYHKoZIzjgEATCCAR8CgYEA/X9TgR11EilS30qc&#xD;
+Luzk5/YRt1I870QAwx4/gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZUKWk&#xD;
+n5/oBHsQIsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HXKu/yIgMZndFI&#xD;
+AccCFQCXYFCPFSMLzLKSuYKi64QL8Fgc9QKBgQD34aCF1ps93su8q1w2uFe5eZSvu/o66oL5V0wL&#xD;
+PQeCZ1FZV4661FlP5nEHEIGAtEkWcSPoTCgWE7fPCTKMyKbhPBZ6i1R8jSjgo64eK7OmdZFuo38L&#xD;
++iE1YvH7YnoBJDvMpPG+qFGQiaiD3+Fa5Z8GkotmXoB7VSVkAUw7/s9JKgOBhQACgYEA5LRac3Qk&#xD;
+DCDOPaeNF5dJQ2r0hgIWZomZV7Z9pHrRqMoepJD5xnJpJY7aA4eUSS+AHS1qOm5I6VTZ68hsOdPZ&#xD;
+CDFF/DiR38BzTxi4ZD0PhtmOjBh32lSNG1nhEq6e9RsyzhUw5FVYHAPnCx2bX4/8Rz8iEMuG0IcC&#xD;
+iAbbzsCfGBwwCwYHKoZIzjgEAwUAAy8AMCwCFCHSZkGVH/yfDwefLd61JbAiRzmpAhQks4ZcvNPE&#xD;
+9b9MDU3azndUo1TEIA==
+</ds:X509Certificate>
+</ds:X509Data>
+</ds:KeyInfo>
+</ds:Signature></apache:RootElement>
\ No newline at end of file