The Gilbert Arenas Dagger
The Gilbert Arenas Dagger

Reputation: 12751

XML Digital Signature Validation throws ResourceResolverException - Post Java Upgrade

I am in the process of updating an application that was stable in Java 6 to Java 8. The application uses SAML authentication, which now breaks during validation post Java update.

My validation code:

public static boolean isValid(Element target, KeySelector keySelector) {
    NodeList nodeList = target.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
    Node sigNode = nodeList.item(0);
    DOMValidateContext context = new DOMValidateContext(keySelector, sigNode);
    XMLSignatureFactory factory = XMLSignatureFactory.getInstance();
    try {
        XMLSignature signature = factory.unmarshalXMLSignature(context);
        if (!signature.validate(context)) { // Throws an XMLSignatureException
        ....

The stack trace is as follows:

java.lang.RuntimeException: javax.xml.crypto.dsig.XMLSignatureException: javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID [5db62480-f1ce-4ea1-be81-b1ebdcb1d9ec]
....
Caused by: javax.xml.crypto.dsig.XMLSignatureException: javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID [5db62480-f1ce-4ea1-be81-b1ebdcb1d9ec]
    at org.jcp.xml.dsig.internal.dom.DOMReference.dereference(DOMReference.java:436) ~[na:1.8.0_20]
    at org.jcp.xml.dsig.internal.dom.DOMReference.validate(DOMReference.java:398) ~[na:1.8.0_20]
    at com.mycompany.commons.authentication.saml.common.SignatureUtil.isValid(SignatureUtil.java:154) ~[classes/:na]
    ... 24 common frames omitted
Caused by: javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID [5db62480-f1ce-4ea1-be81-b1ebdcb1d9ec]
    at org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference(DOMURIDereferencer.java:121) ~[na:1.8.0_20]
    at org.jcp.xml.dsig.internal.dom.DOMReference.dereference(DOMReference.java:430) ~[na:1.8.0_20]
    ... 26 common frames omitted
Caused by: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID [5db62480-f1ce-4ea1-be81-b1ebdcb1d9ec]
    at com.sun.org.apache.xml.internal.security.utils.resolver.implementations.ResolverFragment.engineResolveURI(ResolverFragment.java:89) ~[na:1.8.0_20]
    at com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver.resolve(ResourceResolver.java:300) ~[na:1.8.0_20]
    at com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver.resolve(ResourceResolver.java:285) ~[na:1.8.0_20]
    at org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference(DOMURIDereferencer.java:114) ~[na:1.8.0_20]
    ... 27 common frames omitted

I've been struggling to debug this because I can't step into the source code, but I have managed to narrow in a little bit. Per this oracle guide, there are two phases of validation, Signature and Reference. I'm getting an Exception in the Reference phase. The revision below better isolates the issue.

    ...
    XMLSignatureFactory factory = XMLSignatureFactory.getInstance();
    try {
        XMLSignature signature = factory.unmarshalXMLSignature(context);
        List<Reference> references = DOMSignedInfo.getSignedInfoReferences(signature.getSignedInfo());

        // A DOMURIReference, its URI matches the value of the ID we are searching for 
        Reference reference = references.get(0);

        // The Reference component was what was breaking in the XMLSignature.validate method
        reference.validate(context); // Throws XMLSignatureException

It's also worth noting that I can get a Node from the above Reference component, which is a URI attribute, and its value matches the ID reported in the stack trace, except that it is prefixed with "#". I feel like I'm getting closer, but even if I could make this URI attribute an ID type, it still wouldn't be found because downstream when the ResolverFragment class tries to get an element by ID, the ID search parameter does not have the "#" prefix.

The root issue is detailed here: https://bugs.openjdk.java.net/browse/JDK-8017171. In this link, the following workaround options are noted:

  1. Use a validating schema which will register the elements with ID references.

    • Not sure if this is an option for me, it doesn't look like I own the SAML XML. At the very least, I haven't tracked it down yet... I tried setting it programmatically, as described here, but it didn't work.
  2. Register the ID elements with the DOMValidateContext.setIdAttributeNS method before validating the signature.

    • Not sure what to put in the method parameters or if this even applies in my case. The first method parameter calls for an Element, but I'm dealing with a javax.xml.crypto.dsig.Reference.
  3. Implement a custom URIDereferencer which can find these references and override the builtin URIDereferencer with the DOMValidateContext.setURIDereferencer method.

    • The main problem here is I don't know enough about what the URIDereferencer does to implement a custom one. If I could access the DOMURIDereferencer source code, which is being used, I would know more.

Similar questions have been asked, such as this question, but either the solution did not work for me or I was not able to apply the solution to my case. I haven't heard anyone mention the Reference components that seem to play a big role in the issue I'm facing.

How can I workaround this issue? Please help!

Upvotes: 3

Views: 5476

Answers (2)

Chesare
Chesare

Reputation: 383

I was looking to validate the Body part of a SOAP envelope, and I got this code working (I had to use "id" lowercase, otherwise it didn't worked out):

    DOMValidateContext valContext = new DOMValidateContext(publicKey, nl.item(0));
    NodeList nlb = doc.getElementsByTagNameNS("http://schemas.xmlsoap.org/soap/envelope/", "Body");
    valContext.putNamespacePrefix("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "wsu");
    Node body = nlb.item(0);
    valContext.setIdAttributeNS((Element)body, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "id");

XML look like:

    <soapenv:Body wsu:id="id-540d03b42e954c4ab5e3624b305b42fb4" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">

Thanks for your help!

Upvotes: 0

The Gilbert Arenas Dagger
The Gilbert Arenas Dagger

Reputation: 12751

My problem was I was too hung up on the Reference and not aware enough about the XML document I was working with.

Although I haven't actually seen the XML, via debugging I could derive that it looks something like this.

<Response>
    <Issuer>
    <Status>
        <StatusCode>
    </Status>
    <Assertion ID="THE ID WE NEED!!!!!">
        <Issuer>
        </Issuer>
        <Subject>
            <NameID
        </Subject>
        <Conditions NotBefore=".." NotOnOrAfter="...">
        </Conditions
        <AttributeStatement>
            <Attribute Name="...">
            </Attribute>
            <Attribute>
            </Attribute>
            ...
        </AttributeStatement>
        <Signature dsig="...">
           <SignedInfo>
              <CanonicalizationMethod Algorithm="..."> 
              <SignatureMethod Algorithm="..."> 
              <Reference uri="#...">
           </SignedInfo>
           <SignatureValue>
              ...
           </SignatureValue>
           <KeyInfo>
           </KeyInfo>
        </Signature>
    </Assertion>
</Response> 

The key here being the Assertion node and the ID attribute that is causing problems. Armed with this knowledge, and using the workaround #2 noted in the description, I was able to resolve the issue like so:

    ...
    Node sigNode = nodeList.item(0);
    DOMValidateContext context = new DOMValidateContext(keySelector, sigNode);
    context.setIdAttributeNS((Element) sigNode.getParentNode(), null, "ID");
    XMLSignatureFactory factory = XMLSignatureFactory.getInstance();
    try {
        XMLSignature signature = factory.unmarshalXMLSignature(context);
        if (!signature.validate(context)) { // works now!
        ...

Upvotes: 2

Related Questions