summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine A <>2024-03-07 22:10:10 +0100
committerAntoine A <>2024-03-07 22:10:10 +0100
commit867490864a27a7a74b122c5a6fb3cd34fb3ec8ae (patch)
tree57310c37921b18a683fe8b64c02026968218a733
parentc7344ce7d8466aa6d36d428dd730978d15ceb720 (diff)
downloadlibeufin-867490864a27a7a74b122c5a6fb3cd34fb3ec8ae.tar.gz
libeufin-867490864a27a7a74b122c5a6fb3cd34fb3ec8ae.tar.bz2
libeufin-867490864a27a7a74b122c5a6fb3cd34fb3ec8ae.zip
Replace all Ebics2 JAXB logic with custom XML DSL
-rw-r--r--ebics/src/main/kotlin/Ebics.kt1
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt8
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt175
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt49
-rw-r--r--testbench/src/main/kotlin/Main.kt3
5 files changed, 184 insertions, 52 deletions
diff --git a/ebics/src/main/kotlin/Ebics.kt b/ebics/src/main/kotlin/Ebics.kt
index e3ec9175..d0679c37 100644
--- a/ebics/src/main/kotlin/Ebics.kt
+++ b/ebics/src/main/kotlin/Ebics.kt
@@ -232,6 +232,7 @@ enum class EbicsReturnCode(val errorCode: String) {
EBICS_INTERNAL_ERROR("061099"),
EBICS_TX_RECOVERY_SYNC("061101"),
EBICS_AUTHORISATION_ORDER_IDENTIFIER_FAILED("090003"),
+ EBICS_INVALID_ORDER_DATA_FORMAT("090004"),
EBICS_NO_DOWNLOAD_DATA_AVAILABLE("090005"),
EBICS_INVALID_USER_OR_USER_STATE("091002"),
EBICS_USER_UNKNOWN("091003"),
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
index a9e595f4..d0e1a7f3 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
@@ -23,6 +23,7 @@ import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.groups.*
import com.github.ajalt.clikt.parameters.options.*
import io.ktor.client.*
+import io.ktor.client.plugins.*
import tech.libeufin.common.*
import tech.libeufin.common.crypto.*
import tech.libeufin.ebics.*
@@ -231,7 +232,12 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") {
val cfg = extractEbicsConfig(common.config)
// Config is sane. Go (maybe) making the private keys.
val clientKeys = loadOrGenerateClientKeys(cfg.clientPrivateKeysFilename)
- val httpClient = HttpClient()
+ val httpClient = HttpClient {
+ install(HttpTimeout) {
+ // It can take a lot of time for the bank to generate documents
+ socketTimeoutMillis = 5 * 60 * 1000
+ }
+ }
// Privs exist. Upload their pubs
val keysNotSub = !clientKeys.submitted_ini
if ((!clientKeys.submitted_ini) || forceKeysResubmission)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt
index 1c791886..3de5632e 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt
@@ -25,6 +25,7 @@ package tech.libeufin.nexus.ebics
import org.slf4j.Logger
import org.slf4j.LoggerFactory
+import tech.libeufin.common.*
import tech.libeufin.ebics.*
import tech.libeufin.ebics.ebics_h004.EbicsKeyManagementResponse
import tech.libeufin.ebics.ebics_h004.EbicsNpkdRequest
@@ -57,32 +58,54 @@ fun parseKeysMgmtResponse(
clientEncryptionKey: RSAPrivateCrtKey,
xml: InputStream
): EbicsKeyManagementResponseContent? {
- // TODO throw instead of null
- val jaxb = try {
- XMLUtil.convertToJaxb<EbicsKeyManagementResponse>(xml)
- } catch (e: Exception) {
- tech.libeufin.nexus.logger.error("Could not parse the raw response from bank into JAXB.")
- return null
- }
- var payload: ByteArray? = null
- jaxb.value.body.dataTransfer?.dataEncryptionInfo.apply {
- // non-null indicates that an encrypted payload should be found.
- if (this != null) {
- val encOrderData = jaxb.value.body.dataTransfer?.orderData?.value
- if (encOrderData == null) {
- tech.libeufin.nexus.logger.error("Despite a non-null DataEncryptionInfo, OrderData could not be found, can't decrypt any payload!")
- return null
+ return XmlDestructor.fromStream(xml, "ebicsKeyManagementResponse") {
+ lateinit var technicalReturnCode: EbicsReturnCode
+ lateinit var bankReturnCode: EbicsReturnCode
+ lateinit var reportText: String
+ var payload: ByteArray? = null
+ one("header") {
+ one("mutable") {
+ technicalReturnCode = EbicsReturnCode.lookup(one("ReturnCode").text())
+ reportText = one("ReportText").text()
+ }
+ }
+ one("body") {
+ bankReturnCode = EbicsReturnCode.lookup(one("ReturnCode").text())
+ payload = opt("DataTransfer") {
+ val descriptionInfo = one("DataEncryptionInfo") {
+ DataEncryptionInfo(
+ one("TransactionKey").text().decodeBase64(),
+ one("EncryptionPubKeyDigest").text().decodeBase64()
+ )
+ }
+ decryptAndDecompressPayload(
+ clientEncryptionKey,
+ descriptionInfo,
+ listOf(one("OrderData").text())
+ ).readBytes()
}
- payload = decryptAndDecompressPayload(
- clientEncryptionKey,
- DataEncryptionInfo(this.transactionKey, this.encryptionPubKeyDigest.value),
- listOf(encOrderData)
- ).readBytes()
+ }
+ EbicsKeyManagementResponseContent(technicalReturnCode, bankReturnCode, payload)
+ }
+}
+
+private fun XmlBuilder.RSAKeyXml(key: RSAPrivateCrtKey) {
+ el("ns2:PubKeyValue") {
+ el("ds:RSAKeyValue") {
+ el("ds:Modulus", key.modulus.toByteArray().encodeBase64())
+ el("ds:Exponent", key.publicExponent.toByteArray().encodeBase64())
}
}
- val bankReturnCode = EbicsReturnCode.lookup(jaxb.value.body.returnCode.value) // business error
- val ebicsReturnCode = EbicsReturnCode.lookup(jaxb.value.header.mutable.returnCode) // ebics error
- return EbicsKeyManagementResponseContent(ebicsReturnCode, bankReturnCode, payload)
+}
+
+private fun XMLOrderData(cfg: EbicsSetupConfig, name: String, schema: String, build: XmlBuilder.() -> Unit): String {
+ return XmlBuilder.toString(name) {
+ attr("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#")
+ attr("xmlns:ns2", schema)
+ build()
+ el("ns2:PartnerID", cfg.ebicsPartnerId)
+ el("ns2:UserID", cfg.ebicsUserId)
+ }.toByteArray().inputStream().deflate().readAllBytes().encodeBase64() // TODO opti
}
/**
@@ -93,13 +116,33 @@ fun parseKeysMgmtResponse(
* @return the raw EBICS INI message.
*/
fun generateIniMessage(cfg: EbicsSetupConfig, clientKeys: ClientPrivateKeysFile): ByteArray {
- val iniRequest = EbicsUnsecuredRequest.createIni(
- cfg.ebicsHostId,
- cfg.ebicsUserId,
- cfg.ebicsPartnerId,
- clientKeys.signature_private_key
- )
- val doc = XMLUtil.convertJaxbToDocument(iniRequest)
+ val inner = XMLOrderData(cfg, "ns2:SignaturePubKeyOrderData", "http://www.ebics.org/S001") {
+ el("ns2:SignaturePubKeyInfo") {
+ RSAKeyXml(clientKeys.signature_private_key)
+ el("ns2:SignatureVersion", "A006")
+ }
+ }
+ val doc = XmlBuilder.toDom("ebicsUnsecuredRequest", "urn:org:ebics:H004") {
+ attr("http://www.w3.org/2000/xmlns/", "xmlns", "urn:org:ebics:H004")
+ attr("http://www.w3.org/2000/xmlns/", "xmlns:ds", "http://www.w3.org/2000/09/xmldsig#")
+ attr("Version", "H004")
+ attr("Revision", "1")
+ el("header") {
+ attr("authenticate", "true")
+ el("static") {
+ el("HostID", cfg.ebicsHostId)
+ el("PartnerID", cfg.ebicsPartnerId)
+ el("UserID", cfg.ebicsUserId)
+ el("OrderDetails") {
+ el("OrderType", "INI")
+ el("OrderAttribute", "DZNNN")
+ }
+ el("SecurityMedium", "0200")
+ }
+ el("mutable")
+ }
+ el("body/DataTransfer/OrderData", inner)
+ }
return XMLUtil.convertDomToBytes(doc)
}
@@ -112,14 +155,37 @@ fun generateIniMessage(cfg: EbicsSetupConfig, clientKeys: ClientPrivateKeysFile)
* @return the raw EBICS HIA message.
*/
fun generateHiaMessage(cfg: EbicsSetupConfig, clientKeys: ClientPrivateKeysFile): ByteArray {
- val hiaRequest = EbicsUnsecuredRequest.createHia(
- cfg.ebicsHostId,
- cfg.ebicsUserId,
- cfg.ebicsPartnerId,
- clientKeys.authentication_private_key,
- clientKeys.encryption_private_key
- )
- val doc = XMLUtil.convertJaxbToDocument(hiaRequest)
+ val inner = XMLOrderData(cfg, "ns2:HIARequestOrderData", "urn:org:ebics:H004") {
+ el("ns2:AuthenticationPubKeyInfo") {
+ RSAKeyXml(clientKeys.authentication_private_key)
+ el("ns2:AuthenticationVersion", "X002")
+ }
+ el("ns2:EncryptionPubKeyInfo") {
+ RSAKeyXml(clientKeys.encryption_private_key)
+ el("ns2:EncryptionVersion", "E002")
+ }
+ }
+ val doc = XmlBuilder.toDom("ebicsUnsecuredRequest", "urn:org:ebics:H004") {
+ attr("http://www.w3.org/2000/xmlns/", "xmlns", "urn:org:ebics:H004")
+ attr("http://www.w3.org/2000/xmlns/", "xmlns:ds", "http://www.w3.org/2000/09/xmldsig#")
+ attr("Version", "H004")
+ attr("Revision", "1")
+ el("header") {
+ attr("authenticate", "true")
+ el("static") {
+ el("HostID", cfg.ebicsHostId)
+ el("PartnerID", cfg.ebicsPartnerId)
+ el("UserID", cfg.ebicsUserId)
+ el("OrderDetails") {
+ el("OrderType", "HIA")
+ el("OrderAttribute", "DZNNN")
+ }
+ el("SecurityMedium", "0200")
+ }
+ el("mutable")
+ }
+ el("body/DataTransfer/OrderData", inner)
+ }
return XMLUtil.convertDomToBytes(doc)
}
@@ -131,14 +197,31 @@ fun generateHiaMessage(cfg: EbicsSetupConfig, clientKeys: ClientPrivateKeysFile)
* @return the raw EBICS HPB message.
*/
fun generateHpbMessage(cfg: EbicsSetupConfig, clientKeys: ClientPrivateKeysFile): ByteArray {
- val hpbRequest = EbicsNpkdRequest.createRequest(
- cfg.ebicsHostId,
- cfg.ebicsPartnerId,
- cfg.ebicsUserId,
- getNonce(128),
- DatatypeFactory.newInstance().newXMLGregorianCalendar(GregorianCalendar())
- )
- val doc = XMLUtil.convertJaxbToDocument(hpbRequest)
+ val nonce = getNonce(128)
+ val doc = XmlBuilder.toDom("ebicsNoPubKeyDigestsRequest", "urn:org:ebics:H004") {
+ attr("http://www.w3.org/2000/xmlns/", "xmlns", "urn:org:ebics:H004")
+ attr("http://www.w3.org/2000/xmlns/", "xmlns:ds", "http://www.w3.org/2000/09/xmldsig#")
+ attr("Version", "H004")
+ attr("Revision", "1")
+ el("header") {
+ attr("authenticate", "true")
+ el("static") {
+ el("HostID", cfg.ebicsHostId)
+ el("Nonce", nonce.toHexString())
+ el("Timestamp", Instant.now().xmlDateTime())
+ el("PartnerID", cfg.ebicsPartnerId)
+ el("UserID", cfg.ebicsUserId)
+ el("OrderDetails") {
+ el("OrderType", "HPB")
+ el("OrderAttribute", "DZHNN")
+ }
+ el("SecurityMedium", "0000")
+ }
+ el("mutable")
+ }
+ el("AuthSignature")
+ el("body")
+ }
XMLUtil.signEbicsDocument(doc, clientKeys.authentication_private_key)
return XMLUtil.convertDomToBytes(doc)
} \ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt
index deb25360..e7df9c83 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt
@@ -52,6 +52,51 @@ data class Ebics3Service(
val container: String?
)
+
+
+fun iniRequest(
+ cfg: EbicsSetupConfig,
+ clientKeys: ClientPrivateKeysFile
+): ByteArray {
+ val temp = XmlBuilder.toString("ns2:SignaturePubKeyOrderData") {
+ attr("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#")
+ attr("xmlns:ns2", "http://www.ebics.org/S001")
+ el("ns2:SignaturePubKeyInfo") {
+ el("ns2:PubKeyValue") {
+ el("ds:RSAKeyValue") {
+ el("ds:Modulus", clientKeys.signature_private_key.modulus.toByteArray().encodeBase64())
+ el("ds:Exponent", clientKeys.signature_private_key.publicExponent.toByteArray().encodeBase64())
+ }
+ }
+ el("ns2:SignatureVersion", "A006")
+ }
+ el("ns2:PartnerID", cfg.ebicsPartnerId)
+ el("ns2:UserID", cfg.ebicsUserId)
+ }
+ // TODO in ebics:H005 we MUST use x509 certificates ...
+ println(temp)
+ val inner = temp.toByteArray().inputStream().deflate().readAllBytes().encodeBase64()
+ val doc = XmlBuilder.toDom("ebicsUnsecuredRequest", "urn:org:ebics:H005") {
+ attr("http://www.w3.org/2000/xmlns/", "xmlns", "urn:org:ebics:H005")
+ attr("http://www.w3.org/2000/xmlns/", "xmlns:ds", "http://www.w3.org/2000/09/xmldsig#")
+ attr("Version", "H005")
+ attr("Revision", "1")
+ el("header") {
+ attr("authenticate", "true")
+ el("static") {
+ el("HostID", cfg.ebicsHostId)
+ el("PartnerID", cfg.ebicsPartnerId)
+ el("UserID", cfg.ebicsUserId)
+ el("OrderDetails/AdminOrderType", "INI")
+ el("SecurityMedium", "0200")
+ }
+ el("mutable")
+ }
+ el("body/DataTransfer/OrderData", inner)
+ }
+ return XMLUtil.convertDomToBytes(doc)
+}
+
class Ebics3Impl(
private val cfg: EbicsSetupConfig,
private val bankKeys: BankPublicKeysFile,
@@ -62,8 +107,6 @@ class Ebics3Impl(
val doc = XmlBuilder.toDom("ebicsRequest", "urn:org:ebics:H005") {
attr("http://www.w3.org/2000/xmlns/", "xmlns", "urn:org:ebics:H005")
attr("http://www.w3.org/2000/xmlns/", "xmlns:ds", "http://www.w3.org/2000/09/xmldsig#")
- attr("http://www.w3.org/2000/xmlns/", "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
- attr("http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation", "urn:org:ebics:H005 ebics_request_H005.xsd")
attr("Version", "H005")
attr("Revision", "1")
lambda()
@@ -182,7 +225,7 @@ class Ebics3Impl(
SupportedDocument.CAMT_054 -> Pair("BTD", Ebics3Service("REP", "CH", "camt.054", "08", "ZIP"))
SupportedDocument.PAIN_002_LOGS -> Pair("HAC", null)
}
- return downloadInitialization(orderType, service)
+ return downloadInitialization(orderType, service, startDate, endDate)
}
fun downloadInitialization(orderType: String, service: Ebics3Service? = null, startDate: Instant? = null, endDate: Instant? = null): ByteArray {
diff --git a/testbench/src/main/kotlin/Main.kt b/testbench/src/main/kotlin/Main.kt
index cf50c59d..1c21d8da 100644
--- a/testbench/src/main/kotlin/Main.kt
+++ b/testbench/src/main/kotlin/Main.kt
@@ -121,8 +121,7 @@ class Cli : CliktCommand("Run integration tests on banks provider") {
runBlocking {
step("Init ${kind.name}")
- if (ask("Reset DB ? y/n>") == "y") nexusCmd.test("dbinit -r $flags").assertOk()
- else nexusCmd.test("dbinit $flags").assertOk()
+ nexusCmd.test("dbinit $flags").assertOk()
val cmds = buildMap<String, suspend () -> Unit> {
put("reset-db", suspend {