diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt index a38390dc..ade09db7 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt @@ -40,7 +40,7 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg ): String { return createDialogInitMessage(bankCountryCode, bankCode, KundenID.Anonymous, KundensystemID.Anonymous, KundensystemStatusWerte.NichtBenoetigt, - BPDVersion.VersionNotReceivedYet, UPDVersion.VersionNotReceivedYet, Dialogsprache.Default, productName, productVersion, false) + BPDVersion.VersionNotReceivedYet, UPDVersion.VersionNotReceivedYet, Dialogsprache.Default, productName, productVersion, false, false) } open fun createDialogInitMessage( @@ -54,36 +54,49 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg language: Dialogsprache, productName: String, productVersion: String, - signMessage: Boolean = true + signMessage: Boolean = true, + encryptMessage: Boolean = true ): String { - return createMessage(signMessage, bankCountryCode, bankCode, customerId, listOf( + return createMessage(signMessage, encryptMessage, bankCountryCode, bankCode, customerId, listOf( IdentifikationsSegment(generator.resetSegmentNumber(if (signMessage) 2 else 1), bankCountryCode, bankCode, customerId, customerSystemId, status), Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), bpdVersion, updVersion, language, productName, productVersion) )) } - open fun createMessage(signMessage: Boolean, bankCountryCode: Int, bankCode: String, customerId: String, + open fun createMessage(signMessage: Boolean, encryptMessage: Boolean, bankCountryCode: Int, bankCode: String, customerId: String, payloadSegments: List): String { - val signedPayload = if (signMessage) { signPayload(2, bankCountryCode, bankCode, customerId, payloadSegments) } - else { payloadSegments } + var payload = payloadSegments + val partyIdentification = "0" + val date = utils.formatDateTodayAsInt() + val time = utils.formatTimeNowAsInt() - val payload = signedPayload.joinToString(Nachricht.SegmentSeparator) { it.format() } + if (signMessage) { + payload = signPayload(2, partyIdentification, date, time, bankCountryCode, bankCode, customerId, payload) + } - val messageSize = payload.length + MessageHeaderLength + MessageClosingLength + AddedSeparatorsLength + if (encryptMessage) { + payload = encryptPayload(partyIdentification, date, time, bankCountryCode, bankCode, customerId, payload) + } + + val formattedPayload = formatPayload(payload) + + val messageSize = formattedPayload.length + MessageHeaderLength + MessageClosingLength + AddedSeparatorsLength val messageNumber = Nachrichtennummer.FirstMessageNumber val header = Nachrichtenkopf(ISegmentNumberGenerator.FirstSegmentNumber, messageSize, "0", messageNumber) val closing = Nachrichtenabschluss(generator.getNextSegmentNumber(), messageNumber) - return listOf(header.format(), payload, closing.format()) + return listOf(header.format(), formattedPayload, closing.format()) .joinToString(Nachricht.SegmentSeparator, postfix = Nachricht.SegmentSeparator) } - protected open fun signPayload(headerSegmentNumber: Int, bankCountryCode: Int, bankCode: String, customerId: String, + + protected open fun signPayload(headerSegmentNumber: Int, partyIdentification: String, date: Int, time: Int, + bankCountryCode: Int, bankCode: String, customerId: String, payloadSegments: List): List { val controlReference = "1" // TODO @@ -102,10 +115,26 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg val signatureClosing = Signaturabschluss( generator.getNextSegmentNumber(), controlReference, - "" // TODO + "12345" // TODO ) return listOf(signatureHeader, *payloadSegments.toTypedArray(), signatureClosing) } + + private fun encryptPayload(partyIdentification: String, date: Int, time: Int, + bankCountryCode: Int, bankCode: String, customerId: String, payload: List): List { + + val encryptionHeader = PinTanVerschluesselungskopf(partyIdentification, date, time, bankCountryCode, bankCode, customerId) + + val encryptedData = VerschluesselteDaten(formatPayload(payload) + Nachricht.SegmentSeparator) + + return listOf(encryptionHeader, encryptedData) + } + + + protected open fun formatPayload(payload: List): String { + return payload.joinToString(Nachricht.SegmentSeparator) { it.format() } + } + } \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/BinaerDatenelement.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/BinaerDatenelement.kt new file mode 100644 index 00000000..695dc61c --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/BinaerDatenelement.kt @@ -0,0 +1,42 @@ +package net.dankito.fints.messages.datenelemente.basisformate + +import net.dankito.fints.messages.Existenzstatus +import net.dankito.fints.messages.datenelemente.Datenelement + + +/** + * Binäre Daten werden unverändert in den FinTS-Datensatz eingestellt. Eine Umwandlung in + * eine Zeichendarstellung erfolgt nicht. Es ist zu beachten, dass der FinTS-Basiszeichensatz + * für binäre Daten keine Gültigkeit besitzt. Ferner gelten die speziellen Syntaxregeln für + * binäre Daten (s. Kap. H.1.3). + */ +open class BinaerDatenelement @JvmOverloads constructor(val data: ByteArray, existenzstatus: Existenzstatus, val maxLength: Int? = null) + : Datenelement(existenzstatus) { + + /** + * Für binäre Daten gilt eine besondere Syntaxregelung: Das Auftreten dieser Daten wird eingeleitet mit dem + * Binärdatenkennzeichen (@). Anschließend folgt die Längenangabe zu den binären Daten und der binäre Wert selbst, + * der ebenfalls mit dem Binärdatenkennzeichen eingeleitet wird. Die Länge wird angegeben in Byte (nicht die Länge + * der darstellbaren Zeichen). Hierzu muss sichergestellt sein, dass der binäre Datenstrom in vollen Byte + * dargestellt werden kann (binäre Daten, die nicht im Byteformat vorliegen, können nicht über FinTS transportiert + * werden). Syntaxzeichen, die in binären Daten auftreten, dürfen nicht als solche interpretiert werden. + * + * Bei Elementen, die entsprechende Zeichen enthalten können (z. B. DE „SEPAName) ist eine base64-Kodierung in der + * Spezifikation vorzusehen. + */ + override fun format(): String { + return "@${data.size}@" + String(data) + } + + override fun validate() { + // binary data aren't checked, so they are always valid + + maxLength?.let { + if (data.size > maxLength) { + throwValidationException("Binäre Daten dürfen nur eine maximale Größe von $maxLength Bytes haben, " + + "haben aber ${data.size} Bytes.") + } + } + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/BezeichnerFuerAlgorithmusparameterIV.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/BezeichnerFuerAlgorithmusparameterIV.kt new file mode 100644 index 00000000..0b43317a --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/BezeichnerFuerAlgorithmusparameterIV.kt @@ -0,0 +1,10 @@ +package net.dankito.fints.messages.datenelemente.implementierte.encryption + +import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum + + +enum class BezeichnerFuerAlgorithmusparameterIV(override val code: String) : ICodeEnum { + + InitializationValue_ClearText("1") + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/BezeichnerFuerAlgorithmusparameterIVDatenelement.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/BezeichnerFuerAlgorithmusparameterIVDatenelement.kt new file mode 100644 index 00000000..ed2fbd16 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/BezeichnerFuerAlgorithmusparameterIVDatenelement.kt @@ -0,0 +1,22 @@ +package net.dankito.fints.messages.datenelemente.implementierte.encryption + +import net.dankito.fints.messages.Existenzstatus +import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code +import net.dankito.fints.messages.datenelemente.implementierte.allCodes + + +/** + * Eigenschaft betreffend den Initialisierungswert für die RAH-Verfahren (Die + * Steuerung erfolgt in den BPD, vgl. [Formals]). + * + * Codierung: + * 1: Initialization value, clear text (IVC) + */ +open class BezeichnerFuerAlgorithmusparameterIVDatenelement(parameter: BezeichnerFuerAlgorithmusparameterIV, existenzstatus: Existenzstatus) + : Code(parameter.code, AllowedValues, existenzstatus) { + + companion object { + val AllowedValues = allCodes() + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/BezeichnerFuerAlgorithmusparameterSchluessel.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/BezeichnerFuerAlgorithmusparameterSchluessel.kt new file mode 100644 index 00000000..47be6382 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/BezeichnerFuerAlgorithmusparameterSchluessel.kt @@ -0,0 +1,15 @@ +package net.dankito.fints.messages.datenelemente.implementierte.encryption + +import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum + + +enum class BezeichnerFuerAlgorithmusparameterSchluessel(override val code: String) : ICodeEnum { + + /** + * nicht zugelassen + */ + SymmetrischerSchluessel("5"), + + SymmetrischerSchluessel_VerschluesseltMitOeffentlichenSchluessel("6") + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/BezeichnerFuerAlgorithmusparameterSchluesselDatenelement.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/BezeichnerFuerAlgorithmusparameterSchluesselDatenelement.kt new file mode 100644 index 00000000..d2690e68 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/BezeichnerFuerAlgorithmusparameterSchluesselDatenelement.kt @@ -0,0 +1,24 @@ +package net.dankito.fints.messages.datenelemente.implementierte.encryption + +import net.dankito.fints.messages.Existenzstatus +import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code +import net.dankito.fints.messages.datenelemente.implementierte.allCodes + + +/** + * Eigenschaft des Schlüssels für die RAH-Verfahren (Die Steuerung erfolgt in den BPD, vgl. [Formals]). + * + * Codierung: + * 5: Symmetrischer Schlüssel (nicht zugelassen) + * 6: Symmetrischer Schlüssel, verschlüsselt mit einem öffentlichen Schlüssel bei RAH und RDH (KYP). + */ +open class BezeichnerFuerAlgorithmusparameterSchluesselDatenelement(identifier: BezeichnerFuerAlgorithmusparameterSchluessel) + : Code(identifier.code, AllowedValues, Existenzstatus.Mandatory) { + + companion object { + val AllowedValues = allCodes() + + val FinTsMock = BezeichnerFuerAlgorithmusparameterSchluessel.SymmetrischerSchluessel + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/Komprimierungsfunktion.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/Komprimierungsfunktion.kt new file mode 100644 index 00000000..a64be6fc --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/Komprimierungsfunktion.kt @@ -0,0 +1,26 @@ +package net.dankito.fints.messages.datenelemente.implementierte.encryption + +import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum + + +enum class Komprimierungsfunktion(val abbreviation: String, override val code: String) : ICodeEnum { + + Keine_Kompression("NULL", "0"), + + Lempel_Ziv_Welch("LZW", "1"), + + Optimized_LZW("COM", "2"), + + Lempel_Ziv("LZSS", "3"), + + LZ_Huffman_Coding("LZHuf", "4"), + + PKZIP("ZIP", "5"), + + deflate("GZIP", "6"), + + bzip2("bzip2", "7"), + + Gegenseitig_vereinbart("ZZZ", "999") + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/KomprimierungsfunktionDatenelement.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/KomprimierungsfunktionDatenelement.kt new file mode 100644 index 00000000..f08c7325 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/KomprimierungsfunktionDatenelement.kt @@ -0,0 +1,29 @@ +package net.dankito.fints.messages.datenelemente.implementierte.encryption + +import net.dankito.fints.messages.Existenzstatus +import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code +import net.dankito.fints.messages.datenelemente.implementierte.allCodes + + +/** + * Code der unterstützten Komprimierungsfunktion. + * + * Codierung: + * 0: Keine Kompression (NULL) + * 1: Lempel, Ziv, Welch (LZW) + * 2: Optimized LZW (COM) + * 3: Lempel, Ziv (LZSS) + * 4: LZ + Huffman Coding (LZHuf) + * 5: PKZIP (ZIP) + * 6: deflate (GZIP) (http://www.gzip.org/zlib) + * 7: bzip2 (http://sourceware.cygnus.com/bzip2/) + * 999: Gegenseitig vereinbart (ZZZ) + */ +open class KomprimierungsfunktionDatenelement(algorithm: Komprimierungsfunktion) + : Code(algorithm.code, AllowedValues, Existenzstatus.Mandatory) { + + companion object { + val AllowedValues = allCodes() + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/PinTanVerschluesselteDatenDatenelement.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/PinTanVerschluesselteDatenDatenelement.kt new file mode 100644 index 00000000..e7f2b319 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/PinTanVerschluesselteDatenDatenelement.kt @@ -0,0 +1,22 @@ +package net.dankito.fints.messages.datenelemente.implementierte.encryption + +import net.dankito.fints.messages.Existenzstatus +import net.dankito.fints.messages.datenelemente.Datenelement + + +/** + * For PIN/TAN data doesn't get encrypted at all. + * + * It simply gets, prefixed by '@@', appended to VerschluesselteDaten segment header + */ +class PinTanVerschluesselteDatenDatenelement(val payload: String) : Datenelement(Existenzstatus.Mandatory) { + + override fun format(): String { + return "@${payload.length}@" + payload + } + + override fun validate() { + // payload has already been validated, nothing to do + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/Verschluesselungsalgorithmus.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/Verschluesselungsalgorithmus.kt new file mode 100644 index 00000000..ed01cedb --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/Verschluesselungsalgorithmus.kt @@ -0,0 +1,15 @@ +package net.dankito.fints.messages.datenelemente.implementierte.encryption + +import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum + + +enum class Verschluesselungsalgorithmus(override val code: String) : ICodeEnum { + + /** + * nicht zugelassen + */ + Two_Key_Triple_DES("13"), + + AES_256("14") + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/VerschluesselungsalgorithmusKodiert.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/VerschluesselungsalgorithmusKodiert.kt new file mode 100644 index 00000000..4d24e678 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/VerschluesselungsalgorithmusKodiert.kt @@ -0,0 +1,24 @@ +package net.dankito.fints.messages.datenelemente.implementierte.encryption + +import net.dankito.fints.messages.Existenzstatus +import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code +import net.dankito.fints.messages.datenelemente.implementierte.allCodes + + +/** + * Kodierte Information über den verwendeten Verschlüsselungsalgorithmus. + * + * Codierung: + * 13: 2-Key-Triple-DES (nicht zugelassen) + * 14: AES-256 [AES] + */ +open class VerschluesselungsalgorithmusKodiert(algorithm: Verschluesselungsalgorithmus) + : Code(algorithm.code, AllowedValues, Existenzstatus.Mandatory) { + + companion object { + val AllowedValues = allCodes() + + val FinTsMock = Verschluesselungsalgorithmus.AES_256 + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/VerwendungDesVerschluesselungsalgorithmus.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/VerwendungDesVerschluesselungsalgorithmus.kt new file mode 100644 index 00000000..5516c629 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/VerwendungDesVerschluesselungsalgorithmus.kt @@ -0,0 +1,10 @@ +package net.dankito.fints.messages.datenelemente.implementierte.encryption + +import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum + + +enum class VerwendungDesVerschluesselungsalgorithmus(override val code: String) : ICodeEnum { + + OwnerSymmetric("2") + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/VerwendungDesVerschluesselungsalgorithmusKodiert.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/VerwendungDesVerschluesselungsalgorithmusKodiert.kt new file mode 100644 index 00000000..cab04c52 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/VerwendungDesVerschluesselungsalgorithmusKodiert.kt @@ -0,0 +1,23 @@ +package net.dankito.fints.messages.datenelemente.implementierte.encryption + +import net.dankito.fints.messages.Existenzstatus +import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code +import net.dankito.fints.messages.datenelemente.implementierte.allCodes + + +/** + * Kodierte Information über die Verwendung des Verschlüsselungsalgorithmus. + * + * Im Zusammenhang mit der Verschlüsselung sind derzeit folgende Werte möglich: + * + * Codierung: + * 2: Owner Symmetric (OSY) + */ +open class VerwendungDesVerschluesselungsalgorithmusKodiert + : Code(VerwendungDesVerschluesselungsalgorithmus.OwnerSymmetric.code, AllowedValues, Existenzstatus.Mandatory) { + + companion object { + val AllowedValues = allCodes() + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/WertDesAlgorithmusparametersSchluessel.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/WertDesAlgorithmusparametersSchluessel.kt new file mode 100644 index 00000000..605f55ae --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/encryption/WertDesAlgorithmusparametersSchluessel.kt @@ -0,0 +1,16 @@ +package net.dankito.fints.messages.datenelemente.implementierte.encryption + +import net.dankito.fints.messages.Existenzstatus +import net.dankito.fints.messages.datenelemente.basisformate.BinaerDatenelement + + +/** + * Verschlüsselter Nachrichtenschlüssel für den kryptographischen Algorithmusparameter. + */ +open class WertDesAlgorithmusparametersSchluessel(key: ByteArray) : BinaerDatenelement(key, Existenzstatus.Mandatory, 512) { + + companion object { + val FinTsMock = byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0) + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/signatur/Sicherheitsfunktion.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/signatur/Sicherheitsfunktion.kt index f2de7e7b..50cbe906 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/signatur/Sicherheitsfunktion.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/signatur/Sicherheitsfunktion.kt @@ -209,6 +209,8 @@ enum class Sicherheitsfunktion(override val code: String) : ICodeEnum { PIN_TAN_997("997"), - PIN_TAN_Einschritt_Verfahren("999") + Klartext("998"), + + Einschritt_Verfahren("999") } \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/encryption/VerschluesselungsalgorithmusDatenelementgruppe.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/encryption/VerschluesselungsalgorithmusDatenelementgruppe.kt new file mode 100644 index 00000000..a6702ee2 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/encryption/VerschluesselungsalgorithmusDatenelementgruppe.kt @@ -0,0 +1,37 @@ +package net.dankito.fints.messages.datenelementgruppen.implementierte.encryption + +import net.dankito.fints.messages.Existenzstatus +import net.dankito.fints.messages.datenelemente.implementierte.encryption.* +import net.dankito.fints.messages.datenelemente.implementierte.signatur.Operationsmodus +import net.dankito.fints.messages.datenelemente.implementierte.signatur.OperationsmodusKodiert +import net.dankito.fints.messages.datenelementgruppen.Datenelementgruppe + + +/** + * Angaben zum kryptographischen Algorithmus, zu seinem Operationsmodus, so wie zu + * dessen Einsatz, in diesem Fall für die Nachrichtenverschlüsselung. + * + * + * Abweichende Belegung für PIN/TAN Verfahren (Dokument Sicherheitsverfahren PIN/TAN, B.9.9 DEG „Verschlüsselungsalgorithmus“, S. 59): + * + * Wert des Algorithmusparameters, Schlüssel + * FinTS-Füllwert, z.B. X’00 00 00 00 00 00 00 00’ + * + * Bezeichner für Algorithmusparameter, Schlüssel + * FinTS-Füllwert, z.B. „5“ + * + * Wert des Algorithmusparameters, IV + * Belegung nicht zulässig. + */ +open class VerschluesselungsalgorithmusDatenelementgruppe( + mode: Operationsmodus + +) : Datenelementgruppe(listOf( + VerwendungDesVerschluesselungsalgorithmusKodiert(), // allowed: 2 + OperationsmodusKodiert(mode), // allowed: 2, 18, 19 + VerschluesselungsalgorithmusKodiert(VerschluesselungsalgorithmusKodiert.FinTsMock), // allowed: 13, 14 + WertDesAlgorithmusparametersSchluessel(WertDesAlgorithmusparametersSchluessel.FinTsMock), + BezeichnerFuerAlgorithmusparameterSchluesselDatenelement(BezeichnerFuerAlgorithmusparameterSchluesselDatenelement.FinTsMock), // allowed: 6 + // even though spec says for PIN/TAN no value may be set here ("Belegung nicht zulässig"), this value has to be set: + BezeichnerFuerAlgorithmusparameterIVDatenelement(BezeichnerFuerAlgorithmusparameterIV.InitializationValue_ClearText, Existenzstatus.Mandatory) +), Existenzstatus.Mandatory) \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/signatur/SicherheitsdatumUndUhrzeit.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/signatur/SicherheitsdatumUndUhrzeit.kt index 0b83cbb6..152b6fbd 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/signatur/SicherheitsdatumUndUhrzeit.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelementgruppen/implementierte/signatur/SicherheitsdatumUndUhrzeit.kt @@ -10,7 +10,7 @@ import net.dankito.fints.messages.datenelementgruppen.Datenelementgruppe open class SicherheitsdatumUndUhrzeit(date: Int, time: Int) : Datenelementgruppe(listOf( - DatumUndZeitbezeichnerKodiert(DatumUndZeitbezeichner.Sicherheitszeitstempel), // Als Bezeichner wird „1“ eingestellt, da es sich um einen Sicherheitszeitstempel handelt. - Datum(date, Existenzstatus.Optional), + DatumUndZeitbezeichnerKodiert(DatumUndZeitbezeichner.Sicherheitszeitstempel), // Als Bezeichner wird „1“ eingestellt, da es sich um einen Sicherheitszeitstempel handelt. + Datum(date, Existenzstatus.Optional), Uhrzeit(time, Existenzstatus.Optional) ), Existenzstatus.Mandatory) \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/nachrichten/implementierte/Dialoginitialisierung.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/nachrichten/implementierte/Dialoginitialisierung.kt index 68761914..0253ca19 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/nachrichten/implementierte/Dialoginitialisierung.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/nachrichten/implementierte/Dialoginitialisierung.kt @@ -27,4 +27,17 @@ open class Dialoginitialisierung( IdentifikationsSegment(2, bankCountryCode, bankCode, customerId, customerSystemId, KundensystemStatusWerte.NichtBenoetigt), // TODO: KundensystemStatusWerte Verarbeitungsvorbereitung(3, bpdVersion, updVersion, language, productName, productVersion), Nachrichtenabschluss(4, FirstMessageNumber) -)) \ No newline at end of file +)) { + + /** + * Zur Einleitung des Prozesses der Gewährleistung einer starken Kun- + denauthentifizierung gemäß [PSD2] muss bei TAN-Verfahren ein HKTAN- + Segment ab Segmentversion #6 eingestellt werden, wenn ein Kreditinstitut + die Verwendung von HKTAN #6 unterstützt (BPD). Wenn HKTAN  #6 + nicht gesendet wird, kann der Dialog vom Institut mit dem Rückmeldungs- + code 9075 – Dialog abgebrochen - Starke Authentifizierung + erforderlich abgewiesen werden. + + */ + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanSignaturkopf.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanSignaturkopf.kt index 5fb9dabd..299b9ab9 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanSignaturkopf.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanSignaturkopf.kt @@ -3,7 +3,7 @@ package net.dankito.fints.messages.segmente.implementierte import net.dankito.fints.messages.datenelemente.implementierte.signatur.* -class PinTanSignaturkopf( +open class PinTanSignaturkopf( segmentNumber: Int, securityFunction: Sicherheitsfunktion, securityControlReference: String, diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanVerschluesselungskopf.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanVerschluesselungskopf.kt new file mode 100644 index 00000000..83cacb23 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/PinTanVerschluesselungskopf.kt @@ -0,0 +1,29 @@ +package net.dankito.fints.messages.segmente.implementierte + +import net.dankito.fints.messages.datenelemente.implementierte.encryption.Komprimierungsfunktion +import net.dankito.fints.messages.datenelemente.implementierte.signatur.* + + +open class PinTanVerschluesselungskopf( + partyIdentification: String, + date: Int, + time: Int, + bankCountryCode: Int, + bankCode: String, + userIdentification: String + +) : Verschluesselungskopf( + Sicherheitsverfahren.PIN_TAN_Verfahren, + VersionDesSicherheitsverfahrens.PIN_Zwei_Schritt, + partyIdentification, + date, + time, + OperationsmodusKodiert.FinTsMockValue, + bankCountryCode, + bankCode, + userIdentification, + Schluesselart.Chiffrierschluessel, + Schluesselnummer.FinTsMockValue, + Schluesselversion.FinTsMockValue, + Komprimierungsfunktion.Keine_Kompression +) \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Signaturkopf.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Signaturkopf.kt index 87a103e4..599685ba 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Signaturkopf.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Signaturkopf.kt @@ -48,6 +48,7 @@ open class Signaturkopf( BereichDerSicherheitsapplikationKodiert(BereichDerSicherheitsapplikation.SignaturkopfUndHBCINutzdaten), // allowed: 1 ? RolleDesSicherheitslieferantenKodiert(), // allowed: 1 SicherheitsidentifikationDetails(partyIdentification), + // "Bei softwarebasierten Verfahren wird die Sicherheitsreferenznummer auf Basis des DE Kundensystem-ID und des DE Benutzerkennung der DEG Schlüsselnamen verwaltet. Sicherheitsreferenznummer(1), // TODO: is this always 1? SicherheitsdatumUndUhrzeit(date, time), HashalgorithmusDatenelementgruppe(), diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/VerschluesselteDaten.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/VerschluesselteDaten.kt new file mode 100644 index 00000000..0d5a2c22 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/VerschluesselteDaten.kt @@ -0,0 +1,18 @@ +package net.dankito.fints.messages.segmente.implementierte + +import net.dankito.fints.messages.Existenzstatus +import net.dankito.fints.messages.datenelemente.implementierte.encryption.PinTanVerschluesselteDatenDatenelement +import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf +import net.dankito.fints.messages.segmente.Segment + + +/** + * Dieses Segment enthält die verschlüsselten (und komprimierten) Daten. + */ +open class VerschluesselteDaten( + payload: String + +) : Segment(listOf( + Segmentkopf("HNVSD", 1, 999), + PinTanVerschluesselteDatenDatenelement(payload) +), Existenzstatus.Mandatory) \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Verschluesselungskopf.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Verschluesselungskopf.kt new file mode 100644 index 00000000..6c45d5b0 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/segmente/implementierte/Verschluesselungskopf.kt @@ -0,0 +1,60 @@ +package net.dankito.fints.messages.segmente.implementierte + +import net.dankito.fints.messages.Existenzstatus +import net.dankito.fints.messages.datenelemente.implementierte.NotAllowedDatenelement +import net.dankito.fints.messages.datenelemente.implementierte.encryption.Komprimierungsfunktion +import net.dankito.fints.messages.datenelemente.implementierte.encryption.KomprimierungsfunktionDatenelement +import net.dankito.fints.messages.datenelemente.implementierte.signatur.* +import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf +import net.dankito.fints.messages.datenelementgruppen.implementierte.encryption.VerschluesselungsalgorithmusDatenelementgruppe +import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.Schluesselname +import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.SicherheitsdatumUndUhrzeit +import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.SicherheitsidentifikationDetails +import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.Sicherheitsprofil +import net.dankito.fints.messages.segmente.Segment + + +/** + * Der Verschlüsselungskopf enthält Informationen über die Art des Sicherheitsservice, die + * Verschlüsselungsfunktion und die zu verwendenden Chiffrierschlüssel. + * + * Zum Abgleich mit dem in den BPD definierten RAH-Verschlüsselungsverfahren wird das Feld + * „Bezeichner für Algorithmusparameter, Schlüssel“ in der DEG „Verschlüsselungsalgorithmus“ herangezogen. + * + * + * Abweichende Belegung für PIN/TAN Verfahren (Dokument Sicherheitsverfahren PIN/TAN, B.9.8 Segment „Verschlüsselungskopf“, S. 59) + * + * Sicherheitsfunktion, kodiert + * Es wird der Wert „998“ (Klartext) verwendet. + * + * Zertifikat + * Dieses Feld darf nicht belegt werden. + */ +open class Verschluesselungskopf( + method: Sicherheitsverfahren, + version: VersionDesSicherheitsverfahrens, + partyIdentification: String, + date: Int, + time: Int, + mode: Operationsmodus, + bankCountryCode: Int, + bankCode: String, + userIdentification: String, + key: Schluesselart, + keyNumber: Int, + keyVersion: Int, + algorithm: Komprimierungsfunktion + +) : Segment(listOf( + Segmentkopf("HNVSK", 3, 998), + Sicherheitsprofil(method, version), + SicherheitsfunktionKodiert(Sicherheitsfunktion.Klartext), // allowed: 4 + RolleDesSicherheitslieferantenKodiert(), // allowed: 1, 4 + SicherheitsidentifikationDetails(partyIdentification), + SicherheitsdatumUndUhrzeit(date, time), + VerschluesselungsalgorithmusDatenelementgruppe(mode), + Schluesselname(bankCountryCode, bankCode, userIdentification, key, keyNumber, keyVersion), + KomprimierungsfunktionDatenelement(algorithm), + // Certificate not applicapable for PIN/TAN; it should be also fine to write nothing at all and therefore leave NotAllowedDatenelement away + NotAllowedDatenelement() // Zertifikat is actually a Datenelementgruppe, not a Datenelement +), Existenzstatus.Mandatory) \ No newline at end of file diff --git a/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/MessageBuilderTest.kt b/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/MessageBuilderTest.kt index 6e9a6176..2fbe0895 100644 --- a/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/MessageBuilderTest.kt +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/MessageBuilderTest.kt @@ -1,13 +1,49 @@ package net.dankito.fints.messages +import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache +import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID +import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte import net.dankito.fints.messages.datenelemente.implementierte.Laenderkennzeichen +import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion +import net.dankito.fints.util.FinTsUtils import org.assertj.core.api.Assertions.assertThat import org.junit.Test +import java.util.* class MessageBuilderTest { - private val underTest = MessageBuilder() + companion object { + const val BankCode = "12345678" + + const val CustomerId = "0987654321" + + const val Pin = "12345" + + const val Date = 19880327 + + const val Time = 182752 + + val Language = Dialogsprache.German + + val SecurityFunction = Sicherheitsfunktion.PIN_TAN_911 + + const val ControlReference = "1" + + const val ProductName = "FinTS-TestClient25Stellen" + + const val ProductVersion = "1" + } + + private val underTest = MessageBuilder(utils = object : FinTsUtils() { + override fun formatDate(date: Date): String { + return Date.toString() + } + + override fun formatTime(time: Date): String { + return Time.toString() + } + }) @Test @@ -15,7 +51,7 @@ class MessageBuilderTest { // given val underTest = underTest.createAnonymousDialogInitMessage( - Laenderkennzeichen.Germany, "12345678", "FinTS-TestClient25Stellen", "1") + Laenderkennzeichen.Germany, BankCode, ProductName, ProductVersion) // when val result = underTest.format() @@ -24,9 +60,36 @@ class MessageBuilderTest { assertThat(result).isEqualTo( "HNHBK:1:3+000000000125+300+0+1'" + "HKIDN:2:2+280:12345678+9999999999+0+0'" + - "HKVVB:3:3+0+0+0+FinTS-TestClient25Stellen+1'" + + "HKVVB:3:3+0+0+0+$ProductName+$ProductVersion'" + "HNHBS:4:1+1'" ) } + @Test + fun createDialogInitMessage() { + + // given + val underTest = underTest.createDialogInitMessage(Laenderkennzeichen.Germany, BankCode, CustomerId, + KundensystemID.PinTan, KundensystemStatusWerte.Benoetigt, 0, 0, Language, + ProductName, ProductVersion) + + // when + val result = underTest.format() + + // then + assertThat(normalizeBinaryData(result)).isEqualTo(normalizeBinaryData( + "HNHBK:1:3+000000000363+300+0+1'" + + "HNVSK:998:3+PIN:2+998+1+1::0+1:$Date:$Time+2:16:14:@8@ :5:1+280:$BankCode:$CustomerId:V:0:0+0+'" + + "HNVSD:999:1+@198@" + "HNSHK:2:4+PIN:2+${SecurityFunction.code}+$ControlReference+1+1+1::0+1+1:$Date:$Time+1:999:1+6:10:16+280:$BankCode:$CustomerId:S:0:0'" + + "HKIDN:3:2+280:$BankCode+$CustomerId+0+1'" + + "HKVVB:4:3+0+0+${Language.code}+$ProductName+$ProductVersion'" + + "HNSHA:5:2+$ControlReference++$Pin''" + + "HNHBS:6:1+1'" + )) + } + + protected open fun normalizeBinaryData(message: String): String { + return message.replace(0.toChar(), ' ') + } + } \ No newline at end of file diff --git a/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/VerschluesselungskopfTest.kt b/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/VerschluesselungskopfTest.kt new file mode 100644 index 00000000..1df6486d --- /dev/null +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/segmente/implementierte/VerschluesselungskopfTest.kt @@ -0,0 +1,30 @@ +package net.dankito.fints.messages.segmente.implementierte + +import net.dankito.fints.messages.datenelemente.implementierte.Laenderkennzeichen +import net.dankito.fints.messages.datenelemente.implementierte.signatur.IdentifizierungDerPartei +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test + +class VerschluesselungskopfTest { + + @Test + fun format() { + + // given + val partyIdentification = IdentifizierungDerPartei.SynchronizingCustomerSystemId + val date = 20191002 + val time = 212757 + val bankCode = "12345678" + val customerId = "0987654321" + + val underTest = PinTanVerschluesselungskopf(partyIdentification, date, time, + Laenderkennzeichen.Germany, bankCode, customerId) + + // when + val result = underTest.format() + + // then + assertThat(result).isEqualTo("HNVSK:998:3+PIN:2+998+1+1::0+1:$date:$time+2:2:13:@8@ :5:1+280:$bankCode:$customerId:V:0:0+0") + } + +} \ No newline at end of file