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?
After much fighting with XML Signatures I finally arrived at an acceptable solution (though not ideal).
As it turns out an Exclusive canonicalization is not enough. You need to also add an Exclusive transform after all other transformers. Following the code snippets I wrote above:
List<Transform> transforms = new ArrayList<Transform>()
transforms.add(transform)
fac.newTransform(CanonicalizationMethod.EXCLUSIVE, (TransformParameterSpec) null)
This will make it so that any other namespaces outside of the signed elements will not be taken into account (though it has the added effect that inserting namespace(s) inside the signed element is allowed).
Also it seems that any element in the xpath to the signed element will be taken into account so if you have the following xpath /root/A/B
it will sign tag B however you will not be able to change the tag name of either A or root elements.
This can be overcome by using an xpath with less elements in it such as //B
.
I believe it may be possible to overcome this issue too though so far I have not been able to.
There are parameters, related to namespace, that can be passed to Exclusive XML Canonicalization that describe the InclusiveNamespaces PrefixList.
You could try passing a ExcC14NParameterSpec to newCanonicalizationMethod() using a prefix list to see if that affects the canonicalization of the namespaces.
来源:https://stackoverflow.com/questions/12512424/how-to-sign-only-specific-part-of-xml