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!
}
}