gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] 01/03: implement XML signing, minor refactoring


From: gnunet
Subject: [libeufin] 01/03: implement XML signing, minor refactoring
Date: Mon, 28 Oct 2019 22:46:41 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository libeufin.

commit a9c4bb1841fa79220799f35feb587aaa8b247f2d
Author: Florian Dold <address@hidden>
AuthorDate: Mon Oct 28 15:51:43 2019 +0100

    implement XML signing, minor refactoring
---
 .idea/dictionaries/dold.xml                        |   7 +
 .idea/gradle.xml                                   |   2 +-
 .idea/misc.xml                                     |   2 +-
 sandbox/build.gradle                               |   1 +
 .../libeufin/messages/ebics/hev/ObjectFactory.java |   2 +-
 sandbox/src/main/kotlin/Main.kt                    |   4 +-
 sandbox/src/main/kotlin/XML.kt                     | 193 ++++++++++++++-------
 sandbox/src/test/kotlin/GeneratePrivateKeyTest.kt  |   2 +-
 sandbox/src/test/kotlin/HiaLoadTest.kt             |  13 +-
 sandbox/src/test/kotlin/JaxbTest.kt                |   6 +-
 sandbox/src/test/kotlin/XmlSigTest.kt              |  41 +++++
 sandbox/src/test/kotlin/XsiTypeAttributeTest.kt    |   4 +-
 12 files changed, 197 insertions(+), 80 deletions(-)

diff --git a/.idea/dictionaries/dold.xml b/.idea/dictionaries/dold.xml
new file mode 100644
index 0000000..bef9337
--- /dev/null
+++ b/.idea/dictionaries/dold.xml
@@ -0,0 +1,7 @@
+<component name="ProjectDictionaryState">
+  <dictionary name="dold">
+    <words>
+      <w>ebics</w>
+    </words>
+  </dictionary>
+</component>
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 49f0342..b8ed810 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -9,7 +9,7 @@
         <option name="distributionType" value="DEFAULT_WRAPPED" />
         <option name="externalProjectPath" value="$PROJECT_DIR$" />
         <option name="gradleHome" value="/usr/share/java/gradle" />
-        <option name="gradleJvm" value="11" />
+        <option name="gradleJvm" value="#JAVA_INTERNAL" />
         <option name="modules">
           <set>
             <option value="$PROJECT_DIR$" />
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 25d34a4..c1d4bf5 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="ExternalStorageConfigurationManager" enabled="true" />
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_11" 
project-jdk-name="11" project-jdk-type="JavaSDK" />
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_11" 
default="false" project-jdk-name="12" project-jdk-type="JavaSDK" />
 </project>
\ No newline at end of file
diff --git a/sandbox/build.gradle b/sandbox/build.gradle
index 876e554..4c14e89 100644
--- a/sandbox/build.gradle
+++ b/sandbox/build.gradle
@@ -33,6 +33,7 @@ dependencies {
     compile "javax.activation:activation:1.1"
     compile "org.glassfish.jaxb:jaxb-runtime:2.3.1"
     testCompile group: 'junit', name: 'junit', version: '4.12'
+    compile 'org.apache.santuario:xmlsec:2.1.4'
     runtime rootProject.files("resources")
 }
 
diff --git 
a/sandbox/src/main/java/tech/libeufin/messages/ebics/hev/ObjectFactory.java 
b/sandbox/src/main/java/tech/libeufin/messages/ebics/hev/ObjectFactory.java
index 9eb2b69..8674e2d 100644
--- a/sandbox/src/main/java/tech/libeufin/messages/ebics/hev/ObjectFactory.java
+++ b/sandbox/src/main/java/tech/libeufin/messages/ebics/hev/ObjectFactory.java
@@ -11,7 +11,7 @@ import javax.xml.namespace.QName;
  * This object contains factory methods for each 
  * Java content interface and Java element interface 
  * generated in the tech.libeufin package. 
- * <p>An ObjectFactory allows you to programatically 
+ * <p>An ObjectFactory allows you to programmatically
  * construct new instances of the Java representation 
  * for XML content. The Java representation of XML 
  * content can consist of schema derived interfaces 
