You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@santuario.apache.org by Frank Cornelis <fc...@telenet.be> on 2006/02/03 13:11:56 UTC

XML signature breaks

Hi,

My XML signature breaks when I convert the signed DOM to String and back again to DOM. I'm using jdk1.5.0_06 with -Xbootclasspath/p:xalan-2.7.0.jar:xercesImpl-2.7.1.jar:xml-apis-2.0.2.jar:serializer-2.7.0.jar
The following JUnit test demonstrates the issue. Anyone any idea what I'm doing wrong here?

Thanks in advance,
Frank


package test.unit.signature;

import java.io.StringReader;
import java.io.StringWriter;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import junit.framework.TestCase;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xml.security.Init;
import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.transforms.Transforms;
import org.apache.xml.security.transforms.params.XPathContainer;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.XMLUtils;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

public class SignatureTest extends TestCase {

	private static final Log LOG = LogFactory.getLog(SignatureTest.class);

	public void testSignature() throws Exception {
		// create a document
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
				.newInstance();
		documentBuilderFactory.setNamespaceAware(true);
		javax.xml.parsers.DocumentBuilder documentBuilder = documentBuilderFactory
				.newDocumentBuilder();
		org.w3c.dom.Document testDocument = documentBuilder.newDocument();

		Element rootElement = testDocument.createElementNS("urn:namespace",
				"tns:document");
		/*
		 * XXX: Doing 'tns:document' here makes the verification to fail. With
		 * just 'document' it's OK, but then the namespace gets lost.
		 */
		testDocument.appendChild(rootElement);

		// generate keys
		KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
		PublicKey publicKey = keyPair.getPublic();
		PrivateKey privateKey = keyPair.getPrivate();

		// init xml-security
		Init.init();

		// create and add signature element
		XMLSignature signature = new XMLSignature(testDocument, null,
				XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512,
				Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS);
		Element signatureElement = signature.getElement();
		rootElement.appendChild(signatureElement);

		// add co-sign reference
		Transforms transforms = new Transforms(testDocument);
		XPathContainer xpath = new XPathContainer(testDocument);
		xpath.setXPathNamespaceContext("ds", Constants.SignatureSpecNS);
		xpath.setXPath("not(ancestor-or-self::ds:Signature)");
		transforms.addTransform(Transforms.TRANSFORM_XPATH, xpath
				.getElementPlusReturns());
		transforms.addTransform(Transforms.TRANSFORM_C14N_WITH_COMMENTS);
		signature.addDocument("", transforms,
				MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA512);

		// add public key
		signature.addKeyInfo(publicKey);

		// sign the document
		signature.sign(privateKey);

		// go from DOM to string
		Source source = new DOMSource(testDocument);
		StringWriter stringWriter = new StringWriter();
		Result result = new StreamResult(stringWriter);
		Transformer xformer = TransformerFactory.newInstance().newTransformer();
		xformer.transform(source, result);
		String strSignedDocument = stringWriter.getBuffer().toString();
		LOG.debug("signed document: " + strSignedDocument);

		// go from string to DOM
		DocumentBuilderFactory domFactory = DocumentBuilderFactory
				.newInstance();
		domFactory.setNamespaceAware(true);
		javax.xml.parsers.DocumentBuilder domBuilder = domFactory
				.newDocumentBuilder();
		StringReader stringReader = new StringReader(strSignedDocument);
		InputSource inputSource = new InputSource(stringReader);
		org.w3c.dom.Document signedDocument = domBuilder.parse(inputSource);

		// verify original test document
		NodeList signatureElems = XPathAPI.selectNodeList(testDocument,
				"//ds:Signature", XMLUtils.createDSctx(testDocument, "ds",
						Constants.SignatureSpecNS));
		signatureElement = (Element) signatureElems.item(0);
		XMLSignature signatureToVerify = new XMLSignature(signatureElement,
				null);

		boolean signOrigResult = signatureToVerify
				.checkSignatureValue(publicKey);

		assertTrue(signOrigResult);

		// verify from string loaded document
		signatureElems = XPathAPI.selectNodeList(signedDocument,
				"//ds:Signature", XMLUtils.createDSctx(signedDocument, "ds",
						Constants.SignatureSpecNS));
		signatureElement = (Element) signatureElems.item(0);
		signatureToVerify = new XMLSignature(signatureElement, null);

		boolean signResultLoaded = signatureToVerify
				.checkSignatureValue(publicKey);

		assertTrue(signResultLoaded); // XXX: bang!
	}
}






Re: XML signature breaks

Posted by Sean Mullan <Se...@Sun.COM>.
Frank Cornelis wrote:
> Hi,
> 
> My XML signature breaks when I convert the signed DOM to String and back again to DOM. I'm using jdk1.5.0_06 with -Xbootclasspath/p:xalan-2.7.0.jar:xercesImpl-2.7.1.jar:xml-apis-2.0.2.jar:serializer-2.7.0.jar
> The following JUnit test demonstrates the issue. Anyone any idea what I'm doing wrong here?

Yes.

You have to explicitly add a namespace attribute to the element that 
first defines the namespace:

Element rootElement = testDocument.createElementNS("urn:namespace",
				"tns:document");
// add this line
rootElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:tns
", "urn:namespace");


This did not appear as a namespace attribute until you serialized to a 
String and converted back to DOM. Hence the signature validation 
failure. (I don't know all the exact details as to why it has this 
behavior but maybe someone else on this list does). In any case the 
above fix should work.

--Sean

