[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] branch master updated: refactoring / code cleanup WIP
From: |
gnunet |
Subject: |
[libeufin] branch master updated: refactoring / code cleanup WIP |
Date: |
Wed, 05 Feb 2020 18:13:44 +0100 |
This is an automated email from the git hooks/post-receive script.
dold pushed a commit to branch master
in repository libeufin.
The following commit(s) were added to refs/heads/master by this push:
new 735135e refactoring / code cleanup WIP
735135e is described below
commit 735135e718a8b6ebf35cd479851af71d0471ef24
Author: Florian Dold <address@hidden>
AuthorDate: Wed Feb 5 18:13:29 2020 +0100
refactoring / code cleanup WIP
---
.../main/kotlin/tech/libeufin/nexus/Containers.kt | 26 --
.../kotlin/tech/libeufin/nexus/{DB.kt => Db.kt} | 0
.../main/kotlin/tech/libeufin/nexus/EbicsClient.kt | 248 ++++++++++
.../src/main/kotlin/tech/libeufin/nexus/Helpers.kt | 124 +----
nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 510 +++++++++------------
.../tech/libeufin/sandbox/EbicsProtocolBackend.kt | 467 ++++++++++---------
sandbox/src/main/python/libeufin-cli | 143 +++++-
util/src/main/kotlin/ebics_h004/EbicsRequest.kt | 12 +
util/src/main/kotlin/time.kt | 4 +-
util/src/test/kotlin/SignatureDataTest.kt | 4 +-
10 files changed, 869 insertions(+), 669 deletions(-)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Containers.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/Containers.kt
deleted file mode 100644
index a19be7d..0000000
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Containers.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package tech.libeufin.nexus
-
-import java.security.interfaces.RSAPrivateCrtKey
-import java.security.interfaces.RSAPublicKey
-
-/**
- * This class is a mere container that keeps data found
- * in the database and that is further needed to sign / verify
- * / make messages. And not all the values are needed all
- * the time.
- */
-data class EbicsContainer(
- val partnerId: String,
- val userId: String,
- var bankAuthPub: RSAPublicKey?,
- var bankEncPub: RSAPublicKey?,
- // needed to send the message
- val ebicsUrl: String,
- // needed to craft further messages
- val hostId: String,
- // needed to decrypt data coming from the bank
- val customerEncPriv: RSAPrivateCrtKey,
- // needed to sign documents
- val customerAuthPriv: RSAPrivateCrtKey,
- val customerSignPriv: RSAPrivateCrtKey
-)
\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/Db.kt
similarity index 100%
rename from nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
rename to nexus/src/main/kotlin/tech/libeufin/nexus/Db.kt
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsClient.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsClient.kt
new file mode 100644
index 0000000..2bd0476
--- /dev/null
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsClient.kt
@@ -0,0 +1,248 @@
+package tech.libeufin.nexus
+
+import io.ktor.application.call
+import io.ktor.client.HttpClient
+import io.ktor.http.ContentType
+import io.ktor.http.HttpStatusCode
+import io.ktor.response.respondText
+import tech.libeufin.util.CryptoUtil
+import tech.libeufin.util.EbicsOrderUtil
+import tech.libeufin.util.ebics_h004.EbicsRequest
+import tech.libeufin.util.ebics_h004.EbicsResponse
+import tech.libeufin.util.ebics_h004.EbicsTypes
+import tech.libeufin.util.getGregorianCalendarNow
+import java.lang.StringBuilder
+import java.math.BigInteger
+import java.security.interfaces.RSAPrivateCrtKey
+import java.security.interfaces.RSAPublicKey
+import java.util.*
+import java.util.zip.DeflaterInputStream
+import javax.xml.datatype.XMLGregorianCalendar
+
+/**
+ * This class is a mere container that keeps data found
+ * in the database and that is further needed to sign / verify
+ * / make messages. And not all the values are needed all
+ * the time.
+ */
+data class EbicsSubscriberDetails(
+ val partnerId: String,
+ val userId: String,
+ var bankAuthPub: RSAPublicKey?,
+ var bankEncPub: RSAPublicKey?,
+ // needed to send the message
+ val ebicsUrl: String,
+ // needed to craft further messages
+ val hostId: String,
+ // needed to decrypt data coming from the bank
+ val customerEncPriv: RSAPrivateCrtKey,
+ // needed to sign documents
+ val customerAuthPriv: RSAPrivateCrtKey,
+ val customerSignPriv: RSAPrivateCrtKey
+)
+
+
+fun createDownloadInitializationPhase(
+ subscriberData: EbicsSubscriberDetails,
+ orderType: String,
+ nonce: ByteArray,
+ date: XMLGregorianCalendar
+): EbicsRequest {
+ return EbicsRequest.createForDownloadInitializationPhase(
+ subscriberData.userId,
+ subscriberData.partnerId,
+ subscriberData.hostId,
+ nonce,
+ date,
+ subscriberData.bankEncPub ?: throw BankKeyMissing(
+ HttpStatusCode.PreconditionFailed
+ ),
+ subscriberData.bankAuthPub ?: throw BankKeyMissing(
+ HttpStatusCode.PreconditionFailed
+ ),
+ orderType
+ )
+}
+
+
+fun createUploadInitializationPhase(
+ subscriberData: EbicsSubscriberDetails,
+ orderType: String,
+ cryptoBundle: CryptoUtil.EncryptionResult
+): EbicsRequest {
+ return EbicsRequest.createForUploadInitializationPhase(
+ cryptoBundle,
+ subscriberData.hostId,
+ getNonce(128),
+ subscriberData.partnerId,
+ subscriberData.userId,
+ getGregorianCalendarNow(),
+ subscriberData.bankAuthPub!!,
+ subscriberData.bankEncPub!!,
+ BigInteger.ONE,
+ orderType
+ )
+}
+
+
+/**
+ * Wrapper around the lower decryption routine, that takes a EBICS response
+ * object containing a encrypted payload, and return the plain version of it
+ * (including decompression).
+ */
+fun decryptAndDecompressResponse(chunks: List<String>, transactionKey:
ByteArray, privateKey: RSAPrivateCrtKey, pubDigest: ByteArray): ByteArray {
+ val buf = StringBuilder()
+ chunks.forEach { buf.append(it) }
+ val decoded = Base64.getDecoder().decode(buf.toString())
+ val er = CryptoUtil.EncryptionResult(
+ transactionKey,
+ pubDigest,
+ decoded
+ )
+ val dataCompr = CryptoUtil.decryptEbicsE002(
+ er,
+ privateKey
+ )
+ return EbicsOrderUtil.decodeOrderData(dataCompr)
+}
+
+
+/**
+ * Get the private key that matches the given public key digest.
+ */
+fun getDecryptionKey(subscriberDetails: EbicsSubscriberDetails, pubDigest:
ByteArray): RSAPrivateCrtKey {
+ val authPub =
CryptoUtil.getRsaPublicFromPrivate(subscriberDetails.customerAuthPriv)
+ val encPub =
CryptoUtil.getRsaPublicFromPrivate(subscriberDetails.customerEncPriv)
+ val authPubDigest = CryptoUtil.getEbicsPublicKeyHash(authPub)
+ val encPubDigest = CryptoUtil.getEbicsPublicKeyHash(encPub)
+ if (pubDigest.contentEquals(authPubDigest)) {
+ return subscriberDetails.customerAuthPriv
+ }
+ if (pubDigest.contentEquals(encPubDigest)) {
+ return subscriberDetails.customerEncPriv
+ }
+ throw Exception("no matching private key to decrypt response")
+}
+
+
+/**
+ * Do an EBICS download transaction. This includes the initialization phase,
transaction phase
+ * and receipt phase.
+ */
+suspend fun doEbicsDownloadTransaction(
+ client: HttpClient,
+ subscriberDetails: EbicsSubscriberDetails,
+ orderType: String
+): ByteArray {
+ val initDownloadRequest = createDownloadInitializationPhase(
+ subscriberDetails,
+ orderType,
+ getNonce(128),
+ getGregorianCalendarNow()
+ )
+ val payloadChunks = LinkedList<String>();
+ val initResponse = client.postToBankSigned<EbicsRequest, EbicsResponse>(
+ subscriberDetails.ebicsUrl,
+ initDownloadRequest,
+ subscriberDetails.customerAuthPriv
+ )
+ if (initResponse.value.body.returnCode.value != "000000") {
+ throw EbicsError(initResponse.value.body.returnCode.value)
+ }
+ val initDataTransfer = initResponse.value.body.dataTransfer
+ ?: throw ProtocolViolationError("initial response for download
transaction does not contain data transfer")
+ val dataEncryptionInfo = initDataTransfer.dataEncryptionInfo
+ ?: throw ProtocolViolationError("initial response for download
transaction does not contain date encryption info")
+ val initOrderData = initDataTransfer.orderData.value
+ // FIXME: Also verify that algorithm matches!
+ val decryptionKey = getDecryptionKey(subscriberDetails,
dataEncryptionInfo.encryptionPubKeyDigest.value)
+ payloadChunks.add(initOrderData)
+ val respPayload = decryptAndDecompressResponse(
+ payloadChunks,
+ dataEncryptionInfo.transactionKey,
+ decryptionKey,
+ dataEncryptionInfo.encryptionPubKeyDigest.value
+ )
+ val ackRequest = EbicsRequest.createForDownloadReceiptPhase(
+ initResponse.value.header._static.transactionID ?: throw
BankInvalidResponse(
+ HttpStatusCode.ExpectationFailed
+ ),
+ subscriberDetails.hostId
+ )
+ val ackResponse = client.postToBankSignedAndVerify<EbicsRequest,
EbicsResponse>(
+ subscriberDetails.ebicsUrl,
+ ackRequest,
+ subscriberDetails.bankAuthPub ?: throw BankKeyMissing(
+ HttpStatusCode.PreconditionFailed
+ ),
+ subscriberDetails.customerAuthPriv
+ )
+ if (ackResponse.value.body.returnCode.value != "000000") {
+ throw EbicsError(ackResponse.value.body.returnCode.value)
+ }
+ return respPayload
+}
+
+
+suspend fun doEbicsUploadTransaction(
+ client: HttpClient,
+ subscriberDetails: EbicsSubscriberDetails,
+ orderType: String,
+ payload: ByteArray
+) {
+ if (subscriberDetails.bankEncPub == null) {
+ throw InvalidSubscriberStateError("bank encryption key unknown,
request HPB first")
+ }
+ val usd_encrypted = CryptoUtil.encryptEbicsE002(
+ EbicsOrderUtil.encodeOrderDataXml(
+ signOrder(
+ payload,
+ subscriberDetails.customerSignPriv,
+ subscriberDetails.partnerId,
+ subscriberDetails.userId
+ )
+ ),
+ subscriberDetails.bankEncPub!!
+ )
+ val response = client.postToBankSignedAndVerify<EbicsRequest,
EbicsResponse>(
+ subscriberDetails.ebicsUrl,
+ createUploadInitializationPhase(
+ subscriberDetails,
+ orderType,
+ usd_encrypted
+ ),
+ subscriberDetails.bankAuthPub!!,
+ subscriberDetails.customerAuthPriv
+ )
+ if (response.value.header.mutable.returnCode != "000000") {
+ throw EbicsError(response.value.header.mutable.returnCode)
+ }
+ if (response.value.body.returnCode.value != "000000") {
+ throw EbicsError(response.value.body.returnCode.value)
+ }
+ logger.debug("INIT phase passed!")
+ /* now send actual payload */
+ val compressedInnerPayload = DeflaterInputStream(
+ payload.inputStream()
+ ).use { it.readAllBytes() }
+ val encryptedPayload = CryptoUtil.encryptEbicsE002withTransactionKey(
+ compressedInnerPayload,
+ subscriberDetails.bankEncPub!!,
+ usd_encrypted.plainTransactionKey!!
+ )
+ val tmp = EbicsRequest.createForUploadTransferPhase(
+ subscriberDetails.hostId,
+ response.value.header._static.transactionID!!,
+ BigInteger.ONE,
+ encryptedPayload.encryptedData
+ )
+ val responseTransaction = client.postToBankSignedAndVerify<EbicsRequest,
EbicsResponse>(
+ subscriberDetails.ebicsUrl,
+ tmp,
+ subscriberDetails.bankAuthPub!!,
+ subscriberDetails.customerAuthPriv
+ )
+ if (responseTransaction.value.body.returnCode.value != "000000") {
+ throw EbicsError(response.value.body.returnCode.value)
+ }
+}
\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
index df64b63..ed65340 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
@@ -17,126 +17,6 @@ import java.util.*
import javax.xml.bind.JAXBElement
import javax.xml.datatype.XMLGregorianCalendar
-
-/**
- * Wrapper around the lower decryption routine, that takes a EBICS response
- * object containing a encrypted payload, and return the plain version of it
- * (including decompression).
- */
-fun decryptAndDecompressResponse(response: EbicsResponse, privateKey:
RSAPrivateCrtKey): ByteArray {
- val er = CryptoUtil.EncryptionResult(
- response.body.dataTransfer!!.dataEncryptionInfo!!.transactionKey,
- (response.body.dataTransfer!!.dataEncryptionInfo as
EbicsTypes.DataEncryptionInfo)
- .encryptionPubKeyDigest.value,
-
Base64.getDecoder().decode(response.body.dataTransfer!!.orderData.value)
- )
- val dataCompr = CryptoUtil.decryptEbicsE002(
- er,
- privateKey
- )
- return EbicsOrderUtil.decodeOrderData(dataCompr)
-}
-
-fun createDownloadInitializationPhase(
- subscriberData: EbicsContainer,
- orderType: String,
- nonce: ByteArray,
- date: XMLGregorianCalendar
-): EbicsRequest {
- return EbicsRequest.createForDownloadInitializationPhase(
- subscriberData.userId,
- subscriberData.partnerId,
- subscriberData.hostId,
- nonce,
- date,
- subscriberData.bankEncPub ?: throw BankKeyMissing(
- HttpStatusCode.PreconditionFailed
- ),
- subscriberData.bankAuthPub ?: throw BankKeyMissing(
- HttpStatusCode.PreconditionFailed
- ),
- orderType
- )
-}
-
-fun createDownloadInitializationPhase(
- subscriberData: EbicsContainer,
- orderType: String,
- nonce: ByteArray,
- date: XMLGregorianCalendar,
- dateStart: XMLGregorianCalendar,
- dateEnd: XMLGregorianCalendar
-): EbicsRequest {
- return EbicsRequest.createForDownloadInitializationPhase(
- subscriberData.userId,
- subscriberData.partnerId,
- subscriberData.hostId,
- nonce,
- date,
- subscriberData.bankEncPub ?: throw BankKeyMissing(
- HttpStatusCode.PreconditionFailed
- ),
- subscriberData.bankAuthPub ?: throw BankKeyMissing(
- HttpStatusCode.PreconditionFailed
- ),
- orderType,
- dateStart,
- dateEnd
- )
-}
-
-fun createUploadInitializationPhase(
- subscriberData: EbicsContainer,
- orderType: String,
- cryptoBundle: CryptoUtil.EncryptionResult
-): EbicsRequest {
- return EbicsRequest.createForUploadInitializationPhase(
- cryptoBundle,
- subscriberData.hostId,
- getNonce(128),
- subscriberData.partnerId,
- subscriberData.userId,
- getGregorianDate(),
- subscriberData.bankAuthPub!!,
- subscriberData.bankEncPub!!,
- BigInteger.ONE,
- orderType
- )
-}
-
-/**
- * Usually, queries must return lots of data from within a transaction
- * block. For convenience, we wrap such data into a EbicsContainer, so
- * that only one object is always returned from the transaction block.
- */
-fun containerInit(subscriber: EbicsSubscriberEntity): EbicsContainer {
- var bankAuthPubValue: RSAPublicKey? = null
- if (subscriber.bankAuthenticationPublicKey != null) {
- bankAuthPubValue = CryptoUtil.loadRsaPublicKey(
- subscriber.bankAuthenticationPublicKey?.toByteArray()!!
- )
- }
- var bankEncPubValue: RSAPublicKey? = null
- if (subscriber.bankEncryptionPublicKey != null) {
- bankEncPubValue = CryptoUtil.loadRsaPublicKey(
- subscriber.bankEncryptionPublicKey?.toByteArray()!!
- )
- }
- return EbicsContainer(
- bankAuthPub = bankAuthPubValue,
- bankEncPub = bankEncPubValue,
-
- ebicsUrl = subscriber.ebicsURL,
- hostId = subscriber.hostID,
- userId = subscriber.userID,
- partnerId = subscriber.partnerID,
-
- customerSignPriv =
CryptoUtil.loadRsaPrivateKey(subscriber.signaturePrivateKey.toByteArray()),
- customerAuthPriv =
CryptoUtil.loadRsaPrivateKey(subscriber.authenticationPrivateKey.toByteArray()),
- customerEncPriv =
CryptoUtil.loadRsaPrivateKey(subscriber.encryptionPrivateKey.toByteArray())
- )
-}
-
/**
* Inserts spaces every 2 characters, and a newline after 8 pairs.
*/
@@ -191,7 +71,7 @@ fun signOrder(
* response already converted in JAXB.
*/
suspend inline fun HttpClient.postToBank(url: String, body: String): String {
- LOGGER.debug("Posting: $body")
+ logger.debug("Posting: $body")
val response = try {
this.post<String>(
urlString = url,
@@ -217,7 +97,7 @@ suspend inline fun <reified T, reified S>
HttpClient.postToBankSignedAndVerify(
val doc = XMLUtil.convertJaxbToDocument(body)
XMLUtil.signEbicsDocument(doc, priv)
val response: String = this.postToBank(url,
XMLUtil.convertDomToString(doc))
- LOGGER.debug("About to verify: ${response}")
+ logger.debug("About to verify: ${response}")
val responseDocument = try {
XMLUtil.parseStringIntoDom(response)
} catch (e: Exception) {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
index f065190..6d33f4e 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -38,13 +38,10 @@ import io.ktor.response.respondText
import io.ktor.routing.*
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
-import io.ktor.util.encodeBase64
-import org.jetbrains.exposed.dao.EntityID
import org.jetbrains.exposed.exceptions.ExposedSQLException
import org.jetbrains.exposed.sql.StdOutSqlLogger
import org.jetbrains.exposed.sql.addLogger
import org.jetbrains.exposed.sql.transactions.transaction
-import org.joda.time.DateTime
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.slf4j.event.Level
@@ -55,10 +52,10 @@ import javax.sql.rowset.serial.SerialBlob
import tech.libeufin.util.toHexString
import tech.libeufin.util.CryptoUtil
import tech.libeufin.util.EbicsOrderUtil
-import tech.libeufin.util.XMLUtil
import tech.libeufin.util.ebics_hev.HEVRequest
import tech.libeufin.util.ebics_hev.HEVResponse
import java.math.BigInteger
+import java.security.interfaces.RSAPublicKey
import java.text.SimpleDateFormat
import java.util.*
import java.util.zip.DeflaterInputStream
@@ -82,7 +79,7 @@ fun testData() {
}
}
} catch (e: ExposedSQLException) {
- LOGGER.info("Likely primary key collision for sample data: accepted")
+ logger.info("Likely primary key collision for sample data: accepted")
}
}
@@ -90,26 +87,63 @@ data class NotAnIdError(val statusCode: HttpStatusCode) :
Exception("String ID n
data class BankKeyMissing(val statusCode: HttpStatusCode) :
Exception("Impossible operation: bank keys are missing")
data class SubscriberNotFoundError(val statusCode: HttpStatusCode) :
Exception("Subscriber not found in database")
data class UnreachableBankError(val statusCode: HttpStatusCode) :
Exception("Could not reach the bank")
-data class UnparsableResponse(val statusCode: HttpStatusCode, val rawResponse:
String) : Exception("bank responded: ${rawResponse}")
+data class UnparsableResponse(val statusCode: HttpStatusCode, val rawResponse:
String) :
+ Exception("bank responded: ${rawResponse}")
+
+class ProtocolViolationError(message: String) : Exception("protocol violation:
${message}")
+class InvalidSubscriberStateError(message: String) : Exception("invalid
subscriber state: ${message}")
data class EbicsError(val codeError: String) : Exception("Bank did not
accepted EBICS request, error is: ${codeError}")
data class BadSignature(val statusCode: HttpStatusCode) : Exception("Signature
verification unsuccessful")
data class BadBackup(val statusCode: HttpStatusCode) : Exception("Could not
restore backed up keys")
data class BankInvalidResponse(val statusCode: HttpStatusCode) :
Exception("Missing data from bank response")
-val LOGGER: Logger = LoggerFactory.getLogger("tech.libeufin.nexus")
+val logger: Logger = LoggerFactory.getLogger("tech.libeufin.nexus")
+
+fun getSubscriberDetailsFromId(id: String): EbicsSubscriberDetails {
+ return transaction {
+ val subscriber = EbicsSubscriberEntity.findById(
+ id
+ ) ?: throw SubscriberNotFoundError(
+ HttpStatusCode.NotFound
+ )
+ var bankAuthPubValue: RSAPublicKey? = null
+ if (subscriber.bankAuthenticationPublicKey != null) {
+ bankAuthPubValue = CryptoUtil.loadRsaPublicKey(
+ subscriber.bankAuthenticationPublicKey?.toByteArray()!!
+ )
+ }
+ var bankEncPubValue: RSAPublicKey? = null
+ if (subscriber.bankEncryptionPublicKey != null) {
+ bankEncPubValue = CryptoUtil.loadRsaPublicKey(
+ subscriber.bankEncryptionPublicKey?.toByteArray()!!
+ )
+ }
+ EbicsSubscriberDetails(
+ bankAuthPub = bankAuthPubValue,
+ bankEncPub = bankEncPubValue,
+
+ ebicsUrl = subscriber.ebicsURL,
+ hostId = subscriber.hostID,
+ userId = subscriber.userID,
+ partnerId = subscriber.partnerID,
+
+ customerSignPriv =
CryptoUtil.loadRsaPrivateKey(subscriber.signaturePrivateKey.toByteArray()),
+ customerAuthPriv =
CryptoUtil.loadRsaPrivateKey(subscriber.authenticationPrivateKey.toByteArray()),
+ customerEncPriv =
CryptoUtil.loadRsaPrivateKey(subscriber.encryptionPrivateKey.toByteArray())
+ )
+ }
+}
fun main() {
dbCreateTables()
testData()
- val client = HttpClient(){
+ val client = HttpClient() {
expectSuccess = false // this way, it does not throw exceptions on !=
200 responses.
}
- val logger = LoggerFactory.getLogger("tech.libeufin.nexus")
val server = embeddedServer(Netty, port = 5001) {
install(CallLogging) {
this.level = Level.DEBUG
- this.logger = LOGGER
-
+ this.logger = tech.libeufin.nexus.logger
}
install(ContentNegotiation) {
moshi {
@@ -137,18 +171,28 @@ fun main() {
exception<BadBackup> { cause ->
logger.error("Exception while handling '${call.request.uri}'",
cause)
- call.respondText("Bad backup, or passphrase incorrect\n",
ContentType.Text.Plain, HttpStatusCode.BadRequest)
+ call.respondText(
+ "Bad backup, or passphrase incorrect\n",
+ ContentType.Text.Plain,
+ HttpStatusCode.BadRequest
+ )
}
exception<UnparsableResponse> { cause ->
logger.error("Exception while handling '${call.request.uri}'",
cause)
- call.respondText("Could not parse bank response
(${cause.message})\n", ContentType.Text.Plain, HttpStatusCode
- .InternalServerError)
+ call.respondText(
+ "Could not parse bank response (${cause.message})\n",
ContentType.Text.Plain, HttpStatusCode
+ .InternalServerError
+ )
}
exception<UnreachableBankError> { cause ->
logger.error("Exception while handling '${call.request.uri}'",
cause)
- call.respondText("Could not reach the bank\n",
ContentType.Text.Plain, HttpStatusCode.InternalServerError)
+ call.respondText(
+ "Could not reach the bank\n",
+ ContentType.Text.Plain,
+ HttpStatusCode.InternalServerError
+ )
}
exception<SubscriberNotFoundError> { cause ->
@@ -158,17 +202,29 @@ fun main() {
exception<BadSignature> { cause ->
logger.error("Exception while handling '${call.request.uri}'",
cause)
- call.respondText("Signature verification unsuccessful\n",
ContentType.Text.Plain, HttpStatusCode.NotAcceptable)
+ call.respondText(
+ "Signature verification unsuccessful\n",
+ ContentType.Text.Plain,
+ HttpStatusCode.NotAcceptable
+ )
}
exception<EbicsError> { cause ->
logger.error("Exception while handling '${call.request.uri}'",
cause)
- call.respondText("Bank gave EBICS-error response\n",
ContentType.Text.Plain, HttpStatusCode.NotAcceptable)
+ call.respondText(
+ "Bank gave EBICS-error response\n",
+ ContentType.Text.Plain,
+ HttpStatusCode.NotAcceptable
+ )
}
exception<BankKeyMissing> { cause ->
logger.error("Exception while handling '${call.request.uri}'",
cause)
- call.respondText("Impossible operation: get bank keys
first\n", ContentType.Text.Plain, HttpStatusCode.NotAcceptable)
+ call.respondText(
+ "Impossible operation: get bank keys first\n",
+ ContentType.Text.Plain,
+ HttpStatusCode.NotAcceptable
+ )
}
exception<javax.xml.bind.UnmarshalException> { cause ->
@@ -180,12 +236,14 @@ fun main() {
)
}
}
+
intercept(ApplicationCallPipeline.Fallback) {
if (this.call.response.status() == null) {
call.respondText("Not found (no route matched).\n",
ContentType.Text.Plain, HttpStatusCode.NotFound)
return@intercept finish()
}
}
+
routing {
get("/") {
call.respondText("Hello by Nexus!\n")
@@ -194,203 +252,124 @@ fun main() {
post("/ebics/subscribers/{id}/sendPTK") {
val id = expectId(call.parameters["id"])
- val subscriberData = transaction {
- containerInit(
- EbicsSubscriberEntity.findById(
- id
- ) ?: throw SubscriberNotFoundError(
- HttpStatusCode.NotFound
- )
- )
- }
- val response = client.postToBankSigned<EbicsRequest,
EbicsResponse>(
- subscriberData.ebicsUrl,
- createDownloadInitializationPhase(
- subscriberData,
- "PTK",
- getNonce(128),
- getGregorianDate()
- ),
- subscriberData.customerAuthPriv
- )
- val payload: ByteArray =
- decryptAndDecompressResponse(
- response.value,
- subscriberData.customerAuthPriv
- )
+ val subscriberData = getSubscriberDetailsFromId(id)
+ val response = doEbicsDownloadTransaction(client,
subscriberData, "PTK")
call.respondText(
- payload.toString(Charsets.UTF_8),
+ response.toString(Charsets.UTF_8),
ContentType.Text.Plain,
- HttpStatusCode.OK)
-
+ HttpStatusCode.OK
+ )
return@post
}
post("/ebics/subscribers/{id}/sendHAC") {
val id = expectId(call.parameters["id"])
-
- val subscriberData = transaction {
- containerInit(
- EbicsSubscriberEntity.findById(
- id
- ) ?: throw SubscriberNotFoundError(
- HttpStatusCode.NotFound
- )
- )
- }
- val response = client.postToBankSigned<EbicsRequest,
EbicsResponse>(
- subscriberData.ebicsUrl,
- createDownloadInitializationPhase(
- subscriberData,
- "HAC",
- getNonce(128),
- getGregorianDate()
- ),
- subscriberData.customerAuthPriv
- )
- val payload: ByteArray =
- decryptAndDecompressResponse(
- response.value,
- subscriberData.customerAuthPriv
- )
+ val subscriberData = getSubscriberDetailsFromId(id)
+ val response = doEbicsDownloadTransaction(client,
subscriberData, "HAC")
call.respondText(
- payload.toString(Charsets.UTF_8),
+ response.toString(Charsets.UTF_8),
ContentType.Text.Plain,
- HttpStatusCode.OK)
-
+ HttpStatusCode.OK
+ )
return@post
}
-
post("/ebics/subscribers/{id}/sendC52") {
val id = expectId(call.parameters["id"])
- val body = call.receive<EbicsDateRange>()
-
- val startDate = DateTime.parse(body.start)
- val endDate = DateTime.parse(body.end)
- // will throw DateTimeParseException if strings are malformed.
-
- val subscriberData = transaction {
- containerInit(
- EbicsSubscriberEntity.findById(
- id
- ) ?: throw SubscriberNotFoundError(
- HttpStatusCode.NotFound
- )
- )
- }
- val response = client.postToBankSigned<EbicsRequest,
EbicsResponse>(
- subscriberData.ebicsUrl,
- createDownloadInitializationPhase(
- subscriberData,
- "C52",
- getNonce(128),
- getGregorianDate(),
- getGregorianDate(startDate.year, startDate.monthOfYear
- 1, startDate.dayOfMonth),
- getGregorianDate(endDate.year, endDate.monthOfYear -
1, endDate.dayOfMonth)
- ),
- subscriberData.customerAuthPriv
- )
- val payload: ByteArray =
- decryptAndDecompressResponse(
- response.value,
- subscriberData.customerAuthPriv
- )
- val ackRequest = EbicsRequest.createForDownloadReceiptPhase(
- response.value.header._static.transactionID ?: throw
BankInvalidResponse(
- HttpStatusCode.ExpectationFailed
- ),
- subscriberData.hostId
+ val subscriberData = getSubscriberDetailsFromId(id)
+ val response = doEbicsDownloadTransaction(client,
subscriberData, "C52")
+ call.respondText(
+ response.toString(Charsets.UTF_8),
+ ContentType.Text.Plain,
+ HttpStatusCode.OK
)
+ return@post
+ }
- val ackResponse =
client.postToBankSignedAndVerify<EbicsRequest, EbicsResponse>(
- subscriberData.ebicsUrl,
- ackRequest,
- subscriberData.bankAuthPub ?: throw BankKeyMissing(
- HttpStatusCode.PreconditionFailed
- ),
- subscriberData.customerAuthPriv
+ post("/ebics/subscribers/{id}/sendHtd") {
+ val id = expectId(call.parameters["id"])
+ val subscriberData = getSubscriberDetailsFromId(id)
+ val response = doEbicsDownloadTransaction(client,
subscriberData, "HTD")
+ call.respondText(
+ response.toString(Charsets.UTF_8),
+ ContentType.Text.Plain,
+ HttpStatusCode.OK
)
- logger.debug("C52 final response: " +
XMLUtil.convertJaxbToString<EbicsResponse>(response.value))
- if (ackResponse.value.body.returnCode.value != "000000") {
- throw EbicsError(response.value.body.returnCode.value)
- }
+ return@post
+ }
+ post("/ebics/subscribers/{id}/sendHAA") {
+ val id = expectId(call.parameters["id"])
+ val subscriberData = getSubscriberDetailsFromId(id)
+ val response = doEbicsDownloadTransaction(client,
subscriberData, "HAA")
call.respondText(
- payload.toString(Charsets.UTF_8),
+ response.toString(Charsets.UTF_8),
ContentType.Text.Plain,
- HttpStatusCode.OK)
-
+ HttpStatusCode.OK
+ )
return@post
}
- get("/ebics/subscribers/{id}/sendHtd") {
+
+ post("/ebics/subscribers/{id}/sendHVZ") {
val id = expectId(call.parameters["id"])
- val subscriberData = transaction {
- containerInit(
- EbicsSubscriberEntity.findById(
- id
- ) ?: throw SubscriberNotFoundError(
- HttpStatusCode.NotFound
- )
- )
- }
- val response = client.postToBankSigned<EbicsRequest,
EbicsResponse>(
- subscriberData.ebicsUrl,
- createDownloadInitializationPhase(
- subscriberData,
- "HTD",
- getNonce(128),
- getGregorianDate()
- ),
- subscriberData.customerAuthPriv
+ val subscriberData = getSubscriberDetailsFromId(id)
+ val response = doEbicsDownloadTransaction(client,
subscriberData, "HVZ")
+ call.respondText(
+ response.toString(Charsets.UTF_8),
+ ContentType.Text.Plain,
+ HttpStatusCode.OK
)
- logger.debug("HTD response: " +
XMLUtil.convertJaxbToString<EbicsResponse>(response.value))
- if (response.value.body.returnCode.value != "000000") {
- throw EbicsError(response.value.body.returnCode.value)
- }
-
- val encPubKeyDigestViaBank =
(response.value.body.dataTransfer!!.dataEncryptionInfo as
EbicsTypes.DataEncryptionInfo)
- .encryptionPubKeyDigest.value;
- println("encPubKeyDigestViaBank
${encPubKeyDigestViaBank.toHexString()}")
+ return@post
+ }
- val er = CryptoUtil.EncryptionResult(
-
response.value.body.dataTransfer!!.dataEncryptionInfo!!.transactionKey,
- (response.value.body.dataTransfer!!.dataEncryptionInfo as
EbicsTypes.DataEncryptionInfo)
- .encryptionPubKeyDigest.value,
-
Base64.getDecoder().decode(response.value.body.dataTransfer!!.orderData.value)
+ post("/ebics/subscribers/{id}/sendHVU") {
+ val id = expectId(call.parameters["id"])
+ val subscriberData = getSubscriberDetailsFromId(id)
+ val response = doEbicsDownloadTransaction(client,
subscriberData, "HVU")
+ call.respondText(
+ response.toString(Charsets.UTF_8),
+ ContentType.Text.Plain,
+ HttpStatusCode.OK
)
+ return@post
+ }
- val dataCompr = CryptoUtil.decryptEbicsE002(
- er,
- subscriberData.customerAuthPriv
- )
- val data =
EbicsOrderUtil.decodeOrderDataXml<HTDResponseOrderData>(dataCompr)
- logger.debug("HTD payload is:
${XMLUtil.convertJaxbToString(data)}")
- val ackRequest = EbicsRequest.createForDownloadReceiptPhase(
- response.value.header._static.transactionID ?: throw
BankInvalidResponse(
- HttpStatusCode.ExpectationFailed
- ),
- subscriberData.hostId
+ post("/ebics/subscribers/{id}/sendHPD") {
+ val id = expectId(call.parameters["id"])
+ val subscriberData = getSubscriberDetailsFromId(id)
+ val response = doEbicsDownloadTransaction(client,
subscriberData, "HPD")
+ call.respondText(
+ response.toString(Charsets.UTF_8),
+ ContentType.Text.Plain,
+ HttpStatusCode.OK
)
+ return@post
+ }
- val ackResponse =
client.postToBankSignedAndVerify<EbicsRequest, EbicsResponse>(
- subscriberData.ebicsUrl,
- ackRequest,
- subscriberData.bankAuthPub ?: throw BankKeyMissing(
- HttpStatusCode.PreconditionFailed
- ),
- subscriberData.customerAuthPriv
+ post("/ebics/subscribers/{id}/sendHKD") {
+ val id = expectId(call.parameters["id"])
+ val subscriberData = getSubscriberDetailsFromId(id)
+ val response = doEbicsDownloadTransaction(client,
subscriberData, "HKD")
+ call.respondText(
+ response.toString(Charsets.UTF_8),
+ ContentType.Text.Plain,
+ HttpStatusCode.OK
)
- logger.debug("HTD final response: " +
XMLUtil.convertJaxbToString<EbicsResponse>(response.value))
- if (ackResponse.value.body.returnCode.value != "000000") {
- throw EbicsError(response.value.body.returnCode.value)
- }
+ return@post
+ }
+
+ post("/ebics/subscribers/{id}/sendTSD") {
+ val id = expectId(call.parameters["id"])
+ val subscriberData = getSubscriberDetailsFromId(id)
+ val response = doEbicsDownloadTransaction(client,
subscriberData, "TSD")
call.respondText(
- "Success! Details (temporarily) reported on the Nexus
console.",
+ response.toString(Charsets.UTF_8),
ContentType.Text.Plain,
HttpStatusCode.OK
)
+ return@post
}
+
get("/ebics/subscribers/{id}/keyletter") {
val id = expectId(call.parameters["id"])
var usernameLine = "TODO"
@@ -520,8 +499,9 @@ fun main() {
HttpStatusCode.OK
)
}
+
get("/ebics/subscribers") {
- var ret = EbicsSubscribersResponse()
+ val ret = EbicsSubscribersResponse()
transaction {
EbicsSubscriberEntity.all().forEach {
ret.ebicsSubscribers.add(
@@ -539,6 +519,7 @@ fun main() {
call.respond(ret)
return@get
}
+
get("/ebics/subscribers/{id}") {
val id = expectId(call.parameters["id"])
val response = transaction {
@@ -557,6 +538,7 @@ fun main() {
call.respond(HttpStatusCode.OK, response)
return@get
}
+
get("/ebics/{id}/sendHev") {
val id = expectId(call.parameters["id"])
val (ebicsUrl, hostID) = transaction {
@@ -572,10 +554,12 @@ fun main() {
call.respond(
HttpStatusCode.OK,
EbicsHevResponse(response.value.versionNumber!!.map {
- ProtocolAndVersion(it.value, it.protocolVersion,
hostID) })
+ ProtocolAndVersion(it.value, it.protocolVersion,
hostID)
+ })
)
return@get
}
+
post("/ebics/{id}/subscribers") {
val body = call.receive<EbicsSubscriberInfoRequest>()
val pairA = CryptoUtil.generateRsaKeyPair(2048)
@@ -606,16 +590,10 @@ fun main() {
)
return@post
}
+
post("/ebics/subscribers/{id}/sendIni") {
val id = expectId(call.parameters["id"])
- val subscriberData = transaction {
- containerInit(
- EbicsSubscriberEntity.findById(id)
- ?: throw SubscriberNotFoundError(
- HttpStatusCode.NotFound
- )
- )
- }
+ val subscriberData = getSubscriberDetailsFromId(id)
val iniRequest = EbicsUnsecuredRequest.createIni(
subscriberData.hostId,
subscriberData.userId,
@@ -632,6 +610,7 @@ fun main() {
call.respondText("Bank accepted signature key\n",
ContentType.Text.Plain, HttpStatusCode.OK)
return@post
}
+
post("/ebics/subscribers/{id}/restoreBackup") {
val body = call.receive<EbicsKeysBackup>()
val id = expectId(call.parameters["id"])
@@ -659,10 +638,10 @@ fun main() {
)
} catch (e: Exception) {
e.printStackTrace()
- LOGGER.info("Restoring keys failed, probably due to wrong
passphrase")
+ logger.info("Restoring keys failed, probably due to wrong
passphrase")
throw BadBackup(HttpStatusCode.BadRequest)
}
- LOGGER.info("Restoring keys, creating new user: $id")
+ logger.info("Restoring keys, creating new user: $id")
try {
transaction {
EbicsSubscriberEntity.new(id =
expectId(call.parameters["id"])) {
@@ -704,7 +683,7 @@ fun main() {
bytesToBase64(authPub.encoded),
bytesToBase64(encPub.encoded),
bytesToBase64(sigPub.encoded)
- )
+ )
}
call.respond(
HttpStatusCode.OK,
@@ -725,18 +704,24 @@ fun main() {
hostID = subscriber.hostID,
partnerID = subscriber.partnerID,
ebicsURL = subscriber.ebicsURL,
- authBlob = bytesToBase64(CryptoUtil.encryptKey(
- subscriber.authenticationPrivateKey.toByteArray(),
- body.passphrase
- )),
- encBlob = bytesToBase64(CryptoUtil.encryptKey(
- subscriber.encryptionPrivateKey.toByteArray(),
- body.passphrase
- )),
- sigBlob = bytesToBase64(CryptoUtil.encryptKey(
- subscriber.signaturePrivateKey.toByteArray(),
- body.passphrase
- ))
+ authBlob = bytesToBase64(
+ CryptoUtil.encryptKey(
+
subscriber.authenticationPrivateKey.toByteArray(),
+ body.passphrase
+ )
+ ),
+ encBlob = bytesToBase64(
+ CryptoUtil.encryptKey(
+ subscriber.encryptionPrivateKey.toByteArray(),
+ body.passphrase
+ )
+ ),
+ sigBlob = bytesToBase64(
+ CryptoUtil.encryptKey(
+ subscriber.signaturePrivateKey.toByteArray(),
+ body.passphrase
+ )
+ )
)
}
call.response.headers.append("Content-Disposition",
"attachment")
@@ -745,76 +730,14 @@ fun main() {
response
)
}
- post("/ebics/subscribers/{id}/sendTst") {
+
+ post("/ebics/subscribers/{id}/sendTSU") {
val id = expectId(call.parameters["id"])
- val subscriberData = transaction {
- containerInit(
- EbicsSubscriberEntity.findById(id)
- ?: throw SubscriberNotFoundError(
- HttpStatusCode.NotFound
- )
- )
- }
+ val subscriberData = getSubscriberDetailsFromId(id)
val payload = "PAYLOAD"
- if (subscriberData.bankEncPub == null) {
- call.respondText(
- "Bank encryption key not found, request HPB first!\n",
- ContentType.Text.Plain,
- HttpStatusCode.NotFound
- )
- return@post
- }
- val usd_encrypted = CryptoUtil.encryptEbicsE002(
- EbicsOrderUtil.encodeOrderDataXml(
-
- signOrder(
- payload.toByteArray(),
- subscriberData.customerSignPriv,
- subscriberData.partnerId,
- subscriberData.userId
- )
- ),
- subscriberData.bankEncPub!!
- )
- val response = client.postToBankSignedAndVerify<EbicsRequest,
EbicsResponse>(
- subscriberData.ebicsUrl,
- createUploadInitializationPhase(
- subscriberData,
- "TST",
- usd_encrypted
- ),
- subscriberData.bankAuthPub!!,
- subscriberData.customerAuthPriv
- )
- if (response.value.body.returnCode.value != "000000") {
- throw EbicsError(response.value.body.returnCode.value)
- }
- logger.debug("INIT phase passed!")
- /* now send actual payload */
- val compressedInnerPayload = DeflaterInputStream(
- payload.toByteArray().inputStream()
-
- ).use { it.readAllBytes() }
- val encryptedPayload =
CryptoUtil.encryptEbicsE002withTransactionKey(
- compressedInnerPayload,
- subscriberData.bankEncPub!!,
- usd_encrypted.plainTransactionKey!!
- )
- val tmp = EbicsRequest.createForUploadTransferPhase(
- subscriberData.hostId,
- response.value.header._static.transactionID!!,
- BigInteger.ONE,
- encryptedPayload.encryptedData
- )
- val responseTransaction =
client.postToBankSignedAndVerify<EbicsRequest, EbicsResponse>(
- subscriberData.ebicsUrl,
- tmp,
- subscriberData.bankAuthPub!!,
- subscriberData.customerAuthPriv
- )
- if (responseTransaction.value.body.returnCode.value !=
"000000") {
- throw EbicsError(response.value.body.returnCode.value)
- }
+
+ doEbicsUploadTransaction(client, subscriberData, "TSU",
payload.toByteArray(Charsets.UTF_8))
+
call.respondText(
"TST INITIALIZATION & TRANSACTION phases succeeded\n",
ContentType.Text.Plain,
@@ -824,34 +747,29 @@ fun main() {
post("/ebics/subscribers/{id}/sync") {
val id = expectId(call.parameters["id"])
- val bundle = transaction {
- containerInit(
- EbicsSubscriberEntity.findById(id)
- ?: throw SubscriberNotFoundError(
- HttpStatusCode.NotFound
- )
- )
- }
+ val subscriberDetails = getSubscriberDetailsFromId(id)
val response = client.postToBankSigned<EbicsNpkdRequest,
EbicsKeyManagementResponse>(
- bundle.ebicsUrl,
+ subscriberDetails.ebicsUrl,
EbicsNpkdRequest.createRequest(
- bundle.hostId,
- bundle.partnerId,
- bundle.userId,
+ subscriberDetails.hostId,
+ subscriberDetails.partnerId,
+ subscriberDetails.userId,
getNonce(128),
- getGregorianDate()
+ getGregorianCalendarNow()
),
- bundle.customerAuthPriv
+ subscriberDetails.customerAuthPriv
)
if (response.value.body.returnCode.value != "000000") {
throw EbicsError(response.value.body.returnCode.value)
}
- val encPubKeyDigestViaBank =
(response.value.body.dataTransfer!!.dataEncryptionInfo as
EbicsTypes.DataEncryptionInfo)
- .encryptionPubKeyDigest.value;
- val customerEncPub =
CryptoUtil.getRsaPublicFromPrivate(bundle.customerEncPriv);
+ val encPubKeyDigestViaBank =
+ (response.value.body.dataTransfer!!.dataEncryptionInfo as
EbicsTypes.DataEncryptionInfo)
+ .encryptionPubKeyDigest.value;
+ val customerEncPub =
CryptoUtil.getRsaPublicFromPrivate(subscriberDetails.customerEncPriv);
val encPubKeyDigestViaNexus =
CryptoUtil.getEbicsPublicKeyHash(customerEncPub)
println("encPubKeyDigestViaBank:
${encPubKeyDigestViaBank.toHexString()}")
println("encPubKeyDigestViaNexus:
${encPubKeyDigestViaNexus.toHexString()}")
+ val decryptionKey = getDecryptionKey(subscriberDetails,
encPubKeyDigestViaBank)
val er = CryptoUtil.EncryptionResult(
response.value.body.dataTransfer!!.dataEncryptionInfo!!.transactionKey,
encPubKeyDigestViaBank,
@@ -859,7 +777,7 @@ fun main() {
)
val dataCompr = CryptoUtil.decryptEbicsE002(
er,
- bundle.customerAuthPriv
+ decryptionKey
)
val data =
EbicsOrderUtil.decodeOrderDataXml<HPBResponseOrderData>(dataCompr)
// put bank's keys into database.
@@ -883,16 +801,10 @@ fun main() {
call.respondText("Bank keys stored in database\n",
ContentType.Text.Plain, HttpStatusCode.OK)
return@post
}
+
post("/ebics/subscribers/{id}/sendHia") {
val id = expectId(call.parameters["id"])
- val subscriberData = transaction {
- containerInit(
- EbicsSubscriberEntity.findById(id)
- ?: throw SubscriberNotFoundError(
- HttpStatusCode.NotFound
- )
- )
- }
+ val subscriberData = getSubscriberDetailsFromId(id)
val responseJaxb =
client.postToBankUnsigned<EbicsUnsecuredRequest, EbicsKeyManagementResponse>(
subscriberData.ebicsUrl,
EbicsUnsecuredRequest.createHia(
diff --git
a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
index 162353c..ec1fd5b 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
@@ -40,17 +40,18 @@ import tech.libeufin.util.EbicsOrderUtil
import tech.libeufin.util.XMLUtil
import tech.libeufin.util.*
import tech.libeufin.util.XMLUtil.Companion.signEbicsResponse
-import java.awt.List
import java.math.BigDecimal
import java.security.interfaces.RSAPrivateCrtKey
+import java.security.interfaces.RSAPublicKey
import java.util.*
import java.util.zip.DeflaterInputStream
import java.util.zip.InflaterInputStream
import javax.sql.rowset.serial.SerialBlob
-open class EbicsRequestError(val errorText: String, val errorCode: String) :
- Exception("EBICS request management error: $errorText ($errorCode)")
+open class EbicsRequestError(errorText: String, errorCode: String) :
+ Exception("EBICS request error: $errorText ($errorCode)")
+
class EbicsInvalidRequestError : EbicsRequestError(
"[EBICS_INVALID_REQUEST] Invalid request",
@@ -146,13 +147,13 @@ private fun iterHistory(customerId: Int, header:
EbicsRequest.Header, base: XmlE
(header.static.orderDetails?.orderParams as
EbicsRequest.StandardOrderParams).dateRange!!.start.toString()
} catch (e: Exception) {
LOGGER.debug("Asked to iterate over history with NO start date;
default to now")
- getGregorianDate().toString()
+ getGregorianCalendarNow().toString()
},
try {
(header.static.orderDetails?.orderParams as
EbicsRequest.StandardOrderParams).dateRange!!.end.toString()
} catch (e: Exception) {
LOGGER.debug("Asked to iterate over history with NO end date;
default to now")
- getGregorianDate().toString()
+ getGregorianCalendarNow().toString()
}
) {
@@ -254,9 +255,7 @@ private fun balance(base: XmlElementBuilder) {
* @param type 52 or 53.
*/
private fun constructCamtResponse(type: Int, customerId: Int, header:
EbicsRequest.Header): String {
-
val camt = constructXml(indent = true) {
-
namespace("foo", "bar") // FIXME: set right namespace!
root("foo:BkToCstmrAcctRpt") {
element("GrpHdr") {
@@ -273,21 +272,18 @@ private fun constructCamtResponse(type: Int, customerId:
Int, header: EbicsReque
}
}
}
-
return camt
}
-private fun ApplicationCall.handleEbicsC52(header: EbicsRequest.Header):
ByteArray {
- val userId = header.static.userID!!
+private fun handleEbicsTSD(requestContext: RequestContext): ByteArray {
+ return "Hello World".toByteArray()
+}
- val subscriber = transaction {
- EbicsSubscriberEntity.find {
- stringParam(userId) eq EbicsSubscribersTable.userId // will have
to match partner and system IDs
- }
- }.firstOrNull() ?: throw Exception("Unknown subscriber")
- return constructCamtResponse(52, subscriber.bankCustomer.id.value,
header).toByteArray()
+private fun handleEbicsC52(requestContext: RequestContext): ByteArray {
+ val subscriber = requestContext.subscriber
+ return constructCamtResponse(52, subscriber.bankCustomer.id.value,
requestContext.requestObject.header).toByteArray()
}
private suspend fun ApplicationCall.handleEbicsHia(header:
EbicsUnsecuredRequest.Header, orderData: ByteArray) {
@@ -629,6 +625,238 @@ fun handleEbicsHkd(): ByteArray {
}
+private data class RequestContext(
+ val ebicsHost: EbicsHostEntity,
+ val subscriber: EbicsSubscriberEntity,
+ val clientEncPub: RSAPublicKey,
+ val clientAuthPub: RSAPublicKey,
+ val clientSigPub: RSAPublicKey,
+ val hostEncPriv: RSAPrivateCrtKey,
+ val hostAuthPriv: RSAPrivateCrtKey,
+ val requestObject: EbicsRequest,
+ val uploadTransaction: EbicsUploadTransactionEntity?,
+ val downloadTransaction: EbicsDownloadTransactionEntity?
+)
+
+
+private fun handleEbicsDownloadTransactionInitialization(requestContext:
RequestContext): EbicsResponse {
+ val orderType =
+ requestContext.requestObject.header.static.orderDetails?.orderType ?:
throw EbicsInvalidRequestError()
+ println("handling initialization for order type $orderType")
+ val response = when (orderType) {
+ "HTD" -> handleEbicsHtd()
+ "HKD" -> handleEbicsHkd()
+ /* Temporarily handling C52/C53 with same logic */
+ "C52" -> handleEbicsC52(requestContext)
+ "C53" -> handleEbicsC52(requestContext)
+ "TSD" -> handleEbicsTSD(requestContext)
+ else -> throw EbicsInvalidXmlError()
+ }
+
+ val transactionID = EbicsOrderUtil.generateTransactionId()
+
+ val compressedResponse = DeflaterInputStream(response.inputStream()).use {
+ it.readAllBytes()
+ }
+
+ val enc = CryptoUtil.encryptEbicsE002(compressedResponse,
requestContext.clientEncPub)
+ val encodedResponse = Base64.getEncoder().encodeToString(enc.encryptedData)
+
+ val segmentSize = 4096
+ val totalSize = encodedResponse.length
+ val numSegments = ((totalSize + segmentSize - 1) / segmentSize)
+
+ EbicsDownloadTransactionEntity.new(transactionID) {
+ this.subscriber = requestContext.subscriber
+ this.host = requestContext.ebicsHost
+ this.orderType = orderType
+ this.segmentSize = segmentSize
+ this.transactionKeyEnc = SerialBlob(enc.encryptedTransactionKey)
+ this.encodedResponse = encodedResponse
+ this.numSegments = numSegments
+ this.receiptReceived = false
+ }
+ return EbicsResponse.createForDownloadInitializationPhase(
+ transactionID,
+ numSegments,
+ segmentSize,
+ enc,
+ encodedResponse
+ )
+}
+
+
+private fun handleEbicsUploadTransactionInitialization(requestContext:
RequestContext): EbicsResponse {
+ val orderType =
+ requestContext.requestObject.header.static.orderDetails?.orderType ?:
throw EbicsInvalidRequestError()
+ val transactionID = EbicsOrderUtil.generateTransactionId()
+ val oidn = requestContext.subscriber.nextOrderID++
+ if (EbicsOrderUtil.checkOrderIDOverflow(oidn)) throw NotImplementedError()
+ val orderID = EbicsOrderUtil.computeOrderIDFromNumber(oidn)
+ val numSegments =
+ requestContext.requestObject.header.static.numSegments ?: throw
EbicsInvalidRequestError()
+ val transactionKeyEnc =
+
requestContext.requestObject.body.dataTransfer?.dataEncryptionInfo?.transactionKey
+ ?: throw EbicsInvalidRequestError()
+ val encPubKeyDigest =
+
requestContext.requestObject.body.dataTransfer?.dataEncryptionInfo?.encryptionPubKeyDigest?.value
+ ?: throw EbicsInvalidRequestError()
+ val encSigData =
requestContext.requestObject.body.dataTransfer?.signatureData?.value
+ ?: throw EbicsInvalidRequestError()
+ val decryptedSignatureData = CryptoUtil.decryptEbicsE002(
+ CryptoUtil.EncryptionResult(
+ transactionKeyEnc,
+ encPubKeyDigest,
+ encSigData
+ ), requestContext.hostEncPriv
+ )
+ val plainSigData =
InflaterInputStream(decryptedSignatureData.inputStream()).use {
+ it.readAllBytes()
+ }
+
+ println("creating upload transaction for transactionID $transactionID")
+ EbicsUploadTransactionEntity.new(transactionID) {
+ this.host = requestContext.ebicsHost
+ this.subscriber = requestContext.subscriber
+ this.lastSeenSegment = 0
+ this.orderType = orderType
+ this.orderID = orderID
+ this.numSegments = numSegments.toInt()
+ this.transactionKeyEnc = SerialBlob(transactionKeyEnc)
+ }
+ val sigObj =
XMLUtil.convertStringToJaxb<UserSignatureData>(plainSigData.toString(Charsets.UTF_8))
+ println("got UserSignatureData: ${plainSigData.toString(Charsets.UTF_8)}")
+ for (sig in sigObj.value.orderSignatureList ?: listOf()) {
+ println("inserting order signature for orderID $orderID and orderType
$orderType")
+ EbicsOrderSignatureEntity.new {
+ this.orderID = orderID
+ this.orderType = orderType
+ this.partnerID = sig.partnerID
+ this.userID = sig.userID
+ this.signatureAlgorithm = sig.signatureVersion
+ this.signatureValue = SerialBlob(sig.signatureValue)
+ }
+ }
+
+ return EbicsResponse.createForUploadInitializationPhase(transactionID,
orderID)
+}
+
+
+private fun handleEbicsUploadTransactionTransmission(requestContext:
RequestContext): EbicsResponse {
+ val uploadTransaction = requestContext.uploadTransaction ?: throw
EbicsInvalidRequestError()
+ val requestObject = requestContext.requestObject
+ val requestSegmentNumber =
+
requestContext.requestObject.header.mutable.segmentNumber?.value?.toInt() ?:
throw EbicsInvalidRequestError()
+ val requestTransactionID = requestObject.header.static.transactionID ?:
throw EbicsInvalidRequestError()
+ if (requestSegmentNumber == 1 && uploadTransaction.numSegments == 1) {
+ val encOrderData =
+ requestObject.body.dataTransfer?.orderData ?: throw
EbicsInvalidRequestError()
+ val zippedData = CryptoUtil.decryptEbicsE002(
+ uploadTransaction.transactionKeyEnc.toByteArray(),
+ encOrderData,
+ requestContext.hostEncPriv
+ )
+ val unzippedData =
+ InflaterInputStream(zippedData.inputStream()).use {
it.readAllBytes() }
+ println("got upload data: ${unzippedData.toString(Charsets.UTF_8)}")
+
+ val sigs = EbicsOrderSignatureEntity.find {
+ (EbicsOrderSignaturesTable.orderID eq uploadTransaction.orderID)
and
+ (EbicsOrderSignaturesTable.orderType eq
uploadTransaction.orderType)
+ }
+
+ if (sigs.count() == 0) {
+ throw EbicsInvalidRequestError()
+ }
+
+ for (sig in sigs) {
+ if (sig.signatureAlgorithm == "A006") {
+
+ val signedData = CryptoUtil.digestEbicsOrderA006(unzippedData)
+ val res1 =
CryptoUtil.verifyEbicsA006(sig.signatureValue.toByteArray(), signedData,
requestContext.clientSigPub)
+
+ if (!res1) {
+ throw EbicsInvalidRequestError()
+ }
+
+ } else {
+ throw NotImplementedError()
+ }
+ }
+
+ return EbicsResponse.createForUploadTransferPhase(
+ requestTransactionID,
+ requestSegmentNumber,
+ true,
+ uploadTransaction.orderID
+ )
+ } else {
+ throw NotImplementedError()
+ }
+}
+
+private fun makeReqestContext(requestObject: EbicsRequest): RequestContext {
+ val staticHeader = requestObject.header.static
+ val requestedHostId = staticHeader.hostID
+ val ebicsHost =
+ EbicsHostEntity.find { EbicsHostsTable.hostID.upperCase() eq
requestedHostId.toUpperCase() }
+ .firstOrNull()
+ val requestTransactionID = requestObject.header.static.transactionID
+ var downloadTransaction: EbicsDownloadTransactionEntity? = null
+ var uploadTransaction: EbicsUploadTransactionEntity? = null
+ val subscriber = if (requestTransactionID != null) {
+ println("finding subscriber by transactionID $requestTransactionID")
+ downloadTransaction =
EbicsDownloadTransactionEntity.findById(requestTransactionID.toUpperCase())
+ if (downloadTransaction != null) {
+ downloadTransaction.subscriber
+ } else {
+ uploadTransaction =
EbicsUploadTransactionEntity.findById(requestTransactionID)
+ uploadTransaction?.subscriber
+ }
+ } else {
+ val partnerID = staticHeader.partnerID ?: throw
EbicsInvalidRequestError()
+ val userID = staticHeader.userID ?: throw EbicsInvalidRequestError()
+ findEbicsSubscriber(partnerID, userID, staticHeader.systemID)
+ }
+
+ if (ebicsHost == null) throw EbicsInvalidRequestError()
+
+ /**
+ * NOTE: production logic must check against READY state (the
+ * one activated after the subscriber confirms their keys via post)
+ */
+ if (subscriber == null || subscriber.state != SubscriberState.INITIALIZED)
+ throw EbicsSubscriberStateError()
+
+ val hostAuthPriv = CryptoUtil.loadRsaPrivateKey(
+ ebicsHost.authenticationPrivateKey
+ .toByteArray()
+ )
+ val hostEncPriv = CryptoUtil.loadRsaPrivateKey(
+ ebicsHost.encryptionPrivateKey
+ .toByteArray()
+ )
+ val clientAuthPub =
+
CryptoUtil.loadRsaPublicKey(subscriber.authenticationKey!!.rsaPublicKey.toByteArray())
+ val clientEncPub =
+
CryptoUtil.loadRsaPublicKey(subscriber.encryptionKey!!.rsaPublicKey.toByteArray())
+ val clientSigPub =
+
CryptoUtil.loadRsaPublicKey(subscriber.signatureKey!!.rsaPublicKey.toByteArray())
+
+ return RequestContext(
+ hostAuthPriv = hostAuthPriv,
+ hostEncPriv = hostEncPriv,
+ clientAuthPub = clientAuthPub,
+ clientEncPub = clientEncPub,
+ clientSigPub = clientSigPub,
+ ebicsHost = ebicsHost,
+ requestObject = requestObject,
+ subscriber = subscriber,
+ downloadTransaction = downloadTransaction,
+ uploadTransaction = uploadTransaction
+ )
+}
+
suspend fun ApplicationCall.ebicsweb() {
val requestDocument = receiveEbicsXml()
@@ -673,229 +901,46 @@ suspend fun ApplicationCall.ebicsweb() {
"ebicsRequest" -> {
println("ebicsRequest
${XMLUtil.convertDomToString(requestDocument)}")
val requestObject = requestDocument.toObject<EbicsRequest>()
- val staticHeader = requestObject.header.static
- val requestedHostId = staticHeader.hostID
val responseXmlStr = transaction {
// Step 1 of 3: Get information about the host and subscriber
- val ebicsHost =
- EbicsHostEntity.find { EbicsHostsTable.hostID.upperCase()
eq requestedHostId.toUpperCase() }
- .firstOrNull()
- val requestTransactionID =
requestObject.header.static.transactionID
- var downloadTransaction: EbicsDownloadTransactionEntity? = null
- var uploadTransaction: EbicsUploadTransactionEntity? =
- null
- val subscriber = if (requestTransactionID != null) {
- println("finding subscriber by transactionID
$requestTransactionID")
- downloadTransaction =
EbicsDownloadTransactionEntity.findById(requestTransactionID.toUpperCase())
- if (downloadTransaction != null) {
- downloadTransaction.subscriber
- } else {
- uploadTransaction =
EbicsUploadTransactionEntity.findById(requestTransactionID)
- uploadTransaction?.subscriber
- }
- } else {
- val partnerID = staticHeader.partnerID ?: throw
EbicsInvalidRequestError()
- val userID = staticHeader.userID ?: throw
EbicsInvalidRequestError()
- findEbicsSubscriber(partnerID, userID,
staticHeader.systemID)
- }
-
- if (ebicsHost == null) throw EbicsInvalidRequestError()
-
- /**
- * NOTE: production logic must check against READY state (the
- * one activated after the subscriber confirms their keys via
post)
- */
- if (subscriber == null || subscriber.state !=
SubscriberState.INITIALIZED)
- throw EbicsSubscriberStateError()
-
- val hostAuthPriv = CryptoUtil.loadRsaPrivateKey(
- ebicsHost.authenticationPrivateKey
- .toByteArray()
- )
- val hostEncPriv = CryptoUtil.loadRsaPrivateKey(
- ebicsHost.encryptionPrivateKey
- .toByteArray()
- )
- val clientAuthPub =
-
CryptoUtil.loadRsaPublicKey(subscriber.authenticationKey!!.rsaPublicKey.toByteArray())
- val clientEncPub =
-
CryptoUtil.loadRsaPublicKey(subscriber.encryptionKey!!.rsaPublicKey.toByteArray())
- val clientSigPub =
-
CryptoUtil.loadRsaPublicKey(subscriber.signatureKey!!.rsaPublicKey.toByteArray())
+ val requestContext = makeReqestContext(requestObject)
// Step 2 of 3: Validate the signature
- val verifyResult =
XMLUtil.verifyEbicsDocument(requestDocument, clientAuthPub)
+ val verifyResult =
XMLUtil.verifyEbicsDocument(requestDocument, requestContext.clientAuthPub)
if (!verifyResult) {
throw EbicsInvalidRequestError()
}
+ // Step 3 of 3: Generate response
val ebicsResponse: EbicsResponse = when
(requestObject.header.mutable.transactionPhase) {
EbicsTypes.TransactionPhaseType.INITIALISATION -> {
- val transactionID =
EbicsOrderUtil.generateTransactionId()
- val orderType =
-
requestObject.header.static.orderDetails?.orderType ?: throw
EbicsInvalidRequestError()
- if (staticHeader.numSegments == null) {
- println("handling initialization for order type
$orderType")
- val response = when (orderType) {
- "HTD" -> handleEbicsHtd()
- "HKD" -> handleEbicsHkd()
-
- /* Temporarily handling C52/C53 with same
logic */
- "C52" -> handleEbicsC52(requestObject.header)
- "C53" -> handleEbicsC52(requestObject.header)
- else -> throw EbicsInvalidXmlError()
- }
-
- val compressedResponse =
DeflaterInputStream(response.inputStream()).use {
- it.readAllBytes()
- }
-
- val enc =
CryptoUtil.encryptEbicsE002(compressedResponse, clientEncPub)
- val encodedResponse =
Base64.getEncoder().encodeToString(enc.encryptedData)
-
- val segmentSize = 4096
- val totalSize = encodedResponse.length
- val numSegments = ((totalSize + segmentSize - 1) /
segmentSize)
-
- EbicsDownloadTransactionEntity.new(transactionID) {
- this.subscriber = subscriber
- this.host = ebicsHost
- this.orderType = orderType
- this.segmentSize = segmentSize
- this.transactionKeyEnc =
SerialBlob(enc.encryptedTransactionKey)
- this.encodedResponse = encodedResponse
- this.numSegments = numSegments
- this.receiptReceived = false
- }
- EbicsResponse.createForDownloadInitializationPhase(
- transactionID,
- numSegments,
- segmentSize,
- enc,
- encodedResponse
- )
+ if (requestObject.header.static.numSegments == null) {
+
handleEbicsDownloadTransactionInitialization(requestContext)
} else {
- val oidn = subscriber.nextOrderID++
- if (EbicsOrderUtil.checkOrderIDOverflow(oidn))
throw NotImplementedError()
- val orderID =
EbicsOrderUtil.computeOrderIDFromNumber(oidn)
- val numSegments =
- requestObject.header.static.numSegments ?:
throw EbicsInvalidRequestError()
- val transactionKeyEnc =
-
requestObject.body.dataTransfer?.dataEncryptionInfo?.transactionKey
- ?: throw EbicsInvalidRequestError()
- val encPubKeyDigest =
-
requestObject.body.dataTransfer?.dataEncryptionInfo?.encryptionPubKeyDigest?.value
- if (encPubKeyDigest == null)
- throw EbicsInvalidRequestError()
- val encSigData =
requestObject.body.dataTransfer?.signatureData?.value
- if (encSigData == null)
- throw EbicsInvalidRequestError()
- val decryptedSignatureData =
CryptoUtil.decryptEbicsE002(
- CryptoUtil.EncryptionResult(
- transactionKeyEnc,
- encPubKeyDigest,
- encSigData
- ), hostEncPriv
- )
- val plainSigData =
InflaterInputStream(decryptedSignatureData.inputStream()).use {
- it.readAllBytes()
- }
-
- println("creating upload transaction for
transactionID $transactionID")
- EbicsUploadTransactionEntity.new(transactionID) {
- this.host = ebicsHost
- this.subscriber = subscriber
- this.lastSeenSegment = 0
- this.orderType = orderType
- this.orderID = orderID
- this.numSegments = numSegments.toInt()
- this.transactionKeyEnc =
SerialBlob(transactionKeyEnc)
- }
- val sigObj =
XMLUtil.convertStringToJaxb<UserSignatureData>(plainSigData.toString(Charsets.UTF_8))
- println("got UserSignatureData:
${plainSigData.toString(Charsets.UTF_8)}")
- for (sig in sigObj.value.orderSignatureList ?:
listOf()) {
- println("inserting order signature for orderID
$orderID and orderType $orderType")
- EbicsOrderSignatureEntity.new {
- this.orderID = orderID
- this.orderType = orderType
- this.partnerID = sig.partnerID
- this.userID = sig.userID
- this.signatureAlgorithm =
sig.signatureVersion
- this.signatureValue =
SerialBlob(sig.signatureValue)
- }
- }
-
-
EbicsResponse.createForUploadInitializationPhase(transactionID, orderID)
+
handleEbicsUploadTransactionInitialization(requestContext)
}
}
EbicsTypes.TransactionPhaseType.TRANSFER -> {
- requestTransactionID ?: throw
EbicsInvalidRequestError()
- val requestSegmentNumber =
-
requestObject.header.mutable.segmentNumber?.value?.toInt() ?: throw
EbicsInvalidRequestError()
- if (uploadTransaction != null) {
- if (requestSegmentNumber == 1 &&
uploadTransaction.numSegments == 1) {
- val encOrderData =
- requestObject.body.dataTransfer?.orderData
?: throw EbicsInvalidRequestError()
- val zippedData = CryptoUtil.decryptEbicsE002(
-
uploadTransaction.transactionKeyEnc.toByteArray(),
- encOrderData,
- hostEncPriv
- )
- val unzippedData =
-
InflaterInputStream(zippedData.inputStream()).use { it.readAllBytes() }
- println("got upload data:
${unzippedData.toString(Charsets.UTF_8)}")
-
- val sigs = EbicsOrderSignatureEntity.find {
- (EbicsOrderSignaturesTable.orderID eq
uploadTransaction.orderID) and
-
(EbicsOrderSignaturesTable.orderType eq uploadTransaction.orderType)
- }
-
- if (sigs.count() == 0) {
- throw EbicsInvalidRequestError()
- }
-
- for (sig in sigs) {
- if (sig.signatureAlgorithm == "A006") {
-
- val signedData =
CryptoUtil.digestEbicsOrderA006(unzippedData)
- val res1 =
CryptoUtil.verifyEbicsA006(sig.signatureValue.toByteArray(), signedData,
clientSigPub)
-
- if (!res1) {
- throw EbicsInvalidRequestError()
- }
-
- } else {
- throw NotImplementedError()
- }
- }
-
- EbicsResponse.createForUploadTransferPhase(
- requestTransactionID,
- requestSegmentNumber,
- true,
- uploadTransaction.orderID
- )
- } else {
- throw NotImplementedError()
- }
- } else if (downloadTransaction != null) {
+ if (requestContext.uploadTransaction != null) {
+
handleEbicsUploadTransactionTransmission(requestContext)
+ } else if (requestContext.downloadTransaction != null)
{
throw NotImplementedError()
} else {
throw AssertionError()
}
}
EbicsTypes.TransactionPhaseType.RECEIPT -> {
- requestTransactionID ?: throw
EbicsInvalidRequestError()
- if (downloadTransaction == null)
+ val requestTransactionID =
requestObject.header.static.transactionID ?: throw EbicsInvalidRequestError()
+ if (requestContext.downloadTransaction == null)
throw EbicsInvalidRequestError()
val receiptCode =
requestObject.body.transferReceipt?.receiptCode ?:
throw EbicsInvalidRequestError()
EbicsResponse.createForDownloadReceiptPhase(requestTransactionID, receiptCode
== 0)
}
}
- signEbicsResponse(ebicsResponse, hostAuthPriv)
+ signEbicsResponse(ebicsResponse, requestContext.hostAuthPriv)
}
respondText(responseXmlStr, ContentType.Application.Xml,
HttpStatusCode.OK)
}
diff --git a/sandbox/src/main/python/libeufin-cli
b/sandbox/src/main/python/libeufin-cli
index c621635..9b33bdc 100755
--- a/sandbox/src/main/python/libeufin-cli
+++ b/sandbox/src/main/python/libeufin-cli
@@ -19,7 +19,7 @@ def cli():
def admin(ctx):
pass
-@admin.command(help="Instruct the Bank to create a new EBICS host ID.")
+@admin.command(help="Instruct the sandbox bank to create a new EBICS host ID.")
@click.option(
"--host-id",
help="EBICS host ID",
@@ -48,7 +48,7 @@ def add_host(obj, host_id, ebics_version, bank_base_url):
print(resp.content.decode("utf-8"))
-@admin.command(help="Instruct the Sandbox to create a new EBICS Subscriber")
+@admin.command(help="Instruct the sandbox bank to create a new EBICS
Subscriber")
@click.pass_obj
@click.option(
"--user-id",
@@ -210,7 +210,7 @@ def backup(obj, account_id, output_file, nexus_base_url):
return
if response.status_code != 200:
- print("Unsuccessful status code gotten:
{}".format(response.status_code))
+ print("Received unsuccessful status code:
{}".format(response.status_code))
return
output = open(output_file, "w+")
@@ -220,7 +220,7 @@ def backup(obj, account_id, output_file, nexus_base_url):
print("Backup stored in {}".format(output_file))
-@ebics.command(help="Send TST message")
+@ebics.command(help="Send test upload message (TSU)")
@click.pass_obj
@click.option(
"--account-id",
@@ -230,9 +230,138 @@ def backup(obj, account_id, output_file, nexus_base_url):
@click.argument(
"nexus-base-url"
)
-def tst(obj, account_id, nexus_base_url):
+def tsu(obj, account_id, nexus_base_url):
- url = urljoin(nexus_base_url,
"/ebics/subscribers/{}/sendTst".format(account_id))
+ url = urljoin(nexus_base_url,
"/ebics/subscribers/{}/sendTSU".format(account_id))
+ try:
+ resp = post(url)
+ except Exception:
+ print("Could not reach the nexus")
+ return
+
+ print(resp.content.decode("utf-8"))
+
+
+@ebics.command(help="Send test download message (TSD)")
+@click.pass_obj
+@click.option(
+ "--account-id",
+ help="Numerical ID of the customer at the Nexus",
+ required=True
+)
+@click.argument(
+ "nexus-base-url"
+)
+def tsd(obj, account_id, nexus_base_url):
+
+ url = urljoin(nexus_base_url,
"/ebics/subscribers/{}/sendTSD".format(account_id))
+ try:
+ resp = post(url)
+ except Exception:
+ print("Could not reach the nexus")
+ return
+
+ print(resp.content.decode("utf-8"))
+
+
+@ebics.command(help="Send HAA message")
+@click.pass_obj
+@click.option(
+ "--account-id",
+ help="Numerical ID of the customer at the Nexus",
+ required=True
+)
+@click.argument(
+ "nexus-base-url"
+)
+def haa(obj, account_id, nexus_base_url):
+
+ url = urljoin(nexus_base_url,
"/ebics/subscribers/{}/sendHAA".format(account_id))
+ try:
+ resp = post(url)
+ except Exception:
+ print("Could not reach the bank")
+ return
+
+ print(resp.content.decode("utf-8"))
+
+@ebics.command(help="Send HVZ message")
+@click.pass_obj
+@click.option(
+ "--account-id",
+ help="Numerical ID of the customer at the Nexus",
+ required=True
+)
+@click.argument(
+ "nexus-base-url"
+)
+def hvz(obj, account_id, nexus_base_url):
+
+ url = urljoin(nexus_base_url,
"/ebics/subscribers/{}/sendHVZ".format(account_id))
+ try:
+ resp = post(url)
+ except Exception:
+ print("Could not reach the bank")
+ return
+
+ print(resp.content.decode("utf-8"))
+
+
+@ebics.command(help="Send HVU message")
+@click.pass_obj
+@click.option(
+ "--account-id",
+ help="Numerical ID of the customer at the Nexus",
+ required=True
+)
+@click.argument(
+ "nexus-base-url"
+)
+def hvu(obj, account_id, nexus_base_url):
+
+ url = urljoin(nexus_base_url,
"/ebics/subscribers/{}/sendHVU".format(account_id))
+ try:
+ resp = post(url)
+ except Exception:
+ print("Could not reach the bank")
+ return
+
+ print(resp.content.decode("utf-8"))
+
+@ebics.command(help="Send HPD message")
+@click.pass_obj
+@click.option(
+ "--account-id",
+ help="Numerical ID of the customer at the Nexus",
+ required=True
+)
+@click.argument(
+ "nexus-base-url"
+)
+def hpd(obj, account_id, nexus_base_url):
+
+ url = urljoin(nexus_base_url,
"/ebics/subscribers/{}/sendHPD".format(account_id))
+ try:
+ resp = post(url)
+ except Exception:
+ print("Could not reach the bank")
+ return
+
+ print(resp.content.decode("utf-8"))
+
+
+@ebics.command(help="Send HKD message")
+@click.pass_obj
+@click.option(
+ "--account-id",
+ help="Numerical ID of the customer at the Nexus",
+ required=True
+)
+@click.argument(
+ "nexus-base-url"
+)
+def hkd(obj, account_id, nexus_base_url):
+ url = urljoin(nexus_base_url,
"/ebics/subscribers/{}/sendHKD".format(account_id))
try:
resp = post(url)
except Exception:
@@ -364,7 +493,7 @@ def htd(ctx, account_id, prepare, nexus_base_url):
ctx.invoke(sync)
url = urljoin(nexus_base_url,
"/ebics/subscribers/{}/sendHtd".format(account_id))
try:
- resp = get(url)
+ resp = post(url)
except Exception:
print("Could not reach the bank")
return
diff --git a/util/src/main/kotlin/ebics_h004/EbicsRequest.kt
b/util/src/main/kotlin/ebics_h004/EbicsRequest.kt
index 3a4cefd..6ae487d 100644
--- a/util/src/main/kotlin/ebics_h004/EbicsRequest.kt
+++ b/util/src/main/kotlin/ebics_h004/EbicsRequest.kt
@@ -6,6 +6,7 @@ import tech.libeufin.util.LOGGER
import tech.libeufin.util.XMLUtil
import java.math.BigInteger
import java.security.interfaces.RSAPublicKey
+import java.util.*
import javax.xml.bind.annotation.*
import javax.xml.bind.annotation.adapters.CollapsedStringAdapter
import javax.xml.bind.annotation.adapters.HexBinaryAdapter
@@ -180,6 +181,10 @@ class EbicsRequest {
XmlElement(
name = "StandardOrderParams",
type = StandardOrderParams::class
+ ),
+ XmlElement(
+ name = "GenericOrderParams",
+ type = GenericOrderParams::class
)
)
var orderParams: OrderParams? = null
@@ -254,6 +259,13 @@ class EbicsRequest {
var dateRange: DateRange? = null
}
+ @XmlAccessorType(XmlAccessType.NONE)
+ @XmlType(name = "", propOrder = ["parameterList"])
+ class GenericOrderParams : OrderParams() {
+ @get:XmlElement(type = EbicsTypes.Parameter::class)
+ var parameterList: List<EbicsTypes.Parameter> = LinkedList()
+ }
+
@XmlAccessorType(XmlAccessType.NONE)
@XmlType(name = "", propOrder = ["start", "end"])
class DateRange {
diff --git a/util/src/main/kotlin/time.kt b/util/src/main/kotlin/time.kt
index 2488118..9db12d8 100644
--- a/util/src/main/kotlin/time.kt
+++ b/util/src/main/kotlin/time.kt
@@ -5,14 +5,14 @@ import javax.xml.datatype.DatatypeFactory
import javax.xml.datatype.XMLGregorianCalendar
/* now */
-fun getGregorianDate(): XMLGregorianCalendar {
+fun getGregorianCalendarNow(): XMLGregorianCalendar {
val gregorianCalendar = GregorianCalendar()
val datatypeFactory = DatatypeFactory.newInstance()
return datatypeFactory.newXMLGregorianCalendar(gregorianCalendar)
}
/* explicit point in time */
-fun getGregorianDate(year: Int, month: Int, day: Int): XMLGregorianCalendar {
+fun getGregorianCalendar(year: Int, month: Int, day: Int):
XMLGregorianCalendar {
val gregorianCalendar = GregorianCalendar(year, month, day)
val datatypeFactory = DatatypeFactory.newInstance()
return datatypeFactory.newXMLGregorianCalendar(gregorianCalendar)
diff --git a/util/src/test/kotlin/SignatureDataTest.kt
b/util/src/test/kotlin/SignatureDataTest.kt
index 323b6bd..c4239cc 100644
--- a/util/src/test/kotlin/SignatureDataTest.kt
+++ b/util/src/test/kotlin/SignatureDataTest.kt
@@ -4,7 +4,7 @@ import tech.libeufin.util.CryptoUtil
import tech.libeufin.util.XMLUtil
import tech.libeufin.util.ebics_h004.EbicsRequest
import tech.libeufin.util.ebics_h004.EbicsTypes
-import tech.libeufin.util.getGregorianDate
+import tech.libeufin.util.getGregorianCalendarNow
import java.math.BigInteger
class SignatureDataTest {
@@ -22,7 +22,7 @@ class SignatureDataTest {
static = EbicsRequest.StaticHeaderType().apply {
hostID = "some host ID"
nonce = "nonce".toByteArray()
- timestamp = getGregorianDate()
+ timestamp = getGregorianCalendarNow()
partnerID = "some partner ID"
userID = "some user ID"
orderDetails = EbicsRequest.OrderDetails().apply {
--
To stop receiving notification emails like this one, please contact
address@hidden.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [libeufin] branch master updated: refactoring / code cleanup WIP,
gnunet <=