Reputation: 21
I am stuck trying to get this to work but I keet getting an exception when I try to create a digital dignature for a specific element in my XML document. The source document is a SOAP envelope and the program parses the data from a file and creates the Document object. Basically what I am trying to do is to create a digital signature with multiple parts/ references...mainly the SOAP Body and the TimeStamp node under the Security node of the SOAP Header. So right now I am trying to sign the Body part of the SOAP message. The SOAP Body has a wsu:Id value and I am using that when creating the Reference object. I am using the securityNode reference for inserting the Signature node as that is where it should be anyway. I don't think this is an issue but figured I would state it just in case. I think I am doing this right but it's not working. I saw someone else posted the same issue but no answer was given.
I have tried so many different things and as long as I specify the URI for creating the Reference object then I get the exception. The weird thing is that the exception is thrown at the time of signing the context. Am I doing this right? How do I correct this? Any help is greatly appreciated.
//********* EXCEPTION *********
Exception in thread "main" javax.xml.crypto.dsig.XMLSignatureException: javax.xml.crypto.URIReferenceException: java.lang.NullPointerException at org.jcp.xml.dsig.internal.dom.DOMReference.dereference(Unknown Source) at org.jcp.xml.dsig.internal.dom.DOMReference.digest(Unknown Source) at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.digestReference(Unknown Source) at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source) at XMLDsigTester.main(XMLDsigTester.java:163) Caused by: javax.xml.crypto.URIReferenceException: java.lang.NullPointerException at org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference(Unknown Source) ... 5 more Caused by: java.lang.NullPointerException at com.sun.org.apache.xml.internal.security.utils.resolver.implementations.ResolverDirectHTTP.engineCanResolve(Unknown Source) at com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver.canResolve(Unknown Source) at com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver.getInstance(Unknown Source) ... 6 more javax.xml.crypto.URIReferenceException: java.lang.NullPointerException at org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference(Unknown Source) at org.jcp.xml.dsig.internal.dom.DOMReference.dereference(Unknown Source) at org.jcp.xml.dsig.internal.dom.DOMReference.digest(Unknown Source) at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.digestReference(Unknown Source) at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source) at XMLDsigTester.main(XMLDsigTester.java:163) Caused by: java.lang.NullPointerException at com.sun.org.apache.xml.internal.security.utils.resolver.implementations.ResolverDirectHTTP.engineCanResolve(Unknown Source) at com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver.canResolve(Unknown Source) at com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver.getInstance(Unknown Source) ... 6 more
//******************** JAVA CLass ********************
import javax.xml.crypto.*;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dom.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.*;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.security.*;
import java.util.Collections;
import java.util.Iterator;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.*;
public class XMLDsigTester
{
public static void main(String[] args) throws Exception
{
////////////// DECLARATIONS //////////////////
final String ENVELOPE_TAG = "Envelope";
final String HEADER_TAG = "Header";
final String SECURITY_TAG = "Security";
final String BODY_TAG = "Body";
final String SEPARATOR = ":";
Node envelopeNode = null;
Node headerNode = null;
Node bodyNode = null;
NodeList envelopeChildren = null;
NodeList headerChildren = null;
Node childNode = null;
Node securityNode = null;
String providerName = null;
String sEnvelopeNamespace = null;
String sFullHeaderTagName = null;
String sFullBodyTagName = null;
String sNodeName = null;
int iEnvelopeChildren;
int iHeaderChildren;
////////////// START OF LOGIC //////////////////
providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(new FileInputStream("myfile.xml"));
Provider providerObj = (Provider) Class.forName(providerName).newInstance();
/// Create references to the Envelope, Header, Body and Security nodes ///
envelopeNode = doc.getDocumentElement();
envelopeChildren = envelopeNode.getChildNodes();
iEnvelopeChildren = envelopeChildren.getLength();
sEnvelopeNamespace = envelopeNode.getPrefix();
if (sEnvelopeNamespace != null && !sEnvelopeNamespace.trim().equals(""))
{
sFullHeaderTagName = sEnvelopeNamespace.trim().concat(SEPARATOR).concat(HEADER_TAG);
sFullBodyTagName = sEnvelopeNamespace.trim().concat(SEPARATOR).concat(BODY_TAG);
}
else
{
sFullHeaderTagName = HEADER_TAG;
sFullBodyTagName = BODY_TAG;
}
for (int i=0; i < iEnvelopeChildren; i++)
{
sNodeName = null;
childNode = null;
childNode = envelopeChildren.item(i);
sNodeName = childNode.getNodeName().trim();
if (sNodeName.equalsIgnoreCase(sFullHeaderTagName))
headerNode = childNode;
else if (sNodeName.equalsIgnoreCase(sFullBodyTagName))
bodyNode = childNode;
}
headerChildren = headerNode.getChildNodes();
iHeaderChildren = headerChildren.getLength();
String sLocalNodeName = null;
for (int i=0; i < iHeaderChildren; i++)
{
sLocalNodeName = null;
sNodeName = null;
childNode = null;
childNode = headerChildren.item(i);
sNodeName = childNode.getNodeName().trim();
sLocalNodeName = childNode.getLocalName();
if (sLocalNodeName != null)
if (sLocalNodeName.trim().equalsIgnoreCase(SECURITY_TAG))
{
securityNode = childNode;
break;
}
}
/// Main logic for generating XML signature ///
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM", providerObj);
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair kp = kpg.generateKeyPair();
KeyInfoFactory kif = fac.getKeyInfoFactory();
KeyValue kv = kif.newKeyValue(kp.getPublic());
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
DOMSignContext dsc = new DOMSignContext(kp.getPrivate(), securityNode);
dsc.putNamespacePrefix("http://www.w3.org/2000/09/xmldsig#", "ds");
DigestMethod digestMethod = fac.newDigestMethod(DigestMethod.SHA1, null);
Transform transformObj = fac.newTransform(CanonicalizationMethod.EXCLUSIVE, (TransformParameterSpec) null);
Reference ref = fac.newReference("part-Body-4F4332715C4C1670E10080000A441E26", digestMethod, Collections.singletonList(transformObj), null, null);
CanonicalizationMethod canonMethodObj = fac.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null);
SignatureMethod signatureMethodObj = fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null);
SignedInfo si = fac.newSignedInfo(canonMethodObj, signatureMethodObj, Collections.singletonList(ref));
XMLSignature signature = fac.newXMLSignature(si, ki);
signature.sign(dsc);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(new FileOutputStream("mySignedFile.xml")));
}
} <br>
//******************** INPUT DATA ********************
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsu:Timestamp wsu:Id="ts-4F43326F5C4C1670E10080000A441E26">
<wsu:Created>2012-09-30T22:09:55Z</wsu:Created>
<wsu:Expires>2012-09-30T22:14:55Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
<wsa:Action soap-env:mustUnderstand="1" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
<wsa:MessageID xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">uuid:4f43f2ff-38aa-1a90-e100-80000a441e26</wsa:MessageID>
</soap-env:Header>
<soap-env:Body wsu:Id="part-Body-4F4332715C4C1670E10080000A441E26" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<PurchaseOrder>
<Customer>
<Name>Robert Smith</Name>
<CustomerId>788335</CustomerId>
</Customer>
<Item partNum="C763">
<ProductId>6883-JF3</ProductId>
<Quantity>3</Quantity>
<ShipDate>2002-09-03</ShipDate>
<Name>ThinkPad X20</Name>
</Item>
</PurchaseOrder>
</soap-env:Body>
</soap-env:Envelope>
Upvotes: 2
Views: 7647
Reputation: 747
I hope it’s not too late :). I’ve been doing a few tests on your code with your input. The first thing you need to know is you need to specify the node names using an “#nodeId”, which in your example should be “#part-Body-4F4332715C4C1670E10080000A441E26” (you are missing the ‘#’ symbol). The next problem is related to the "Id" which is in the “wsu” namespace; if it were only “Id” instead “wsu:Id” your code will locate the “soap-env:Body” node, but this isn’t the case. If you can add an additional “Id” attribute or modify the one of your example removing the "wsu" namespace preffix then you're done. If you don’t then you need a few modifications, adding a transform so you can select the node you want to sign. You should replace your lines:
Transform transformObj = fac.newTransform(CanonicalizationMethod.EXCLUSIVE, (TransformParameterSpec) null);
Reference ref = fac.newReference("part-Body-4F4332715C4C1670E10080000A441E26", digestMethod, Collections.singletonList(transformObj), null, null);
with:
List<Transform> transforms = new ArrayList<Transform>(2);
Map<String, String> namespaces = new HashMap<String, String>(1);
namespaces.put("soap-env", "http://schemas.xmlsoap.org/soap/envelope/");
XPathFilterParameterSpec paramsXpath = new XPathFilterParameterSpec("/soap-env:Envelope/soap-env:Body", namespaces);
transforms.add(fac.newTransform(Transform.XPATH, (TransformParameterSpec) paramsXpath));
Transform transformObj = fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null);
transforms.add(transformObj);
Reference ref = fac.newReference("", digestMethod, transforms, null, null);
In this new code you add a transform to the reference that selects the “soap-env:Body” node from the xml input you provided, using XPath. This node will be signed and added to the "Security" node.
Upvotes: 4