> 
> Thanks in advance,
> Frank
> 
> 
> package test.unit.signature;
> 
> import java.io.StringReader;
> import java.io.StringWriter;
> import java.security.KeyPair;
> import java.security.KeyPairGenerator;
> import java.security.PrivateKey;
> import java.security.PublicKey;
> 
> import javax.xml.parsers.DocumentBuilderFactory;
> import javax.xml.transform.Result;
> import javax.xml.transform.Source;
> import javax.xml.transform.Transformer;
> import javax.xml.transform.TransformerFactory;
> import javax.xml.transform.dom.DOMSource;
> import javax.xml.transform.stream.StreamResult;
> 
> import junit.framework.TestCase;
> 
> import org.apache.commons.logging.Log;
> import org.apache.commons.logging.LogFactory;
> import org.apache.xml.security.Init;
> import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
> import org.apache.xml.security.c14n.Canonicalizer;
> import org.apache.xml.security.signature.XMLSignature;
> import org.apache.xml.security.transforms.Transforms;
> import org.apache.xml.security.transforms.params.XPathContainer;
> import org.apache.xml.security.utils.Constants;
> import org.apache.xml.security.utils.XMLUtils;
> import org.apache.xpath.XPathAPI;
> import org.w3c.dom.Element;
> import org.w3c.dom.NodeList;
> import org.xml.sax.InputSource;
> 
> public class SignatureTest extends TestCase {
> 
> 	private static final Log LOG = LogFactory.getLog(SignatureTest.class);
> 
> 	public void testSignature() throws Exception {
> 		// create a document
> 		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
> 				.newInstance();
> 		documentBuilderFactory.setNamespaceAware(true);
> 		javax.xml.parsers.DocumentBuilder documentBuilder = documentBuilderFactory
> 				.newDocumentBuilder();
> 		org.w3c.dom.Document testDocument = documentBuilder.newDocument();
> 
> 		Element rootElement = testDocument.createElementNS("urn:namespace",
> 				"tns:document");
> 		/*
> 		 * XXX: Doing 'tns:document' here makes the verification to fail. With
> 		 * just 'document' it's OK, but then the namespace gets lost.
> 		 */
> 		testDocument.appendChild(rootElement);
> 
> 		// generate keys
> 		KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
> 		PublicKey publicKey = keyPair.getPublic();
> 		PrivateKey privateKey = keyPair.getPrivate();
> 
> 		// init xml-security
> 		Init.init();
> 
> 		// create and add signature element
> 		XMLSignature signature = new XMLSignature(testDocument, null,
> 				XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512,
> 				Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS);
> 		Element signatureElement = signature.getElement();
> 		rootElement.appendChild(signatureElement);
> 
> 		// add co-sign reference
> 		Transforms transforms = new Transforms(testDocument);
> 		XPathContainer xpath = new XPathContainer(testDocument);
> 		xpath.setXPathNamespaceContext("ds", Constants.SignatureSpecNS);
> 		xpath.setXPath("not(ancestor-or-self::ds:Signature)");
> 		transforms.addTransform(Transforms.TRANSFORM_XPATH, xpath
> 				.getElementPlusReturns());
> 		transforms.addTransform(Transforms.TRANSFORM_C14N_WITH_COMMENTS);
> 		signature.addDocument("", transforms,
> 				MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA512);
> 
> 		// add public key
> 		signature.addKeyInfo(publicKey);
> 
> 		// sign the document
> 		signature.sign(privateKey);
> 
> 		// go from DOM to string
> 		Source source = new DOMSource(testDocument);
> 		StringWriter stringWriter = new StringWriter();
> 		Result result = new StreamResult(stringWriter);
> 		Transformer xformer = TransformerFactory.newInstance().newTransformer();
> 		xformer.transform(source, result);
> 		String strSignedDocument = stringWriter.getBuffer().toString();
> 		LOG.debug("signed document: " + strSignedDocument);
> 
> 		// go from string to DOM
> 		DocumentBuilderFactory domFactory = DocumentBuilderFactory
> 				.newInstance();
> 		domFactory.setNamespaceAware(true);
> 		javax.xml.parsers.DocumentBuilder domBuilder = domFactory
> 				.newDocumentBuilder();
> 		StringReader stringReader = new StringReader(strSignedDocument);
> 		InputSource inputSource = new InputSource(stringReader);
> 		org.w3c.dom.Document signedDocument = domBuilder.parse(inputSource);
> 
> 		// verify original test document
> 		NodeList signatureElems = XPathAPI.selectNodeList(testDocument,
> 				"//ds:Signature", XMLUtils.createDSctx(testDocument, "ds",
> 						Constants.SignatureSpecNS));
> 		signatureElement = (Element) signatureElems.item(0);
> 		XMLSignature signatureToVerify = new XMLSignature(signatureElement,
> 				null);
> 
> 		boolean signOrigResult = signatureToVerify
> 				.checkSignatureValue(publicKey);
> 
> 		assertTrue(signOrigResult);
> 
> 		// verify from string loaded document
> 		signatureElems = XPathAPI.selectNodeList(signedDocument,
> 				"//ds:Signature", XMLUtils.createDSctx(signedDocument, "ds",
> 						Constants.SignatureSpecNS));
> 		signatureElement = (Element) signatureElems.item(0);
> 		signatureToVerify = new XMLSignature(signatureElement, null);
> 
> 		boolean signResultLoaded = signatureToVerify
> 				.checkSignatureValue(publicKey);
> 
> 		assertTrue(signResultLoaded); // XXX: bang!
> 	}
> }
> 
> 
> 
> 
>