From 6012c72413078fceea56e435dd8dcc845a0c5dba Mon Sep 17 00:00:00 2001 From: Rune Flobakk Date: Fri, 23 Nov 2018 10:14:06 +0100 Subject: [PATCH] Make it work on Java 11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error was that the XMLSignature.sign invocation was given the original document, and mutated the document that was signed. For the signing to work properly it is now given a _new_ empty Document. This is a much cleaner approach, as the original document instance (which is signed) is not mutated. The overly awkward use of misunderstood object-orient'ness in these APIs of the JDK are confusing to say the least. And don't get me started on the thread safety, or lack thereof, of all the factories and stuff. Co-authored-by: Øyvind Nerbråten --- pom.xml | 2 +- src/main/java/xmlsign/SignTest.java | 7 +++--- src/main/java/xmlsign/Utils.java | 37 ++++++++++++++++++++++------- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index 2b2f01f..d6b7825 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ UTF-8 - 10 + 11 diff --git a/src/main/java/xmlsign/SignTest.java b/src/main/java/xmlsign/SignTest.java index d4fc3e5..77d1788 100644 --- a/src/main/java/xmlsign/SignTest.java +++ b/src/main/java/xmlsign/SignTest.java @@ -23,6 +23,7 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.util.List; +import static xmlsign.Utils.newEmptyXmlDocument; import static xmlsign.Utils.parseXml; import static xmlsign.Utils.pretty; import static xmlsign.Utils.sha256; @@ -63,10 +64,10 @@ public static void main(String ... args) throws Exception { System.out.print("*** Document before signing:\n" + pretty(documentToSign)); - // ka-boom on JDK 11! - xmlSignature.sign(new DOMSignContext(signKey, documentToSign)); + Document signedDocument = newEmptyXmlDocument(); + xmlSignature.sign(new DOMSignContext(signKey, signedDocument)); - System.out.println("\n*** Document after signing:\n" + pretty(documentToSign)); + System.out.println("\n*** Document after signing:\n" + pretty(signedDocument)); } diff --git a/src/main/java/xmlsign/Utils.java b/src/main/java/xmlsign/Utils.java index 9553277..acf8aa1 100644 --- a/src/main/java/xmlsign/Utils.java +++ b/src/main/java/xmlsign/Utils.java @@ -2,10 +2,9 @@ import org.w3c.dom.Document; import org.w3c.dom.Node; -import org.xml.sax.SAXException; +import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; @@ -15,22 +14,44 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import static java.nio.charset.StandardCharsets.UTF_8; -final class Utils { +public final class Utils { + + /** + * The factory's methods to create documentbuilders are thread safe. + */ + private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); + static { + DOCUMENT_BUILDER_FACTORY.setNamespaceAware(true); + } + + static Document newEmptyXmlDocument() { + return newDocument(DocumentBuilder::newDocument); + } static Document parseXml(String xml) { + return newDocument(builder -> builder.parse(new ByteArrayInputStream(xml.getBytes(UTF_8)))); + } + + static Document newDocument(DocumentCreator documentCreator) { try { - return DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().parse(new ByteArrayInputStream(xml.getBytes(UTF_8))); - } catch (SAXException | IOException | ParserConfigurationException e) { + return documentCreator.createDocument(DOCUMENT_BUILDER_FACTORY.newDocumentBuilder()); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } + @FunctionalInterface + interface DocumentCreator { + Document createDocument(DocumentBuilder builder) throws Exception; + } + static byte[] sha256(String s) { return sha256(s.getBytes(UTF_8)); } @@ -43,8 +64,6 @@ static byte[] sha256(byte[] bytes) { } } - private Utils() {} - static String pretty(Node xml) throws TransformerException { ByteArrayOutputStream out = new ByteArrayOutputStream(); Transformer tr = TransformerFactory.newDefaultInstance().newTransformer(); @@ -53,4 +72,6 @@ static String pretty(Node xml) throws TransformerException { tr.transform(new DOMSource(xml), new StreamResult(out)); return out.toString(UTF_8); } + + private Utils() {} }