libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit f332cafcc7ffc97cf22aaf3283f5de4894e2d25b
parent d0a6a2f4fe36dad06c9814683035339fa1276b4d
Author: Florian Dold <florian.dold@gmail.com>
Date:   Tue, 16 Jun 2020 14:36:29 +0530

PDF key letter generation

Diffstat:
Mintegration-tests/util.py | 2+-
Mnexus/build.gradle | 3+++
Mnexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 19+++++++++++++++++++
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 107 insertions(+), 1 deletion(-)

diff --git a/integration-tests/util.py b/integration-tests/util.py @@ -20,7 +20,7 @@ def checkPort(port): def kill(name, s): print(f"terminating {name} ...") s.terminate() - s.wait(1) + s.wait() print("terminated!") diff --git a/nexus/build.gradle b/nexus/build.gradle @@ -82,6 +82,9 @@ dependencies { implementation "io.ktor:ktor-auth:$ktor_version" implementation "io.ktor:ktor-jackson:$ktor_version" + // PDF generation + implementation 'com.itextpdf:itext7-core:7.1.11' + testImplementation group: 'junit', name: 'junit', version: '4.12' } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -35,6 +35,10 @@ import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.options.default import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.options.prompt +import com.itextpdf.kernel.pdf.PdfDocument +import com.itextpdf.kernel.pdf.PdfWriter +import com.itextpdf.layout.Document +import com.itextpdf.layout.element.Paragraph import io.ktor.application.ApplicationCall import io.ktor.application.ApplicationCallPipeline import io.ktor.application.call @@ -70,6 +74,7 @@ import org.slf4j.event.Level import tech.libeufin.nexus.ebics.* import tech.libeufin.util.* import tech.libeufin.util.CryptoUtil.hashpw +import java.io.ByteArrayOutputStream import java.io.PrintWriter import java.io.StringWriter import java.net.URLEncoder @@ -778,6 +783,20 @@ fun serverMain(dbName: String) { call.respond(object {}) } + get("/bank-connections/{connid}/keyletter") { + val conn = transaction { + authenticateRequest(call.request) + requireBankConnection(call, "connid") + } + when (conn.type) { + "ebics" -> { + val pdfBytes = getEbicsKeyLetterPdf(conn) + call.respondBytes(pdfBytes, ContentType("application", "pdf")) + } + else -> throw NexusError(HttpStatusCode.NotImplemented, "keyletter not supporte dfor ${conn.type}") + } + } + get("/bank-connections/{connid}/messages") { val ret = transaction { val list = BankMessageList() diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt @@ -26,6 +26,11 @@ package tech.libeufin.nexus.ebics import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.itextpdf.kernel.pdf.PdfDocument +import com.itextpdf.kernel.pdf.PdfWriter +import com.itextpdf.layout.Document +import com.itextpdf.layout.element.AreaBreak +import com.itextpdf.layout.element.Paragraph import io.ktor.application.Application import io.ktor.application.ApplicationCall import io.ktor.application.call @@ -47,6 +52,10 @@ import tech.libeufin.nexus.* import tech.libeufin.nexus.logger import tech.libeufin.util.* import tech.libeufin.util.ebics_h004.HTDResponseOrderData +import java.io.ByteArrayOutputStream +import java.security.interfaces.RSAPrivateCrtKey +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter import java.util.* import javax.crypto.EncryptedPrivateKeyInfo @@ -479,4 +488,79 @@ suspend fun connectEbics(client: HttpClient, connId: String) { subscriberEntity.bankEncryptionPublicKey = ExposedBlob((hpbData.encryptionPubKey.encoded)) } } +} + +fun formatHex(ba: ByteArray): String { + var out = "" + for (i in ba.indices) { + val b = ba[i] + if (i > 0 && i % 16 == 0) { + out += "\n" + } + out += java.lang.String.format("%02X", b) + out += " " + } + return out +} + +fun getEbicsKeyLetterPdf(conn: NexusBankConnectionEntity): ByteArray { + val ebicsSubscriber = transaction { getEbicsSubscriberDetails(conn.id.value) } + + val po = ByteArrayOutputStream() + val pdfWriter = PdfWriter(po) + val pdfDoc = PdfDocument(pdfWriter) + val date = LocalDateTime.now() + val dateStr = date.format(DateTimeFormatter.ISO_LOCAL_DATE) + + fun writeCommon(doc: Document) { + doc.add(Paragraph(""" + Datum: $dateStr + Teilnehmer: ${conn.id.value} + Host-ID: ${ebicsSubscriber.hostId} + User-ID: ${ebicsSubscriber.userId} + Partner-ID: ${ebicsSubscriber.partnerId} + ES version: A006 + """.trimIndent())) + } + + fun writeKey(doc: Document, priv: RSAPrivateCrtKey) { + val pub = CryptoUtil.getRsaPublicFromPrivate(priv) + val hash = CryptoUtil.getEbicsPublicKeyHash(pub) + doc.add(Paragraph("Exponent:\n${formatHex(pub.publicExponent.toByteArray())}")) + doc.add(Paragraph("Modulus:\n${formatHex(pub.modulus.toByteArray())}")) + doc.add(Paragraph("SHA-256 hash:\n${formatHex(hash)}")) + } + + fun writeSigLine(doc: Document) { + doc.add(Paragraph("Ort / Datum: ________________")) + doc.add(Paragraph("Firma / Name: ________________")) + doc.add(Paragraph("Unterschrift: ________________")) + } + + Document(pdfDoc).use { + it.add(Paragraph("Signaturschlüssel").setFontSize(24f)) + writeCommon(it) + it.add(Paragraph("Öffentlicher Schlüssel (Public key for the electronic signature)")) + writeKey(it, ebicsSubscriber.customerSignPriv) + it.add(Paragraph("\n")) + writeSigLine(it) + it.add(AreaBreak()) + + it.add(Paragraph("Authentifikationsschlüssel").setFontSize(24f)) + writeCommon(it) + it.add(Paragraph("Öffentlicher Schlüssel (Public key for the identification and authentication signature)")) + writeKey(it, ebicsSubscriber.customerAuthPriv) + it.add(Paragraph("\n")) + writeSigLine(it) + it.add(AreaBreak()) + + it.add(Paragraph("Verschlüsselungsschlüssel").setFontSize(24f)) + writeCommon(it) + it.add(Paragraph("Öffentlicher Schlüssel (Public encryption key)")) + it.add(Paragraph("\n")) + writeKey(it, ebicsSubscriber.customerSignPriv) + writeSigLine(it) + } + pdfWriter.flush() + return po.toByteArray() } \ No newline at end of file