diff --git a/sandbox/src/main/kotlin/Main.kt b/sandbox/src/main/kotlin/Main.kt
index 7122e03..4cca079 100644
--- a/sandbox/src/main/kotlin/Main.kt
+++ b/sandbox/src/main/kotlin/Main.kt
@@ -149,7 +149,7 @@ object OkHelper {
  * @return the modified document
  */
 fun downcastXml(document: Document, node: String, type: String) : Document {
-    logger.debug("Downcasting: ${xmlProcess.convertDomToString(document)}")
+    logger.debug("Downcasting: ${XML.convertDomToString(document)}")
     val x: Element = document.getElementsByTagNameNS(
         "urn:org:ebics:H004",
         "OrderDetails"
@@ -395,7 +395,7 @@ private suspend fun ApplicationCall.ebicsweb() {
     val body: String = receiveText()
     logger.debug("Data received: $body")
 
-    val bodyDocument: Document? = xmlProcess.parseStringIntoDom(body)
+    val bodyDocument: Document? = XML.parseStringIntoDom(body)
 
     if (bodyDocument == null || (!xmlProcess.validateFromDom(bodyDocument))) {
         var response = EbicsResponse(
diff --git a/sandbox/src/main/kotlin/XML.kt b/sandbox/src/main/kotlin/XML.kt
index c9b96e4..5d0b124 100644
--- a/sandbox/src/main/kotlin/XML.kt
+++ b/sandbox/src/main/kotlin/XML.kt
@@ -21,6 +21,8 @@ package tech.libeufin.sandbox
 
 import com.sun.org.apache.xerces.internal.dom.DOMInputImpl
 import org.w3c.dom.Document
+import org.w3c.dom.Node
+import org.w3c.dom.NodeList
 import org.w3c.dom.ls.LSInput
 import org.w3c.dom.ls.LSResourceResolver
 import org.xml.sax.ErrorHandler
@@ -28,25 +30,60 @@ import org.xml.sax.InputSource
 import org.xml.sax.SAXException
 import org.xml.sax.SAXParseException
 import java.io.*
+import java.security.PrivateKey
+import java.security.PublicKey
+import java.util.*
 import javax.xml.XMLConstants
 import javax.xml.bind.JAXBContext
 import javax.xml.bind.JAXBElement
 import javax.xml.bind.JAXBException
 import javax.xml.bind.Marshaller
+import javax.xml.crypto.*
+import javax.xml.crypto.dom.DOMURIReference
+import javax.xml.crypto.dsig.*
+import javax.xml.crypto.dsig.dom.DOMSignContext
+import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec
+import javax.xml.crypto.dsig.spec.TransformParameterSpec
 import javax.xml.parsers.DocumentBuilderFactory
 import javax.xml.parsers.ParserConfigurationException
-import javax.xml.transform.*
+import javax.xml.transform.OutputKeys
+import javax.xml.transform.Source
+import javax.xml.transform.TransformerFactory
 import javax.xml.transform.dom.DOMSource
 import javax.xml.transform.stream.StreamResult
 import javax.xml.transform.stream.StreamSource
 import javax.xml.validation.SchemaFactory
-
+import javax.xml.xpath.XPath
+import javax.xml.xpath.XPathConstants
+import javax.xml.xpath.XPathFactory
 
 /**
  * This class takes care of importing XSDs and validate
  * XMLs against those.
  */
 class XML {
+    private class EbicsSigUriDereferencer : URIDereferencer {
+        override fun dereference(myRef: URIReference?, myCtx: 
XMLCryptoContext?): Data {
+            val ebicsXpathExpr = "//*[@authenticate='true']"
+            if (myRef !is DOMURIReference)
+                throw Exception("invalid type")
+            if (myRef.uri != "#xpointer($ebicsXpathExpr)")
+                throw Exception("invalid EBICS XML signature URI: 
'${myRef.uri}'")
+            val xp: XPath = XPathFactory.newInstance().newXPath()
+            val nodeSet = 
xp.compile(ebicsXpathExpr).evaluate(myRef.here.ownerDocument, 
XPathConstants.NODESET)
+            if (nodeSet !is NodeList)
+                throw Exception("invalid type")
+            if (nodeSet.length <= 0) {
+                throw Exception("no nodes to sign")
+            }
+            val nodeList = LinkedList<Node>()
+            for (i in 0 until nodeSet.length) {
+                nodeList.add(nodeSet.item(i))
+            }
+            return NodeSetData { nodeList.iterator() }
+        }
+    }
+
     /**
      * Validator for EBICS messages.
      */
@@ -96,33 +133,6 @@ class XML {
     }
 
 
-    /**
-     * Parse string into XML DOM.
-     * @param xmlString the string to parse.
-     * @return the DOM representing @a xmlString
-     */
-    fun parseStringIntoDom(xmlString: String): Document? {
-
-        val factory = DocumentBuilderFactory.newInstance()
-        factory.isNamespaceAware = true
-
-        try {
-            val xmlInputStream = ByteArrayInputStream(xmlString.toByteArray())
-            val builder = factory.newDocumentBuilder()
-            val document = builder.parse(InputSource(xmlInputStream))
-
-            return document
-
-        } catch (e: ParserConfigurationException) {
-            e.printStackTrace()
-        } catch (e: SAXException) {
-            e.printStackTrace()
-        } catch (e: IOException) {
-            e.printStackTrace()
-        }
-        return null
-    }
-
     /**
      *
      * @param xmlDoc the XML document to validate
@@ -175,7 +185,7 @@ class XML {
      * @param document the document to convert into JAXB.
      * @return the JAXB object reflecting the original XML document.
      */
-    fun <T>convertDomToJaxb(finalType: Class<T>, document: Document) : 
JAXBElement<T> {
+    fun <T> convertDomToJaxb(finalType: Class<T>, document: Document): 
JAXBElement<T> {
 
         val jc = JAXBContext.newInstance(finalType)
 
@@ -191,7 +201,7 @@ class XML {
      * @param documentString the string to convert into JAXB.
      * @return the JAXB object reflecting the original XML document.
      */
-    fun <T>convertStringToJaxb(finalType: Class<T>, documentString: String) : 
JAXBElement<T> {
+    fun <T> convertStringToJaxb(finalType: Class<T>, documentString: String): 
JAXBElement<T> {
 
         val jc = JAXBContext.newInstance(finalType.packageName)
 
@@ -204,7 +214,6 @@ class XML {
     }
 
 
-
     /**
      * Return the DOM representation of the Java object, using the JAXB
      * interface.  FIXME: narrow input type to JAXB type!
@@ -213,7 +222,7 @@ class XML {
      *               has already got its setters called.
      * @return the DOM Document, or null (if errors occur).
      */
-    fun <T>convertJaxbToDom(obj: JAXBElement<T>): Document? {
+    fun <T> convertJaxbToDom(obj: JAXBElement<T>): Document? {
 
         try {
             val jc = JAXBContext.newInstance(obj.declaredType)
@@ -237,19 +246,42 @@ class XML {
     }
 
     /**
-     * Extract String from DOM.
+     * Extract String from JAXB.
      *
-     * @param document the DOM to extract the string from.
-     * @return the final String, or null if errors occur.
+     * @param obj the JAXB instance
+     * @return String representation of @a object, or null if errors occur
      */
-    fun convertDomToString(document: Document): String? {
+    fun <T> convertJaxbToString(obj: JAXBElement<T>): String? {
+        val sw = StringWriter()
 
         try {
+            val jc = JAXBContext.newInstance(obj.declaredType)
+            /* Getting the string.  */
+            val m = jc.createMarshaller()
+            m.marshal(obj, sw)
+            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true)
+
+        } catch (e: JAXBException) {
+            e.printStackTrace()
+            return "Bank fatal error."
+        }
+
+        return sw.toString()
+    }
+
+    companion object {
+        /**
+         * Extract String from DOM.
+         *
+         * @param document the DOM to extract the string from.
+         * @return the final String, or null if errors occur.
+         */
+        fun convertDomToString(document: Document): String? {
             /* Make Transformer.  */
             val tf = TransformerFactory.newInstance()
             val t = tf.newTransformer()
 
-            t.setOutputProperty(OutputKeys.INDENT, "no")
+            t.setOutputProperty(OutputKeys.INDENT, "yes")
 
             /* Make string writer.  */
             val sw = StringWriter()
@@ -257,36 +289,73 @@ class XML {
             /* Extract string.  */
             t.transform(DOMSource(document), StreamResult(sw))
             return sw.toString()
+        }
 
-        } catch (e: TransformerConfigurationException) {
-            e.printStackTrace()
-        } catch (e: TransformerException) {
-            e.printStackTrace()
+        fun convertNodeToString(node: Node): String? {
+            /* Make Transformer.  */
+            val tf = TransformerFactory.newInstance()
+            val t = tf.newTransformer()
+
+            t.setOutputProperty(OutputKeys.INDENT, "yes")
+
+            /* Make string writer.  */
+            val sw = StringWriter()
+
+            /* Extract string.  */
+            t.transform(DOMSource(node), StreamResult(sw))
+            return sw.toString()
         }
-        return null
-    }
 
-    /**
-     * Extract String from JAXB.
-     *
-     * @param obj the JAXB instance
-     * @return String representation of @a object, or null if errors occur
-     */
-    fun <T> convertJaxbToString(obj: JAXBElement<T>): String? {
-        val sw = StringWriter()
+        /**
+         * Parse string into XML DOM.
+         * @param xmlString the string to parse.
+         * @return the DOM representing @a xmlString
+         */
+        fun parseStringIntoDom(xmlString: String): Document {
+            val factory = DocumentBuilderFactory.newInstance().apply {
+                isNamespaceAware = true
+            }
+            val xmlInputStream = ByteArrayInputStream(xmlString.toByteArray())
+            val builder = factory.newDocumentBuilder()
+            return builder.parse(InputSource(xmlInputStream))
+        }
 
-        try {
-            val jc = JAXBContext.newInstance(obj.declaredType)
-            /* Getting the string.  */
-            val m = jc.createMarshaller()
-            m.marshal(obj, sw)
-            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true)
 
-        } catch (e: JAXBException) {
-            e.printStackTrace()
-            return "Bank fatal error."
+        fun signEbicsDocument(doc: Document, signingPriv: PrivateKey): Unit {
+            val xpath = XPathFactory.newInstance().newXPath()
+            val authSigNode = 
xpath.compile("/*[1]/AuthSignature").evaluate(doc, XPathConstants.NODE)
+            if (authSigNode !is Node)
+                throw java.lang.Exception("no AuthSignature")
+            val fac = XMLSignatureFactory.getInstance("DOM")
+            val c14n = fac.newTransform(CanonicalizationMethod.INCLUSIVE, null 
as TransformParameterSpec?)
+            val ref: Reference =
+                fac.newReference(
+                    "#xpointer(//*[@authenticate='true'])",
+                    fac.newDigestMethod(DigestMethod.SHA256, null),
+                    listOf(c14n),
+                    null,
+                    null
+                )
+            val canon: CanonicalizationMethod =
+                
fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, null as 
C14NMethodParameterSpec?)
+            val signatureMethod = 
fac.newSignatureMethod(SignatureMethod.RSA_SHA256, null)
+            val si: SignedInfo = fac.newSignedInfo(canon, signatureMethod, 
listOf(ref))
+            val sig: XMLSignature = fac.newXMLSignature(si, null)
+            val dsc = DOMSignContext(signingPriv, authSigNode)
+            dsc.defaultNamespacePrefix = "ds"
+            dsc.uriDereferencer = EbicsSigUriDereferencer()
+
+            sig.sign(dsc)
+
+            val innerSig = authSigNode.firstChild
+            while (innerSig.hasChildNodes()) {
+                authSigNode.appendChild(innerSig.firstChild)
+            }
+            authSigNode.removeChild(innerSig)
         }
 
-        return sw.toString()
+        fun verifyEbicsDocument(doc: Document, signingPub: PublicKey): Boolean 
{
+            return false
+        }
     }
 }
\ No newline at end of file
diff --git a/sandbox/src/test/kotlin/GeneratePrivateKeyTest.kt 
b/sandbox/src/test/kotlin/GeneratePrivateKeyTest.kt
index d5f6496..cddd900 100644
--- a/sandbox/src/test/kotlin/GeneratePrivateKeyTest.kt
+++ b/sandbox/src/test/kotlin/GeneratePrivateKeyTest.kt
@@ -15,7 +15,7 @@ class GeneratePrivateKeyTest {
     @Test
     fun loadOrGeneratePrivateKey() {
 
-        val x = getOrMakePrivateKey()
+        getOrMakePrivateKey()
 
         assertTrue(
             transaction {
diff --git a/sandbox/src/test/kotlin/HiaLoadTest.kt 
b/sandbox/src/test/kotlin/HiaLoadTest.kt
index 349b180..89810a1 100644
--- a/sandbox/src/test/kotlin/HiaLoadTest.kt
+++ b/sandbox/src/test/kotlin/HiaLoadTest.kt
@@ -12,10 +12,11 @@ class HiaLoadTest {
         val processor = XML()
         val classLoader = ClassLoader.getSystemClassLoader()
         val hia = classLoader.getResource("HIA.xml")
-        val hiaDom = processor.parseStringIntoDom(hia.readText())
-        val x: Element = hiaDom?.getElementsByTagNameNS(
+        val hiaDom = XML.parseStringIntoDom(hia.readText())
+        val x: Element = hiaDom.getElementsByTagNameNS(
             "urn:org:ebics:H004",
-            "OrderDetails")?.item(0) as Element
+            "OrderDetails"
+        )?.item(0) as Element
 
         x.setAttributeNS(
             "http://www.w3.org/2001/XMLSchema-instance";,
@@ -25,9 +26,7 @@ class HiaLoadTest {
 
         processor.convertDomToJaxb<EbicsUnsecuredRequest>(
             EbicsUnsecuredRequest::class.java,
-            hiaDom!!)
+            hiaDom
+        )
     }
-
-
-
 }
\ No newline at end of file
diff --git a/sandbox/src/test/kotlin/JaxbTest.kt 
b/sandbox/src/test/kotlin/JaxbTest.kt
index 5de2cec..f4c9f90 100644
--- a/sandbox/src/test/kotlin/JaxbTest.kt
+++ b/sandbox/src/test/kotlin/JaxbTest.kt
@@ -80,11 +80,11 @@ class JaxbTest {
      */
     @Test
     fun domToJaxb() {
-
         val ini = 
classLoader.getResource("ebics_ini_request_sample_patched.xml")
-        val iniDom = processor.parseStringIntoDom(ini.readText())
+        val iniDom = XML.parseStringIntoDom(ini.readText())
         processor.convertDomToJaxb<EbicsUnsecuredRequest>(
             EbicsUnsecuredRequest::class.java,
-            iniDom!!)
+            iniDom
+        )
     }
 }
\ No newline at end of file
diff --git a/sandbox/src/test/kotlin/XmlSigTest.kt 
b/sandbox/src/test/kotlin/XmlSigTest.kt
new file mode 100644
index 0000000..e80b880
--- /dev/null
+++ b/sandbox/src/test/kotlin/XmlSigTest.kt
@@ -0,0 +1,41 @@
+package tech.libeufin.sandbox
+
+import org.junit.Test
+import org.w3c.dom.Node
+import org.w3c.dom.NodeList
+import java.security.KeyPairGenerator
+import java.util.*
+import javax.xml.crypto.NodeSetData
+import javax.xml.crypto.URIDereferencer
+import javax.xml.crypto.dom.DOMURIReference
+import javax.xml.crypto.dsig.*
+import javax.xml.crypto.dsig.dom.DOMSignContext
+import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec
+import javax.xml.crypto.dsig.spec.TransformParameterSpec
+import javax.xml.xpath.XPath
+import javax.xml.xpath.XPathConstants
+import javax.xml.xpath.XPathFactory
+
+
+class XmlSigTest {
+
+    @Test
+    fun basicSigningTest() {
+        val doc = XML.parseStringIntoDom("""
+            <foo xmlns:ds="http://www.w3.org/2000/09/xmldsig#";>
+                <AuthSignature />
+                <bar authenticate='true'>bla</bar>Hello World
+                <spam>
+                eggs
+                
+                ham
+                </spam>
+            </foo>
+        """.trimIndent())
+        val kpg = KeyPairGenerator.getInstance("RSA")
+        kpg.initialize(2048)
+        val pair = kpg.genKeyPair()
+        XML.signEbicsDocument(doc, pair.private)
+        println(XML.convertDomToString(doc))
+    }
+}
\ No newline at end of file
diff --git a/sandbox/src/test/kotlin/XsiTypeAttributeTest.kt 
b/sandbox/src/test/kotlin/XsiTypeAttributeTest.kt
index 73c4811..030f9a9 100644
--- a/sandbox/src/test/kotlin/XsiTypeAttributeTest.kt
+++ b/sandbox/src/test/kotlin/XsiTypeAttributeTest.kt
@@ -14,8 +14,8 @@ class XsiTypeAttributeTest {
         val processor = XML()
         val classLoader = ClassLoader.getSystemClassLoader()
         val ini = classLoader.getResource("ebics_ini_request_sample.xml")
-        val iniDom = processor.parseStringIntoDom(ini.readText())
-        val x: Element = iniDom?.getElementsByTagName("OrderDetails")?.item(0) 
as Element
+        val iniDom = XML.parseStringIntoDom(ini.readText())
+        val x: Element = iniDom.getElementsByTagName("OrderDetails")?.item(0) 
as Element
 
         x.setAttributeNS(
             "http://www.w3.org/2001/XMLSchema-instance";,

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]