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






Reply via email to