[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] branch master updated: ebics transactions WIP
From: |
gnunet |
Subject: |
[libeufin] branch master updated: ebics transactions WIP |
Date: |
Thu, 07 Nov 2019 14:37:57 +0100 |
This is an automated email from the git hooks/post-receive script.
dold pushed a commit to branch master
in repository libeufin.
The following commit(s) were added to refs/heads/master by this push:
new d1e60b1 ebics transactions WIP
d1e60b1 is described below
commit d1e60b16cb9fef51729a5d0fed9d09c56b5f8836
Author: Florian Dold <address@hidden>
AuthorDate: Thu Nov 7 14:37:47 2019 +0100
ebics transactions WIP
---
.idea/uiDesigner.xml | 124 +++++++++
.../kotlin/tech/libeufin/sandbox/CryptoUtil.kt | 6 -
.../src/main/kotlin/tech/libeufin/sandbox/DB.kt | 17 +-
.../kotlin/tech/libeufin/sandbox/EbicsOrderUtil.kt | 17 ++
.../src/main/kotlin/tech/libeufin/sandbox/Main.kt | 284 ++++++++++++++++++++-
.../main/kotlin/tech/libeufin/sandbox/XMLUtil.kt | 21 +-
.../kotlin/tech/libeufin/sandbox/XmlBinding.kt | 23 ++
.../src/main/kotlin/tech/libeufin/sandbox/hex.kt | 7 +
.../libeufin/schema/ebics_h004/EbicsResponse.kt | 11 +-
.../tech/libeufin/schema/ebics_h004/EbicsTypes.kt | 5 +-
sandbox/src/test/kotlin/XmlUtilTest.kt | 16 ++
11 files changed, 490 insertions(+), 41 deletions(-)
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..e96534f
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Palette2">
+ <group name="Swing">
+ <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal
Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false"
auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="1" hsize-policy="6" anchor="0"
fill="1" />
+ </item>
+ <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical
Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false"
auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="1" anchor="0"
fill="2" />
+ </item>
+ <item class="javax.swing.JPanel"
icon="/com/intellij/uiDesigner/icons/panel.png" removable="false"
auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0"
fill="3" />
+ </item>
+ <item class="javax.swing.JScrollPane"
icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false"
auto-create-binding="false" can-attach-label="true">
+ <default-constraints vsize-policy="7" hsize-policy="7" anchor="0"
fill="3" />
+ </item>
+ <item class="javax.swing.JButton"
icon="/com/intellij/uiDesigner/icons/button.png" removable="false"
auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="0"
fill="1" />
+ <initial-values>
+ <property name="text" value="Button" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JRadioButton"
icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false"
auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8"
fill="0" />
+ <initial-values>
+ <property name="text" value="RadioButton" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JCheckBox"
icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false"
auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8"
fill="0" />
+ <initial-values>
+ <property name="text" value="CheckBox" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JLabel"
icon="/com/intellij/uiDesigner/icons/label.png" removable="false"
auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="8"
fill="0" />
+ <initial-values>
+ <property name="text" value="Label" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JTextField"
icon="/com/intellij/uiDesigner/icons/textField.png" removable="false"
auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8"
fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JPasswordField"
icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false"
auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8"
fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JFormattedTextField"
icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false"
auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8"
fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextArea"
icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false"
auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0"
fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextPane"
icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false"
auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0"
fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JEditorPane"
icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false"
auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0"
fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JComboBox"
icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false"
auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="2" anchor="8"
fill="1" />
+ </item>
+ <item class="javax.swing.JTable"
icon="/com/intellij/uiDesigner/icons/table.png" removable="false"
auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0"
fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JList"
icon="/com/intellij/uiDesigner/icons/list.png" removable="false"
auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="2" anchor="0"
fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTree"
icon="/com/intellij/uiDesigner/icons/tree.png" removable="false"
auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0"
fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTabbedPane"
icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false"
auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0"
fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSplitPane"
icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false"
auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0"
fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSpinner"
icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false"
auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8"
fill="1" />
+ </item>
+ <item class="javax.swing.JSlider"
icon="/com/intellij/uiDesigner/icons/slider.png" removable="false"
auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8"
fill="1" />
+ </item>
+ <item class="javax.swing.JSeparator"
icon="/com/intellij/uiDesigner/icons/separator.png" removable="false"
auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0"
fill="3" />
+ </item>
+ <item class="javax.swing.JProgressBar"
icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false"
auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0"
fill="1" />
+ </item>
+ <item class="javax.swing.JToolBar"
icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false"
auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0"
fill="1">
+ <preferred-size width="-1" height="20" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JToolBar$Separator"
icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false"
auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="0"
fill="1" />
+ </item>
+ <item class="javax.swing.JScrollBar"
icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false"
auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="0" anchor="0"
fill="2" />
+ </item>
+ </group>
+ </component>
+</project>
\ No newline at end of file
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/CryptoUtil.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/CryptoUtil.kt
index adfa371..db4eee5 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/CryptoUtil.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/CryptoUtil.kt
@@ -164,11 +164,5 @@ class CryptoUtil {
val data = symmetricCipher.doFinal(enc.encryptedData)
return data
}
-
- fun ByteArray.toHexString() : String {
- return this.joinToString("") {
- java.lang.String.format("%02x", it)
- }
- }
}
}
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 74a2475..8e1d678 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -154,6 +154,8 @@ object EbicsSubscribersTable : IntIdTable() {
val encryptionKey = reference("encryptionKey",
EbicsSubscriberPublicKeysTable).nullable()
val authenticationKey = reference("authorizationKey",
EbicsSubscriberPublicKeysTable).nullable()
+ val nextOrderID = integer("nextOrderID")
+
val state = enumeration("state", SubscriberState::class)
}
@@ -168,6 +170,8 @@ class EbicsSubscriberEntity(id: EntityID<Int>) :
IntEntity(id) {
var encryptionKey by EbicsSubscriberPublicKeyEntity optionalReferencedOn
EbicsSubscribersTable.encryptionKey
var authenticationKey by EbicsSubscriberPublicKeyEntity
optionalReferencedOn EbicsSubscribersTable.authenticationKey
+ var nextOrderID by EbicsSubscribersTable.nextOrderID
+
var state by EbicsSubscribersTable.state
}
@@ -177,8 +181,8 @@ object EbicsDownloadTransactionsTable : IdTable<String>() {
val orderType = text("orderType")
val host = reference("host", EbicsHostsTable)
val subscriber = reference("subscriber", EbicsSubscribersTable)
- val encodedResponse = blob("encodedResponse")
- val orderID = text("orderID")
+ val encodedResponse = text("encodedResponse")
+ val transactionKeyEnc = blob("transactionKeyEnc")
val numSegments = integer("numSegments")
val segmentSize = integer("segmentSize")
val receiptReceived = bool("receiptReceived")
@@ -189,11 +193,11 @@ class EbicsDownloadTransactionEntity(id:
EntityID<String>) : Entity<String>(id)
companion object : EntityClass<String,
EbicsDownloadTransactionEntity>(EbicsDownloadTransactionsTable)
var orderType by EbicsDownloadTransactionsTable.orderType
- var host by EbicsDownloadTransactionsTable.host
- var subscriber by EbicsDownloadTransactionsTable.host
+ var host by EbicsHostEntity referencedOn
EbicsDownloadTransactionsTable.host
+ var subscriber by EbicsSubscriberEntity referencedOn
EbicsDownloadTransactionsTable.subscriber
var encodedResponse by EbicsDownloadTransactionsTable.encodedResponse
- var orderID by EbicsDownloadTransactionsTable.orderID
var numSegments by EbicsDownloadTransactionsTable.numSegments
+ var transactionKeyEnc by EbicsDownloadTransactionsTable.transactionKeyEnc
var segmentSize by EbicsDownloadTransactionsTable.segmentSize
var receiptReceived by EbicsDownloadTransactionsTable.receiptReceived
}
@@ -208,7 +212,8 @@ fun dbCreateTables() {
SchemaUtils.create(
BankCustomersTable,
EbicsSubscribersTable,
- EbicsHostsTable
+ EbicsHostsTable,
+ EbicsDownloadTransactionsTable
)
}
}
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsOrderUtil.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsOrderUtil.kt
index 2763afe..5652011 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsOrderUtil.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsOrderUtil.kt
@@ -19,6 +19,7 @@
package tech.libeufin.sandbox
+import java.security.SecureRandom
import java.util.zip.DeflaterInputStream
import java.util.zip.InflaterInputStream
@@ -40,5 +41,21 @@ class EbicsOrderUtil private constructor() {
it.readAllBytes()
}
}
+
+ fun generateTransactionId(): String {
+ val rng = SecureRandom()
+ val res = ByteArray(16)
+ rng.nextBytes(res)
+ return res.toHexString()
+ }
+
+ /**
+ * Calculate the resulting size of base64-encoding data of the given
length,
+ * including padding.
+ */
+ fun calculateBase64EncodedLength(dataLength: Int): Int {
+ val blocks = (dataLength + 3 - 1) / 3
+ return blocks * 4
+ }
}
}
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 11db448..435a4e8 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -40,6 +40,7 @@ import io.ktor.routing.routing
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import org.apache.xml.security.binding.xmldsig.RSAKeyValueType
+import org.apache.xml.security.binding.xmldsig.SignatureType
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction
import org.slf4j.Logger
@@ -49,20 +50,28 @@ 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.interfaces.RSAPublicKey
import java.text.DateFormat
+import java.util.*
+import java.util.zip.DeflaterInputStream
import javax.sql.rowset.serial.SerialBlob
import javax.xml.bind.JAXBContext
val logger: Logger = LoggerFactory.getLogger("tech.libeufin.sandbox")
-data class EbicsRequestError(val statusCode: HttpStatusCode) :
Exception("Ebics request error")
+open class EbicsRequestError(val errorText: String, val errorCode: String) :
+ Exception("EBICS request management error: $errorText ($errorCode)")
+
+class EbicsInvalidRequestError : EbicsRequestError("[EBICS_INVALID_REQUEST]
Invalid request", "060102")
open class EbicsKeyManagementError(val errorText: String, val errorCode:
String) :
Exception("EBICS key management error: $errorText ($errorCode)")
class EbicsInvalidXmlError : EbicsKeyManagementError("[EBICS_INVALID_XML]",
"091010")
+class EbicsInvalidOrderType :
EbicsRequestError("[EBICS_UNSUPPORTED_ORDER_TYPE] Order type not supported",
"091005")
+
private suspend fun ApplicationCall.respondEbicsKeyManagement(
errorText: String,
errorCode: String,
@@ -158,7 +167,7 @@ private suspend fun ApplicationCall.handleEbicsHia(header:
EbicsUnsecuredRequest
val ebicsSubscriber = findEbicsSubscriber(header.static.partnerID,
header.static.userID, header.static.systemID)
if (ebicsSubscriber == null) {
logger.warn("ebics subscriber not found")
- throw EbicsRequestError(HttpStatusCode.NotFound)
+ throw EbicsInvalidRequestError()
}
ebicsSubscriber.authenticationKey = EbicsSubscriberPublicKeyEntity.new
{
this.rsaPublicKey = SerialBlob(authPub.encoded)
@@ -188,7 +197,7 @@ private suspend fun ApplicationCall.handleEbicsIni(header:
EbicsUnsecuredRequest
findEbicsSubscriber(header.static.partnerID, header.static.userID,
header.static.systemID)
if (ebicsSubscriber == null) {
logger.warn("ebics subscriber ('${header.static.partnerID}' /
'${header.static.userID}' / '${header.static.systemID}') not found")
- throw EbicsRequestError(HttpStatusCode.NotFound)
+ throw EbicsInvalidRequestError()
}
ebicsSubscriber.signatureKey = EbicsSubscriberPublicKeyEntity.new {
this.rsaPublicKey = SerialBlob(sigPub.encoded)
@@ -213,10 +222,10 @@ private suspend fun ApplicationCall.handleEbicsHpb(
val ebicsSubscriber =
findEbicsSubscriber(header.static.partnerID, header.static.userID,
header.static.systemID)
if (ebicsSubscriber == null) {
- throw EbicsRequestError(HttpStatusCode.Unauthorized)
+ throw EbicsInvalidRequestError()
}
if (ebicsSubscriber.state != SubscriberState.INITIALIZED) {
- throw EbicsRequestError(HttpStatusCode.Forbidden)
+ throw EbicsInvalidRequestError()
}
val authPubBlob = ebicsSubscriber.authenticationKey!!.rsaPublicKey
val encPubBlob = ebicsSubscriber.encryptionKey!!.rsaPublicKey
@@ -301,6 +310,176 @@ inline fun <reified T> Document.toObject(): T {
}
+fun handleEbicsHtd(): ByteArray {
+ val htd = HTDResponseOrderData().apply {
+ this.partnerInfo = HTDResponseOrderData.PartnerInfo().apply {
+ this.accountInfoList = listOf(
+ HTDResponseOrderData.AccountInfo().apply {
+ this.id = "acctid1"
+ this.accountHolder = "Mina Musterfrau"
+ this.accountNumberList = listOf(
+ HTDResponseOrderData.GeneralAccountNumber().apply {
+ this.international = true
+ this.value = "DE21500105174751659277"
+ }
+ )
+ this.currency = "EUR"
+ this.description = "ACCT"
+ this.bankCodeList = listOf(
+ HTDResponseOrderData.GeneralBankCode().apply {
+ this.international = true
+ this.value = "INGDDEFFXXX"
+ }
+ )
+ }
+ )
+ this.addressInfo = HTDResponseOrderData.AddressInfo().apply {
+ this.name = "Foo"
+ }
+ this.bankInfo = HTDResponseOrderData.BankInfo().apply {
+ this.hostID = "host01"
+ }
+ this.orderInfoList = listOf(
+ HTDResponseOrderData.AuthOrderInfoType().apply {
+ this.description = "foo"
+ this.orderType = "C53"
+ this.transferType = "Download"
+ },
+ HTDResponseOrderData.AuthOrderInfoType().apply {
+ this.description = "foo"
+ this.orderType = "C52"
+ this.transferType = "Download"
+ },
+ HTDResponseOrderData.AuthOrderInfoType().apply {
+ this.description = "foo"
+ this.orderType = "CCC"
+ this.transferType = "Upload"
+ }
+ )
+ }
+ this.userInfo = HTDResponseOrderData.UserInfo().apply {
+ this.name = "Some User"
+ this.userID = HTDResponseOrderData.UserIDType().apply {
+ this.status = 5
+ this.value = "USER1"
+ }
+ this.permissionList = listOf(
+ HTDResponseOrderData.UserPermission().apply {
+ this.orderTypes = "C54 C53 C52 CCC"
+ }
+ )
+ }
+ }
+
+ val str = XMLUtil.convertJaxbToString(htd)
+ return str.toByteArray()
+}
+
+
+fun createEbicsResponseForDownloadInitializationPhase(
+ transactionID: String,
+ numSegments: Int,
+ segmentSize: Int,
+ enc: CryptoUtil.EncryptionResult,
+ encodedData: String
+): EbicsResponse {
+ return EbicsResponse().apply {
+ this.version = "H004"
+ this.revision = 1
+ this.header = EbicsResponse.Header().apply {
+ this.authenticate = true
+ this._static = EbicsResponse.StaticHeaderType().apply {
+ this.transactionID = transactionID
+ this.numSegments = BigInteger.valueOf(numSegments.toLong())
+ }
+ this.mutable = EbicsResponse.MutableHeaderType().apply {
+ this.transactionPhase =
EbicsTypes.TransactionPhaseType.INITIALISATION
+ this.segmentNumber = EbicsResponse.SegmentNumber().apply {
+ this.lastSegment = (numSegments == 1)
+ this.value = BigInteger.valueOf(1)
+ }
+ this.reportText = "[EBICS_OK] OK"
+ this.returnCode = "000000"
+ }
+ }
+ this.authSignature = SignatureType()
+ this.body = EbicsResponse.Body().apply {
+ this.returnCode = EbicsResponse.ReturnCode().apply {
+ this.authenticate = true
+ this.value = "000000"
+ }
+ this.dataTransfer = EbicsResponse.DataTransferResponseType().apply
{
+ this.dataEncryptionInfo =
EbicsTypes.DataEncryptionInfo().apply {
+ this.authenticate = true
+ this.encryptionPubKeyDigest =
EbicsTypes.PubKeyDigest().apply {
+ this.algorithm =
"http://www.w3.org/2001/04/xmlenc#sha256"
+ this.version = "E002"
+ this.value = enc.pubKeyDigest
+ }
+ this.transactionKey = enc.encryptedTransactionKey
+ }
+ this.orderData = EbicsResponse.OrderData().apply {
+ this.value = encodedData.substring(0,
Math.min(segmentSize, encodedData.length))
+ }
+ }
+ }
+ }
+}
+
+
+fun createEbicsResponseForDownloadTransferPhase() {
+
+}
+
+
+fun createEbicsResponseForDownloadReceiptPhase(transactionID: String,
positiveAck: Boolean): EbicsResponse {
+ return EbicsResponse().apply {
+ this.version = "H004"
+ this.revision = 1
+ this.header = EbicsResponse.Header().apply {
+ this.authenticate = true
+ this._static = EbicsResponse.StaticHeaderType().apply {
+ this.transactionID = transactionID
+ }
+ this.mutable = EbicsResponse.MutableHeaderType().apply {
+ this.transactionPhase = EbicsTypes.TransactionPhaseType.RECEIPT
+ if (positiveAck) {
+ this.reportText = "[EBICS_DOWNLOAD_POSTPROCESS_DONE]
Received positive receipt"
+ this.returnCode = "011000"
+ } else {
+ this.reportText = "[EBICS_DOWNLOAD_POSTPROCESS_DONE]
Received negative receipt"
+ this.returnCode = "011001"
+ }
+ }
+ }
+ this.authSignature = SignatureType()
+ this.body = EbicsResponse.Body().apply {
+ this.returnCode = EbicsResponse.ReturnCode().apply {
+ this.authenticate = true
+ this.value = "000000"
+ }
+ }
+ }
+}
+
+
+private suspend fun ApplicationCall.handleEbicsDownloadInitialization() {
+
+}
+
+private suspend fun ApplicationCall.handleEbicsDownloadTransfer() {
+
+}
+
+private suspend fun ApplicationCall.handleEbicsDownloadReceipt() {
+
+}
+
+private suspend fun ApplicationCall.handleEbicsUploadInitialization() {
+
+}
+
+
private suspend fun ApplicationCall.ebicsweb() {
val requestDocument = receiveEbicsXml()
@@ -344,24 +523,111 @@ private suspend fun ApplicationCall.ebicsweb() {
println("ebicsRequest
${XMLUtil.convertDomToString(requestDocument)}")
val requestObject = requestDocument.toObject<EbicsRequest>()
val staticHeader = requestObject.header.static
+
when (requestObject.header.mutable.transactionPhase) {
EbicsTypes.TransactionPhaseType.INITIALISATION -> {
val partnerID = staticHeader.partnerID ?: throw
EbicsInvalidXmlError()
val userID = staticHeader.userID ?: throw
EbicsInvalidXmlError()
- transaction {
+ val respText = transaction {
val subscriber =
findEbicsSubscriber(partnerID, userID,
staticHeader.systemID)
?: throw EbicsInvalidXmlError()
- val authPub =
+ val requestedHostId =
requestObject.header.static.hostID
+ val ebicsHost = EbicsHostEntity.find {
EbicsHostsTable.hostID eq requestedHostId }.firstOrNull()
+ if (ebicsHost == null)
+ throw EbicsInvalidRequestError()
+ val hostAuthPriv = CryptoUtil.loadRsaPrivateKey(
+ ebicsHost.authenticationPrivateKey
+ .toByteArray()
+ )
+ val clientAuthPub =
CryptoUtil.loadRsaPublicKey(subscriber.authenticationKey!!.rsaPublicKey.toByteArray())
- val verifyResult =
XMLUtil.verifyEbicsDocument(requestDocument, authPub)
+ val clientEncPub =
+
CryptoUtil.loadRsaPublicKey(subscriber.encryptionKey!!.rsaPublicKey.toByteArray())
+ val verifyResult =
XMLUtil.verifyEbicsDocument(requestDocument, clientAuthPub)
println("ebicsRequest verification result:
$verifyResult")
+ val transactionID =
EbicsOrderUtil.generateTransactionId()
+ val orderType =
requestObject.header.static.orderDetails?.orderType
+
+ val response = when (orderType) {
+ "HTD" -> handleEbicsHtd()
+ else -> throw EbicsInvalidXmlError()
+ }
+
+ val compressedResponse =
DeflaterInputStream(response.inputStream()).use {
+ it.readAllBytes()
+ }
+
+ val enc =
CryptoUtil.encryptEbicsE002(compressedResponse, clientEncPub)
+ val encodedResponse =
Base64.getEncoder().encodeToString(enc.encryptedData)
+
+ val segmentSize = 4096
+ val totalSize = encodedResponse.length
+ val numSegments = ((totalSize + segmentSize - 1) /
segmentSize)
+
+ println("inner response: " +
response.toString(Charsets.UTF_8))
+
+ println("total size: $totalSize")
+ println("num segments: $numSegments")
+
+ EbicsDownloadTransactionEntity.new(transactionID) {
+ this.subscriber = subscriber
+ this.host = ebicsHost
+ this.orderType = orderType
+ this.segmentSize = segmentSize
+ this.transactionKeyEnc =
SerialBlob(enc.encryptedTransactionKey)
+ this.encodedResponse = encodedResponse
+ this.numSegments = numSegments
+ this.receiptReceived = false
+ }
+
+ val ebicsResponse =
createEbicsResponseForDownloadInitializationPhase(
+ transactionID,
+ numSegments, segmentSize, enc, encodedResponse
+ )
+ val docText =
XMLUtil.convertJaxbToString(ebicsResponse)
+ val doc = XMLUtil.parseStringIntoDom(docText)
+ XMLUtil.signEbicsDocument(doc, hostAuthPriv)
+ val signedDoc = XMLUtil.convertDomToString(doc)
+ println("response: $signedDoc")
+ docText
}
+ respondText(respText, ContentType.Application.Xml,
HttpStatusCode.OK)
+ return
}
EbicsTypes.TransactionPhaseType.TRANSFER -> {
}
EbicsTypes.TransactionPhaseType.RECEIPT -> {
+ val respText = transaction {
+ val requestedHostId =
requestObject.header.static.hostID
+ val ebicsHost = EbicsHostEntity.find {
EbicsHostsTable.hostID eq requestedHostId }.firstOrNull()
+ if (ebicsHost == null)
+ throw EbicsInvalidRequestError()
+ val hostAuthPriv = CryptoUtil.loadRsaPrivateKey(
+ ebicsHost.authenticationPrivateKey
+ .toByteArray()
+ )
+ val transactionID =
requestObject.header.static.transactionID
+ if (transactionID == null)
+ throw EbicsInvalidRequestError()
+ val downloadTransaction =
EbicsDownloadTransactionEntity.findById(transactionID)
+ if (downloadTransaction == null)
+ throw EbicsInvalidRequestError()
+ println("sending receipt for transaction ID
$transactionID")
+ val receiptCode =
requestObject.body.transferReceipt?.receiptCode
+ if (receiptCode == null)
+ throw EbicsInvalidRequestError()
+ val ebicsResponse =
createEbicsResponseForDownloadReceiptPhase(transactionID, receiptCode == 0)
+ val docText =
XMLUtil.convertJaxbToString(ebicsResponse)
+ val doc = XMLUtil.parseStringIntoDom(docText)
+ XMLUtil.signEbicsDocument(doc, hostAuthPriv)
+ val signedDoc = XMLUtil.convertDomToString(doc)
+ println("response: $signedDoc")
+ docText
+ }
+ respondText(respText, ContentType.Application.Xml,
HttpStatusCode.OK)
+ return
}
}
@@ -397,6 +663,7 @@ fun main() {
userId = "USER1"
systemId = null
state = SubscriberState.NEW
+ nextOrderID = 1
}
}
@@ -414,6 +681,7 @@ fun main() {
call.respondText("Internal server error.",
ContentType.Text.Plain, HttpStatusCode.InternalServerError)
}
}
+ // TODO: add another intercept call that adds schema validation before
the response is sent
intercept(ApplicationCallPipeline.Fallback) {
if (this.call.response.status() == null) {
call.respondText("Not found (no route matched).\n",
ContentType.Text.Plain, HttpStatusCode.NotFound)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLUtil.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLUtil.kt
index 5eb8bc8..436375c 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLUtil.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLUtil.kt
@@ -74,24 +74,20 @@ class XMLUtil private constructor() {
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)
+ val nodeSet =
xp.compile("//*[@authenticate='true']/descendant-or-self::node()").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 bytes = ByteArrayOutputStream()
+ val nodeList = ArrayList<Node>()
for (i in 0 until nodeSet.length) {
val node = nodeSet.item(i)
- org.apache.xml.security.Init.init()
- // Despite the transform later, this canonicalization step is
absolutely necessary,
- // as the canonicalizeSubtree method preserves namespaces that
are not in the subtree
- // being canonicalized, but in the parent hierarchy of the
document.
- val canon: Canonicalizer =
Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS)
- val cxml = canon.canonicalizeSubtree(node)
- bytes.writeBytes(cxml)
+ nodeList.add(node)
}
- return OctetStreamData(ByteArrayInputStream(bytes.toByteArray()))
+ return NodeSetData { nodeList.iterator() }
}
}
@@ -336,8 +332,12 @@ class XMLUtil private constructor() {
dsc.defaultNamespacePrefix = "ds"
dsc.uriDereferencer = EbicsSigUriDereferencer()
+ dsc.setProperty("javax.xml.crypto.dsig.cacheReference", true)
+
sig.sign(dsc)
+ println("canon data: " +
sig.signedInfo.canonicalizedData.readAllBytes().toString(Charsets.UTF_8))
+
val innerSig = authSigNode.firstChild
while (innerSig.hasChildNodes()) {
authSigNode.appendChild(innerSig.firstChild)
@@ -375,6 +375,7 @@ class XMLUtil private constructor() {
authSigNode.parentNode.removeChild(authSigNode)
val fac = XMLSignatureFactory.getInstance("DOM")
val dvc = DOMValidateContext(signingPub, sigEl)
+ dvc.setProperty("javax.xml.crypto.dsig.cacheReference", true)
dvc.uriDereferencer = EbicsSigUriDereferencer()
val sig = fac.unmarshalXMLSignature(dvc)
// FIXME: check that parameters are okay!s
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/XmlBinding.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/XmlBinding.kt
new file mode 100644
index 0000000..79a77ca
--- /dev/null
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/XmlBinding.kt
@@ -0,0 +1,23 @@
+package tech.libeufin.sandbox
+
+@Retention(AnnotationRetention.RUNTIME)
+annotation class XmlSchemaContext
+
+@Retention(AnnotationRetention.RUNTIME)
+annotation class XmlElement
+
+@Retention(AnnotationRetention.RUNTIME)
+annotation class XmlAttribute
+
+@Retention(AnnotationRetention.RUNTIME)
+annotation class XmlValue
+
+@Retention(AnnotationRetention.RUNTIME)
+annotation class XmlWrapper
+
+@Retention(AnnotationRetention.RUNTIME)
+annotation class XmlAdapter
+
+class XmlBinding<T> {
+
+}
\ No newline at end of file
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/hex.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/hex.kt
new file mode 100644
index 0000000..11b4afb
--- /dev/null
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/hex.kt
@@ -0,0 +1,7 @@
+package tech.libeufin.sandbox
+
+fun ByteArray.toHexString() : String {
+ return this.joinToString("") {
+ java.lang.String.format("%02x", it)
+ }
+}
diff --git
a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsResponse.kt
b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsResponse.kt
index 32ace05..4ef5d2e 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsResponse.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsResponse.kt
@@ -29,9 +29,6 @@ class EbicsResponse {
@get:XmlElement(required = true)
lateinit var body: Body
- @get:XmlAnyAttribute
- var otherAttributes = HashMap<QName, String>()
-
@XmlAccessorType(XmlAccessType.NONE)
@XmlType(name = "", propOrder = ["_static", "mutable"])
class Header {
@@ -101,7 +98,7 @@ class EbicsResponse {
@XmlAccessorType(XmlAccessType.NONE)
class OrderData {
@get:XmlValue
- lateinit var value: ByteArray
+ lateinit var value: String
}
@XmlAccessorType(XmlAccessType.NONE)
@@ -128,10 +125,8 @@ class EbicsResponse {
@XmlAccessorType(XmlAccessType.NONE)
@XmlType(name = "ResponseStaticHeaderType", propOrder = ["transactionID",
"numSegments"])
class StaticHeaderType {
- @get:XmlElement(name = "TransactionID", type = String::class)
- @get:XmlJavaTypeAdapter(HexBinaryAdapter::class)
- @get:XmlSchemaType(name = "hexBinary")
- var transactionID: ByteArray? = null
+ @get:XmlElement(name = "TransactionID")
+ var transactionID: String? = null
@get:XmlElement(name = "NumSegments")
@get:XmlSchemaType(name = "positiveInteger")
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
index ce9f543..b8b7b2c 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsTypes.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsTypes.kt
@@ -52,6 +52,7 @@ class EbicsTypes private constructor() {
}
+ @XmlType(name = "", propOrder = ["encryptionPubKeyDigest",
"transactionKey"])
@XmlAccessorType(XmlAccessType.NONE)
class DataEncryptionInfo {
@get:XmlAttribute(name = "authenticate", required = true)
@@ -62,12 +63,10 @@ class EbicsTypes private constructor() {
@get:XmlElement(name = "TransactionKey", required = true)
lateinit var transactionKey: ByteArray
-
- @get:XmlAnyElement(lax = true)
- var any: List<Any>? = null
}
@XmlAccessorType(XmlAccessType.NONE)
+ @XmlType(name = "", propOrder = ["value"])
class PubKeyDigest {
/**
* Version of the *digest* of the public key.
diff --git a/sandbox/src/test/kotlin/XmlUtilTest.kt
b/sandbox/src/test/kotlin/XmlUtilTest.kt
index 0bf182f..9661e37 100644
--- a/sandbox/src/test/kotlin/XmlUtilTest.kt
+++ b/sandbox/src/test/kotlin/XmlUtilTest.kt
@@ -56,6 +56,22 @@ class XmlUtilTest {
kotlin.test.assertFalse(XMLUtil.verifyEbicsDocument(doc,
otherPair.public))
}
+ @Test
+ fun multiAuthSigningTest() {
+ val doc = XMLUtil.parseStringIntoDom("""
+ <myMessage xmlns:ebics="urn:org:ebics:H004">
+ <ebics:AuthSignature />
+ <foo authenticate="true">Hello World</foo>
+ <bar authenticate="true">Another one!</bar>
+ </myMessage>
+ """.trimIndent())
+ val kpg = KeyPairGenerator.getInstance("RSA")
+ kpg.initialize(2048)
+ val pair = kpg.genKeyPair()
+ XMLUtil.signEbicsDocument(doc, pair.private)
+ kotlin.test.assertTrue(XMLUtil.verifyEbicsDocument(doc, pair.public))
+ }
+
@Test
fun testRefSignature() {
val classLoader = ClassLoader.getSystemClassLoader()
--
To stop receiving notification emails like this one, please contact
address@hidden.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [libeufin] branch master updated: ebics transactions WIP,
gnunet <=