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
+kiaJk/IsZAEZFgNzdW4xFDASBgNVBAMTC1NlYW4gTXVsbGFuMB4XDTA4MDMwNDIwNTYxOFoXDTA4
+MDYwMjIwNTYxOFowQDETMBEGCgmSJomT8ixkARkWA2NvbTETMBEGCgmSJomT8ixkARkWA3N1bjEU
+MBIGA1UEAxMLU2VhbiBNdWxsYW4wggG4MIIBLAYHKoZIzjgEATCCAR8CgYEA/X9TgR11EilS30qc
+Luzk5/YRt1I870QAwx4/gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZUKWk
+n5/oBHsQIsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HXKu/yIgMZndFI
+AccCFQCXYFCPFSMLzLKSuYKi64QL8Fgc9QKBgQD34aCF1ps93su8q1w2uFe5eZSvu/o66oL5V0wL
+PQeCZ1FZV4661FlP5nEHEIGAtEkWcSPoTCgWE7fPCTKMyKbhPBZ6i1R8jSjgo64eK7OmdZFuo38L
++iE1YvH7YnoBJDvMpPG+qFGQiaiD3+Fa5Z8GkotmXoB7VSVkAUw7/s9JKgOBhQACgYEA5LRac3Qk
+DCDOPaeNF5dJQ2r0hgIWZomZV7Z9pHrRqMoepJD5xnJpJY7aA4eUSS+AHS1qOm5I6VTZ68hsOdPZ
+CDFF/DiR38BzTxi4ZD0PhtmOjBh32lSNG1nhEq6e9RsyzhUw5FVYHAPnCx2bX4/8Rz8iEMuG0IcC
+iAbbzsCfGBwwCwYHKoZIzjgEAwUAAy8AMCwCFCHSZkGVH/yfDwefLd61JbAiRzmpAhQks4ZcvNPE
+9b9MDU3azndUo1TEIA==
+</ds:X509Certificate>
+</ds:X509Data>
+</ds:KeyInfo>
+</ds:Signature></apache:RootElement>
\ No newline at end of file