From 00e8988a760d8e73225424185701c1f59508840a Mon Sep 17 00:00:00 2001 From: Antoine A <> Date: Tue, 26 Mar 2024 23:44:27 +0100 Subject: Clean ebics setup code --- .../main/kotlin/tech/libeufin/nexus/EbicsSetup.kt | 63 +++++++------------ .../tech/libeufin/nexus/ebics/EbicsKeyMng.kt | 72 ++++++++++------------ 2 files changed, 55 insertions(+), 80 deletions(-) diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt index f9c83eba..a4870834 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt @@ -27,6 +27,7 @@ import io.ktor.client.plugins.* import tech.libeufin.common.* import tech.libeufin.common.crypto.* import tech.libeufin.nexus.ebics.* +import tech.libeufin.nexus.ebics.EbicsKeyMng.Order.* import java.nio.file.* import java.time.Instant import kotlin.io.path.* @@ -50,16 +51,6 @@ private fun loadOrGenerateClientKeys(path: Path): ClientPrivateKeysFile { return newKeys } -/** - * Expresses the type of keying message that the user wants - * to send to the bank. - */ -enum class KeysOrderType { - INI, - HIA, - HPB -} - /** * @return the "this" string with a space every two characters. */ @@ -107,36 +98,31 @@ suspend fun doKeysRequestAndUpdateState( cfg: EbicsSetupConfig, privs: ClientPrivateKeysFile, client: HttpClient, - orderType: KeysOrderType + order: EbicsKeyMng.Order ) { - logger.info("Doing key request ${orderType.name}") - val impl = EbicsKeyMng(cfg, privs, true) - val req = when(orderType) { - KeysOrderType.INI -> impl.INI() - KeysOrderType.HIA -> impl.HIA() - KeysOrderType.HPB -> impl.HPB() - } - val xml = client.postToBank(cfg.hostBaseUrl, req, "$orderType") + logger.info("Doing key request $order") + val req = EbicsKeyMng(cfg, privs, true).request(order) + val xml = client.postToBank(cfg.hostBaseUrl, req, order.name) val resp = EbicsKeyMng.parseResponse(xml, privs.encryption_private_key) - when (orderType) { - KeysOrderType.INI, KeysOrderType.HIA -> { + when (order) { + INI, HIA -> { if (resp.technicalCode == EbicsReturnCode.EBICS_INVALID_USER_OR_USER_STATE) { - throw Exception("$orderType status code ${resp.technicalCode}: either your IDs are incorrect, or you already have keys registered with this bank") + throw Exception("$order status code ${resp.technicalCode}: either your IDs are incorrect, or you already have keys registered with this bank") } } - KeysOrderType.HPB -> { + HPB -> { if (resp.technicalCode == EbicsReturnCode.EBICS_AUTHENTICATION_FAILED) { - throw Exception("$orderType status code ${resp.technicalCode}: could not download bank keys, send client keys (and/or related PDF document with --generate-registration-pdf) to the bank") + throw Exception("$order status code ${resp.technicalCode}: could not download bank keys, send client keys (and/or related PDF document with --generate-registration-pdf) to the bank") } } } - val orderData = resp.okOrFail("${orderType.name}") - when (orderType) { - KeysOrderType.INI -> privs.submitted_ini = true - KeysOrderType.HIA -> privs.submitted_hia = true - KeysOrderType.HPB -> { + val orderData = resp.okOrFail(order.name) + when (order) { + INI -> privs.submitted_ini = true + HIA -> privs.submitted_hia = true + HPB -> { val orderData = requireNotNull(orderData) { "HPB: missing order data" } @@ -149,15 +135,15 @@ suspend fun doKeysRequestAndUpdateState( try { persistBankKeys(bankKeys, cfg.bankPublicKeysFilename) } catch (e: Exception) { - throw Exception("Could not update the ${orderType.name} state on disk", e) + throw Exception("Could not update the $order state on disk", e) } } } - if (orderType != KeysOrderType.HPB) { + if (order != HPB) { try { persistClientKeys(privs, cfg.clientPrivateKeysFilename) } catch (e: Exception) { - throw Exception("Could not update the ${orderType.name} state on disk", e) + throw Exception("Could not update the $order state on disk", e) } } @@ -214,7 +200,7 @@ 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 client = HttpClient { install(HttpTimeout) { // It can take a lot of time for the bank to generate documents socketTimeoutMillis = 5 * 60 * 1000 @@ -223,21 +209,16 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") { // Privs exist. Upload their pubs val keysNotSub = !clientKeys.submitted_ini if ((!clientKeys.submitted_ini) || forceKeysResubmission) - doKeysRequestAndUpdateState(cfg, clientKeys, httpClient, KeysOrderType.INI) + doKeysRequestAndUpdateState(cfg, clientKeys, client, INI) // Eject PDF if the keys were submitted for the first time, or the user asked. if (keysNotSub || generateRegistrationPdf) makePdf(clientKeys, cfg) if ((!clientKeys.submitted_hia) || forceKeysResubmission) - doKeysRequestAndUpdateState(cfg, clientKeys, httpClient, KeysOrderType.HIA) + doKeysRequestAndUpdateState(cfg, clientKeys, client, HIA) // Checking if the bank keys exist on disk. var bankKeys = loadBankKeys(cfg.bankPublicKeysFilename) if (bankKeys == null) { - doKeysRequestAndUpdateState( - cfg, - clientKeys, - httpClient, - KeysOrderType.HPB - ) + doKeysRequestAndUpdateState(cfg, clientKeys, client, HPB) logger.info("Bank keys stored at ${cfg.bankPublicKeysFilename}") bankKeys = loadBankKeys(cfg.bankPublicKeysFilename)!! } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt index 48d10d18..02db79a0 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt @@ -32,6 +32,7 @@ import java.time.ZoneId import java.util.* import javax.xml.datatype.DatatypeFactory import java.security.interfaces.* +import tech.libeufin.nexus.ebics.EbicsKeyMng.Order.* /** EBICS protocol for key management */ class EbicsKeyMng( @@ -40,45 +41,38 @@ class EbicsKeyMng( private val ebics3: Boolean ) { private val schema = if (ebics3) "H005" else "H004" - fun INI(): ByteArray { - val data = XMLOrderData(cfg, "SignaturePubKeyOrderData", "http://www.ebics.org/S00${if (ebics3) 2 else 1}") { - el("SignaturePubKeyInfo") { - RSAKeyXml(clientKeys.signature_private_key) - el("SignatureVersion", "A006") - } - } - return request("ebicsUnsecuredRequest", "INI", "0200", data) + + enum class Order { + INI, + HIA, + HPB } - fun HIA(): ByteArray { - val data = XMLOrderData(cfg, "HIARequestOrderData", "urn:org:ebics:$schema") { - el("AuthenticationPubKeyInfo") { - RSAKeyXml(clientKeys.authentication_private_key) - el("AuthenticationVersion", "X002") + fun request(order: Order): ByteArray { + val (name, securityMedium, orderAttribute) = when (order) { + INI, HIA -> Triple("ebicsUnsecuredRequest", "0200", "DZNNN") + HPB -> Triple("ebicsNoPubKeyDigestsRequest", "0000", "DZHNN") + } + val data = when (order) { + INI -> XMLOrderData(cfg, "SignaturePubKeyOrderData", "http://www.ebics.org/S00${if (ebics3) 2 else 1}") { + el("SignaturePubKeyInfo") { + RSAKeyXml(clientKeys.signature_private_key) + el("SignatureVersion", "A006") + } } - el("EncryptionPubKeyInfo") { - RSAKeyXml(clientKeys.encryption_private_key) - el("EncryptionVersion", "E002") + HIA -> XMLOrderData(cfg, "HIARequestOrderData", "urn:org:ebics:$schema") { + el("AuthenticationPubKeyInfo") { + RSAKeyXml(clientKeys.authentication_private_key) + el("AuthenticationVersion", "X002") + } + el("EncryptionPubKeyInfo") { + RSAKeyXml(clientKeys.encryption_private_key) + el("EncryptionVersion", "E002") + } } + HPB -> null } - return request("ebicsUnsecuredRequest", "HIA", "0200", data) - } - - fun HPB(): ByteArray { - val nonce = getNonce(128) - return request("ebicsNoPubKeyDigestsRequest", "HPB", "0000", timestamp = Instant.now(), sign = true) - } - - /* ----- Helpers ----- */ - - private fun request( - name: String, - order: String, - securityMedium: String, - data: String? = null, - timestamp: Instant? = null, - sign: Boolean = false - ): ByteArray { + val sign = order == HPB val doc = XmlBuilder.toDom(name, "urn:org:ebics:$schema") { attr("http://www.w3.org/2000/xmlns/", "xmlns", "urn:org:ebics:$schema") attr("http://www.w3.org/2000/xmlns/", "xmlns:ds", "http://www.w3.org/2000/09/xmldsig#") @@ -88,18 +82,18 @@ class EbicsKeyMng( attr("authenticate", "true") el("static") { el("HostID", cfg.ebicsHostId) - if (timestamp != null) { + if (order == HPB) { el("Nonce", getNonce(128).encodeUpHex()) - el("Timestamp", timestamp.xmlDateTime()) + el("Timestamp", Instant.now().xmlDateTime()) } el("PartnerID", cfg.ebicsPartnerId) el("UserID", cfg.ebicsUserId) el("OrderDetails") { if (ebics3) { - el("AdminOrderType", order) + el("AdminOrderType", order.name) } else { - el("OrderType", order) - el("OrderAttribute", if (order == "HPB") "DZHNN" else "DZNNN") + el("OrderType", order.name) + el("OrderAttribute", orderAttribute) } } el("SecurityMedium", securityMedium) -- cgit v1.2.3