Reputation: 12751
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:
Use a validating schema which will register the elements with ID references.
Register the ID elements with the DOMValidateContext.setIdAttributeNS method before validating the signature.
Implement a custom URIDereferencer which can find these references and override the builtin URIDereferencer with the DOMValidateContext.setURIDereferencer method.
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
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
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