Implemented 'encrypting' message
This commit is contained in:
parent
bed585fc04
commit
b9733189e6
|
@ -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<Segment>): 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<Segment>): List<Segment> {
|
||||
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<Segment>): List<Segment> {
|
||||
|
||||
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<Segment>): String {
|
||||
return payload.joinToString(Nachricht.SegmentSeparator) { it.format() }
|
||||
}
|
||||
|
||||
}
|
|
@ -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.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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")
|
||||
|
||||
}
|
|
@ -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<BezeichnerFuerAlgorithmusparameterIV>()
|
||||
}
|
||||
|
||||
}
|
|
@ -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")
|
||||
|
||||
}
|
|
@ -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<BezeichnerFuerAlgorithmusparameterSchluessel>()
|
||||
|
||||
val FinTsMock = BezeichnerFuerAlgorithmusparameterSchluessel.SymmetrischerSchluessel
|
||||
}
|
||||
|
||||
}
|
|
@ -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")
|
||||
|
||||
}
|
|
@ -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<Komprimierungsfunktion>()
|
||||
}
|
||||
|
||||
}
|
|
@ -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 '@<payload_length>@', 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
|
||||
}
|
||||
|
||||
}
|
|
@ -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")
|
||||
|
||||
}
|
|
@ -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<Verschluesselungsalgorithmus>()
|
||||
|
||||
val FinTsMock = Verschluesselungsalgorithmus.AES_256
|
||||
}
|
||||
|
||||
}
|
|
@ -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")
|
||||
|
||||
}
|
|
@ -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<VerwendungDesVerschluesselungsalgorithmus>()
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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")
|
||||
|
||||
}
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
||||
))
|
||||
)) {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
*/
|
||||
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -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(),
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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(), ' ')
|
||||
}
|
||||
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue