[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] branch master updated: Sending C52 to the bank.
From: |
gnunet |
Subject: |
[libeufin] branch master updated: Sending C52 to the bank. |
Date: |
Thu, 28 Nov 2019 18:35:57 +0100 |
This is an automated email from the git hooks/post-receive script.
marcello pushed a commit to branch master
in repository libeufin.
The following commit(s) were added to refs/heads/master by this push:
new 0a5fec8 Sending C52 to the bank.
0a5fec8 is described below
commit 0a5fec853dac05f66dce1ea857ff1579fe0a935e
Author: Marcello Stanisci <address@hidden>
AuthorDate: Thu Nov 28 18:35:34 2019 +0100
Sending C52 to the bank.
---
nexus/src/main/kotlin/Helpers.kt | 300 +++++++++++++++++++++
nexus/src/main/kotlin/JSON.kt | 6 +
nexus/src/main/kotlin/Main.kt | 267 +++---------------
.../libeufin/schema/ebics_h004/EbicsRequest.kt | 35 +++
4 files changed, 380 insertions(+), 228 deletions(-)
diff --git a/nexus/src/main/kotlin/Helpers.kt b/nexus/src/main/kotlin/Helpers.kt
new file mode 100644
index 0000000..e46d144
--- /dev/null
+++ b/nexus/src/main/kotlin/Helpers.kt
@@ -0,0 +1,300 @@
+package tech.libeufin.nexus
+
+import io.ktor.client.HttpClient
+import io.ktor.client.request.post
+import io.ktor.http.HttpStatusCode
+import tech.libeufin.sandbox.CryptoUtil
+import tech.libeufin.sandbox.XMLUtil
+import tech.libeufin.sandbox.logger
+import tech.libeufin.sandbox.toByteArray
+import tech.libeufin.schema.ebics_h004.EbicsRequest
+import tech.libeufin.schema.ebics_s001.UserSignatureData
+import java.math.BigInteger
+import java.security.PrivateKey
+import java.security.SecureRandom
+import java.security.interfaces.RSAPrivateCrtKey
+import java.security.interfaces.RSAPublicKey
+import java.util.*
+import javax.xml.bind.JAXBElement
+import javax.xml.datatype.DatatypeFactory
+import javax.xml.datatype.XMLGregorianCalendar
+
+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,
+ nonce: ByteArray,
+ date: XMLGregorianCalendar
+): EbicsRequest {
+
+ return EbicsRequest.createForUploadInitializationPhase(
+ cryptoBundle,
+ subscriberData.hostId,
+ getNonce(128),
+ subscriberData.partnerId,
+ subscriberData.userId,
+ getGregorianDate(),
+ subscriberData.bankAuthPub!!,
+ subscriberData.bankEncPub!!,
+ BigInteger.ONE,
+ orderType
+ )
+}
+
+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.
+ */
+fun chunkString(input: String): String {
+
+ val ret = StringBuilder()
+ var columns = 0
+
+ for (i in input.indices) {
+
+ if ((i + 1).rem(2) == 0) {
+
+ if (columns == 7) {
+ ret.append(input[i] + "\n")
+ columns = 0
+ continue
+ }
+
+ ret.append(input[i] + " ")
+ columns++
+ continue
+ }
+ ret.append(input[i])
+ }
+
+ return ret.toString()
+
+}
+
+fun expectId(param: String?): Int {
+
+ try {
+ return param!!.toInt()
+ } catch (e: Exception) {
+ throw NotAnIdError(HttpStatusCode.BadRequest)
+ }
+}
+
+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.
+ */
+suspend inline fun HttpClient.postToBank(url: String, body: String): String {
+
+ val response = try {
+ this.post<String>(
+ urlString = url,
+ block = {
+ this.body = body
+ }
+ )
+ } catch (e: Exception) {
+ throw UnreachableBankError(HttpStatusCode.InternalServerError)
+ }
+
+ return response
+}
+
+/**
+ * DO verify the bank's signature
+ */
+suspend inline fun <reified T, reified S> HttpClient.postToBankSignedAndVerify(
+ url: String,
+ body: T,
+ pub: RSAPublicKey,
+ priv: RSAPrivateCrtKey
+): JAXBElement<S> {
+
+ val doc = XMLUtil.convertJaxbToDocument(body)
+ XMLUtil.signEbicsDocument(doc, priv)
+
+ val response: String = this.postToBank(url,
XMLUtil.convertDomToString(doc))
+ logger.debug("About to verify: ${response}")
+
+ val responseString = try {
+
+ XMLUtil.parseStringIntoDom(response)
+ } catch (e: Exception) {
+
+ throw UnparsableResponse(HttpStatusCode.BadRequest, response)
+ }
+
+ if (!XMLUtil.verifyEbicsDocument(responseString, pub)) {
+
+ throw BadSignature(HttpStatusCode.NotAcceptable)
+ }
+
+ try {
+
+ return XMLUtil.convertStringToJaxb(response)
+ } catch (e: Exception) {
+
+ throw UnparsableResponse(HttpStatusCode.BadRequest, response)
+ }
+}
+
+suspend inline fun <reified T, reified S> HttpClient.postToBankSigned(
+ url: String,
+ body: T,
+ priv: PrivateKey
+): JAXBElement<S> {
+
+ val doc = XMLUtil.convertJaxbToDocument(body)
+ XMLUtil.signEbicsDocument(doc, priv)
+
+ val response: String = this.postToBank(url,
XMLUtil.convertDomToString(doc))
+
+ try {
+ return XMLUtil.convertStringToJaxb(response)
+ } catch (e: Exception) {
+ throw UnparsableResponse(HttpStatusCode.BadRequest, response)
+ }
+}
+
+
+
+/**
+ * do NOT verify the bank's signature
+ */
+suspend inline fun <reified T, reified S> HttpClient.postToBankUnsigned(
+ url: String,
+ body: T
+): JAXBElement<S> {
+
+ val response: String = this.postToBank(url,
XMLUtil.convertJaxbToString(body))
+
+ try {
+ return XMLUtil.convertStringToJaxb(response)
+ } catch (e: Exception) {
+ throw UnparsableResponse(HttpStatusCode.BadRequest, response)
+ }
+}
+
+/**
+ * @param size in bits
+ */
+fun getNonce(size: Int): ByteArray {
+ val sr = SecureRandom()
+ val ret = ByteArray(size / 8)
+ sr.nextBytes(ret)
+ return ret
+}
+
+/* explicit point in time */
+fun getGregorianDate(year: Int, month: Int, day: Int): XMLGregorianCalendar {
+ val gregorianCalendar = GregorianCalendar(year, month, day)
+ val datatypeFactory = DatatypeFactory.newInstance()
+ return datatypeFactory.newXMLGregorianCalendar(gregorianCalendar)
+}
+
+/* now */
+fun getGregorianDate(): XMLGregorianCalendar {
+ val gregorianCalendar = GregorianCalendar()
+ val datatypeFactory = DatatypeFactory.newInstance()
+ return datatypeFactory.newXMLGregorianCalendar(gregorianCalendar)
+}
diff --git a/nexus/src/main/kotlin/JSON.kt b/nexus/src/main/kotlin/JSON.kt
index 42aa7ee..605bade 100644
--- a/nexus/src/main/kotlin/JSON.kt
+++ b/nexus/src/main/kotlin/JSON.kt
@@ -9,6 +9,12 @@ data class EbicsBackupRequest(
val passphrase: String
)
+
+data class EbicsDateRange(
+ val start: String, // ISO 8601 calendar date
+ val end: String // ISO 8601 calendar date
+)
+
/**
* This object is used twice: as a response to the backup request,
* and as a request to the backup restore. Note: in the second case
diff --git a/nexus/src/main/kotlin/Main.kt b/nexus/src/main/kotlin/Main.kt
index d240e21..21c3b6f 100644
--- a/nexus/src/main/kotlin/Main.kt
+++ b/nexus/src/main/kotlin/Main.kt
@@ -60,6 +60,7 @@ import javax.crypto.EncryptedPrivateKeyInfo
import javax.xml.datatype.DatatypeFactory
import javax.xml.datatype.XMLGregorianCalendar
import java.security.interfaces.RSAPublicKey
+import java.time.LocalDate
fun testData() {
@@ -82,212 +83,6 @@ 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.
- */
-fun chunkString(input: String): String {
-
- val ret = StringBuilder()
- var columns = 0
-
- for (i in input.indices) {
-
- if ((i + 1).rem(2) == 0) {
-
- if (columns == 7) {
- ret.append(input[i] + "\n")
- columns = 0
- continue
- }
-
- ret.append(input[i] + " ")
- columns++
- continue
- }
- ret.append(input[i])
- }
-
- return ret.toString()
-
-}
-
-fun expectId(param: String?): Int {
-
- try {
- return param!!.toInt()
- } catch (e: Exception) {
- throw NotAnIdError(HttpStatusCode.BadRequest)
- }
-}
-
-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.
- */
-suspend inline fun HttpClient.postToBank(url: String, body: String): String {
-
- val response = try {
- this.post<String>(
- urlString = url,
- block = {
- this.body = body
- }
- )
- } catch (e: Exception) {
- throw UnreachableBankError(HttpStatusCode.InternalServerError)
- }
-
- return response
-}
-
-/**
- * DO verify the bank's signature
- */
-suspend inline fun <reified T, reified S>HttpClient.postToBankSignedAndVerify(
- url: String,
- body: T,
- pub: RSAPublicKey,
- priv: RSAPrivateCrtKey): JAXBElement<S> {
-
- val doc = XMLUtil.convertJaxbToDocument(body)
- XMLUtil.signEbicsDocument(doc, priv)
-
- val response: String = this.postToBank(url,
XMLUtil.convertDomToString(doc))
- logger.debug("About to verify: ${response}")
-
- val responseString = try {
-
- XMLUtil.parseStringIntoDom(response)
- } catch (e: Exception) {
-
- throw UnparsableResponse(HttpStatusCode.BadRequest, response)
- }
-
- if (!XMLUtil.verifyEbicsDocument(responseString, pub)) {
-
- throw BadSignature(HttpStatusCode.NotAcceptable)
- }
-
- try {
-
- return XMLUtil.convertStringToJaxb(response)
- } catch (e: Exception) {
-
- throw UnparsableResponse(HttpStatusCode.BadRequest, response)
- }
-}
-
-suspend inline fun <reified T, reified S>HttpClient.postToBankSigned(
- url: String,
- body: T,
- priv: PrivateKey): JAXBElement<S> {
-
- val doc = XMLUtil.convertJaxbToDocument(body)
- XMLUtil.signEbicsDocument(doc, priv)
-
- val response: String = this.postToBank(url,
XMLUtil.convertDomToString(doc))
-
- try {
- return XMLUtil.convertStringToJaxb(response)
- } catch (e: Exception) {
- throw UnparsableResponse(HttpStatusCode.BadRequest, response)
- }
-}
-
-
-
-/**
- * do NOT verify the bank's signature
- */
-suspend inline fun <reified T, reified S>HttpClient.postToBankUnsigned(
- url: String,
- body: T
-): JAXBElement<S> {
-
- val response: String = this.postToBank(url,
XMLUtil.convertJaxbToString(body))
-
- try {
- return XMLUtil.convertStringToJaxb(response)
- } catch (e: Exception) {
- throw UnparsableResponse(HttpStatusCode.BadRequest, response)
- }
-}
-
-/**
- * @param size in bits
- */
-fun getNonce(size: Int): ByteArray {
- val sr = SecureRandom()
- val ret = ByteArray(size / 8)
- sr.nextBytes(ret)
- return ret
-}
-
-fun getGregorianDate(): XMLGregorianCalendar {
- val gregorianCalendar = GregorianCalendar()
- val datatypeFactory = DatatypeFactory.newInstance()
- return datatypeFactory.newXMLGregorianCalendar(gregorianCalendar)
-}
-
data class NotAnIdError(val statusCode: HttpStatusCode) : Exception("String ID
not convertible in number")
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")
@@ -398,27 +193,50 @@ fun main() {
return@get
}
- get("/ebics/subscribers/{id}/sendHtd") {
+ post("/ebics/subscribers/{id}/sendC52") {
val id = expectId(call.parameters["id"])
+ val body = call.receive<EbicsDateRange>()
+
+ val startDate = LocalDate.parse(body.start)
+ val endDate = LocalDate.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,
- EbicsRequest.createForDownloadInitializationPhase(
- subscriberData.userId,
- subscriberData.partnerId,
- subscriberData.hostId,
+ createDownloadInitializationPhase(
+ subscriberData,
+ "C52",
getNonce(128),
getGregorianDate(),
- subscriberData.bankEncPub ?: throw
BankKeyMissing(HttpStatusCode.PreconditionFailed),
- subscriberData.bankAuthPub ?: throw
BankKeyMissing(HttpStatusCode.PreconditionFailed),
- "HTD"
+ getGregorianDate(startDate.year, startDate.monthValue,
startDate.dayOfMonth),
+ getGregorianDate(endDate.year, endDate.monthValue,
endDate.dayOfMonth)
),
subscriberData.customerAuthPriv
)
+ }
+
+ get("/ebics/subscribers/{id}/sendHtd") {
+ 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
+ )
+
logger.debug("HTD response: " +
XMLUtil.convertJaxbToString<EbicsResponse>(response.value))
if (response.value.body.returnCode.value != "000000") {
@@ -790,7 +608,6 @@ fun main() {
EbicsSubscriberEntity.findById(id) ?: throw
SubscriberNotFoundError(HttpStatusCode.NotFound)
)
}
-
val payload = "PAYLOAD"
val usd_encrypted = CryptoUtil.encryptEbicsE002(
EbicsOrderUtil.encodeOrderDataXml(
@@ -807,20 +624,15 @@ fun main() {
val response = client.postToBankSignedAndVerify<EbicsRequest,
EbicsResponse>(
subscriberData.ebicsUrl,
- EbicsRequest.createForUploadInitializationPhase(
+ createUploadInitializationPhase(
+ subscriberData,
+ "TST",
usd_encrypted,
- subscriberData.hostId,
getNonce(128),
- subscriberData.partnerId,
- subscriberData.userId,
- getGregorianDate(),
- subscriberData.bankAuthPub!!,
- subscriberData.bankEncPub!!,
- BigInteger.ONE,
- "TST"
+ getGregorianDate()
),
subscriberData.bankAuthPub!!,
- subscriberData.customerAuthPriv
+ subscriberData.customerEncPriv
)
if (response.value.body.returnCode.value != "000000") {
@@ -865,8 +677,7 @@ fun main() {
HttpStatusCode.OK
)
}
-
-
+
post("/ebics/subscribers/{id}/sync") {
val id = expectId(call.parameters["id"])
val bundle = transaction {
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 da756fb..b733c95 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
@@ -305,6 +305,41 @@ class EbicsRequest {
}
+ /* Take a time range (useful for C52 and C53) */
+ fun createForDownloadInitializationPhase(
+ userId: String,
+ partnerId: String,
+ hostId: String,
+ nonceArg: ByteArray,
+ date: XMLGregorianCalendar,
+ bankEncPub: RSAPublicKey,
+ bankAuthPub: RSAPublicKey,
+ aOrderType: String,
+ dateStart: XMLGregorianCalendar,
+ dateEnd: XMLGregorianCalendar
+ ): EbicsRequest {
+
+ val tmp = this.createForDownloadInitializationPhase(
+ userId,
+ partnerId,
+ hostId,
+ nonceArg,
+ date,
+ bankEncPub,
+ bankAuthPub,
+ aOrderType
+ )
+
+ (tmp.header.static.orderDetails?.orderParams as
StandardOrderParams).apply {
+ dateRange?.apply {
+ start = dateStart
+ end = dateEnd
+ }
+ }
+
+ return tmp
+ }
+
fun createForDownloadInitializationPhase(
userId: String,
partnerId: String,
--
To stop receiving notification emails like this one, please contact
address@hidden.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [libeufin] branch master updated: Sending C52 to the bank.,
gnunet <=