[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] branch master updated (393bbe0 -> a168b4e)
From: |
gnunet |
Subject: |
[libeufin] branch master updated (393bbe0 -> a168b4e) |
Date: |
Mon, 25 Nov 2019 22:08:10 +0100 |
This is an automated email from the git hooks/post-receive script.
marcello pushed a change to branch master
in repository libeufin.
from 393bbe0 no PUT for CLI
new 611f2a3 provide TST command to the Python CLI.
new dcac56c EbicsRequest helper.
new a168b4e Move request constructors into companion objects.
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
nexus/src/main/kotlin/Containers.kt | 27 +-
nexus/src/main/kotlin/Main.kt | 583 +++++++--------------
.../libeufin/schema/ebics_h004/EbicsNpkdRequest.kt | 32 ++
.../libeufin/schema/ebics_h004/EbicsRequest.kt | 204 ++++++-
.../libeufin/schema/ebics_h004/EbicsResponse.kt | 2 +-
.../schema/ebics_h004/EbicsUnsecuredRequest.kt | 116 ++++
sandbox/src/main/python/libeufin-cli | 20 +
7 files changed, 573 insertions(+), 411 deletions(-)
diff --git a/nexus/src/main/kotlin/Containers.kt
b/nexus/src/main/kotlin/Containers.kt
index b3a6a09..8c69ee1 100644
--- a/nexus/src/main/kotlin/Containers.kt
+++ b/nexus/src/main/kotlin/Containers.kt
@@ -2,6 +2,9 @@ package tech.libeufin.nexus
import javax.crypto.SecretKey
import org.w3c.dom.Document
+import java.security.PrivateKey
+import java.security.interfaces.RSAPrivateCrtKey
+import java.security.interfaces.RSAPublicKey
import javax.xml.bind.JAXBElement
@@ -11,27 +14,27 @@ import javax.xml.bind.JAXBElement
* / make messages. And not all the values are needed all
* the time.
*/
-data class EbicsContainer<T>(
+data class EbicsContainer(
- // needed to verify responses
- val bankAuthPubBlob: ByteArray? = null,
+ val partnerId: String,
- val bankEncPubBlob: ByteArray? = null,
+ val userId: String,
+
+
+ var bankAuthPub: RSAPublicKey?,
+ var bankEncPub: RSAPublicKey?,
// needed to send the message
- val ebicsUrl: String? = null,
+ val ebicsUrl: String,
// needed to craft further messages
- val hostId: String? = null,
-
- // needed to encrypt order data during all the phases
- val plainTransactionKey: SecretKey? = null,
+ val hostId: String,
// needed to decrypt data coming from the bank
- val customerEncPrivBlob: ByteArray? = null,
+ val customerEncPriv: RSAPrivateCrtKey,
// needed to sign documents
- val customerAuthPrivBlob: ByteArray? = null,
+ val customerAuthPriv: RSAPrivateCrtKey,
- val jaxb: T? = null
+ val customerSignPriv: RSAPrivateCrtKey
)
\ No newline at end of file
diff --git a/nexus/src/main/kotlin/Main.kt b/nexus/src/main/kotlin/Main.kt
index 61a41d5..519f06e 100644
--- a/nexus/src/main/kotlin/Main.kt
+++ b/nexus/src/main/kotlin/Main.kt
@@ -30,7 +30,6 @@ import io.ktor.features.ContentNegotiation
import io.ktor.features.StatusPages
import io.ktor.gson.gson
import io.ktor.http.ContentType
-import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.request.receive
import io.ktor.request.uri
@@ -39,7 +38,6 @@ import io.ktor.response.respondText
import io.ktor.routing.*
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
-import org.apache.commons.codec.digest.Crypt
import org.apache.xml.security.binding.xmldsig.RSAKeyValueType
import org.apache.xml.security.binding.xmldsig.SignatureType
import org.jetbrains.exposed.sql.transactions.transaction
@@ -49,22 +47,19 @@ import tech.libeufin.schema.ebics_h004.*
import java.text.DateFormat
import javax.sql.rowset.serial.SerialBlob
import javax.xml.bind.JAXBElement
-import org.w3c.dom.Document
import tech.libeufin.schema.ebics_s001.SignatureTypes
import tech.libeufin.schema.ebics_s001.UserSignatureData
-import java.awt.Container
import java.math.BigInteger
import java.security.PrivateKey
-import java.security.PublicKey
import java.security.SecureRandom
+import java.security.interfaces.RSAPrivateCrtKey
import java.text.SimpleDateFormat
-import java.time.Instant.now
import java.util.*
import java.util.zip.DeflaterInputStream
-import java.util.zip.InflaterInputStream
import javax.crypto.EncryptedPrivateKeyInfo
import javax.xml.datatype.DatatypeFactory
import javax.xml.datatype.XMLGregorianCalendar
+import java.security.interfaces.RSAPublicKey
fun testData() {
@@ -87,6 +82,39 @@ fun testData() {
}
}
+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.authenticationPrivateKey.toByteArray())
+ )
+
+}
+
/**
* Inserts spaces every 2 characters, and a newline after 8 pairs.
*/
@@ -125,6 +153,32 @@ fun expectId(param: String?): Int {
}
}
+fun signOrder(
+ orderBlob: ByteArray,
+ signKey: RSAPrivateCrtKey,
+ partnerId: String,
+ userId: String
+): UserSignatureData {
+
+ val ES_signature = CryptoUtil.signEbicsA006(
+ CryptoUtil.digestEbicsOrderA006(orderBlob),
+ signKey
+ )
+ val userSignatureData = UserSignatureData().apply {
+ orderSignatureList = listOf(
+ UserSignatureData.OrderSignatureData().apply {
+ signatureVersion = "A006"
+ signatureValue = ES_signature
+ partnerID = partnerId
+ userID = userId
+ }
+ )
+ }
+
+ return userSignatureData
+}
+
+
/**
* @return null when the bank could not be reached, otherwise returns the
* response already converted in JAXB.
@@ -151,8 +205,8 @@ suspend inline fun HttpClient.postToBank(url: String, body:
String): String {
suspend inline fun <reified T, reified S>HttpClient.postToBankSignedAndVerify(
url: String,
body: T,
- pub: PublicKey,
- priv: PrivateKey): JAXBElement<S> {
+ pub: RSAPublicKey,
+ priv: RSAPrivateCrtKey): JAXBElement<S> {
val doc = XMLUtil.convertJaxbToDocument(body)
XMLUtil.signEbicsDocument(doc, priv)
@@ -242,6 +296,7 @@ data class UnparsableResponse(val statusCode:
HttpStatusCode, val rawResponse: S
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")
@@ -345,66 +400,24 @@ fun main() {
get("/ebics/subscribers/{id}/sendHtd") {
val id = expectId(call.parameters["id"])
- val bundle = transaction {
- val subscriber = EbicsSubscriberEntity.findById(id) ?:
throw SubscriberNotFoundError(HttpStatusCode.NotFound)
- val request = EbicsRequest().apply {
- version = "H004"
- revision = 1
- header = EbicsRequest.Header().apply {
- authenticate = true
- static = EbicsRequest.StaticHeaderType().apply {
- userID = subscriber.userID
- partnerID = subscriber.partnerID
- hostID = subscriber.hostID
- nonce = getNonce(128)
- timestamp = getGregorianDate()
- partnerID = subscriber.partnerID
- orderDetails =
EbicsRequest.OrderDetails().apply {
- orderType = "HTD"
- orderAttribute = "DZHNN"
- orderParams =
EbicsRequest.StandardOrderParams()
- }
- bankPubKeyDigests =
EbicsRequest.BankPubKeyDigests().apply {
- authentication =
EbicsTypes.PubKeyDigest().apply {
- algorithm =
"http://www.w3.org/2001/04/xmlenc#sha256"
- version = "X002"
- value =
CryptoUtil.getEbicsPublicKeyHash(
- CryptoUtil.loadRsaPublicKey(
-
(subscriber.bankAuthenticationPublicKey ?: throw
BankKeyMissing(HttpStatusCode.NotAcceptable)).toByteArray()
- )
- )
- }
- encryption =
EbicsTypes.PubKeyDigest().apply {
- algorithm =
"http://www.w3.org/2001/04/xmlenc#sha256"
- version = "E002"
- value =
CryptoUtil.getEbicsPublicKeyHash(
- CryptoUtil.loadRsaPublicKey(
-
(subscriber.bankEncryptionPublicKey ?: throw
BankKeyMissing(HttpStatusCode.NotAcceptable)).toByteArray()
- )
- )
- }
- securityMedium = "0000"
- }
- mutable = EbicsRequest.MutableHeader().apply {
- transactionPhase =
EbicsTypes.TransactionPhaseType.INITIALISATION
- }
- authSignature = SignatureType()
- }
- }
- body = EbicsRequest.Body()
- }
+ val subscriberData = transaction {
+ containerInit(EbicsSubscriberEntity.findById(id) ?: throw
SubscriberNotFoundError(HttpStatusCode.NotFound))
+ }
- EbicsContainer(
- ebicsUrl = subscriber.ebicsURL,
- customerEncPrivBlob =
subscriber.encryptionPrivateKey.toByteArray(),
- customerAuthPrivBlob =
subscriber.authenticationPrivateKey.toByteArray(),
- jaxb = request,
- hostId = subscriber.hostID
+ val response = client.postToBankUnsigned<EbicsRequest,
EbicsResponse>(
+ subscriberData.ebicsUrl,
+ EbicsRequest.createForDownloadInitializationPhase(
+ subscriberData.userId,
+ subscriberData.partnerId,
+ subscriberData.hostId,
+ getNonce(128),
+ getGregorianDate(),
+ subscriberData.bankEncPub ?: throw
BankKeyMissing(HttpStatusCode.PreconditionFailed),
+ subscriberData.bankAuthPub ?: throw
BankKeyMissing(HttpStatusCode.PreconditionFailed),
+ "HTD"
)
- }
-
- val response = client.postToBankUnsigned<EbicsRequest,
EbicsResponse>(bundle.ebicsUrl!!, bundle.jaxb!!)
+ )
logger.debug("HTD response: " +
XMLUtil.convertJaxbToString<EbicsResponse>(response.value))
if (response.value.body.returnCode.value != "000000") {
@@ -420,41 +433,25 @@ fun main() {
Base64.getDecoder().decode(response.value.body.dataTransfer!!.orderData.value)
)
- val dataCompr = CryptoUtil.decryptEbicsE002(er,
CryptoUtil.loadRsaPrivateKey(bundle.customerEncPrivBlob!!))
+ val dataCompr = CryptoUtil.decryptEbicsE002(
+ er,
+ subscriberData.customerEncPriv
+ )
val data =
EbicsOrderUtil.decodeOrderDataXml<HTDResponseOrderData>(dataCompr)
logger.debug("HTD payload is:
${XMLUtil.convertJaxbToString(data)}")
-
- val ackRequest = EbicsRequest().apply {
- header = EbicsRequest.Header().apply {
- version = "H004"
- revision = 1
- authenticate = true
- static = EbicsRequest.StaticHeaderType().apply {
- hostID = bundle.hostId!!
- transactionID =
response.value.header._static.transactionID
- }
- mutable = EbicsRequest.MutableHeader().apply {
- transactionPhase =
EbicsTypes.TransactionPhaseType.RECEIPT
- }
- }
- authSignature = SignatureType()
-
- body = EbicsRequest.Body().apply {
- transferReceipt = EbicsRequest.TransferReceipt().apply
{
- authenticate = true
- receiptCode = 0 // always true at this point.
- }
- }
- }
+ val ackRequest = EbicsRequest.createForDownloadReceiptPhase(
+ response.value.header._static.transactionID ?: throw
BankInvalidResponse(HttpStatusCode.ExpectationFailed),
+ subscriberData.userId
+ )
val ackResponse =
client.postToBankSignedAndVerify<EbicsRequest, EbicsResponse>(
- bundle.ebicsUrl,
+ subscriberData.ebicsUrl,
ackRequest,
- CryptoUtil.loadRsaPublicKey(bundle.bankAuthPubBlob!!),
- CryptoUtil.loadRsaPrivateKey(bundle.customerAuthPrivBlob!!)
+ subscriberData.bankAuthPub ?: throw
BankKeyMissing(HttpStatusCode.PreconditionFailed),
+ subscriberData.customerAuthPriv
)
logger.debug("HTD final response: " +
XMLUtil.convertJaxbToString<EbicsResponse>(response.value))
@@ -682,58 +679,22 @@ fun main() {
val id = expectId(call.parameters["id"]) // caught above
val iniRequest = EbicsUnsecuredRequest()
- val url = transaction {
- val subscriber = EbicsSubscriberEntity.findById(id) ?:
throw SubscriberNotFoundError(HttpStatusCode.NotFound)
- val tmpKey =
CryptoUtil.loadRsaPrivateKey(subscriber.signaturePrivateKey.toByteArray())
-
- iniRequest.apply {
- version = "H004"
- revision = 1
- header = EbicsUnsecuredRequest.Header().apply {
- authenticate = true
- static =
EbicsUnsecuredRequest.StaticHeaderType().apply {
- orderDetails =
EbicsUnsecuredRequest.OrderDetails().apply {
- orderAttribute = "DZNNN"
- orderType = "INI"
- securityMedium = "0000"
- hostID = subscriber.hostID
- userID = subscriber.userID
- partnerID = subscriber.partnerID
- systemID = subscriber.systemID
- }
-
- }
- mutable =
EbicsUnsecuredRequest.Header.EmptyMutableHeader()
- }
- body = EbicsUnsecuredRequest.Body().apply {
- dataTransfer =
EbicsUnsecuredRequest.UnsecuredDataTransfer().apply {
- orderData =
EbicsUnsecuredRequest.OrderData().apply {
- value = EbicsOrderUtil.encodeOrderDataXml(
-
SignatureTypes.SignaturePubKeyOrderData().apply {
- signaturePubKeyInfo =
SignatureTypes.SignaturePubKeyInfoType().apply {
- signatureVersion = "A006"
- pubKeyValue =
SignatureTypes.PubKeyValueType().apply {
- rsaKeyValue =
org.apache.xml.security.binding.xmldsig.RSAKeyValueType().apply {
- exponent =
tmpKey.publicExponent.toByteArray()
- modulus =
tmpKey.modulus.toByteArray()
- }
- }
- }
- userID = subscriber.userID
- partnerID = subscriber.partnerID
-
- }
- )
- }
- }
- }
- }
- subscriber.ebicsURL
+ val subscriberData = transaction {
+ containerInit(
+ EbicsSubscriberEntity.findById(id) ?: throw
SubscriberNotFoundError(HttpStatusCode.NotFound)
+ )
}
+ val theRequest = EbicsUnsecuredRequest.createIni(
+ subscriberData.hostId,
+ subscriberData.userId,
+ subscriberData.partnerId,
+ subscriberData.customerSignPriv
+ )
+
val responseJaxb =
client.postToBankUnsigned<EbicsUnsecuredRequest, EbicsKeyManagementResponse>(
- url,
- iniRequest
+ subscriberData.ebicsUrl,
+ theRequest
)
if (responseJaxb.value.body.returnCode.value != "000000") {
@@ -825,177 +786,74 @@ fun main() {
val id = expectId(call.parameters["id"])
- val innerPayload = "ES-PAYLOAD"
-
-
- val container = transaction {
- val subscriber = EbicsSubscriberEntity.findById(id) ?:
throw SubscriberNotFoundError(HttpStatusCode.NotFound)
-
- // first prepare ES content
- val ES_signature = CryptoUtil.signEbicsA006(
-
CryptoUtil.digestEbicsOrderA006(innerPayload.toByteArray()),
-
CryptoUtil.loadRsaPrivateKey(subscriber.signaturePrivateKey.toByteArray())
+ val subscriberData = transaction {
+ containerInit(
+ EbicsSubscriberEntity.findById(id) ?: throw
SubscriberNotFoundError(HttpStatusCode.NotFound)
)
+ }
- val userSignatureData = UserSignatureData().apply {
- orderSignatureList = listOf(
- UserSignatureData.OrderSignatureData().apply {
- signatureVersion = "A006"
- signatureValue = ES_signature
- partnerID = subscriber.partnerID
- userID = subscriber.userID
- }
- )
- }
+ val payload = "PAYLOAD"
+ val usd_encrypted = CryptoUtil.encryptEbicsE002(
+ EbicsOrderUtil.encodeOrderDataXml(
- val usd_compressed =
EbicsOrderUtil.encodeOrderDataXml(userSignatureData)
- val usd_encrypted = CryptoUtil.encryptEbicsE002(
- usd_compressed,
- CryptoUtil.loadRsaPublicKey(
- subscriber.bankEncryptionPublicKey?.toByteArray()
?: throw BankKeyMissing(HttpStatusCode.NotFound)
+ signOrder(
+ payload.toByteArray(),
+ subscriberData.customerSignPriv,
+ subscriberData.partnerId,
+ subscriberData.userId
)
- )
-
- val tmp = EbicsRequest().apply {
- header = EbicsRequest.Header().apply {
- version = "H004"
- revision = 1
- authenticate = true
- static = EbicsRequest.StaticHeaderType().apply {
- hostID = subscriber.hostID
- nonce = getNonce(128)
- timestamp = getGregorianDate()
- partnerID = subscriber.partnerID
- userID = subscriber.userID
- orderDetails =
EbicsRequest.OrderDetails().apply {
- orderType = "TST"
- orderAttribute = "OZHNN"
- orderParams =
EbicsRequest.StandardOrderParams()
- }
- bankPubKeyDigests =
EbicsRequest.BankPubKeyDigests().apply {
- authentication =
EbicsTypes.PubKeyDigest().apply {
- algorithm =
"http://www.w3.org/2001/04/xmlenc#sha256"
- version = "X002"
- value =
CryptoUtil.getEbicsPublicKeyHash(
- CryptoUtil.loadRsaPublicKey(
-
subscriber.bankAuthenticationPublicKey?.toByteArray() ?: throw BankKeyMissing(
-
HttpStatusCode.PreconditionFailed)
- )
- )
- }
- encryption =
EbicsTypes.PubKeyDigest().apply {
- algorithm =
"http://www.w3.org/2001/04/xmlenc#sha256"
- version = "E002"
- value =
CryptoUtil.getEbicsPublicKeyHash(
- CryptoUtil.loadRsaPublicKey(
-
subscriber.bankEncryptionPublicKey?.toByteArray() ?: throw BankKeyMissing(
-
HttpStatusCode.PreconditionFailed
- )
- )
- )
- }
- }
- securityMedium = "0000"
- numSegments = BigInteger.ONE
- }
- mutable = EbicsRequest.MutableHeader().apply {
- transactionPhase =
EbicsTypes.TransactionPhaseType.INITIALISATION
- }
- }
- authSignature = SignatureType()
- body = EbicsRequest.Body().apply {
- dataTransfer = EbicsRequest.DataTransfer().apply {
- signatureData =
EbicsRequest.SignatureData().apply {
- authenticate = true
- value = usd_encrypted.encryptedData
- }
- dataEncryptionInfo =
EbicsTypes.DataEncryptionInfo().apply {
- transactionKey =
usd_encrypted.encryptedTransactionKey
- authenticate = true
- encryptionPubKeyDigest =
EbicsTypes.PubKeyDigest().apply {
- algorithm =
"http://www.w3.org/2001/04/xmlenc#sha256"
- version = "E002"
- value =
CryptoUtil.getEbicsPublicKeyHash(
- CryptoUtil.loadRsaPublicKey(
-
subscriber.bankEncryptionPublicKey?.toByteArray() ?: throw BankKeyMissing(
-
HttpStatusCode.PreconditionFailed
- )
- )
- )
- }
- }
- }
- }
- }
+ ),
+ subscriberData.bankEncPub!!
+ )
- EbicsContainer(
- jaxb = tmp,
- ebicsUrl = subscriber.ebicsURL,
- bankAuthPubBlob =
subscriber.bankAuthenticationPublicKey?.toByteArray() ?: throw BankKeyMissing(
- HttpStatusCode.PreconditionFailed
- ),
- plainTransactionKey =
usd_encrypted.plainTransactionKey,
- customerAuthPrivBlob =
subscriber.authenticationPrivateKey.toByteArray(),
- bankEncPubBlob =
subscriber.bankEncryptionPublicKey?.toByteArray() ?: throw BankKeyMissing(
- HttpStatusCode.PreconditionFailed
- ),
- hostId = subscriber.hostID
- )
- }
val response = client.postToBankSignedAndVerify<EbicsRequest,
EbicsResponse>(
- container.ebicsUrl!!,
- container.jaxb!!,
- CryptoUtil.loadRsaPublicKey(container.bankAuthPubBlob!!),
-
CryptoUtil.loadRsaPrivateKey(container.customerAuthPrivBlob!!)
+ subscriberData.ebicsUrl,
+ EbicsRequest.createForUploadInitializationPhase(
+ usd_encrypted,
+ subscriberData.hostId,
+ getNonce(128),
+ subscriberData.partnerId,
+ subscriberData.userId,
+ getGregorianDate(),
+ subscriberData.bankAuthPub!!,
+ subscriberData.bankEncPub!!,
+ BigInteger.ONE,
+ "TST"
+ ),
+ 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(
- innerPayload.toByteArray().inputStream()
+ payload.toByteArray().inputStream()
).use { it.readAllBytes() }
val encryptedPayload =
CryptoUtil.encryptEbicsE002withTransactionKey(
compressedInnerPayload,
- CryptoUtil.loadRsaPublicKey(container.bankEncPubBlob!!),
- container.plainTransactionKey!!
+ subscriberData.bankEncPub!!,
+ usd_encrypted.plainTransactionKey!!
)
- val tmp = EbicsRequest().apply {
- header = EbicsRequest.Header().apply {
- version = "H004"
- revision = 1
- authenticate = true
- static = EbicsRequest.StaticHeaderType().apply {
- hostID = container.hostId!!
- transactionID =
response.value.header._static.transactionID
- }
- mutable = EbicsRequest.MutableHeader().apply {
- transactionPhase =
EbicsTypes.TransactionPhaseType.TRANSFER
- segmentNumber = EbicsTypes.SegmentNumber().apply {
- lastSegment = true
- value = BigInteger.ONE
- }
- }
- }
-
- authSignature = SignatureType()
- body = EbicsRequest.Body().apply {
- dataTransfer = EbicsRequest.DataTransfer().apply {
- orderData = encryptedPayload.encryptedData
- }
- }
- }
+ val tmp = EbicsRequest.createForUploadTransferPhase(
+ subscriberData.hostId,
+ response.value.header._static.transactionID!!,
+ BigInteger.ONE,
+ encryptedPayload.encryptedData
+ )
val responseTransaction =
client.postToBankSignedAndVerify<EbicsRequest, EbicsResponse>(
- container.ebicsUrl,
+ subscriberData.ebicsUrl,
tmp,
- CryptoUtil.loadRsaPublicKey(container.bankAuthPubBlob),
-
CryptoUtil.loadRsaPrivateKey(container.customerAuthPrivBlob)
+ subscriberData.bankAuthPub!!,
+ subscriberData.customerAuthPriv
)
if (responseTransaction.value.body.returnCode.value !=
"000000") {
@@ -1013,42 +871,21 @@ fun main() {
post("/ebics/subscribers/{id}/sync") {
val id = expectId(call.parameters["id"])
val bundle = transaction {
- val subscriber = EbicsSubscriberEntity.findById(id) ?:
throw SubscriberNotFoundError(HttpStatusCode.NotFound)
- val hpbRequest = EbicsNpkdRequest().apply {
- version = "H004"
- revision = 1
- header = EbicsNpkdRequest.Header().apply {
- authenticate = true
- mutable = EbicsNpkdRequest.EmptyMutableHeader()
- static = EbicsNpkdRequest.StaticHeaderType().apply
{
- hostID = subscriber.hostID
- partnerID = subscriber.partnerID
- userID = subscriber.userID
- securityMedium = "0000"
- orderDetails = EbicsNpkdRequest.OrderDetails()
- orderDetails.orderType = "HPB"
- orderDetails.orderAttribute = "DZHNN"
- nonce = getNonce(128)
- timestamp = getGregorianDate()
- }
- }
- body = EbicsNpkdRequest.EmptyBody()
- authSignature = SignatureType()
- }
-
- EbicsContainer<EbicsNpkdRequest>(
- ebicsUrl = subscriber.ebicsURL,
- customerEncPrivBlob =
subscriber.encryptionPrivateKey.toByteArray(),
- customerAuthPrivBlob =
subscriber.authenticationPrivateKey.toByteArray(),
- jaxb = hpbRequest
-
+ containerInit(
+ EbicsSubscriberEntity.findById(id) ?: throw
SubscriberNotFoundError(HttpStatusCode.NotFound)
)
}
val response = client.postToBankSigned<EbicsNpkdRequest,
EbicsKeyManagementResponse>(
bundle.ebicsUrl!!,
- bundle.jaxb!!,
- CryptoUtil.loadRsaPrivateKey(bundle.customerAuthPrivBlob!!)
+ EbicsNpkdRequest.createRequest(
+ bundle.hostId,
+ bundle.partnerId,
+ bundle.userId,
+ getNonce(128),
+ getGregorianDate()
+ ),
+ bundle.customerAuthPriv
)
if (response.value.body.returnCode.value != "000000") {
@@ -1064,24 +901,28 @@ fun main() {
val dataCompr = CryptoUtil.decryptEbicsE002(
er,
- CryptoUtil.loadRsaPrivateKey(bundle.customerEncPrivBlob!!))
- val data =
EbicsOrderUtil.decodeOrderDataXml<HPBResponseOrderData>(dataCompr)
-
- val bankAuthPubBlob =
CryptoUtil.loadRsaPublicKeyFromComponents(
-
data.authenticationPubKeyInfo.pubKeyValue.rsaKeyValue.modulus,
-
data.authenticationPubKeyInfo.pubKeyValue.rsaKeyValue.exponent
- )
-
- val bankEncPubBlob = CryptoUtil.loadRsaPublicKeyFromComponents(
- data.encryptionPubKeyInfo.pubKeyValue.rsaKeyValue.modulus,
- data.encryptionPubKeyInfo.pubKeyValue.rsaKeyValue.exponent
+ bundle.customerEncPriv
)
+ val data =
EbicsOrderUtil.decodeOrderDataXml<HPBResponseOrderData>(dataCompr)
// put bank's keys into database.
transaction {
val subscriber = EbicsSubscriberEntity.findById(id)
- subscriber!!.bankAuthenticationPublicKey =
SerialBlob(bankAuthPubBlob.encoded)
- subscriber!!.bankEncryptionPublicKey =
SerialBlob(bankEncPubBlob.encoded)
+
+ subscriber!!.bankAuthenticationPublicKey = SerialBlob(
+
+ CryptoUtil.loadRsaPublicKeyFromComponents(
+
data.authenticationPubKeyInfo.pubKeyValue.rsaKeyValue.modulus,
+
data.authenticationPubKeyInfo.pubKeyValue.rsaKeyValue.exponent
+ ).encoded
+ )
+
+ subscriber!!.bankEncryptionPublicKey = SerialBlob(
+ CryptoUtil.loadRsaPublicKeyFromComponents(
+
data.authenticationPubKeyInfo.pubKeyValue.rsaKeyValue.modulus,
+
data.authenticationPubKeyInfo.pubKeyValue.rsaKeyValue.exponent
+ ).encoded
+ )
}
call.respondText("Bank keys stored in database\n",
ContentType.Text.Plain, HttpStatusCode.OK)
@@ -1090,72 +931,23 @@ fun main() {
post("/ebics/subscribers/{id}/sendHia") {
- val id = expectId(call.parameters["id"]) // caught above
+ val id = expectId(call.parameters["id"])
- val bundle = transaction {
- val subscriber = EbicsSubscriberEntity.findById(id) ?:
throw SubscriberNotFoundError(HttpStatusCode.NotFound)
- val tmpAiKey =
CryptoUtil.loadRsaPrivateKey(subscriber.authenticationPrivateKey.toByteArray())
- val tmpEncKey =
CryptoUtil.loadRsaPrivateKey(subscriber.encryptionPrivateKey.toByteArray())
-
- val hiaRequest = EbicsUnsecuredRequest().apply {
- version = "H004"
- revision = 1
- header = EbicsUnsecuredRequest.Header().apply {
- authenticate = true
- static =
EbicsUnsecuredRequest.StaticHeaderType().apply {
- orderDetails =
EbicsUnsecuredRequest.OrderDetails().apply {
- orderAttribute = "DZNNN"
- orderType = "HIA"
- securityMedium = "0000"
- hostID = subscriber.hostID
- userID = subscriber.userID
- partnerID = subscriber.partnerID
- systemID = subscriber.systemID
- }
- }
- mutable =
EbicsUnsecuredRequest.Header.EmptyMutableHeader()
- }
- body = EbicsUnsecuredRequest.Body().apply {
- dataTransfer =
EbicsUnsecuredRequest.UnsecuredDataTransfer().apply {
- orderData =
EbicsUnsecuredRequest.OrderData().apply {
- value = EbicsOrderUtil.encodeOrderDataXml(
- HIARequestOrderData().apply {
- authenticationPubKeyInfo =
EbicsTypes.AuthenticationPubKeyInfoType().apply {
- pubKeyValue =
EbicsTypes.PubKeyValueType().apply {
- rsaKeyValue =
RSAKeyValueType().apply {
- exponent =
tmpAiKey.publicExponent.toByteArray()
- modulus =
tmpAiKey.modulus.toByteArray()
- }
- }
- authenticationVersion = "X002"
- }
- encryptionPubKeyInfo =
EbicsTypes.EncryptionPubKeyInfoType().apply {
- pubKeyValue =
EbicsTypes.PubKeyValueType().apply {
- rsaKeyValue =
RSAKeyValueType().apply {
- exponent =
tmpEncKey.publicExponent.toByteArray()
- modulus =
tmpEncKey.modulus.toByteArray()
- }
- }
- encryptionVersion = "E002"
-
- }
- partnerID = subscriber.partnerID
- userID = subscriber.userID
- }
- )
- }
- }
- }
- }
- EbicsContainer<EbicsUnsecuredRequest>(
- ebicsUrl = subscriber.ebicsURL,
- jaxb = hiaRequest
+ val subscriberData = transaction {
+ containerInit(
+ EbicsSubscriberEntity.findById(id) ?: throw
SubscriberNotFoundError(HttpStatusCode.NotFound)
)
}
val responseJaxb =
client.postToBankUnsigned<EbicsUnsecuredRequest, EbicsKeyManagementResponse>(
- bundle.ebicsUrl!!,
- bundle.jaxb!!
+ subscriberData.ebicsUrl,
+ EbicsUnsecuredRequest.createHia(
+ subscriberData.hostId,
+ subscriberData.userId,
+ subscriberData.partnerId,
+ subscriberData.customerAuthPriv,
+ subscriberData.customerEncPriv
+ )
)
if (responseJaxb.value.body.returnCode.value != "000000") {
@@ -1165,7 +957,8 @@ fun main() {
call.respondText(
"Bank accepted authentication and encryption keys\n",
ContentType.Text.Plain,
- HttpStatusCode.OK)
+ HttpStatusCode.OK
+ )
return@post
}
diff --git
a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsNpkdRequest.kt
b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsNpkdRequest.kt
index 4657020..34f7ed2 100644
---
a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsNpkdRequest.kt
+++
b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsNpkdRequest.kt
@@ -100,4 +100,36 @@ class EbicsNpkdRequest {
@XmlAccessorType(XmlAccessType.NONE)
class EmptyBody
+
+ companion object {
+ fun createRequest(
+ hostId: String,
+ partnerId: String,
+ userId: String,
+ aNonce: ByteArray,
+ date: XMLGregorianCalendar
+ ): EbicsNpkdRequest {
+ return EbicsNpkdRequest().apply {
+ version = "H004"
+ revision = 1
+ header = EbicsNpkdRequest.Header().apply {
+ authenticate = true
+ mutable = EbicsNpkdRequest.EmptyMutableHeader()
+ static = EbicsNpkdRequest.StaticHeaderType().apply {
+ hostID = hostId
+ partnerID = partnerId
+ userID = userId
+ securityMedium = "0000"
+ orderDetails = EbicsNpkdRequest.OrderDetails()
+ orderDetails.orderType = "HPB"
+ orderDetails.orderAttribute = "DZHNN"
+ nonce = aNonce
+ timestamp = date
+ }
+ }
+ body = EbicsNpkdRequest.EmptyBody()
+ authSignature = SignatureType()
+ }
+ }
+ }
}
\ No newline at end of file
diff --git
a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsRequest.kt
b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsRequest.kt
index ffe797d..da756fb 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsRequest.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsRequest.kt
@@ -1,7 +1,11 @@
package tech.libeufin.schema.ebics_h004
+import io.ktor.http.HttpStatusCode
import org.apache.xml.security.binding.xmldsig.SignatureType
+import tech.libeufin.sandbox.CryptoUtil
+import tech.libeufin.sandbox.toByteArray
import java.math.BigInteger
+import java.security.interfaces.RSAPublicKey
import javax.xml.bind.annotation.*
import javax.xml.bind.annotation.adapters.CollapsedStringAdapter
import javax.xml.bind.annotation.adapters.HexBinaryAdapter
@@ -149,7 +153,8 @@ class EbicsRequest {
@XmlAccessorType(XmlAccessType.NONE)
@XmlType(
name = "",
- propOrder = ["orderType", "orderID", "orderAttribute", "orderParams"])
+ propOrder = ["orderType", "orderID", "orderAttribute", "orderParams"]
+ )
class OrderDetails {
@get:XmlElement(name = "OrderType", required = true)
@get:XmlJavaTypeAdapter(CollapsedStringAdapter::class)
@@ -173,7 +178,9 @@ class EbicsRequest {
@get:XmlElements(
XmlElement(
name = "StandardOrderParams",
- type = StandardOrderParams::class))
+ type = StandardOrderParams::class
+ )
+ )
var orderParams: OrderParams? = null
}
@@ -265,4 +272,195 @@ class EbicsRequest {
@get:XmlElement(name = "Encryption")
lateinit var encryption: EbicsTypes.PubKeyDigest
}
-}
+
+ companion object {
+
+ fun createForDownloadReceiptPhase(
+ transactionId: String,
+ hostId: String
+
+ ): EbicsRequest {
+ return EbicsRequest().apply {
+ header = Header().apply {
+ version = "H004"
+ revision = 1
+ authenticate = true
+ static = StaticHeaderType().apply {
+ hostID = hostId
+ transactionID = transactionId
+ }
+ mutable = MutableHeader().apply {
+ transactionPhase =
EbicsTypes.TransactionPhaseType.RECEIPT
+ }
+ }
+ authSignature = SignatureType()
+
+ body = Body().apply {
+ transferReceipt = TransferReceipt().apply {
+ authenticate = true
+ receiptCode = 0 // always true at this point.
+ }
+ }
+ }
+
+ }
+
+ fun createForDownloadInitializationPhase(
+ userId: String,
+ partnerId: String,
+ hostId: String,
+ nonceArg: ByteArray,
+ date: XMLGregorianCalendar,
+ bankEncPub: RSAPublicKey,
+ bankAuthPub: RSAPublicKey,
+ aOrderType: String
+
+ ): EbicsRequest {
+
+ return EbicsRequest().apply {
+ version = "H004"
+ revision = 1
+ authSignature = SignatureType()
+ body = Body()
+ header = Header().apply {
+ authenticate = true
+ static = EbicsRequest.StaticHeaderType().apply {
+ userID = userId
+ partnerID = partnerId
+ hostID = hostId
+ nonce = nonceArg
+ timestamp = date
+ partnerID = partnerId
+ orderDetails = EbicsRequest.OrderDetails().apply {
+ orderType = aOrderType
+ orderAttribute = "DZHNN"
+ orderParams = EbicsRequest.StandardOrderParams()
+ }
+ bankPubKeyDigests =
EbicsRequest.BankPubKeyDigests().apply {
+ authentication = EbicsTypes.PubKeyDigest().apply {
+ algorithm =
"http://www.w3.org/2001/04/xmlenc#sha256"
+ version = "X002"
+ value =
CryptoUtil.getEbicsPublicKeyHash(bankAuthPub)
+ }
+ encryption = EbicsTypes.PubKeyDigest().apply {
+ algorithm =
"http://www.w3.org/2001/04/xmlenc#sha256"
+ version = "E002"
+ value =
CryptoUtil.getEbicsPublicKeyHash(bankEncPub)
+ }
+ securityMedium = "0000"
+ }
+ mutable = EbicsRequest.MutableHeader().apply {
+ transactionPhase =
EbicsTypes.TransactionPhaseType.INITIALISATION
+ }
+ }
+ }
+ }
+ }
+
+ fun createForUploadInitializationPhase(
+ cryptoBundle: CryptoUtil.EncryptionResult,
+ hostId: String,
+ nonceArg: ByteArray,
+ partnerId: String,
+ userId: String,
+ date: XMLGregorianCalendar,
+ bankAuthPub: RSAPublicKey,
+ bankEncPub: RSAPublicKey,
+ segmentsNumber: BigInteger,
+ aOrderType: String
+ ): EbicsRequest {
+
+ return EbicsRequest().apply {
+ header = EbicsRequest.Header().apply {
+ version = "H004"
+ revision = 1
+ authenticate = true
+ static = EbicsRequest.StaticHeaderType().apply {
+ hostID = hostId
+ nonce = nonceArg
+ timestamp = date
+ partnerID = partnerId
+ userID = userId
+ orderDetails = EbicsRequest.OrderDetails().apply {
+ orderType = aOrderType
+ orderAttribute = "OZHNN"
+ orderParams = EbicsRequest.StandardOrderParams()
+ }
+ bankPubKeyDigests =
EbicsRequest.BankPubKeyDigests().apply {
+ authentication = EbicsTypes.PubKeyDigest().apply {
+ algorithm =
"http://www.w3.org/2001/04/xmlenc#sha256"
+ version = "X002"
+ value =
CryptoUtil.getEbicsPublicKeyHash(bankAuthPub)
+ }
+ encryption = EbicsTypes.PubKeyDigest().apply {
+ algorithm =
"http://www.w3.org/2001/04/xmlenc#sha256"
+ version = "E002"
+ value =
CryptoUtil.getEbicsPublicKeyHash(bankEncPub)
+ }
+ }
+ securityMedium = "0000"
+ numSegments = segmentsNumber
+ }
+ mutable = EbicsRequest.MutableHeader().apply {
+ transactionPhase =
EbicsTypes.TransactionPhaseType.INITIALISATION
+ }
+ }
+ authSignature = SignatureType()
+ body = EbicsRequest.Body().apply {
+ dataTransfer = EbicsRequest.DataTransfer().apply {
+ signatureData = EbicsRequest.SignatureData().apply {
+ authenticate = true
+ value = cryptoBundle.encryptedData
+ }
+ dataEncryptionInfo =
EbicsTypes.DataEncryptionInfo().apply {
+ transactionKey =
cryptoBundle.encryptedTransactionKey
+ authenticate = true
+ encryptionPubKeyDigest =
EbicsTypes.PubKeyDigest().apply {
+ algorithm =
"http://www.w3.org/2001/04/xmlenc#sha256"
+ version = "E002"
+ value =
CryptoUtil.getEbicsPublicKeyHash(bankEncPub)
+ }
+ }
+ }
+ }
+ }
+
+
+ }
+
+ fun createForUploadTransferPhase(
+ hostId: String,
+ transactionId: String,
+ segNumber: BigInteger,
+ encryptedData: ByteArray
+
+ ): EbicsRequest {
+
+ return EbicsRequest().apply {
+ header = Header().apply {
+ version = "H004"
+ revision = 1
+ authenticate = true
+ static = StaticHeaderType().apply {
+ hostID = hostId
+ transactionID = transactionId
+ }
+ mutable = MutableHeader().apply {
+ transactionPhase =
EbicsTypes.TransactionPhaseType.TRANSFER
+ segmentNumber = EbicsTypes.SegmentNumber().apply {
+ lastSegment = true
+ value = segNumber
+ }
+ }
+ }
+
+ authSignature = SignatureType()
+ body = EbicsRequest.Body().apply {
+ dataTransfer = EbicsRequest.DataTransfer().apply {
+ orderData = encryptedData
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git
a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsResponse.kt
b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsResponse.kt
index c2dc841..4ba6949 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsResponse.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsResponse.kt
@@ -22,7 +22,7 @@ class EbicsResponse {
var revision: Int? = null
@get:XmlElement(required = true)
- lateinit var header: EbicsResponse.Header
+ lateinit var header: Header
@get:XmlElement(name = "AuthSignature", required = true)
lateinit var authSignature: SignatureType
diff --git
a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsUnsecuredRequest.kt
b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsUnsecuredRequest.kt
index ae56e21..373bc8b 100644
---
a/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsUnsecuredRequest.kt
+++
b/sandbox/src/main/kotlin/tech/libeufin/schema/ebics_h004/EbicsUnsecuredRequest.kt
@@ -1,5 +1,9 @@
package tech.libeufin.schema.ebics_h004
+import org.apache.xml.security.binding.xmldsig.RSAKeyValueType
+import tech.libeufin.sandbox.EbicsOrderUtil
+import tech.libeufin.schema.ebics_s001.SignatureTypes
+import java.security.interfaces.RSAPrivateCrtKey
import javax.xml.bind.annotation.*
import javax.xml.bind.annotation.adapters.CollapsedStringAdapter
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter
@@ -102,4 +106,116 @@ class EbicsUnsecuredRequest {
@get:XmlJavaTypeAdapter(CollapsedStringAdapter::class)
lateinit var orderAttribute: String
}
+
+ companion object {
+
+ fun createHia(
+ hostId: String,
+ userId: String,
+ partnerId: String,
+ authKey: RSAPrivateCrtKey,
+ encKey: RSAPrivateCrtKey
+
+ ): EbicsUnsecuredRequest {
+
+ return EbicsUnsecuredRequest().apply {
+
+ version = "H004"
+ revision = 1
+ header = EbicsUnsecuredRequest.Header().apply {
+ authenticate = true
+ static = EbicsUnsecuredRequest.StaticHeaderType().apply {
+ orderDetails =
EbicsUnsecuredRequest.OrderDetails().apply {
+ orderAttribute = "DZNNN"
+ orderType = "HIA"
+ securityMedium = "0000"
+ hostID = hostId
+ userID = userId
+ partnerID = partnerId
+ }
+ }
+ mutable = EbicsUnsecuredRequest.Header.EmptyMutableHeader()
+ }
+ body = EbicsUnsecuredRequest.Body().apply {
+ dataTransfer =
EbicsUnsecuredRequest.UnsecuredDataTransfer().apply {
+ orderData = EbicsUnsecuredRequest.OrderData().apply {
+ value = EbicsOrderUtil.encodeOrderDataXml(
+ HIARequestOrderData().apply {
+ authenticationPubKeyInfo =
EbicsTypes.AuthenticationPubKeyInfoType().apply {
+ pubKeyValue =
EbicsTypes.PubKeyValueType().apply {
+ rsaKeyValue =
RSAKeyValueType().apply {
+ exponent =
authKey.publicExponent.toByteArray()
+ modulus =
authKey.modulus.toByteArray()
+ }
+ }
+ authenticationVersion = "X002"
+ }
+ encryptionPubKeyInfo =
EbicsTypes.EncryptionPubKeyInfoType().apply {
+ pubKeyValue =
EbicsTypes.PubKeyValueType().apply {
+ rsaKeyValue =
RSAKeyValueType().apply {
+ exponent =
encKey.publicExponent.toByteArray()
+ modulus =
encKey.modulus.toByteArray()
+ }
+ }
+ encryptionVersion = "E002"
+
+ }
+ partnerID = partnerId
+ userID = userId
+ }
+ )
+ }
+ }
+ }
+ }
+ }
+
+ fun createIni(
+ hostId: String,
+ userId: String,
+ partnerId: String,
+ signKey: RSAPrivateCrtKey
+
+ ): EbicsUnsecuredRequest {
+ return EbicsUnsecuredRequest().apply {
+ version = "H004"
+ revision = 1
+ header = EbicsUnsecuredRequest.Header().apply {
+ authenticate = true
+ static = EbicsUnsecuredRequest.StaticHeaderType().apply {
+ orderDetails =
EbicsUnsecuredRequest.OrderDetails().apply {
+ orderAttribute = "DZNNN"
+ orderType = "INI"
+ securityMedium = "0000"
+ hostID = hostId
+ userID = userId
+ partnerID = partnerId
+ }
+ }
+ mutable = EbicsUnsecuredRequest.Header.EmptyMutableHeader()
+ }
+ body = Body().apply {
+ dataTransfer =
EbicsUnsecuredRequest.UnsecuredDataTransfer().apply {
+ orderData = EbicsUnsecuredRequest.OrderData().apply {
+ value = EbicsOrderUtil.encodeOrderDataXml(
+
SignatureTypes.SignaturePubKeyOrderData().apply {
+ signaturePubKeyInfo =
SignatureTypes.SignaturePubKeyInfoType().apply {
+ signatureVersion = "A006"
+ pubKeyValue =
SignatureTypes.PubKeyValueType().apply {
+ rsaKeyValue =
org.apache.xml.security.binding.xmldsig.RSAKeyValueType().apply {
+ exponent =
signKey.publicExponent.toByteArray()
+ modulus =
signKey.modulus.toByteArray()
+ }
+ }
+ }
+ userID = userId
+ partnerID = partnerId
+ }
+ )
+ }
+ }
+ }
+ }
+ }
+ }
}
diff --git a/sandbox/src/main/python/libeufin-cli
b/sandbox/src/main/python/libeufin-cli
index 0afc57c..64648fc 100755
--- a/sandbox/src/main/python/libeufin-cli
+++ b/sandbox/src/main/python/libeufin-cli
@@ -101,6 +101,26 @@ def backup(obj, customer_id, output_file):
print("Backup stored in {}".format(output_file))
+
+@ebics.command(help="send TST message")
+@click.pass_obj
+@click.option(
+ "--customer-id",
+ help="numerical ID of the customer at the Nexus",
+ required=False,
+ default=1)
+def tst(obj, customer_id):
+
+ url = urljoin(obj["base_url"],
"/ebics/subscribers/{}/sendTst".format(customer_id))
+ try:
+ resp = post(url)
+ except Exception:
+ print("Could not reach the bank")
+ return
+
+ print(resp.content.decode("utf-8"))
+
+
@ebics.command(help="send INI message")
@click.pass_obj
@click.option(
--
To stop receiving notification emails like this one, please contact
address@hidden.
- [libeufin] branch master updated (393bbe0 -> a168b4e),
gnunet <=