libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit a70730d13cb85abcd6d5066410785b5fba435ab1
parent 2ae33e7e5c60094a3c150ed77dbf38c33f1e0751
Author: Florian Dold <florian.dold@gmail.com>
Date:   Sat,  9 Nov 2019 11:04:39 +0100

order signatures WIP

Diffstat:
Msandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt | 102++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsTypes.kt | 2+-
Msandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/HKDResponseOrderData.kt | 2+-
Dsandbox/src/main/kotlin/tech/libeufin/schema/ebics_s001/EbicsMessages.kt | 91-------------------------------------------------------------------------------
Asandbox/src/main/kotlin/tech/libeufin/schema/ebics_s001/SignatureTypes.kt | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asandbox/src/main/kotlin/tech/libeufin/schema/ebics_s001/UserSignatureData.kt | 12++++++++++++
Msandbox/src/test/kotlin/EbicsMessagesTest.kt | 66+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
7 files changed, 264 insertions(+), 104 deletions(-)

diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt @@ -27,17 +27,13 @@ import io.ktor.request.receiveText import io.ktor.response.respond import io.ktor.response.respondText import org.apache.xml.security.binding.xmldsig.RSAKeyValueType -import org.apache.xml.security.binding.xmldsig.SignatureType -import org.jetbrains.exposed.sql.lowerCase import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.upperCase import org.w3c.dom.Document import tech.libeufin.schema.ebics_h004.* import tech.libeufin.schema.ebics_hev.HEVResponse import tech.libeufin.schema.ebics_hev.SystemReturnCodeType -import tech.libeufin.schema.ebics_s001.SignaturePubKeyOrderData -import java.math.BigInteger -import java.security.PrivateKey +import tech.libeufin.schema.ebics_s001.SignatureTypes import java.security.interfaces.RSAPrivateCrtKey import java.util.* import java.util.zip.DeflaterInputStream @@ -142,7 +138,7 @@ private suspend fun ApplicationCall.handleEbicsHia(header: EbicsUnsecuredRequest private suspend fun ApplicationCall.handleEbicsIni(header: EbicsUnsecuredRequest.Header, orderData: ByteArray) { - val keyObject = EbicsOrderUtil.decodeOrderDataXml<SignaturePubKeyOrderData>(orderData) + val keyObject = EbicsOrderUtil.decodeOrderDataXml<SignatureTypes.SignaturePubKeyOrderData>(orderData) val sigPubXml = keyObject.signaturePubKeyInfo.pubKeyValue.rsaKeyValue val sigPub = CryptoUtil.loadRsaPublicKeyFromComponents(sigPubXml.modulus, sigPubXml.exponent) @@ -230,7 +226,8 @@ private suspend fun ApplicationCall.handleEbicsHpb( */ private fun ApplicationCall.ensureEbicsHost(requestHostID: String): EbicsHostPublicInfo { return transaction { - val ebicsHost = EbicsHostEntity.find { EbicsHostsTable.hostID.upperCase() eq requestHostID.toUpperCase() }.firstOrNull() + val ebicsHost = + EbicsHostEntity.find { EbicsHostsTable.hostID.upperCase() eq requestHostID.toUpperCase() }.firstOrNull() if (ebicsHost == null) { logger.warn("client requested unknown HostID") throw EbicsKeyManagementError("[EBICS_INVALID_HOST_ID]", "091011") @@ -341,6 +338,91 @@ fun handleEbicsHtd(): ByteArray { } +fun handleEbicsHkd(): ByteArray { + val hkd = HKDResponseOrderData().apply { + this.partnerInfo = EbicsTypes.PartnerInfo().apply { + this.accountInfoList = listOf( + EbicsTypes.AccountInfo().apply { + this.id = "acctid1" + this.accountHolder = "Mina Musterfrau" + this.accountNumberList = listOf( + EbicsTypes.GeneralAccountNumber().apply { + this.international = true + this.value = "DE21500105174751659277" + } + ) + this.currency = "EUR" + this.description = "ACCT" + this.bankCodeList = listOf( + EbicsTypes.GeneralBankCode().apply { + this.international = true + this.value = "INGDDEFFXXX" + } + ) + }, + EbicsTypes.AccountInfo().apply { + this.id = "glsdemo" + this.accountHolder = "Mina Musterfrau" + this.accountNumberList = listOf( + EbicsTypes.GeneralAccountNumber().apply { + this.international = true + this.value = "DE91430609670123123123" + } + ) + this.currency = "EUR" + this.description = "glsdemoacct" + this.bankCodeList = listOf( + EbicsTypes.GeneralBankCode().apply { + this.international = true + this.value = "GENODEM1GLS" + } + ) + } + ) + this.addressInfo = EbicsTypes.AddressInfo().apply { + this.name = "Foo" + } + this.bankInfo = EbicsTypes.BankInfo().apply { + this.hostID = "host01" + } + this.orderInfoList = listOf( + EbicsTypes.AuthOrderInfoType().apply { + this.description = "foo" + this.orderType = "C53" + this.transferType = "Download" + }, + EbicsTypes.AuthOrderInfoType().apply { + this.description = "foo" + this.orderType = "C52" + this.transferType = "Download" + }, + EbicsTypes.AuthOrderInfoType().apply { + this.description = "foo" + this.orderType = "CCC" + this.transferType = "Upload" + } + ) + } + this.userInfoList = listOf( + EbicsTypes.UserInfo().apply { + this.name = "Some User" + this.userID = EbicsTypes.UserIDType().apply { + this.status = 1 + this.value = "USER1" + } + this.permissionList = listOf( + EbicsTypes.UserPermission().apply { + this.orderTypes = "C54 C53 C52 CCC" + } + ) + }) + } + + val str = XMLUtil.convertJaxbToString(hkd) + return str.toByteArray() +} + + fun signEbicsResponseX002(ebicsResponse: EbicsResponse, privateKey: RSAPrivateCrtKey): String { val doc = XMLUtil.convertJaxbToDocument(ebicsResponse) XMLUtil.signEbicsDocument(doc, privateKey) @@ -408,7 +490,9 @@ suspend fun ApplicationCall.ebicsweb() { val responseXmlStr = transaction { // Step 1 of 3: Get information about the host and subscriber - val ebicsHost = EbicsHostEntity.find { EbicsHostsTable.hostID.upperCase() eq requestedHostId.toUpperCase() }.firstOrNull() + val ebicsHost = + EbicsHostEntity.find { EbicsHostsTable.hostID.upperCase() eq requestedHostId.toUpperCase() } + .firstOrNull() val requestTransactionID = requestObject.header.static.transactionID var downloadTransaction: EbicsDownloadTransactionEntity? = null var uploadTransaction: EbicsUploadTransactionEntity? = @@ -459,6 +543,7 @@ suspend fun ApplicationCall.ebicsweb() { println("handling initialization for order type $orderType") val response = when (orderType) { "HTD" -> handleEbicsHtd() + "HKD" -> handleEbicsHkd() else -> throw EbicsInvalidXmlError() } @@ -516,6 +601,7 @@ suspend fun ApplicationCall.ebicsweb() { val plainSigData = InflaterInputStream(decryptedSignatureData.inputStream()).use { it.readAllBytes() } + //val sigDataObject = XMLUtil.convertStringToJaxb<OrderSignatureData>(plainSigData) println("signature data: ${plainSigData.toString(Charsets.UTF_8)}") println("creating upload transaction for transactionID $transactionID") EbicsUploadTransactionEntity.new(transactionID) { diff --git a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsTypes.kt b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsTypes.kt @@ -31,7 +31,7 @@ import javax.xml.datatype.XMLGregorianCalendar /** * EBICS type definitions that are shared between other requests / responses / order types. */ -class EbicsTypes private constructor() { +object EbicsTypes { /** * EBICS client product. Identifies the software that accesses the EBICS host. */ diff --git a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/HKDResponseOrderData.kt b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/HKDResponseOrderData.kt @@ -4,7 +4,7 @@ import java.security.Permission import javax.xml.bind.annotation.* @XmlAccessorType(XmlAccessType.NONE) -@XmlType(name = "", propOrder = ["partnerInfo", "userInfo"]) +@XmlType(name = "", propOrder = ["partnerInfo", "userInfoList"]) @XmlRootElement(name = "HTDResponseOrderData") class HKDResponseOrderData { @get:XmlElement(name = "PartnerInfo", required = true) diff --git a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_s001/EbicsMessages.kt b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_s001/EbicsMessages.kt @@ -1,91 +0,0 @@ -/* - * This file is part of LibEuFin. - * Copyright (C) 2019 Stanisci and Dold. - - * LibEuFin is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation; either version 3, or - * (at your option) any later version. - - * LibEuFin is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General - * Public License for more details. - - * You should have received a copy of the GNU Affero General Public - * License along with LibEuFin; see the file COPYING. If not, see - * <http://www.gnu.org/licenses/> - */ - -package tech.libeufin.schema.ebics_s001 - -import org.apache.xml.security.binding.xmldsig.RSAKeyValueType -import org.apache.xml.security.binding.xmldsig.X509DataType -import javax.xml.bind.annotation.* -import javax.xml.bind.annotation.adapters.CollapsedStringAdapter -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter -import javax.xml.datatype.XMLGregorianCalendar - - -@XmlAccessorType(XmlAccessType.NONE) -@XmlType( - name = "PubKeyValueType", namespace = "http://www.ebics.org/S001", propOrder = [ - "rsaKeyValue", - "timeStamp" - ] -) -class PubKeyValueType { - @get:XmlElement(name = "RSAKeyValue", namespace = "http://www.w3.org/2000/09/xmldsig#", required = true) - lateinit var rsaKeyValue: RSAKeyValueType - - @get:XmlElement(name = "TimeStamp") - @get:XmlSchemaType(name = "dateTime") - var timeStamp: XMLGregorianCalendar? = null -} - - -@XmlAccessorType(XmlAccessType.NONE) -@XmlType( - name = "", - propOrder = [ - "x509Data", - "pubKeyValue", - "signatureVersion" - ] -) -class SignaturePubKeyInfoType { - @get:XmlElement(name = "X509Data") - var x509Data: X509DataType? = null - - @get:XmlElement(name = "PubKeyValue", required = true) - lateinit var pubKeyValue: PubKeyValueType - - @get:XmlElement(name = "SignatureVersion", required = true) - @get:XmlJavaTypeAdapter(CollapsedStringAdapter::class) - lateinit var signatureVersion: String -} - - -/** - * EBICS INI payload. - */ -@XmlAccessorType(XmlAccessType.NONE) -@XmlType( - name = "", - propOrder = ["signaturePubKeyInfo", "partnerID", "userID"] -) -@XmlRootElement(name = "SignaturePubKeyOrderData") -class SignaturePubKeyOrderData { - @get:XmlElement(name = "SignaturePubKeyInfo", required = true) - lateinit var signaturePubKeyInfo: SignaturePubKeyInfoType - - @get:XmlElement(name = "PartnerID", required = true) - @get:XmlJavaTypeAdapter(CollapsedStringAdapter::class) - @get:XmlSchemaType(name = "token") - lateinit var partnerID: String - - @get:XmlElement(name = "UserID", required = true) - @get:XmlJavaTypeAdapter(CollapsedStringAdapter::class) - @get:XmlSchemaType(name = "token") - lateinit var userID: String -} diff --git a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_s001/SignatureTypes.kt b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_s001/SignatureTypes.kt @@ -0,0 +1,92 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2019 Stanisci and Dold. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + +package tech.libeufin.schema.ebics_s001 + +import org.apache.xml.security.binding.xmldsig.RSAKeyValueType +import org.apache.xml.security.binding.xmldsig.X509DataType +import javax.xml.bind.annotation.* +import javax.xml.bind.annotation.adapters.CollapsedStringAdapter +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter +import javax.xml.datatype.XMLGregorianCalendar + + +object SignatureTypes { + + @XmlAccessorType(XmlAccessType.NONE) + @XmlType( + name = "PubKeyValueType", namespace = "http://www.ebics.org/S001", propOrder = [ + "rsaKeyValue", + "timeStamp" + ] + ) + class PubKeyValueType { + @get:XmlElement(name = "RSAKeyValue", namespace = "http://www.w3.org/2000/09/xmldsig#", required = true) + lateinit var rsaKeyValue: RSAKeyValueType + + @get:XmlElement(name = "TimeStamp") + @get:XmlSchemaType(name = "dateTime") + var timeStamp: XMLGregorianCalendar? = null + } + + @XmlAccessorType(XmlAccessType.NONE) + @XmlType( + name = "", + propOrder = [ + "x509Data", + "pubKeyValue", + "signatureVersion" + ] + ) + class SignaturePubKeyInfoType { + @get:XmlElement(name = "X509Data") + var x509Data: X509DataType? = null + + @get:XmlElement(name = "PubKeyValue", required = true) + lateinit var pubKeyValue: PubKeyValueType + + @get:XmlElement(name = "SignatureVersion", required = true) + @get:XmlJavaTypeAdapter(CollapsedStringAdapter::class) + lateinit var signatureVersion: String + } + + /** + * EBICS INI payload. + */ + @XmlAccessorType(XmlAccessType.NONE) + @XmlType( + name = "", + propOrder = ["signaturePubKeyInfo", "partnerID", "userID"] + ) + @XmlRootElement(name = "SignaturePubKeyOrderData") + class SignaturePubKeyOrderData { + @get:XmlElement(name = "SignaturePubKeyInfo", required = true) + lateinit var signaturePubKeyInfo: SignaturePubKeyInfoType + + @get:XmlElement(name = "PartnerID", required = true) + @get:XmlJavaTypeAdapter(CollapsedStringAdapter::class) + @get:XmlSchemaType(name = "token") + lateinit var partnerID: String + + @get:XmlElement(name = "UserID", required = true) + @get:XmlJavaTypeAdapter(CollapsedStringAdapter::class) + @get:XmlSchemaType(name = "token") + lateinit var userID: String + } +} +\ No newline at end of file diff --git a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_s001/UserSignatureData.kt b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_s001/UserSignatureData.kt @@ -0,0 +1,11 @@ +package tech.libeufin.schema.ebics_s001 + +import javax.xml.bind.annotation.XmlAccessType +import javax.xml.bind.annotation.XmlAccessorType +import javax.xml.bind.annotation.XmlRootElement + +@XmlAccessorType(XmlAccessType.NONE) +@XmlRootElement(name = "UserSignatureData") +class UserSignatureData { + +} +\ No newline at end of file diff --git a/sandbox/src/test/kotlin/EbicsMessagesTest.kt b/sandbox/src/test/kotlin/EbicsMessagesTest.kt @@ -7,7 +7,7 @@ import org.w3c.dom.Element import tech.libeufin.schema.ebics_h004.* import tech.libeufin.schema.ebics_hev.HEVResponse import tech.libeufin.schema.ebics_hev.SystemReturnCodeType -import tech.libeufin.schema.ebics_s001.SignaturePubKeyOrderData +import tech.libeufin.schema.ebics_s001.SignatureTypes import javax.xml.datatype.DatatypeFactory import kotlin.test.assertNotNull import kotlin.test.assertTrue @@ -22,7 +22,7 @@ class EbicsMessagesTest { fun testImportNonRoot() { val classLoader = ClassLoader.getSystemClassLoader() val ini = classLoader.getResource("ebics_ini_inner_key.xml") - val jaxb = XMLUtil.convertStringToJaxb<SignaturePubKeyOrderData>(ini.readText()) + val jaxb = XMLUtil.convertStringToJaxb<SignatureTypes.SignaturePubKeyOrderData>(ini.readText()) assertEquals("A006", jaxb.value.signaturePubKeyInfo.signatureVersion) } @@ -129,7 +129,7 @@ class EbicsMessagesTest { "ebics_ini_inner_key.xml" ) assertNotNull(file) - XMLUtil.convertStringToJaxb<SignaturePubKeyOrderData>(file.readText()) + XMLUtil.convertStringToJaxb<SignatureTypes.SignaturePubKeyOrderData>(file.readText()) } val modulus = jaxbKey.value.signaturePubKeyInfo.pubKeyValue.rsaKeyValue.modulus @@ -232,6 +232,66 @@ class EbicsMessagesTest { assert(XMLUtil.validateFromString(str)) } + + @Test + fun testHkd() { + val hkd = HKDResponseOrderData().apply { + this.partnerInfo = EbicsTypes.PartnerInfo().apply { + this.accountInfoList = listOf( + EbicsTypes.AccountInfo().apply { + this.id = "acctid1" + this.accountHolder = "Mina Musterfrau" + this.accountNumberList = listOf( + EbicsTypes.GeneralAccountNumber().apply { + this.international = true + this.value = "AT411100000237571500" + } + ) + this.currency = "EUR" + this.description = "some account" + this.bankCodeList = listOf( + EbicsTypes.GeneralBankCode().apply { + this.international = true + this.value = "ABAGATWWXXX" + } + ) + } + ) + this.addressInfo = EbicsTypes.AddressInfo().apply { + this.name = "Foo" + } + this.bankInfo = EbicsTypes.BankInfo().apply { + this.hostID = "MYHOST" + } + this.orderInfoList = listOf( + EbicsTypes.AuthOrderInfoType().apply { + this.description = "foo" + this.orderType = "CCC" + this.orderFormat = "foo" + this.transferType = "Upload" + } + ) + } + this.userInfoList = listOf( + EbicsTypes.UserInfo().apply { + this.name = "Some User" + this.userID = EbicsTypes.UserIDType().apply { + this.status = 2 + this.value = "myuserid" + } + this.permissionList = listOf( + EbicsTypes.UserPermission().apply { + this.orderTypes = "CCC ABC" + } + ) + }) + } + + val str = XMLUtil.convertJaxbToString(hkd) + println(str) + assert(XMLUtil.validateFromString(str)) + } + @Test fun testEbicsRequestInitializationPhase() { val ebicsRequestObj = EbicsRequest().apply {