JDK-7094155 : JSR105 code throws javax.xml.crypto.URIReferenceException when running into Java 7 VM
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.xml.crypto
  • Affected Version: 7
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_7
  • CPU: x86
  • Submitted: 2011-09-23
  • Updated: 2012-10-23
  • Resolved: 2012-09-12
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8
7u4Fixed 8 b14Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
JSR105 code throws javax.xml.crypto.URIReference when running into Java 7 VM. A referenced node is not found.

REGRESSION.  Last worked in version 6u26

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See attached source code.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
XML signature should be produced.
ACTUAL -
a javax.xml.crypto.dsig.XMLSignatureException is thrown with Java 7.

XML signature is produced with Java 1.6.

ERROR MESSAGES/STACK TRACES THAT OCCUR :

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 Main.main(Main.java:332)
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.IdResolver.isElement(Unknown Source)
	at com.sun.org.apache.xml.internal.security.utils.IdResolver.getEl(Unknown Source)
	at com.sun.org.apache.xml.internal.security.utils.IdResolver.getElementBySearching(Unknown Source)
	at com.sun.org.apache.xml.internal.security.utils.IdResolver.getElementById(Unknown Source)
	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)
	... 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 Main.main(Main.java:332)
Caused by: java.lang.NullPointerException
	at com.sun.org.apache.xml.internal.security.utils.IdResolver.isElement(Unknown Source)
	at com.sun.org.apache.xml.internal.security.utils.IdResolver.getEl(Unknown Source)
	at com.sun.org.apache.xml.internal.security.utils.IdResolver.getElementBySearching(Unknown Source)
	at com.sun.org.apache.xml.internal.security.utils.IdResolver.getElementById(Unknown Source)
	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)
	... 6 more

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

import javax.xml.crypto.Data;
import javax.xml.crypto.NodeSetData;
import javax.xml.crypto.URIDereferencer;
import javax.xml.crypto.URIReference;
import javax.xml.crypto.URIReferenceException;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dom.DOMStructure;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.XMLObject;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.jcp.xml.dsig.internal.dom.ApacheNodeSetData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;

import com.sun.org.apache.xml.internal.security.utils.Base64;


public class JSR105Test {

