diff options
author | Antoine A <> | 2024-03-11 17:14:24 +0100 |
---|---|---|
committer | Antoine A <> | 2024-03-11 17:14:24 +0100 |
commit | 0d62875dd2287857c5da172dcb3301062880810a (patch) | |
tree | 6880751fb475a9f0db8dcedbf8ef8e78a299f729 /nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt | |
parent | 861389ea98224e86f28fdf06570a260c3ae12f90 (diff) | |
download | libeufin-0d62875dd2287857c5da172dcb3301062880810a.tar.gz libeufin-0d62875dd2287857c5da172dcb3301062880810a.tar.bz2 libeufin-0d62875dd2287857c5da172dcb3301062880810a.zip |
Support both 2.5 and 3 EBICS version for download
Diffstat (limited to 'nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt')
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt new file mode 100644 index 00000000..1847beec --- /dev/null +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt @@ -0,0 +1,212 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2024 Taler Systems S.A. + + * 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.nexus.ebics + +import org.w3c.dom.Document +import tech.libeufin.common.crypto.CryptoUtil +import tech.libeufin.common.* +import tech.libeufin.nexus.* +import tech.libeufin.nexus.BankPublicKeysFile +import tech.libeufin.nexus.ClientPrivateKeysFile +import tech.libeufin.nexus.EbicsSetupConfig +import java.io.InputStream +import java.time.Instant +import java.time.ZoneId +import java.util.* +import javax.xml.datatype.DatatypeFactory +import java.security.interfaces.* + +/** EBICS protocol for key management */ +class Ebics3KeyMng( + private val cfg: EbicsSetupConfig, + private val clientKeys: ClientPrivateKeysFile +) { + fun INI(): ByteArray { + 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) + } + + fun HIA(): ByteArray { + 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) + } + + fun HPB(): ByteArray { + 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.encodeUpHex()) + 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, "H004") + return XMLUtil.convertDomToBytes(doc) + } + + /* ----- Helpers ----- */ + + private fun XmlBuilder.RSAKeyXml(key: RSAPrivateCrtKey) { + el("ns2:PubKeyValue") { + el("ds:RSAKeyValue") { + el("ds:Modulus", key.modulus.encodeBase64()) + el("ds:Exponent", key.publicExponent.encodeBase64()) + } + } + } + + private fun XMLOrderData(cfg: EbicsSetupConfig, name: String, schema: String, build: XmlBuilder.() -> Unit): String { + return XmlBuilder.toBytes(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) + }.inputStream().deflate().encodeBase64() + } + + companion object { + fun parseResponse(doc: Document, clientEncryptionKey: RSAPrivateCrtKey): EbicsResponse<InputStream?> { + return XmlDestructor.fromDoc(doc, "ebicsKeyManagementResponse") { + lateinit var technicalCode: EbicsReturnCode + lateinit var bankCode: EbicsReturnCode + var payload: InputStream? = null + one("header") { + one("mutable") { + technicalCode = EbicsReturnCode.lookup(one("ReturnCode").text()) + } + } + one("body") { + bankCode = EbicsReturnCode.lookup(one("ReturnCode").text()) + payload = opt("DataTransfer") { + val descriptionInfo = one("DataEncryptionInfo") { + DataEncryptionInfo( + one("TransactionKey").text().decodeBase64(), + one("EncryptionPubKeyDigest").text().decodeBase64() + ) + } + val chunk = one("OrderData").text().decodeBase64() + decryptAndDecompressPayload( + clientEncryptionKey, + descriptionInfo, + listOf(chunk) + ) + } + } + EbicsResponse( + technicalCode = technicalCode, + bankCode, + content = payload + ) + } + } + + fun parseHpbOrder(data: InputStream): Pair<RSAPublicKey, RSAPublicKey> { + return XmlDestructor.fromStream(data, "HPBResponseOrderData") { + val authPub = one("AuthenticationPubKeyInfo").one("PubKeyValue").one("RSAKeyValue") { + CryptoUtil.loadRsaPublicKeyFromComponents( + one("Modulus").text().decodeBase64(), + one("Exponent").text().decodeBase64(), + ) + } + val encPub = one("EncryptionPubKeyInfo").one("PubKeyValue").one("RSAKeyValue") { + CryptoUtil.loadRsaPublicKeyFromComponents( + one("Modulus").text().decodeBase64(), + one("Exponent").text().decodeBase64(), + ) + } + Pair(authPub, encPub) + } + } + } +}
\ No newline at end of file |