summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine A <>2024-03-07 11:55:38 +0100
committerAntoine A <>2024-03-07 11:55:38 +0100
commit4044e69399c8c911dd926c4f8d4164964a08e0f0 (patch)
treeb06cf3210c83de97e3544e2b0144df7a17f2c4b1
parentb0387f142c863b9314af99cb232c98aa98c4c889 (diff)
downloadlibeufin-4044e69399c8c911dd926c4f8d4164964a08e0f0.tar.gz
libeufin-4044e69399c8c911dd926c4f8d4164964a08e0f0.tar.bz2
libeufin-4044e69399c8c911dd926c4f8d4164964a08e0f0.zip
Use XML builder for all Ebics3 upload phases
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt2
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt177
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt18
3 files changed, 105 insertions, 92 deletions
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
index d38ee64c..48d25cd7 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
@@ -170,7 +170,7 @@ private fun submitBatch(
db.mem[orderId] = "Init"
DatabaseSubmissionState.success
} catch (e: NexusSubmitException) {
- logger.error(e.message)
+ e.fmtLog(logger)
when (e.stage) {
/**
* Permanent failure: the pain.001 was invalid. For example a Payto
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 8bb3140f..b4ff4877 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics3.kt
@@ -69,6 +69,104 @@ class Ebics3Impl(
return XMLUtil.convertDomToBytes(doc)
}
+ fun uploadInitialization(preparedUploadData: PreparedUploadData): ByteArray {
+ val nonce = getNonce(128)
+ return signedRequest {
+ 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)
+ // SystemID
+ // Product
+ el("OrderDetails") {
+ el("AdminOrderType", "BTU")
+ el("BTUOrderParams") {
+ el("Service") {
+ el("ServiceName", "MCT")
+ el("Scope", "CH")
+ el("MsgName") {
+ attr("version", "09")
+ text("pain.001")
+ }
+ }
+ el("SignatureFlag", "true")
+ }
+ }
+ el("BankPubKeyDigests") {
+ el("Authentication") {
+ attr("Version", "X002")
+ attr("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256")
+ text(CryptoUtil.getEbicsPublicKeyHash(bankKeys.bank_authentication_public_key).encodeBase64())
+ }
+ el("Encryption") {
+ attr("Version", "E002")
+ attr("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256")
+ text(CryptoUtil.getEbicsPublicKeyHash(bankKeys.bank_encryption_public_key).encodeBase64())
+ }
+ // Signature
+ }
+ el("SecurityMedium", "0000")
+ el("NumSegments", "1") // TODO test upload of many segment
+
+ }
+ el("mutable") {
+ el("TransactionPhase", "Initialisation")
+ }
+ }
+ el("AuthSignature")
+ el("body") {
+ el("DataTransfer") {
+ el("DataEncryptionInfo") {
+ attr("authenticate", "true")
+ el("EncryptionPubKeyDigest") {
+ attr("Version", "E002")
+ attr("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256")
+ text(CryptoUtil.getEbicsPublicKeyHash(bankKeys.bank_encryption_public_key).encodeBase64())
+ }
+ el("TransactionKey", preparedUploadData.transactionKey.encodeBase64())
+ }
+ el("SignatureData") {
+ attr("authenticate", "true")
+ text(preparedUploadData.userSignatureDataEncrypted.encodeBase64())
+ }
+ el("DataDigest") {
+ attr("SignatureVersion", "A006")
+ text(preparedUploadData.dataDigest.encodeBase64())
+ }
+ }
+ }
+ }
+ }
+
+ fun uploadTransfer(
+ transactionId: String,
+ uploadData: PreparedUploadData
+ ): ByteArray {
+ val chunkIndex = 1 // TODO test upload of many segment
+ return signedRequest {
+ el("header") {
+ attr("authenticate", "true")
+ el("static") {
+ el("HostID", cfg.ebicsHostId)
+ el("TransactionID", transactionId)
+ }
+ el("mutable") {
+ el("TransactionPhase", "Transfer")
+ el("SegmentNumber") {
+ attr("lastSegment", "true")
+ text(chunkIndex.toString())
+ }
+ }
+ }
+ el("AuthSignature")
+ el("body/DataTransfer/OrderData", uploadData.encryptedPayloadChunks[chunkIndex - 1])
+ }
+ }
+
fun downloadInitialization(whichDoc: SupportedDocument, startDate: Instant? = null, endDate: Instant? = null): ByteArray {
val nonce = getNonce(128)
return signedRequest {
@@ -193,84 +291,7 @@ class Ebics3Impl(
}
}
-/**
- * Creates the EBICS 3 document for the init phase of an upload
- * transaction.
- *
- * @param cfg configuration handle.
- * @param preparedUploadData business payload to send.
- * @param bankkeys bank public keys.
- * @param clientKeys client private keys.
- * @param orderService EBICS 3 document defining the request type
- * @return raw XML of the EBICS 3 init phase.
- */
-fun createEbics3RequestForUploadInitialization(
- cfg: EbicsSetupConfig,
- preparedUploadData: PreparedUploadData,
- bankkeys: BankPublicKeysFile,
- clientKeys: ClientPrivateKeysFile,
- orderService: Ebics3Request.OrderDetails.Service
-): ByteArray {
- val nonce = getNonce(128)
- val req = Ebics3Request.createForUploadInitializationPhase(
- preparedUploadData.transactionKey,
- preparedUploadData.userSignatureDataEncrypted,
- preparedUploadData.dataDigest,
- cfg.ebicsHostId,
- nonce,
- cfg.ebicsPartnerId,
- cfg.ebicsUserId,
- DatatypeFactory.newInstance().newXMLGregorianCalendar(GregorianCalendar()),
- bankkeys.bank_authentication_public_key,
- bankkeys.bank_encryption_public_key,
- BigInteger.ONE,
- orderService
- )
- val doc = XMLUtil.convertJaxbToDocument(
- req,
- withSchemaLocation = "urn:org:ebics:H005 ebics_request_H005.xsd"
- )
- XMLUtil.signEbicsDocument(
- doc,
- clientKeys.authentication_private_key,
- withEbics3 = true
- )
- return XMLUtil.convertDomToBytes(doc)
-}
-
-/**
- * Crafts one EBICS 3 request for the upload transfer phase. Currently
- * only 1-chunk payloads are supported.
- *
- * @param cfg configuration handle.
- * @param clientKeys client private keys.
- * @param transactionId EBICS transaction ID obtained from an init phase.
- * @param uploadData business content to upload.
- *
- * @return raw XML document.
- */
-fun createEbics3RequestForUploadTransferPhase(
- cfg: EbicsSetupConfig,
- clientKeys: ClientPrivateKeysFile,
- transactionId: String,
- uploadData: PreparedUploadData
-): ByteArray {
- val chunkIndex = 1 // only 1-chunk communication currently supported.
- val req = Ebics3Request.createForUploadTransferPhase(
- cfg.ebicsHostId,
- transactionId,
- BigInteger.valueOf(chunkIndex.toLong()),
- uploadData.encryptedPayloadChunks[chunkIndex - 1]
- )
- val doc = XMLUtil.convertJaxbToDocument(req)
- XMLUtil.signEbicsDocument(
- doc,
- clientKeys.authentication_private_key,
- withEbics3 = true
- )
- return XMLUtil.convertDomToBytes(doc)
-}
-
+// TODO this function should not be here
/**
* Collects all the steps to prepare the submission of a pain.001
* document to the bank, and finally send it. Indirectly throws
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 a44839a6..490156bb 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt
@@ -448,6 +448,7 @@ fun prepareUploadPayload(
?: throw Exception("Could not generate the transaction key, cannot encrypt the payload!")
// Then only E002 symmetric (with ephemeral key) encrypt.
val compressedInnerPayload = payload.inputStream().deflate().readAllBytes()
+ // TODO stream
val encryptedPayload = CryptoUtil.encryptEbicsE002withTransactionKey(
compressedInnerPayload,
bankKeys.bank_encryption_public_key,
@@ -509,15 +510,10 @@ suspend fun doEbicsUpload(
orderService: Ebics3Request.OrderDetails.Service,
payload: ByteArray,
): EbicsResponseContent = withContext(NonCancellable) {
+ val impl = Ebics3Impl(cfg, bankKeys, clientKeys)
// TODO use a lambda and pass the order detail there for atomicity ?
val preparedPayload = prepareUploadPayload(cfg, clientKeys, bankKeys, payload)
- val initXml = createEbics3RequestForUploadInitialization(
- cfg,
- preparedPayload,
- bankKeys,
- clientKeys,
- orderService
- )
+ val initXml = impl.uploadInitialization(preparedPayload)
val initResp = postEbics( // may throw EbicsEarlyException
client,
cfg,
@@ -537,12 +533,7 @@ suspend fun doEbicsUpload(
"EBICS upload init phase did not return a transaction ID, cannot do the transfer phase.",
sideEc = EbicsSideError.EBICS_UPLOAD_TRANSACTION_ID_MISSING
)
- val transferXml = createEbics3RequestForUploadTransferPhase(
- cfg,
- clientKeys,
- tId,
- preparedPayload
- )
+ val transferXml = impl.uploadTransfer(tId, preparedPayload)
val transferResp = postEbics(
client,
cfg,
@@ -550,6 +541,7 @@ suspend fun doEbicsUpload(
transferXml,
isEbics3 = true
)
+ logger.debug("Download init phase done. EBICS- and bank-technical codes are: ${transferResp.technicalReturnCode}, ${transferResp.bankReturnCode}")
if (!areCodesOk(transferResp)) throw EbicsUploadException(
"EBICS upload transfer failed",
phase = EbicsPhase.transmission,