I am trying to do some XML Signature by signing only parts of the xml however after much searching I have not been able to find a solution.
I am using java to sign an XML using Xpath2 transform and EXCLUSIVE canonicalization. If I have the following XML
<?xml version="1.0" encoding="UTF-8"?>
<msg xmlns="http://someaddress/ad/m1" xmlns:ns1="http://someotheraddres/ad/m2" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#">
<header>
<id>wsfrwerwerwer</id>
<name>addr</name>
<somenode>
<trace>ND</trace>
</somenode>
</header>
<payload><ns0:addr xmlns:ns0="http://someaddres/ad/m3"><ns2:data xmlns:ns2="http://someaddres/ad/m3">
<ns2:name>somevalue</ns2:name>
<ns2:value>354</ns2:value>
</ns2:data>
</ns0:addr>
</payload>
</msg>
And sign it, I get the following output (Real data replaced with dummy)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<msg xmlns="http://someaddress/ad/m1" xmlns:ns1="http://someotheraddres/ad/m2" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#">
<header>
<id>wsfrwerwerwer</id>
<name>addr</name>
<somenode>
<trace>ND</trace>
</somenode>
</header>
<payload>
<ns0:addr xmlns:ns0="http://someaddres/ad/m3">
<ns2:data xmlns:ns2="http://someaddres/ad/m3">
<ns2:name>somevalue</ns2:name>
<ns2:value>354</ns2:value>
</ns2:data>
</ns0:addr>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2002/06/xmldsig-filter2">
<XPath xmlns="http://www.w3.org/2002/06/xmldsig-filter2" xmlns:ns0="http://someaddres/ad/m3" Filter="intersect">//*[local-name()='addr']/*</XPath>
</Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>sdlfjdeklsdfngf</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>femhjgklnlkl</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>swerwerwrwerwerwe</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</payload>
</msg>
If I validate the signature, everything is fine however the issue here is that right after this I perform an XSLT in the XML which perform some changes to some elements but not the signed element (ns0:addr
) which is left intact. Even though I explicitly say that only the "addr" element should be signed, if I try to perform changes to any of its parents (payload
, msg
or addr
), it then fails the signature when (according to my understanding) it should not. If I perform changes to other elements such as anything inside header, the signature is still valid.
I have tested the XPath expression (//*[local-name()='addr']/*
) and it selects the correct data to be signed (ns2:data
) but it seems to be taking also all elements leading to it starting from the root element (msg
, addr
).
I have also tried to use different transforms such as UNION but that does not work at all.
Does anybody know what the issue might be? Is there any way, in Java, to see exactly what is being signed when signing the XML for debugging purposes?
EDIT:
The xslt run later will be doing things like moving namespaces from the ns0:addr element to the root element (msg) and also it will be changing the main element name and namespace from msg to newmsg (and a different default namespace) but leaving the signed data (ns2:data
) intact.
The code used to sign it is more or less the code mentioned here http://docs.oracle.com/javase/7/docs/technotes/guides/security/xmldsig/XMLDigitalSignature.html
Except instead of a ENVELOPED transform I am using an XPATH2 transform:
Map<String, String> namespaceMap = new HashMap<String, String>(0);
namespaceMap.put("ns0", "http://someaddres/ad/m3");
XPathType xPathType = new XPathType(xPathParameters, Filter.INTERSECT, namespaceMap);
List<XPathType> xPathList = new ArrayList<XPathType>(0);
xPathList.add(xPathType)
XPathFilter2ParameterSpec xPathFilter2ParameterSpec = new XPathFilter2ParameterSpec(xPathList);
transform = fac.newTransform(CanonicalizationMethod.XPATH2, xPathFilter2ParameterSpec);
And also instead of ENVELOPED I am using EXCLUSIVE
canonicalisationMethod = fac.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null);
EDIT2:
I have managed to enable finer debugging of the xml signing process and got the following:
FINER: Pre-digested input:
21-Sep-2012 10:51:39 org.jcp.xml.dsig.internal.DigesterOutputStream write
FINER: <ns2:data xmlns="http://someaddress/ad/m1" xmlns:ns0="http://someaddres/ad/m3" xmlns:ns1="http://someotheraddres/ad/m2" xmlns:ns2="http://someaddres/ad/m3"> <ns2:name>somevalue</ns2:name> <ns2:value>354</ns2:value> </ns2:data>
It seems to be signing the correct data however it is also adding some extra namespaces to the signature which makes me wonder if that is the issue since those namspaces are taken from the parent elements.
Anybody know how to make it not get all extra namespaces added?