I am migrating my application from Java 8 to Java 11. In Java 8, my input xml file was parsing correctly, but in Java 11 the nodeList.getLength()
is returning 0
.
Please refer the code as per which I am always getting the error: Cannot find signature
.
I have tried using document.getElementsByTagName("Signature")
instead of getElementsByTagNameNS()
, which returns correct length
. However the unmarshalXMLSignature()
method then gives an error that Document implementation must support DOM Level 2 and be namespace aware.
Is there any challenge with using getElementsByTagNameNS()
method in Java 11?
private DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
. . .
document = documentBuilderFactory.newDocumentBuilder().parse(input);
final NodeList nodeList = document.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nodeList.getLength() == 0) {
throw new SecurityException("Cannot find signature");
}
final XMLValidateContext validateContext = new DOMValidateContext(newX509KeySelector(), nodeList.item(0));
final XMLSignature signature = getXMLSignatureFactory().unmarshalXMLSignature(validateContext);
. . .
Edit 1: The input xml is
<?xml version="1.0" encoding="UTF-8" standalone="no"?><License>
<id>999</id>
<companyName>TRXX</companyName>
<creationDate>01-01-2016</creationDate>
<expirationDate>never</expirationDate>
<features>
<feature>
<name>drwwy</name>
<value>true</value>
<description>drwwy module</description>
</feature>
<feature>
<name>pnp</name>
<value>true</value>
<description>Index Number Management module</description>
</feature>
</features>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI="">
<Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>l5Cl4E=</DigestValue></Reference></SignedInfo><SignatureValue>R3zBkE=</SignatureValue>
<KeyInfo><X509Data><X509SubjectName>CN=TRXX Inc,OU=Product Development,O=TRXX Inc,L=Scotle,ST=Atlanta,C=US</X509SubjectName><X509Certificate>MIY</X509Certificate>
</X509Data></KeyInfo></Signature></License>
Edit 2: I figured out, earlier I was injecting DocumentBuilderFactory which is part of JDK , but it is no longer allowed in Java 11. That is why I instantiated it using DocumentBuilderFactory.newInstance(), but with this one my nodelist length comes as zero. Can we use any other way to instantiate DocumentBuilderFactory object ?
If you use document.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
you get an empty nodeList and the source code provided throws your new SecurityException("Cannot find signature")
.
If you document.getElementsByTagName("Signature")
, the Signature element is found but without namespace awareness, throwing this exception later on:
javax.xml.crypto.MarshalException: Document implementation must support DOM Level 2 and be namespace aware
at org.jcp.xml.dsig.internal.dom.DOMXMLSignatureFactory.unmarshal(DOMXMLSignatureFactory.java:189)
at org.jcp.xml.dsig.internal.dom.DOMXMLSignatureFactory.unmarshalXMLSignature(DOMXMLSignatureFactory.java:150)
When researching the term getElementsByTagNameNS(XMLSignature.XMLNS, "Signature")
I found code examples where they activate namespace awareness on the new instance of DocumentBuilderFactory manually:
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
This should fix your new SecurityException("Cannot find signature")
issue.
Regarding your previous question you not only migrated Java 8 to 11, but also CDI API, OpenWebBeans, Apache Tomcat and maybe other modules too, someone else must have done this activation of namespace awareness for you previously. Or saying it differently - you @Injected a namespace aware DocumentBuilderFactory instance.
Edit:
For manual configuration and injection of DocumentBuilderFactory
, create a method that @Produces
an instance of the DocumentBuilderFactory
and provides that instance for injection elsewhere:
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
import javax.xml.parsers.DocumentBuilderFactory;
@Dependent
public class DocumentBuilderFactoryProducer {
@Produces
@ApplicationScoped
public DocumentBuilderFactory createDocumentBuilderFactory() {
DocumentBuilderFactory result = DocumentBuilderFactory.newInstance();
result.setNamespaceAware(true);
// other customizations ...
return result;
}
}
This can be injected into other beans like so:
@Inject
private DocumentBuilderFactory documentBuilderFactory;