	private static PrivateKey privateKey;
	private static java.security.cert.Certificate certificate = null;
	private static Element data;
	private static Element signedProperties;
		
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		try {
			
			XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM");
			String signatureId = "MySignatureId";
			
			// Retrieve a certificate object
			retrieveCertificateAndPrivateKey();
			if (null == certificate || null == privateKey) {
				throw new Exception("no certificate");
			}
						
			Document document = createDocument(null);
			data = createElement(document, "DummyData", null, null);
			CanonicalizationMethod canonicalizationMethod = null;
					
			List referencesList = new ArrayList();
			List objectsList = new ArrayList();
			
			String signedPropertiesId =  signatureId + "_SignedProperties";
			
			// xad:QualifyingProperties
			Element qualifyingProperties = createElement(document, "QualifyingProperties", "xad", "http://uri.etsi.org/01903/v1.3.2#");
			qualifyingProperties.setAttributeNS(null, "Target", "#"+signatureId);
			qualifyingProperties.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:"+"xad", "http://uri.etsi.org/01903/v1.3.2#");
			
			// xad:SignedProperties
			signedProperties = createElement(document, "SignedProperties", "xad", "http://uri.etsi.org/01903/v1.3.2#");
			signedProperties.setAttributeNS(null, "Id", signedPropertiesId);
			qualifyingProperties.appendChild(signedProperties);
			
			
			
			// xad:UnsignedProperties
			Element unsignedProperties = createElement(document, "UnsignedProperties", "xad", "http://uri.etsi.org/01903/v1.3.2#");
			qualifyingProperties.appendChild(unsignedProperties);
			DOMStructure qualifyingPropertiesStructure = new DOMStructure(qualifyingProperties);
			XMLObject xadesObject = xmlSignatureFactory.newXMLObject(Collections.singletonList(qualifyingPropertiesStructure), null, null, null);
			objectsList.add(xadesObject);
			
			// xad:SignedSignatureProperties
			Element signedSignatureProperties = createElement(document, "SignedSignatureProperties", "xad", "http://uri.etsi.org/01903/v1.3.2#");
			signedProperties.appendChild(signedSignatureProperties);
			
			// xad:SigningTime, yyyy-MM-dd'T'HH:mm:ssZ
			Element signingTime = createElement(document, "SigningTime", "xad", "http://uri.etsi.org/01903/v1.3.2#");
			Text signingTimeText = document.createTextNode(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(new Date()));
			signingTime.appendChild(signingTimeText);
			signedSignatureProperties.appendChild(signingTime);
			
			// xad:SigningCertificate
			Element signingCertificateElement = createElement(document, "SigningCertificate", "xad", "http://uri.etsi.org/01903/v1.3.2#");
			signedSignatureProperties.appendChild(signingCertificateElement);
			Element cert = createElement(document, "Cert", "xad", "http://uri.etsi.org/01903/v1.3.2#");
			signingCertificateElement.appendChild(cert);
			Element certDigest = createElement(document, "CertDigest", "xad", "http://uri.etsi.org/01903/v1.3.2#");
			cert.appendChild(certDigest);
			Element issuerSerial = createElement(document, "IssuerSerial", "xad", "http://uri.etsi.org/01903/v1.3.2#");
			cert.appendChild(issuerSerial);
			Element digestMethodElement = createElement(document, "DigestMethod", "ds", "http://www.w3.org/2000/09/xmldsig#");
			digestMethodElement.setAttribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
			Element digestValueElement = createElement(document, "DigestValue", "ds", "http://www.w3.org/2000/09/xmldsig#");
			try {
				byte[] hashBytes = Util.digestStream(new ByteArrayInputStream(certificate.getEncoded()));
			} catch (Exception e) {
				e.printStackTrace();
				throw new Exception("unable to compute certificate digest");
			}
			Text digestValueText = document.createTextNode(new String(Base64.encode(hashBytes));
			digestValueElement.appendChild(digestValueText);
			certDigest.appendChild(digestMethodElement);
			certDigest.appendChild(digestValueElement);
			Element x509IssuerName  = createElement(document, "X509IssuerName", "ds", "http://www.w3.org/2000/09/xmldsig#");
			issuerSerial.appendChild(x509IssuerName);
			Text x509IssuerNameText = document.createTextNode(certificate.getSubjectDistinguishedName());
			x509IssuerName.appendChild(x509IssuerNameText);
			Element x509IssuerSerialNumber  = createElement(document, "X509SerialNumber", "ds", "http://www.w3.org/2000/09/xmldsig#");
			issuerSerial.appendChild(x509IssuerSerialNumber);
			Text x509IssuerSerialNumberText = document.createTextNode(certificate.getSerialNumber().toString());
			x509IssuerSerialNumber.appendChild(x509IssuerSerialNumberText);
			
			// xad:SignaturePolicyIdentifier
			Element signaturePolicyIdentifier = createElement(document, "SignaturePolicyIdentifier", "xad", "http://uri.etsi.org/01903/v1.3.2#");
			signedSignatureProperties.appendChild(signaturePolicyIdentifier);
			
			try {
				C14NMethodParameterSpec c14nSpec = null;
				canonicalizationMethod = xmlSignatureFactory.newCanonicalizationMethod("http://www.w3.org/2001/10/xml-exc-c14n#", c14nSpec);
			}  catch (Exception e) {
				e.printStackTrace();
				throw new Exception("a cryptographic algorithm seems unsupported");
			}
			
			String xadesReferenceNodeId = signedPropertiesId + "_Reference";
			Reference xadesReference = xmlSignatureFactory.newReference("#"+signedPropertiesId, xmlSignatureFactory.newDigestMethod("http://www.w3.org/2000/09/xmldsig#sha1", null), Collections.singletonList(canonicalizationMethod), "http://uri.etsi.org/01903/#SignedProperties", xadesReferenceNodeId);
			referencesList.add(xadesReference);

			//
			// Prepare the data
			//
			XMLStructure dataContent = new DOMStructure(data);
		
			//
			// Prepare the transforms
			//
			SignatureMethod signatureMethod;
			C14NMethodParameterSpec c14nSpec = null;
			ArrayList standardTransformList = new ArrayList();
			
			try {
				signatureMethod = xmlSignatureFactory.newSignatureMethod("http://www.w3.org/2000/09/xmldsig#rsa-sha1", null);
				canonicalizationMethod = xmlSignatureFactory.newCanonicalizationMethod("http://www.w3.org/TR/2001/REC-xml-c14n-20010315", c14nSpec);
			}  catch (Exception e) {
				e.printStackTrace();
				throw new Exception("a cryptographic algorithm seems unsupported");
			}
	
			standardTransformList.add(canonicalizationMethod); // always c14n
			
			//
			// Build the data reference & object
			//
			int currentObjectCount = 0;
			String dataObjectNodeId = "";
			String dataReferenceUri = dataObjectNodeId;
			String dataReferenceType = null;
			String dataObjectMimeType = "text/xml";

			// use the signature Id for <ds:object>
			dataObjectNodeId = signatureId + "_D" + currentObjectCount++;
			dataReferenceUri = "#"+dataObjectNodeId;
			String dataReferenceNodeId = dataObjectNodeId + "_Reference";
							
			//
			// Add the data reference & object
			//
			Reference dataReference = xmlSignatureFactory.newReference(dataReferenceUri, xmlSignatureFactory.newDigestMethod("http://www.w3.org/2000/09/xmldsig#sha1", null), standardTransformList, dataReferenceType, dataReferenceNodeId);
			referencesList.add(0, dataReference);
			
			XMLObject dataObject = xmlSignatureFactory.newXMLObject(Collections.singletonList(dataContent), dataObjectNodeId, dataObjectMimeType, null);
			objectsList.add(0, dataObject);
			
			// SignedInfo creation from references
			try {
				canonicalizationMethod = xmlSignatureFactory.newCanonicalizationMethod("http://www.w3.org/2001/10/xml-exc-c14n#", c14nSpec);
			}  catch (Exception e) {
				e.printStackTrace();
				throw new Exception("a cryptographic algorithm seems unsupported");
			}
			SignedInfo signedInfo = xmlSignatureFactory.newSignedInfo(canonicalizationMethod, signatureMethod, referencesList);
					
			// KeyInfo creation
			KeyInfoFactory keyInfoFactory = xmlSignatureFactory.getKeyInfoFactory();
			List x509Content = new ArrayList();
			x509Content.add((X509Certificate)certificate);
			X509Data x509Data = keyInfoFactory.newX509Data(x509Content);
			
			KeyInfo keyInfo = keyInfoFactory.newKeyInfo(Collections.singletonList(x509Data));
	
			// Compute signature
			XMLSignature signature = xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo, objectsList, signatureId, signatureId+"_SV");
			DOMSignContext domSignContext = new DOMSignContext(privateKey, document);
			domSignContext.setDefaultNamespacePrefix("ds");
	
			try {
				signature.sign(domSignContext);
			}
			catch (Exception e) {
				e.printStackTrace();
				throw new Exception("signature failed !");
			}
			
		} catch (Exception e)  {
			e.printStackTrace();
		}
	}
	
	/**
	 * Retrieve a certificate
	 */
	private static void retrieveCertificateAndPrivateKey() {
		// TODO Auto-generated method stub
	}

	/**
	 * Element creation helper
	 */
	protected static Element createElement(Document document, String elementName, String namespacePrefix, String namespaceURI) {
		String finalName = (null==namespacePrefix)?elementName:namespacePrefix+":"+elementName;
		return document.createElementNS(namespaceURI, finalName);
	}
	
	/**
	 * Document creation helper
	 * @throws Exception
	 */
	protected static Document createDocument(InputStream data) throws Exception {
		Document document = null;
		try {
			DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
			documentBuilderFactory.setNamespaceAware(true);
			if (null!=data) {
				document = documentBuilderFactory.newDocumentBuilder().parse(data);
			} else {
				document = documentBuilderFactory.newDocumentBuilder().newDocument();
			}
		} catch (Exception e) {
			throw new Exception("unable to create or parse the XML document");
		}
		return document;
	}
}

---------- END SOURCE ----------

Comments
EVALUATION The problem is that you are using a DOM API that is not namespace aware: Element.setAttribute. You should always use the DOM APIs that take namespace URIs. Otherwise you may encounter other issues when signing or validating XML signatures. In this particular case, we are able to fix the problem in the implementation.
26-10-2011

WORK AROUND Always use the DOM namespace-safe methods. Ex: replace Element.setAttribute() with setAttributeNS: digestMethodElement.setAttributeNS(XMLSignature.XMLNS, "Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
26-10-2011

EVALUATION This has been fixed in the Apache Santuario code base. See https://issues.apache.org/bugzilla/show_bug.cgi?id=49692 I will port that fix to the JDK.
20-10-2011