Hi all. I need to sign a SAML2 message and I'm using JSR105 for this. I'm using my own implementation of SAML2 but I need to sign the assertions. I've created a message with the following code and is signed without problems. However, I can't validate the signature. As you can see the elements that contains the nodes are OMElement. I would be very pleased if you could tell me what's the problem (or if you can propose a better solution to sign SAML2 assertion, as far as I know the Axis2 security module doesn't use SAML2)
The error I get is: Exception in thread "main" javax.xml.crypto.dsig.XMLSignatureException: javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID 1 at org.jcp.xml.dsig.internal.dom.DOMReference.dereference(DOMReference.java :366) at org.jcp.xml.dsig.internal.dom.DOMReference.validate(DOMReference.java :318) at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.validate( DOMXMLSignature.java:230) at SignedSoap.main(SignedSoap.java:272) Caused by: javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID 1 at org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference( DOMURIDereferencer.java:84) at org.jcp.xml.dsig.internal.dom.DOMReference.dereference(DOMReference.java :358) ... 3 more Caused by: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID 1 at com.sun.org.apache.xml.internal.security.utils.resolver.implementations.ResolverFragment.engineResolve(Unknown Source) at com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver.resolve(Unknown Source) at org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference( DOMURIDereferencer.java:77) ... 4 more javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID 1 at org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference( DOMURIDereferencer.java:84) at org.jcp.xml.dsig.internal.dom.DOMReference.dereference(DOMReference.java :358) at org.jcp.xml.dsig.internal.dom.DOMReference.validate(DOMReference.java :318) at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.validate( DOMXMLSignature.java:230) at SignedSoap.main(SignedSoap.java:272) Caused by: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID 1 at com.sun.org.apache.xml.internal.security.utils.resolver.implementations.ResolverFragment.engineResolve(Unknown Source) at com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver.resolve(Unknown Source) at org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference( DOMURIDereferencer.java:77) ... 4 more My code is as follows import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.Provider; import java.security.SecureRandom; import java.util.Calendar; import java.util.Collections; import javax.xml.crypto.dsig.*; import javax.xml.crypto.dom.*; import javax.xml.crypto.dsig.dom.*; import javax.xml.crypto.dsig.keyinfo.*; import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; import javax.xml.soap.*; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.*; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.*; import org.apache.axiom.om.OMAttribute; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMNamespace; import org.apache.axiom.om.impl.dom.factory.OMDOMFactory; import org.apache.xmlbeans.impl.piccolo.xml.XMLStreamReader; import org.w3c.dom.*; import org.w3c.dom.Node; import org.xml.sax.InputSource; //import org.xmlsoap.schemas.soap.encoding.DateTime; /** * Construct a SOAP message, sign it and then validate the signature. * This implementation follows the * <a ref="http://www.w3.org/TR/SOAP-dsig/"> * W3C Note on digital signatures in SOAP messages * </a>. * The validating key is included in the signature. * DOM Level 2 is used throughout. * * The following SOAP message is signed: * <pre><code> * * <?xml version="1.0" encoding="UTF-8"?> * <soap-env:Envelope * xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"> * <soap-env:Header> * <SOAP-SEC:Signature * mustUnderstand="1" * xmlns:SOAP-SEC="http://schemas.xmlsoap.org/soap/security/2000-12"/> * </soap-env:Header> * <soap-env:Body id="Body"> * <m:GetLastTradePrice xmlns:m="http://wombats.ztrade.com"> * <symbol>SUNW</symbol> * </m:GetLastTradePrice> * </soap-env:Body> * </soap-env:Envelope> * * </code></pre> */ public class SignedSoap { private static boolean debug = false; public static void main(String[] args) throws Exception { int argc = args.length; if (argc == 1) { if (args[0].equalsIgnoreCase("-help")) { System.out.println("Usage: SignedSoap [-debug]"); System.out.println(" -debug\tactivates debug messages"); return; } debug = args[0].equalsIgnoreCase("-debug"); } // Create the SOAP message OMDOMFactory omfact=new OMDOMFactory(); //Node nodo1=fd.newDomNode(options); OMNamespace namespace=omfact.createOMNamespace(" http://schemas.xmlsoap.org/soap/security/2000-12", "soapenv"); OMNamespace namespace2=omfact.createOMNamespace(" http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "wsu"); OMAttribute attr=omfact.createOMAttribute("mustUnderstand",namespace,"1"); OMAttribute id=omfact.createOMAttribute("id",namespace,"1"); OMAttribute id2=omfact.createOMAttribute("id",namespace,"2"); OMElement sec=omfact.createOMElement("Security", " http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd ","wsse"); OMElement timestamp=omfact.createOMElement("Timestamp",namespace2); OMElement created=omfact.createOMElement("Created",namespace2); OMElement expires=omfact.createOMElement("Expires",namespace2); created.setText("12122007"); expires.setText("12122008"); timestamp.addAttribute(id); timestamp.addChild(created); timestamp.addChild(expires); sec.addChild(timestamp); System.out.println("Generating the DOM tree..."); // Get input source org.w3c.dom.Node security = (Node)sec; //if (debug) { // dumpDOMDocument(root); //} // Generate a DSA key pair System.out.println("Generating the DSA keypair..."); KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA"); kpg.initialize(1024, new SecureRandom("not so random".getBytes())); KeyPair keypair = kpg.generateKeyPair(); // Assemble the signature parts System.out.println("Preparing the signature..."); String providerName = System.getProperty ("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI"); XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance()); Reference ref = sigFactory.newReference("#1", sigFactory.newDigestMethod(DigestMethod.SHA1, null)); SignedInfo signedInfo = sigFactory.newSignedInfo( sigFactory.newCanonicalizationMethod( CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS, (C14NMethodParameterSpec) null), sigFactory.newSignatureMethod(SignatureMethod.DSA_SHA1, null), Collections.singletonList(ref)); KeyInfoFactory kif = sigFactory.getKeyInfoFactory(); KeyValue kv = kif.newKeyValue(keypair.getPublic()); KeyInfo keyInfo = kif.newKeyInfo(Collections.singletonList(kv)); XMLSignature sig = sigFactory.newXMLSignature(signedInfo, keyInfo); // Insert XML signature into DOM tree and sign System.out.println("Signing the SOAP message..."); // Find where to insert signature Element ts = getFirstChildElement(envelope); DOMSignContext sigContext = new DOMSignContext(keypair.getPrivate(), sec); // Need to distinguish the Signature element in DSIG (from that in SOAP) sigContext.putNamespacePrefix(XMLSignature.XMLNS, "ds"); // register Body ID attribute getNextSiblingElement( sigContext.setIdAttributeNS (ts, "http://schemas.xmlsoap.org/soap/security/2000-12","id"); sig.sign(sigContext); if (debug) { dumpDOMDocument(envelope); } // Validate the XML signature // Locate the signature element Element sigElement = getNextSiblingElement(ts); // Validate the signature using the public key generated above DOMValidateContext valContext = new DOMValidateContext(keypair.getPublic(), sigElement); // register Body ID attribute getNextSiblingElement( valContext.setIdAttributeNS (ts, "http://schemas.xmlsoap.org/soap/security/2000-12","id"); boolean isValid = sig.validate(valContext); System.out.println("Validating the signature... " + (isValid ? "valid" : "invalid")); } /* * Outputs DOM representation to the standard output stream. * * @param root The DOM representation to be outputted */ private static void dumpDOMDocument(org.w3c.dom.Node root) throws TransformerException, TransformerConfigurationException { System.out.println("\n"); // Create a new transformer object Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); // Dump the DOM representation to standard output transformer.transform(new DOMSource(root), new StreamResult(System.out)); System.out.println("\n"); } /** * Returns the first child element of the specified node, or null if there * is no such element. * * @param node the node * @return the first child element of the specified node, or null if there * is no such element * @throws NullPointerException if <code>node == null</code> */ private static Element getFirstChildElement(org.w3c.dom.Node node) { org.w3c.dom.Node child = node.getFirstChild(); while (child != null && child.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) { child = child.getNextSibling(); } return (Element) child; } /** * Returns the next sibling element of the specified node, or null if there * is no such element. * * @param node the node * @return the next sibling element of the specified node, or null if there * is no such element * @throws NullPointerException if <code>node == null</code> */ public static Element getNextSiblingElement(org.w3c.dom.Node node) { org.w3c.dom.Node sibling = node.getNextSibling(); while (sibling != null && sibling.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) { sibling = sibling.getNextSibling(); } return (Element) sibling; } } Thanks in advance, regards. Christina.
