diff options
author | Antoine A <> | 2024-03-06 17:07:27 +0100 |
---|---|---|
committer | Antoine A <> | 2024-03-06 20:51:07 +0100 |
commit | 509c14f45fcbeb1eb09da1e16fa0ba4958db6854 (patch) | |
tree | 560ff9625ba5e232886b1eab9f08de55bc28de46 | |
parent | c5f106ddc3335ef63080ab4ebe4868f1f8917520 (diff) | |
download | libeufin-509c14f45fcbeb1eb09da1e16fa0ba4958db6854.tar.gz libeufin-509c14f45fcbeb1eb09da1e16fa0ba4958db6854.tar.bz2 libeufin-509c14f45fcbeb1eb09da1e16fa0ba4958db6854.zip |
Ebics3 HAC
5 files changed, 28 insertions, 175 deletions
diff --git a/ebics/src/main/kotlin/ebics_h005/Ebics3Request.kt b/ebics/src/main/kotlin/ebics_h005/Ebics3Request.kt index c8370d4c..bc48fa6f 100644 --- a/ebics/src/main/kotlin/ebics_h005/Ebics3Request.kt +++ b/ebics/src/main/kotlin/ebics_h005/Ebics3Request.kt @@ -404,7 +404,7 @@ class Ebics3Request { date: XMLGregorianCalendar, bankEncPub: RSAPublicKey, bankAuthPub: RSAPublicKey, - myOrderParams: OrderDetails.BTDOrderParams + myOrderParams: OrderDetails.BTDOrderParams? ): Ebics3Request { return Ebics3Request().apply { version = "H005" @@ -421,7 +421,7 @@ class Ebics3Request { timestamp = date partnerID = partnerId orderDetails = OrderDetails().apply { - this.adminOrderType = "BTD" + this.adminOrderType = if (myOrderParams == null) "HAC" else "BTD" this.btdOrderParams = myOrderParams } bankPubKeyDigests = BankPubKeyDigests().apply { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt index f66e77a7..bea1c3f3 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt @@ -84,31 +84,19 @@ private suspend fun downloadHelper( doc: SupportedDocument, processing: (InputStream) -> Unit ) { - val isEbics3 = doc != SupportedDocument.PAIN_002_LOGS - val initXml = if (isEbics3) { - createEbics3DownloadInitialization( - ctx.cfg, - ctx.bankKeys, - ctx.clientKeys, - doc, - lastExecutionTime - ) - } else { - createEbics25DownloadInit( - ctx.cfg, - ctx.clientKeys, - ctx.bankKeys, - doc, - lastExecutionTime - ) - } + val initXml = createEbics3DownloadInitialization( + ctx.cfg, + ctx.bankKeys, + ctx.clientKeys, + doc, + lastExecutionTime + ) return ebicsDownload( ctx.httpClient, ctx.cfg, ctx.clientKeys, ctx.bankKeys, initXml, - isEbics3, processing ) } 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 b561be8d..1c791886 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt @@ -43,120 +43,6 @@ import javax.xml.datatype.DatatypeFactory private val logger: Logger = LoggerFactory.getLogger("libeufin-nexus-ebics2") /** - * Creates a EBICS 2.5 download init. message. So far only used - * to fetch the PostFinance bank accounts. - */ -fun createEbics25DownloadInit( - cfg: EbicsSetupConfig, - clientKeys: ClientPrivateKeysFile, - bankKeys: BankPublicKeysFile, - whichDoc: SupportedDocument, - startDate: Instant? = null, - endDate: Instant? = null, -): ByteArray { - val orderType = when(whichDoc) { - SupportedDocument.PAIN_002 -> "Z01" - SupportedDocument.CAMT_052 -> "Z52" - SupportedDocument.CAMT_053 -> "Z53" - SupportedDocument.CAMT_054 -> "Z54" - SupportedDocument.PAIN_002_LOGS -> "HAC" - } - val nonce = getNonce(128) - val req = EbicsRequest.createForDownloadInitializationPhase( - cfg.ebicsUserId, - cfg.ebicsPartnerId, - cfg.ebicsHostId, - nonce, - DatatypeFactory.newInstance().newXMLGregorianCalendar( - GregorianCalendar( - TimeZone.getTimeZone(ZoneId.systemDefault()) - ) - ), - bankKeys.bank_encryption_public_key, - bankKeys.bank_authentication_public_key, - orderType, - EbicsRequest.StandardOrderParams().apply { - if (startDate != null) { - val r = EbicsDateRange(startDate, endDate ?: Instant.now()) - dateRange = EbicsRequest.DateRange().apply { - start = getXmlDate(r.start) - end = getXmlDate(r.end) - } - } - } - ) - val doc = XMLUtil.convertJaxbToDocument(req) - XMLUtil.signEbicsDocument( - doc, - clientKeys.authentication_private_key, - withEbics3 = false - ) - return XMLUtil.convertDomToBytes(doc) -} - -/** - * Creates raw XML for an EBICS receipt phase. - * - * @param cfg configuration handle. - * @param clientKeys user EBICS private keys. - * @param transactionId transaction ID of the EBICS communication that - * should receive this receipt. - * @param success was the download successfully processed - * @return receipt request in XML. - */ -fun createEbics25DownloadReceiptPhase( - cfg: EbicsSetupConfig, - clientKeys: ClientPrivateKeysFile, - transactionId: String, - success: Boolean -): ByteArray { - val req = EbicsRequest.createForDownloadReceiptPhase( - transactionId, - cfg.ebicsHostId, - success - ) - val doc = XMLUtil.convertJaxbToDocument(req) - XMLUtil.signEbicsDocument( - doc, - clientKeys.authentication_private_key, - withEbics3 = false - ) - return XMLUtil.convertDomToBytes(doc) -} - -/** - * Creates raw XML for an EBICS transfer phase. - * - * @param cfg configuration handle. - * @param clientKeys user EBICS private keys. - * @param segNumber which segment we ask the bank. - * @param totalSegments how many segments compose the whole EBICS transaction. - * @param transactionId ID of the EBICS transaction that transports all the segments. - * @return raw XML string of the request. - */ -fun createEbics25DownloadTransferPhase( - cfg: EbicsSetupConfig, - clientKeys: ClientPrivateKeysFile, - segNumber: Int, - totalSegments: Int, - transactionId: String -): ByteArray { - val req = EbicsRequest.createForDownloadTransferPhase( - hostID = cfg.ebicsHostId, - segmentNumber = segNumber, - numSegments = totalSegments, - transactionID = transactionId - ) - val doc = XMLUtil.convertJaxbToDocument(req) - XMLUtil.signEbicsDocument( - doc, - clientKeys.authentication_private_key, - withEbics3 = false - ) - return XMLUtil.convertDomToBytes(doc) -} - -/** * Parses the raw XML that came from the bank into the Nexus representation. * * @param clientEncryptionKey client private encryption key, used to decrypt 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 e1b17699..29a5e5e7 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt @@ -124,14 +124,14 @@ fun createEbics3DownloadInitialization( DatatypeFactory.newInstance().newXMLGregorianCalendar(GregorianCalendar()), bankAuthPub = bankkeys.bank_authentication_public_key, bankEncPub = bankkeys.bank_encryption_public_key, - myOrderParams = Ebics3Request.OrderDetails.BTDOrderParams().apply { + myOrderParams = if (whichDoc == SupportedDocument.PAIN_002_LOGS) null else Ebics3Request.OrderDetails.BTDOrderParams().apply { service = Ebics3Request.OrderDetails.Service().apply { serviceName = when(whichDoc) { SupportedDocument.PAIN_002 -> "PSR" SupportedDocument.CAMT_052 -> "STM" SupportedDocument.CAMT_053 -> "EOP" SupportedDocument.CAMT_054 -> "REP" - SupportedDocument.PAIN_002_LOGS -> throw Exception("HAC (--only-logs) not available in EBICS 3") + SupportedDocument.PAIN_002_LOGS -> "HAC" } scope = "CH" container = Ebics3Request.OrderDetails.Service.Container().apply { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt index c4231455..edbef2e9 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt @@ -113,6 +113,7 @@ suspend fun HttpClient.postToBank(bankUrl: String, msg: ByteArray): InputStream setBody(msg) } if (res.status != HttpStatusCode.OK) { + println(res.bodyAsText()) throw Exception("Invalid response status: ${res.status}") } return res.bodyAsChannel().toInputStream() @@ -273,7 +274,6 @@ private fun areCodesOk(ebicsResponseContent: EbicsResponseContent) = * @param clientKeys client EBICS private keys. * @param bankKeys bank EBICS public keys. * @param reqXml raw EBICS XML request of the init phase. - * @param isEbics3 true for EBICS 3, false otherwise. * @param processing processing lambda receiving EBICS files as a byte stream if the transaction was not empty. * @return T if the transaction was successful. If the failure is at the EBICS * level EbicsSideException is thrown else ités the exception of the processing lambda. @@ -284,7 +284,6 @@ suspend fun ebicsDownload( clientKeys: ClientPrivateKeysFile, bankKeys: BankPublicKeysFile, reqXml: ByteArray, - isEbics3: Boolean, processing: (InputStream) -> Unit ) = coroutineScope { val scope = this @@ -293,7 +292,7 @@ suspend fun ebicsDownload( // error loop until the pending transaction timeout. // TODO find a way to cancel the pending transaction ? withContext(NonCancellable) { - val initResp = postEbics(client, cfg, bankKeys, reqXml, isEbics3) + val initResp = postEbics(client, cfg, bankKeys, reqXml, true) logger.debug("Download init phase done. EBICS- and bank-technical codes are: ${initResp.technicalReturnCode}, ${initResp.bankReturnCode}") if (initResp.technicalReturnCode != EbicsReturnCode.EBICS_OK) { throw Exception("Download init phase has EBICS-technical error: ${initResp.technicalReturnCode}") @@ -332,11 +331,9 @@ suspend fun ebicsDownload( for (x in 2 .. howManySegments) { if (!scope.isActive) break // request segment number x. - val transReq = if (isEbics3) - createEbics3DownloadTransferPhase(cfg, clientKeys, x, howManySegments, tId) - else createEbics25DownloadTransferPhase(cfg, clientKeys, x, howManySegments, tId) + val transReq = createEbics3DownloadTransferPhase(cfg, clientKeys, x, howManySegments, tId) - val transResp = postEbics(client, cfg, bankKeys, transReq, isEbics3) + val transResp = postEbics(client, cfg, bankKeys, transReq, true) if (!areCodesOk(transResp)) { throw EbicsSideException( "EBICS transfer segment #$x failed.", @@ -350,9 +347,7 @@ suspend fun ebicsDownload( ebicsChunks.add(chunk) } suspend fun receipt(success: Boolean) { - val receiptXml = if (isEbics3) - createEbics3DownloadReceiptPhase(cfg, clientKeys, tId, success) - else createEbics25DownloadReceiptPhase(cfg, clientKeys, tId, success) + val receiptXml = createEbics3DownloadReceiptPhase(cfg, clientKeys, tId, success) // Sending the receipt to the bank. postEbics( @@ -360,7 +355,7 @@ suspend fun ebicsDownload( cfg, bankKeys, receiptXml, - isEbics3 + true ) } if (scope.isActive) { @@ -433,33 +428,17 @@ fun prepareUploadPayload( clientKeys: ClientPrivateKeysFile, bankKeys: BankPublicKeysFile, payload: ByteArray, - isEbics3: Boolean ): PreparedUploadData { - val encryptionResult: CryptoUtil.EncryptionResult = if (isEbics3) { - val innerSignedEbicsXml = signOrderEbics3( // A006 signature. - payload, - clientKeys.signature_private_key, - cfg.ebicsPartnerId, - cfg.ebicsUserId - ) - val userSignatureDataEncrypted = CryptoUtil.encryptEbicsE002( - EbicsOrderUtil.encodeOrderDataXml(innerSignedEbicsXml), - bankKeys.bank_encryption_public_key - ) - userSignatureDataEncrypted - } else { - val innerSignedEbicsXml = signOrder( // A006 signature. - payload, - clientKeys.signature_private_key, - cfg.ebicsPartnerId, - cfg.ebicsUserId - ) - val userSignatureDataEncrypted = CryptoUtil.encryptEbicsE002( - EbicsOrderUtil.encodeOrderDataXml(innerSignedEbicsXml), - bankKeys.bank_encryption_public_key - ) - userSignatureDataEncrypted - } + val innerSignedEbicsXml = signOrderEbics3( // A006 signature. + payload, + clientKeys.signature_private_key, + cfg.ebicsPartnerId, + cfg.ebicsUserId + ) + val encryptionResult = CryptoUtil.encryptEbicsE002( + EbicsOrderUtil.encodeOrderDataXml(innerSignedEbicsXml), + bankKeys.bank_encryption_public_key + ) val plainTransactionKey = encryptionResult.plainTransactionKey ?: throw Exception("Could not generate the transaction key, cannot encrypt the payload!") // Then only E002 symmetric (with ephemeral key) encrypt. @@ -526,7 +505,7 @@ suspend fun doEbicsUpload( payload: ByteArray, ): EbicsResponseContent = withContext(NonCancellable) { // TODO use a lambda and pass the order detail there for atomicity ? - val preparedPayload = prepareUploadPayload(cfg, clientKeys, bankKeys, payload, isEbics3 = true) + val preparedPayload = prepareUploadPayload(cfg, clientKeys, bankKeys, payload) val initXml = createEbics3RequestForUploadInitialization( cfg, preparedPayload, |