7

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?

Alexandre Thenorio
  • 2,288
  • 3
  • 31
  • 50
  • Yes, you are signing the complete XML (just by looking at ``). Can you show us the code you use to sign the XML document? – dusan Sep 20 '12 at 14:08
  • I'll try to put some of the code later once I am in front of it again. Yes the `uri=""` however this is due to using xpath instead to select which node is signed. Shouldn't that be the case? Also if I change other inner nodes such as header the signature is still valid which leads me to believe that something is working just not the way it should be. – Alexandre Thenorio Sep 20 '12 at 15:12
  • It looks OK to me. Can you list your XML after the xslt transform? Also, when you say you modified `msg` or `payload`, did you add a child node or do a rename? – pd40 Sep 21 '12 at 00:50
  • Actually I realized a little mistake I wrote above. I ama ctually signing the `ns2:data` element instead of addr. The xml after the transform changes the namespace at the msg element and the actual msg element itself (to something like newmsg) and also moves one of the namespaces of the addr element up to the root msg element (at that point called newmsg). This is enough to invalidate the signature even though the signed element is unchanged. – Alexandre Thenorio Sep 21 '12 at 06:00
  • I have added some more information – Alexandre Thenorio Sep 21 '12 at 07:40
  • I've hit a wall. It does not seem possible to sign only a part of the xml while not bothering all it's parents. Debugging shows it is signing said part but also adding some extra namespaces. Is it suppose to ignore namespaces when signing thus such a change is still valid or am I missing something? – Alexandre Thenorio Sep 24 '12 at 19:15

2 Answers2

1

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.

Alexandre Thenorio
  • 2,288
  • 3
  • 31
  • 50
0

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.

pd40
  • 3,187
  • 3
  • 20
  • 29
  • 1
    Thanks. I have tried using this: `List prefixList = new ArrayList(); prefixList.add("ns2"); C14NMethodParameterSpec c14NMethodParameterSpec = new ExcC14NParameterSpec(prefixList); canonicalisationMethod = xmlSigFactory.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, c14NMethodParameterSpec);` but no change happened. The signture seems to change depending on if I add more or less prefixes to the list but it still does not allow me to change the elements above what should be signed. – Alexandre Thenorio Sep 21 '12 at 11:29