Implemented signing messages (but cannot tell yet if it's working)

This commit is contained in:
dankl 2019-10-04 17:00:54 +02:00 committed by dankito
parent a6a7930c29
commit bed585fc04
73 changed files with 1496 additions and 58 deletions

View File

@ -1,12 +1,17 @@
package net.dankito.fints package net.dankito.fints
import net.dankito.fints.messages.MessageBuilder import net.dankito.fints.messages.MessageBuilder
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.model.AccountCredentials
import net.dankito.fints.model.BankInfo import net.dankito.fints.model.BankInfo
import net.dankito.fints.model.ProductInfo import net.dankito.fints.model.ProductInfo
import net.dankito.fints.util.IBase64Service import net.dankito.fints.util.IBase64Service
import net.dankito.utils.web.client.IWebClient import net.dankito.utils.web.client.IWebClient
import net.dankito.utils.web.client.OkHttpWebClient import net.dankito.utils.web.client.OkHttpWebClient
import net.dankito.utils.web.client.RequestParameters import net.dankito.utils.web.client.RequestParameters
import net.dankito.utils.web.client.WebClientResponse
open class FinTsClient( open class FinTsClient(
@ -16,15 +21,37 @@ open class FinTsClient(
) { ) {
fun getBankInfo(bankInfo: BankInfo, productInfo: ProductInfo) { fun getAnonymousBankInfo(bankInfo: BankInfo, productInfo: ProductInfo) {
val requestBody = messageBuilder.createAnonymousDialogInitMessage(bankInfo.countryCode, bankInfo.bankCode, val requestBody = messageBuilder.createAnonymousDialogInitMessage(bankInfo.countryCode, bankInfo.bankCode,
productInfo.productName, productInfo.productVersion) productInfo.productName, productInfo.productVersion)
val response = getResponseForMessage(requestBody, bankInfo)
handleResponse(response)
}
fun getBankInfo(credentials: AccountCredentials, bankInfo: BankInfo, productInfo: ProductInfo) {
val requestBody = messageBuilder.createDialogInitMessage(bankInfo.countryCode, bankInfo.bankCode,
credentials.customerId, KundensystemID.PinTan, KundensystemStatusWerte.Benoetigt, 0, 0, Dialogsprache.German,
productInfo.productName, productInfo.productVersion)
val response = getResponseForMessage(requestBody, bankInfo)
handleResponse(response)
}
protected open fun getResponseForMessage(requestBody: String, bankInfo: BankInfo): WebClientResponse {
val encodedRequestBody = base64Service.encode(requestBody) val encodedRequestBody = base64Service.encode(requestBody)
val response = webClient.post(RequestParameters(bankInfo.finTsServerAddress, encodedRequestBody, "application/octet-stream")) return webClient.post(
RequestParameters(bankInfo.finTsServerAddress, encodedRequestBody, "application/octet-stream")
)
}
protected open fun handleResponse(response: WebClientResponse) {
val responseBody = response.body val responseBody = response.body
if (response.isSuccessful && responseBody != null) { if (response.isSuccessful && responseBody != null) {
val decodedResponse = decodeBase64Response(responseBody) val decodedResponse = decodeBase64Response(responseBody)

View File

@ -18,7 +18,7 @@ package net.dankito.fints.messages
* nicht den Richtlinien entspricht, so ist dieser abzuweisen. Eine kreditinstitutsseitige Korrektur * nicht den Richtlinien entspricht, so ist dieser abzuweisen. Eine kreditinstitutsseitige Korrektur
* der Auftragsdaten erfolgt nicht. * der Auftragsdaten erfolgt nicht.
*/ */
class HbciCharset { open class HbciCharset {
companion object { companion object {
val DefaultCharset = Charsets.ISO_8859_1 val DefaultCharset = Charsets.ISO_8859_1

View File

@ -1,21 +1,21 @@
package net.dankito.fints.messages package net.dankito.fints.messages
import net.dankito.fints.messages.datenelemente.implementierte.* import net.dankito.fints.messages.datenelemente.implementierte.*
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
import net.dankito.fints.messages.nachrichten.Nachricht import net.dankito.fints.messages.nachrichten.Nachricht
import net.dankito.fints.messages.segmente.ISegmentNumberGenerator import net.dankito.fints.messages.segmente.ISegmentNumberGenerator
import net.dankito.fints.messages.segmente.Segment import net.dankito.fints.messages.segmente.Segment
import net.dankito.fints.messages.segmente.SegmentNumberGenerator import net.dankito.fints.messages.segmente.SegmentNumberGenerator
import net.dankito.fints.messages.segmente.implementierte.IdentifikationsSegment import net.dankito.fints.messages.segmente.implementierte.*
import net.dankito.fints.messages.segmente.implementierte.Nachrichtenabschluss import net.dankito.fints.util.FinTsUtils
import net.dankito.fints.messages.segmente.implementierte.Nachrichtenkopf
import net.dankito.fints.messages.segmente.implementierte.Verarbeitungsvorbereitung
/** /**
* Takes the Segments of they payload, may signs and encrypts them, calculates message size, * Takes the Segments of they payload, may signs and encrypts them, calculates message size,
* adds the message header and closing, and formats the whole message to string. * adds the message header and closing, and formats the whole message to string.
*/ */
open class MessageBuilder(protected val generator: ISegmentNumberGenerator = SegmentNumberGenerator()) { open class MessageBuilder(protected val generator: ISegmentNumberGenerator = SegmentNumberGenerator(),
protected val utils: FinTsUtils = FinTsUtils()) {
companion object { companion object {
const val MessageHeaderLength = 30 const val MessageHeaderLength = 30
@ -39,8 +39,8 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
productVersion: String productVersion: String
): String { ): String {
return createDialogInitMessage(bankCountryCode, bankCode, KundenID.Anonymous, KundensystemID.Anonymous, return createDialogInitMessage(bankCountryCode, bankCode, KundenID.Anonymous, KundensystemID.Anonymous, KundensystemStatusWerte.NichtBenoetigt,
BPDVersion.VersionNotReceivedYet, UPDVersion.VersionNotReceivedYet, Dialogsprache.Default, productName, productVersion) BPDVersion.VersionNotReceivedYet, UPDVersion.VersionNotReceivedYet, Dialogsprache.Default, productName, productVersion, false)
} }
open fun createDialogInitMessage( open fun createDialogInitMessage(
@ -48,23 +48,29 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
bankCode: String, bankCode: String,
customerId: String, customerId: String,
customerSystemId: String, customerSystemId: String,
status: KundensystemStatusWerte,
bpdVersion: Int, bpdVersion: Int,
updVersion: Int, updVersion: Int,
language: Dialogsprache, language: Dialogsprache,
productName: String, productName: String,
productVersion: String productVersion: String,
signMessage: Boolean = true
): String { ): String {
return createMessage(listOf( return createMessage(signMessage, bankCountryCode, bankCode, customerId, listOf(
IdentifikationsSegment(generator.resetSegmentNumber(1), bankCountryCode, bankCode, customerId, customerSystemId), IdentifikationsSegment(generator.resetSegmentNumber(if (signMessage) 2 else 1), bankCountryCode, bankCode, customerId, customerSystemId, status),
Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), bpdVersion, updVersion, language, productName, productVersion) Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), bpdVersion, updVersion, language, productName, productVersion)
)) ))
} }
open fun createMessage(payloadSegments: List<Segment>): String { open fun createMessage(signMessage: Boolean, bankCountryCode: Int, bankCode: String, customerId: String,
payloadSegments: List<Segment>): String {
val payload = payloadSegments.joinToString(Nachricht.SegmentSeparator) { it.format() } val signedPayload = if (signMessage) { signPayload(2, bankCountryCode, bankCode, customerId, payloadSegments) }
else { payloadSegments }
val payload = signedPayload.joinToString(Nachricht.SegmentSeparator) { it.format() }
val messageSize = payload.length + MessageHeaderLength + MessageClosingLength + AddedSeparatorsLength val messageSize = payload.length + MessageHeaderLength + MessageClosingLength + AddedSeparatorsLength
val messageNumber = Nachrichtennummer.FirstMessageNumber val messageNumber = Nachrichtennummer.FirstMessageNumber
@ -77,4 +83,29 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
.joinToString(Nachricht.SegmentSeparator, postfix = Nachricht.SegmentSeparator) .joinToString(Nachricht.SegmentSeparator, postfix = Nachricht.SegmentSeparator)
} }
protected open fun signPayload(headerSegmentNumber: Int, bankCountryCode: Int, bankCode: String, customerId: String,
payloadSegments: List<Segment>): List<Segment> {
val controlReference = "1" // TODO
val signatureHeader = PinTanSignaturkopf(
headerSegmentNumber,
Sicherheitsfunktion.PIN_TAN_911, // TODO
controlReference,
"0",
utils.formatDateTodayAsInt(),
utils.formatTimeNowAsInt(),
bankCountryCode,
bankCode,
customerId
)
val signatureClosing = Signaturabschluss(
generator.getNextSegmentNumber(),
controlReference,
"" // TODO
)
return listOf(signatureHeader, *payloadSegments.toTypedArray(), signatureClosing)
}
} }

View File

@ -0,0 +1,18 @@
package net.dankito.fints.messages.datenelemente.abgeleiteteformate
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.NumerischesDatenelement
/**
* Format: JJJJMMTT gemäß ISO 8601
*
* Erlaubt sind alle existenten Datumsangaben.
*/
open class Datum(date: Int, existenzstatus: Existenzstatus) : NumerischesDatenelement(date, 8, existenzstatus) {
companion object {
const val HbciDateFormat = "yyyyMMdd"
}
}

View File

@ -9,5 +9,5 @@ import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDat
* *
* Maximal 30 Zeichen * Maximal 30 Zeichen
*/ */
abstract class Identifikation(identifikation: String, existenzstatus: Existenzstatus) abstract class Identifikation(identification: String, existenzstatus: Existenzstatus)
: AlphanumerischesDatenelement(identifikation, existenzstatus, 30) : AlphanumerischesDatenelement(identification, existenzstatus, 30)

View File

@ -0,0 +1,19 @@
package net.dankito.fints.messages.datenelemente.abgeleiteteformate
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.ZiffernDatenelement
/**
* Format: hhmmss gemäß ISO 8601
*
* Gültige Uhrzeit. Es ist immer Ortszeit des sendenden Systems einzustellen.
* Unterschiedliche Zeitzonen werden nicht unterstützt
*/
open class Uhrzeit(time: Int, existenzstatus: Existenzstatus) : ZiffernDatenelement(time, 6, existenzstatus) {
companion object {
const val HbciTimeFormat = "HHmmss"
}
}

View File

@ -15,7 +15,7 @@ import net.dankito.fints.messages.datenelemente.basisformate.NumerischesDatenele
* die im Nachrichtenkopf eingestellt ist und lediglich das syntaktische Format der * die im Nachrichtenkopf eingestellt ist und lediglich das syntaktische Format der
* Nachricht, nicht jedoch deren Inhalt kennzeichnet. * Nachricht, nicht jedoch deren Inhalt kennzeichnet.
*/ */
class BPDVersion(version: Int, existenzstatus: Existenzstatus) : NumerischesDatenelement(version, 3, existenzstatus) { open class BPDVersion(version: Int, existenzstatus: Existenzstatus) : NumerischesDatenelement(version, 3, existenzstatus) {
companion object { companion object {
const val VersionNotReceivedYet = 0 const val VersionNotReceivedYet = 0

View File

@ -0,0 +1,14 @@
package net.dankito.fints.messages.datenelemente.implementierte
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Identifikation
/**
* Eindeutig vergebene Kennung, anhand deren die Identifizierung des Benutzers erfolgt.
* Die Vergabe obliegt dem Kreditinstitut. Das Kreditinstitut hat zu gewährleisten,
* dass die Benutzerkennung institutsweit eindeutig ist. Sie kann beliebige Informationen
* enthalten, darf aber bei Verwendung des RAH-Verfahrens aus Sicherheitsgründen nicht aus
* benutzer- oder kreditinstitutsspezifischen Merkmalen hergeleitet werden.
*/
open class Benutzerkennung(identification: String) : Identifikation(identification, Existenzstatus.Mandatory)

View File

@ -4,4 +4,4 @@ import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Identifikation import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Identifikation
class DialogId(dialogId: String) : Identifikation(dialogId, Existenzstatus.Mandatory) open class DialogId(dialogId: String) : Identifikation(dialogId, Existenzstatus.Mandatory)

View File

@ -1,7 +1,7 @@
package net.dankito.fints.messages.datenelemente.implementierte package net.dankito.fints.messages.datenelemente.implementierte
enum class Dialogsprache(val code: String) { enum class Dialogsprache(override val code: String) : ICodeEnum {
Default("0"), Default("0"),

View File

@ -20,16 +20,11 @@ import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code
* - 2: Englisch, Code en (English), Subset Englisch, Codeset 1 (Latin 1) * - 2: Englisch, Code en (English), Subset Englisch, Codeset 1 (Latin 1)
* - 3: Französisch, Code fr (French), Subset Französisch, Codeset 1 (Latin 1) * - 3: Französisch, Code fr (French), Subset Französisch, Codeset 1 (Latin 1)
*/ */
class DialogspracheDatenelement(language: Dialogsprache, existenzstatus: Existenzstatus) open class DialogspracheDatenelement(language: Dialogsprache, existenzstatus: Existenzstatus)
: Code(language.code, AllowedValues, existenzstatus) { : Code(language.code, AllowedValues, existenzstatus) {
companion object { companion object {
val AllowedValues = listOf( val AllowedValues = allCodes<Dialogsprache>()
Dialogsprache.Default.code,
Dialogsprache.German.code,
Dialogsprache.English.code,
Dialogsprache.French.code
)
} }
} }

View File

@ -1,6 +0,0 @@
package net.dankito.fints.messages.datenelemente.implementierte
import net.dankito.fints.messages.Existenzstatus
open class FinTsKundensystemStatus : KundensystemStatus(KundensystemStatusWerte.NichtBenoetigt, Existenzstatus.Mandatory)

View File

@ -31,4 +31,4 @@ import net.dankito.fints.messages.datenelemente.basisformate.NumerischesDatenele
* - Version 2.2: 220 (Spezifikationsstatus: obsolet) * - Version 2.2: 220 (Spezifikationsstatus: obsolet)
* - Version 3.0: 300 * - Version 3.0: 300
*/ */
class HbciVersionDatenelement(version: HbciVersion) : NumerischesDatenelement(version.versionNumber, 3, Existenzstatus.Mandatory) open class HbciVersionDatenelement(version: HbciVersion) : NumerischesDatenelement(version.versionNumber, 3, Existenzstatus.Mandatory)

View File

@ -0,0 +1,12 @@
package net.dankito.fints.messages.datenelemente.implementierte
inline fun <reified T : Enum<T>> allCodes(): List<String> {
return enumValues<T>().map { (it as ICodeEnum).code }
}
interface ICodeEnum {
val code: String
}

View File

@ -21,6 +21,8 @@ open class KundensystemID(customerSystemId: String) : Identifikation(customerSys
companion object { companion object {
const val Anonymous = "0" const val Anonymous = "0"
const val PinTan = Anonymous
} }
} }

View File

@ -17,12 +17,7 @@ open class KundensystemStatus(status: KundensystemStatusWerte, existenzstatus: E
: Code(status.code, AllowedValues, existenzstatus) { : Code(status.code, AllowedValues, existenzstatus) {
companion object { companion object {
val AllowedValues = listOf( val AllowedValues = allCodes<KundensystemStatusWerte>()
KundensystemStatusWerte.NichtBenoetigt.code,
KundensystemStatusWerte.Benoetigt.code
)
val Anonymous = KundensystemStatusWerte.NichtBenoetigt
} }
} }

View File

@ -1,7 +1,7 @@
package net.dankito.fints.messages.datenelemente.implementierte package net.dankito.fints.messages.datenelemente.implementierte
enum class KundensystemStatusWerte(val code: String) { enum class KundensystemStatusWerte(override val code: String) : ICodeEnum {
NichtBenoetigt("0"), NichtBenoetigt("0"),

View File

@ -9,4 +9,4 @@ import net.dankito.fints.messages.datenelemente.basisformate.ZiffernDatenelement
* Das DE ist mit führenden Nullen auf die vorgegebene feste Länge aufzufüllen. * Das DE ist mit führenden Nullen auf die vorgegebene feste Länge aufzufüllen.
* Dies ist erforderlich, damit die Nachrichtenlänge nicht mit der Länge des DE variiert. * Dies ist erforderlich, damit die Nachrichtenlänge nicht mit der Länge des DE variiert.
*/ */
class Nachrichtengroesse(messageSize: Int) : ZiffernDatenelement(messageSize, 12, Existenzstatus.Mandatory) open class Nachrichtengroesse(messageSize: Int) : ZiffernDatenelement(messageSize, 12, Existenzstatus.Mandatory)

View File

@ -17,7 +17,7 @@ import net.dankito.fints.messages.datenelemente.basisformate.NumerischesDatenele
* Nachrichten, deren Nummerierung nicht streng monoton aufsteigend erfolgt ist, werden * Nachrichten, deren Nummerierung nicht streng monoton aufsteigend erfolgt ist, werden
* institutsseitig bzw. kundenseitig abgelehnt. * institutsseitig bzw. kundenseitig abgelehnt.
*/ */
class Nachrichtennummer(number: Int) : NumerischesDatenelement(number, 4, Existenzstatus.Mandatory) { open class Nachrichtennummer(number: Int) : NumerischesDatenelement(number, 4, Existenzstatus.Mandatory) {
companion object { companion object {
const val FirstMessageNumber = 1 const val FirstMessageNumber = 1

View File

@ -0,0 +1,7 @@
package net.dankito.fints.messages.datenelemente.implementierte
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.TextDatenelement
open class NotAllowedDatenelement : TextDatenelement("", Existenzstatus.NotAllowed)

View File

@ -15,5 +15,5 @@ import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDat
* Kundenprodukte, die nach dem durch die Deutsche Kreditwirtschaft festgelegten Verfahren registriert * Kundenprodukte, die nach dem durch die Deutsche Kreditwirtschaft festgelegten Verfahren registriert
* sind, müssen in dieses DE die vergebene Produktregistrierungsnummer einstellen. * sind, müssen in dieses DE die vergebene Produktregistrierungsnummer einstellen.
*/ */
class Produktbezeichnung(name: String, existenzstatus: Existenzstatus) open class Produktbezeichnung(name: String, existenzstatus: Existenzstatus)
: AlphanumerischesDatenelement(name, existenzstatus, 25) : AlphanumerischesDatenelement(name, existenzstatus, 25)

View File

@ -11,5 +11,5 @@ import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDat
* Kundenprodukt, nicht eine ggf. verwendete interne FinTS-/HBCI-Bibliothek, zu füllen, um * Kundenprodukt, nicht eine ggf. verwendete interne FinTS-/HBCI-Bibliothek, zu füllen, um
* Support-Anfragen leichter beantworten zu können. * Support-Anfragen leichter beantworten zu können.
*/ */
class Produktversion(version: String, existenzstatus: Existenzstatus) open class Produktversion(version: String, existenzstatus: Existenzstatus)
: AlphanumerischesDatenelement(version, existenzstatus, 5) : AlphanumerischesDatenelement(version, existenzstatus, 5)

View File

@ -8,7 +8,7 @@ import net.dankito.fints.messages.datenelemente.basisformate.NumerischesDatenele
* Versionsnummer der Userparameterdaten (UPD). Bei jeder kreditinstitutsseitigen Änderung * Versionsnummer der Userparameterdaten (UPD). Bei jeder kreditinstitutsseitigen Änderung
* wird die Version inkrementiert. (S. auch DE BPD-Version). * wird die Version inkrementiert. (S. auch DE BPD-Version).
*/ */
class UPDVersion(version: Int, existenzstatus: Existenzstatus) : NumerischesDatenelement(version, 3, existenzstatus) { open class UPDVersion(version: Int, existenzstatus: Existenzstatus) : NumerischesDatenelement(version, 3, existenzstatus) {
companion object { companion object {
const val VersionNotReceivedYet = 0 const val VersionNotReceivedYet = 0

View File

@ -0,0 +1,12 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum
enum class BereichDerSicherheitsapplikation(val abbreviation: String, override val code: String) : ICodeEnum {
SignaturkopfUndHBCINutzdaten("SHM", "1"),
VonSignaturkopfBisSignaturabschluss("SHT", "2")
}

View File

@ -0,0 +1,31 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code
import net.dankito.fints.messages.datenelemente.implementierte.allCodes
/**
* Information darüber, welche Daten vom kryptographischen Prozess verarbeitet werden.
* Diese Information wird benötigt um z. B. zwischen relevanter und belangloser Reihenfolge
* von Signaturen zu unterscheiden (vgl. [HBCI], Kapitel VI.4).
*
* Wenn SHM gewählt wird, so bedeutet dies, dass nur über den eigenen Signaturkopf sowie die
* HBCI-Nutzdaten ein Hashwert gebildet wird, der in die Signatur eingeht. Dies entspricht
* bei Mehrfachsignaturen einer bedeutungslosen Reihenfolge.
*
* Wenn SHT gewählt wird, dann werden auch alle schon vorhandenen Signaturköpfe und
* -abschlüsse mitsigniert. Das heißt, dass die Reihenfolge der Signaturen relevant ist.
*
* Codierung:
* - 1: Signaturkopf und HBCI-Nutzdaten (SHM)
* - 2: Von Signaturkopf bis Signaturabschluss (SHT)
*/
open class BereichDerSicherheitsapplikationKodiert(domain: BereichDerSicherheitsapplikation)
: Code(domain.code, AllowedValues, Existenzstatus.Mandatory) {
companion object {
val AllowedValues = allCodes<BereichDerSicherheitsapplikation>()
}
}

View File

@ -0,0 +1,13 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
/**
* Bezeichner für den Hashalgorithmusparameter.
*
* Codierung:
* 1: IVC (Initialization value, clear text)
*/
open class BezeichnerFuerHashalgorithmusparameter : AlphanumerischesDatenelement("1", Existenzstatus.Mandatory, 3)

View File

@ -0,0 +1,14 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
/**
* Identifikation der Funktion der beschriebenen Partei, in diesem Falle des Kunden.
*
* Codierung:
* - 1: Message Sender (MS), wenn ein Kunde etwas an sein Kreditinstitut sendet.
* - 2: Message Receiver (MR), wenn das Kreditinstitut etwas an seinen Kunden sendet.
*/
open class BezeichnerFuerSicherheitspartei : AlphanumerischesDatenelement("1", Existenzstatus.Mandatory, 3)

View File

@ -0,0 +1,12 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum
enum class DatumUndZeitbezeichner(override val code: String) : ICodeEnum {
Sicherheitszeitstempel("1"),
CertificateRevocationTime("2")
}

View File

@ -0,0 +1,22 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code
import net.dankito.fints.messages.datenelemente.implementierte.allCodes
/**
* Enthält die Bedeutung des Zeitstempels.
*
* Codierung:
* 1: Sicherheitszeitstempel (STS)
* 6: Certificate Revocation Time (CRT)
*/
open class DatumUndZeitbezeichnerKodiert(identifier: DatumUndZeitbezeichner)
: Code(identifier.code, AllowedValues, Existenzstatus.Mandatory) {
companion object {
val AllowedValues = allCodes<DatumUndZeitbezeichner>()
}
}

View File

@ -0,0 +1,18 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum
enum class Hashalgorithmus(override val code: String) : ICodeEnum {
SHA_256("3"),
SHA_384("4"),
SHA_512("5"),
SHA_256_SHA_256("6"),
Gegenseitig_vereinbart("999")
}

View File

@ -0,0 +1,26 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code
import net.dankito.fints.messages.datenelemente.implementierte.allCodes
/**
* Code des verwendeten Hash-Algorithmus.
*
* Codierung:
* 1: SHA-1 (nicht zugelassen)
* 2: belegt
* 3: SHA-256
* 4: SHA-384
* 5: SHA-512
* 6: SHA-256 / SHA-256
* 999: Gegenseitig vereinbart (ZZZ); (nicht zugelassen)
*/
open class HashalgorithmusKodiert(algorithm: Hashalgorithmus) : Code(algorithm.code, AllowedValues, Existenzstatus.Mandatory) {
companion object {
val AllowedValues = allCodes<Hashalgorithmus>()
}
}

View File

@ -0,0 +1,17 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Identifikation
/**
* Code, welcher die (Kommunikations-)Partei identifiziert. Bei Verwendung des
* RAH-Verfahrens ist die Kundensystem-ID einzustellen.
*/
open class IdentifizierungDerPartei(identification: String) : Identifikation(identification, Existenzstatus.Optional) {
companion object {
const val SynchronizingCustomerSystemId = "0"
}
}

View File

@ -0,0 +1,43 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum
enum class Operationsmodus(override val code: String) : ICodeEnum {
/**
* Nur für Verschlüsselung erlaubt (vgl. [HBCI], Kapitel VI.2.2)
*/
Cipher_Block_Chaining("2"),
/**
* nicht zugelassen
*/
ISO_9796_1("16"),
/**
* nicht zugelassen
*/
ISO_9796_2_mit_Zufallszahl("17"),
/**
* nicht zugelassen
*/
RSASSA_PKCS_1_V1_5("18"),
/**
* Nur für Verschlüsselung erlaubt
*/
RSAES_PKCS_1_V1_5("18"),
/**
* Nur für Signatur erlaubt
*/
RSASSA_PSS("19"),
/**
* nicht zugelassen
*/
Gegenseitig_vereinbart("999")
}

View File

@ -0,0 +1,29 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code
import net.dankito.fints.messages.datenelemente.implementierte.allCodes
/**
* Information über den Operationsmodus für den jeweils verwendeten Kryptoalgorithmus
* (zur Signaturbildung oder zur Verschlüsselung).
*
* Codierung:
* Siehe S. 102 oder [Operationsmodus]
*
*
* Abweichende Belegung für PIN/TAN Verfahren (Dokument Sicherheitsverfahren PIN/TAN, B.9.6 DEG Signaturalgorithmus, S. 58):
*
* Operationsmodus, kodiert
* FinTS-Füllwert, z. B. 16
*/
open class OperationsmodusKodiert(mode: Operationsmodus) : Code(mode.code, AllowedValues, Existenzstatus.Mandatory) {
companion object {
val AllowedValues = allCodes<Operationsmodus>()
val FinTsMockValue = Operationsmodus.ISO_9796_1
}
}

View File

@ -0,0 +1,7 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
open class PinOrTan(pinOrTan: String) : AlphanumerischesDatenelement(pinOrTan, Existenzstatus.Mandatory)

View File

@ -0,0 +1,27 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
/**
* Der Inhalt dieses Feldes sollte derzeit nicht ausgewertet werden. Optional können aber
* die nachfolgenden Festlegungen angewendet werden, sofern dies zwischen Kunde und
* Kreditinstitut zuvor vereinbart wurde:
*
* 1. Dialoginitialisierung und -ende:
* Die Rolle wird durch den Dialogführenden bestimmt. Es ist nur eine Signatur erlaubt.
* Erlaubt ist nur der Wert ISS/wert12.
*
* 2. Auftragsnachricht:
* Grundsätzlich gilt: Sobald die Rolle WIT verwendet wird, muss dieser Benutzer mit der
* Benutzerkennung aus der Dialoginitialisierung arbeiten. Auch der Benutzer WIT muss
* bankseitig entsprechend der Auftragsart am Konto des Benutzers ISS berechtigt sein.
* Die Reihenfolge der Signaturen ist beliebig.
*
* [Tabelle mit Werten]
*
* Auch bei Belegung dieses Feldes kann das Kundenprodukt nicht davon ausgehen, dass das
* Feld kreditinstitutsseitig ausgewertet wird.
*/
open class RolleDesSicherheitslieferantenKodiert : AlphanumerischesDatenelement("1", Existenzstatus.Mandatory, 3)

View File

@ -0,0 +1,14 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum
enum class Schluesselart(override val code: String) : ICodeEnum {
SchluesselZurErzeugungDigitalerSignaturen("D"),
Signierschluessel("S"),
Chiffrierschluessel("V")
}

View File

@ -0,0 +1,33 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code
import net.dankito.fints.messages.datenelemente.implementierte.allCodes
/**
* Information über die Art des Schlüssels.
*
* Beim Sicherheitsverfahren RAH steht die Schlüsselart in engem Zusammenhang mit dem
* Datenelement "Verwendungszweck für öffentlichen Schlüssel". Die Inhalte beider
* Datenelemente sind konsistent zu halten.
*
* Codierung:
* D: Schlüssel zur Erzeugung digitaler Signaturen (DS-Schlüssel)
* S: Signierschlüssel
* V: Chiffrierschlüssel
*
* Der DS-Schlüssel steht nur im Zusammenhang mit einer Bankensignaturkarte zur Verfügung.
*
* Im Falle der Bankensignaturkarte ergibt sich folgende Zuordnung zu den Kartenschlüsseln:
* - DS-Schlüssel: SK.CH.DS
* - Signierschlüssel: SK.CH.AUT
* - Chiffrierschlüssel: SK.CH.KE
*/
open class SchluesselartDatenelement(key: Schluesselart) : Code(key.code, AllowedValues, Existenzstatus.Mandatory) {
companion object {
val AllowedValues = allCodes<Schluesselart>()
}
}

View File

@ -0,0 +1,16 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.NumerischesDatenelement
/**
* Schlüsselnummer des entsprechenden Schlüssels.
*/
open class Schluesselnummer(number: Int) : NumerischesDatenelement(number, 3, Existenzstatus.Mandatory) {
companion object {
const val FinTsMockValue = 0
}
}

View File

@ -0,0 +1,16 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.NumerischesDatenelement
/**
* Versionsnummer des entsprechenden Schlüssels.
*/
open class Schluesselversion(version: Int) : NumerischesDatenelement(version, 3, Existenzstatus.Mandatory) {
companion object {
const val FinTsMockValue = 0
}
}

View File

@ -0,0 +1,214 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum
enum class Sicherheitsfunktion(override val code: String) : ICodeEnum {
RAH_7_S("2"),
RAH_7_D("1"),
RAH_9_S("2"),
RAH_10_S("2"),
PIN_TAN_900("900"),
PIN_TAN_901("901"),
PIN_TAN_902("902"),
PIN_TAN_903("903"),
PIN_TAN_904("904"),
PIN_TAN_905("905"),
PIN_TAN_906("906"),
PIN_TAN_907("907"),
PIN_TAN_908("908"),
PIN_TAN_909("909"),
PIN_TAN_910("910"),
PIN_TAN_911("911"),
PIN_TAN_912("912"),
PIN_TAN_913("913"),
PIN_TAN_914("914"),
PIN_TAN_915("915"),
PIN_TAN_916("916"),
PIN_TAN_917("917"),
PIN_TAN_918("918"),
PIN_TAN_919("919"),
PIN_TAN_920("920"),
PIN_TAN_921("921"),
PIN_TAN_922("922"),
PIN_TAN_923("923"),
PIN_TAN_924("924"),
PIN_TAN_925("925"),
PIN_TAN_926("926"),
PIN_TAN_927("927"),
PIN_TAN_928("928"),
PIN_TAN_929("929"),
PIN_TAN_930("930"),
PIN_TAN_931("931"),
PIN_TAN_932("932"),
PIN_TAN_933("933"),
PIN_TAN_934("934"),
PIN_TAN_935("935"),
PIN_TAN_936("936"),
PIN_TAN_937("937"),
PIN_TAN_938("938"),
PIN_TAN_939("939"),
PIN_TAN_940("940"),
PIN_TAN_941("941"),
PIN_TAN_942("942"),
PIN_TAN_943("943"),
PIN_TAN_944("944"),
PIN_TAN_945("945"),
PIN_TAN_946("946"),
PIN_TAN_947("947"),
PIN_TAN_948("948"),
PIN_TAN_949("949"),
PIN_TAN_950("950"),
PIN_TAN_951("951"),
PIN_TAN_952("952"),
PIN_TAN_953("953"),
PIN_TAN_954("954"),
PIN_TAN_955("955"),
PIN_TAN_956("956"),
PIN_TAN_957("957"),
PIN_TAN_958("958"),
PIN_TAN_959("959"),
PIN_TAN_960("960"),
PIN_TAN_961("961"),
PIN_TAN_962("962"),
PIN_TAN_963("963"),
PIN_TAN_964("964"),
PIN_TAN_965("965"),
PIN_TAN_966("966"),
PIN_TAN_967("967"),
PIN_TAN_968("968"),
PIN_TAN_969("969"),
PIN_TAN_970("970"),
PIN_TAN_971("971"),
PIN_TAN_972("972"),
PIN_TAN_973("973"),
PIN_TAN_974("974"),
PIN_TAN_975("975"),
PIN_TAN_976("976"),
PIN_TAN_977("977"),
PIN_TAN_978("978"),
PIN_TAN_979("979"),
PIN_TAN_980("980"),
PIN_TAN_981("981"),
PIN_TAN_982("982"),
PIN_TAN_983("983"),
PIN_TAN_984("984"),
PIN_TAN_985("985"),
PIN_TAN_986("986"),
PIN_TAN_987("987"),
PIN_TAN_988("988"),
PIN_TAN_989("989"),
PIN_TAN_990("990"),
PIN_TAN_991("991"),
PIN_TAN_992("992"),
PIN_TAN_993("993"),
PIN_TAN_994("994"),
PIN_TAN_995("995"),
PIN_TAN_996("996"),
PIN_TAN_997("997"),
PIN_TAN_Einschritt_Verfahren("999")
}

View File

@ -0,0 +1,31 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code
import net.dankito.fints.messages.datenelemente.implementierte.allCodes
/**
* Abhängig von Sicherheitsprofil und Schlüsseltyp und HBCI-Version ist folgender Wert einzustellen:
*
* | Sicherheitsprofil | Schlüsseltyp | ab FinTS V3.0 |
* | RAH-7 | S | 2 |
* | RAH-7 | D | 1 |
* | RAH-9 | S | 2 |
* | RAH-10 | S | 2 |
*
*
* Abweichende Belegung für PIN/TAN Verfahren (Dokument Sicherheitsverfahren PIN/TAN, B.9.4 Segment Signaturkopf, S. 58):
*
* Sicherheitsfunktion, kodiert
* Beim Ein-Schritt-Verfahren ist der Wert 999 einzustellen, beim Zwei-Schritt-Verfahren der entsprechende
* in der BPD mitgeteilte Wert für das konkrete Verfahren 900 bis 997 (vgl. Kapitel B.8.2).
*/
open class SicherheitsfunktionKodiert(securityFunction: Sicherheitsfunktion)
: Code(securityFunction.code, AllowedValues, Existenzstatus.Mandatory) {
companion object {
val AllowedValues = allCodes<Sicherheitsfunktion>()
}
}

View File

@ -0,0 +1,12 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
/**
* Referenzinformation, mit der die Verbindung zwischen Signaturkopf und dazu gehörigem
* Signaturabschluss hergestellt werden kann. Die Sicherheitskontrollreferenz im
* Signaturkopf muss mit der entsprechenden Information im Signaturabschluss übereinstimmen.
*/
open class Sicherheitskontrollreferenz(reference: String) : AlphanumerischesDatenelement(reference, Existenzstatus.Mandatory, 14)

View File

@ -0,0 +1,18 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.NumerischesDatenelement
/**
* Sicherheitsrelevante Nachrichtenidentifikation (Signatur-ID), welche zur Verhinderung der
* Doppeleinreichung, respektive Garantie der Nachrichtensequenzintegrität eingesetzt werden kann.
*
* Bei chipkartenbasierten Verfahren ist der Sequenzzähler der Chipkarte einzustellen. Dies ist
* bei Typ-1 Karten der Wert EF_SEQ in der Application DF_BANKING und bei SECCOS Banken-
* Signaturkarten der Wert usage counter der beiden Signierschlüssel SK.CH.DS und SK.CH.AUT.
*
* Bei softwarebasierten Verfahren wird die Sicherheitsreferenznummer auf Basis des DE
* Kundensystem-ID und des DE Benutzerkennung der DEG Schlüsselnamen verwaltet.
*/
open class Sicherheitsreferenznummer(number: Int) : NumerischesDatenelement(number, 16, Existenzstatus.Mandatory)

View File

@ -0,0 +1,12 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum
enum class Sicherheitsverfahren(override val code: String) : ICodeEnum {
RSA_AES_Hybridverfahren("RAH"),
PIN_TAN_Verfahren("PIN")
}

View File

@ -0,0 +1,23 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code
import net.dankito.fints.messages.datenelemente.implementierte.allCodes
/**
* Code des unterstützten Signatur- bzw. Verschlüsselungsalgorithmus.
*
* Weitere Informationen zu den Verfahren sind Kapitel B.1 zu entnehmen.
*
* Codierung:
* - RAH: RSA-AES-Hybridverfahren
* - PIN: PIN/TAN-Verfahren
*/
open class SicherheitsverfahrenCode(method: Sicherheitsverfahren) : Code(method.code, AllowedValues, Existenzstatus.Mandatory) {
companion object {
val AllowedValues = allCodes<Sicherheitsverfahren>()
}
}

View File

@ -0,0 +1,12 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum
enum class Signaturalgorithmus(override val code: String) : ICodeEnum {
NichtZugelassen("1"),
RSA_Algorithmus("10")
}

View File

@ -0,0 +1,30 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
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 Signaturalgorithmus.
*
* Codierung:
* 1: nicht zugelassen
* 10: RSA-Algorithmus (bei RAH)
*
*
* Abweichende Belegung für PIN/TAN Verfahren (Dokument Sicherheitsverfahren PIN/TAN, B.9.6 DEG Signaturalgorithmus, S. 58):
*
* Signaturalgorithmus, kodiert
* FinTS-Füllwert, z. B. 10
*/
open class SignaturalgorithmusKodiert(algorithm: Signaturalgorithmus)
: Code(algorithm.code, AllowedValues, Existenzstatus.Mandatory) {
companion object {
val AllowedValues = allCodes<Signaturalgorithmus>()
val FinTsMockValue = Signaturalgorithmus.RSA_Algorithmus
}
}

View File

@ -0,0 +1,16 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
enum class VersionDesSicherheitsverfahrens(val methodNumber: Int) {
PIN_Ein_Schritt(1),
PIN_Zwei_Schritt(2),
RAH_7(7),
RAH_9(9),
RAH_10(10)
}

View File

@ -0,0 +1,33 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.NumerischesDatenelement
/**
* Version des unterstützten Sicherheitsverfahrens (s. Sicherheitsverfahren, Code).
*
* In Kombination mit dem Sicherheitsverfahren RAH sind die folgenden Versionen gültig:
*
* | Version | Signaturverfahren | Schlüssellänge (bit) | Hashverfahren | Schlüsselart* |
* | 7 | PKCS#1 PSS | ..2048 | SHA-256 | D, S, V |
* | 9 | PKCS#1 PSS | ..2048 | SHA-256 | S, V |
* | 10 | PKCS#1 PSS | ..2048 | SHA-256 | S, V |
*
* *s. Element Schlüsselart
*
* Andere als die genannten Profile sind nicht zulässig.
*
* Um Multibankfähigkeit zu gewährleisten, ist die Unterstützung des Verfahrens RAH-9 kundenund
* kreditisinstitutsseitig verpflichtend.
*
*
* Abweichende Belegung für PIN/TAN Verfahren (Dokument Sicherheitsverfahren PIN/TAN, B.9.1 DEG Sicherheitsprofil, S. 58):
*
* Version des Sicherheitsverfahrens
* - 1 : bei allen Nachrichten, wenn Dialog im Einschritt-Verfahren
* - 2 : bei allen Nachrichten, wenn Dialog im Zwei-Schritt-Verfahren
*/
open class VersionDesSicherheitsverfahrensDatenelement(version: VersionDesSicherheitsverfahrens)
: NumerischesDatenelement(version.methodNumber, 3, Existenzstatus.Mandatory)

View File

@ -0,0 +1,15 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Code
/**
* Kodierte Information über die Verwendung des Hashalgorithmus.
*
* Im Zusammenhang mit Hash-Funktionen ist derzeit nur folgender Wert möglich:
*
* Codierung:
* 1: Owner Hashing (OHA)
*/
open class VerwendungDesHashalgorithmusKodiert: Code("1", listOf("1"), Existenzstatus.Mandatory)

View File

@ -0,0 +1,15 @@
package net.dankito.fints.messages.datenelemente.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement
/**
* Kodierte Information über die Verwendung des Signaturalgorithmus.
*
* Im Zusammenhang mit Signaturbildung ist derzeit nur folgender Wert möglich:
*
* Codierung:
* 6: Owner Signing (OSG)
*/
open class VerwendungDesSignaturalgorithmusKodiert : AlphanumerischesDatenelement("6", Existenzstatus.Mandatory, 3)

View File

@ -9,8 +9,8 @@ import net.dankito.fints.messages.datenelementgruppen.Datenelementgruppe
open class Segmentkopf @JvmOverloads constructor( open class Segmentkopf @JvmOverloads constructor(
identifier: String, identifier: String,
segmentNumber: Int,
segmentVersion: Int, segmentVersion: Int,
segmentNumber: Int = 0,
bezugssegment: Int? = null bezugssegment: Int? = null
) : Datenelementgruppe(listOf( ) : Datenelementgruppe(listOf(

View File

@ -0,0 +1,36 @@
package net.dankito.fints.messages.datenelementgruppen.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.implementierte.signatur.PinOrTan
import net.dankito.fints.messages.datenelementgruppen.Datenelementgruppe
/**
* Bei nicht-schlüsselbasierten Sicherheitsverfahren kann der Benutzer hier Angaben
* zur Authentisierung machen. Ob das Feld verpflichtend ist, ist vom jeweiligen
* Sicherheitsverfahren abhängig.
*
* Format: s. Spezifikation Sicherheitsverfahren PIN/TAN
*
* Abweichende Belegung für PIN/TAN Verfahren (Dokument Sicherheitsverfahren PIN/TAN, B.9.7 Segment Signaturabschluss, S. 59)
*
* Es ist der Signaturabschluss gemäß [HBCI] ab Segmentversion 2 zu verwenden.
*
* Validierungsresultat
* Dieses Feld darf nicht belegt werden.
*
* Benutzerdefinierte Signatur
* Hier werden bei Verwendung des PIN/TAN-Verfahrens PIN und TAN eingestellt. Bei
* Verwendung des Zwei-Schritt-Verfahrens mit Prozessvariante 2 darf eine TAN
* ausschließlich über den Geschäftsvorfall HKTAN eingereicht werden, wobei pro
* HKTAN nur die Verarbeitung einer einzelnen TAN zulässig ist. Ansonsten darf die
* DE TAN im Signaturabschluss nicht belegt werden; ihr Inhalt wird in diesem Fall
* ignoriert und die TAN vom Institut entwertet. Gleiches gilt bei der nicht
* zulässigen Übermittlung von mehreren TANs mit HKTAN. Bei der Verwendung im Rahmen
* des Sicherheitsverfahrens HBCI darf die DEG nicht belegt werden. Ihr Inhalt wird
* in diesem Fall ignoriert.
*/
open class BenutzerdefinierteSignatur(pinOrTan: String)
: Datenelementgruppe(listOf(
PinOrTan(pinOrTan)
), Existenzstatus.Mandatory)

View File

@ -0,0 +1,25 @@
package net.dankito.fints.messages.datenelementgruppen.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.implementierte.signatur.BezeichnerFuerHashalgorithmusparameter
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Hashalgorithmus
import net.dankito.fints.messages.datenelemente.implementierte.signatur.HashalgorithmusKodiert
import net.dankito.fints.messages.datenelemente.implementierte.signatur.VerwendungDesHashalgorithmusKodiert
import net.dankito.fints.messages.datenelementgruppen.Datenelementgruppe
/**
* Angaben zu einem kryptographischen Algorithmus, seinen Operationsmodus, sowie dessen Einsatz.
*
*
* Abweichende Belegung für PIN/TAN Verfahren (Dokument Sicherheitsverfahren PIN/TAN, B.9.5 DEG Hashalgorithmus, S. 58):
*
* Wert des Hashalgorithmusparameters
* Dieses Feld darf nicht belegt werden.
*/
open class HashalgorithmusDatenelementgruppe
: Datenelementgruppe(listOf(
VerwendungDesHashalgorithmusKodiert(),
HashalgorithmusKodiert(Hashalgorithmus.Gegenseitig_vereinbart), // allowed: 3, 4, 5, 6
BezeichnerFuerHashalgorithmusparameter()
), Existenzstatus.Mandatory)

View File

@ -0,0 +1,46 @@
package net.dankito.fints.messages.datenelementgruppen.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.implementierte.Benutzerkennung
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Schluesselart
import net.dankito.fints.messages.datenelemente.implementierte.signatur.SchluesselartDatenelement
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Schluesselnummer
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Schluesselversion
import net.dankito.fints.messages.datenelementgruppen.Datenelementgruppe
import net.dankito.fints.messages.datenelementgruppen.implementierte.Kreditinstitutskennung
/**
* Verwendeter Schlüsselname in strukturierter Form. Mit dieser Information kann die Referenz
* auf einen Schlüssel hergestellt werden.
*
* Dabei enthält das DE Benutzerkennung bei Schlüsseln des Kunden die Benutzerkennung, mit
* der der Kunde eindeutig identifiziert wird. Bei Schlüsseln des Kreditinstituts ist dagegen
* eine beliebige Kennung einzustellen, die dazu dient, den Kreditinstitutsschlüssel eindeutig
* zu identifizieren. Diese Kennung darf weder einer anderen gültigen Benutzerkennung des
* Kreditinstituts noch der Benutzerkennung für den anonymen Zugang entsprechen.
*
*
* Abweichende Belegung für PIN/TAN Verfahren (Dokument Sicherheitsverfahren PIN/TAN, B.9.2 DEG Schlüsselname, S. 58):
*
* Schlüsselnummer
* FinTS-Füllwert, z. B. 0
*
* Schlüsselversion
* FinTS-Füllwert, z. B. 0
*/
open class Schluesselname(
bankCountryCode: Int,
bankCode: String,
userIdentification: String,
key: Schluesselart,
keyNumber: Int,
keyVersion: Int
)
: Datenelementgruppe(listOf(
Kreditinstitutskennung(bankCountryCode, bankCode),
Benutzerkennung(userIdentification),
SchluesselartDatenelement(key),
Schluesselnummer(keyNumber),
Schluesselversion(keyVersion)
), Existenzstatus.Mandatory)

View File

@ -0,0 +1,16 @@
package net.dankito.fints.messages.datenelementgruppen.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Datum
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Uhrzeit
import net.dankito.fints.messages.datenelemente.implementierte.signatur.DatumUndZeitbezeichner
import net.dankito.fints.messages.datenelemente.implementierte.signatur.DatumUndZeitbezeichnerKodiert
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),
Uhrzeit(time, Existenzstatus.Optional)
), Existenzstatus.Mandatory)

View File

@ -0,0 +1,32 @@
package net.dankito.fints.messages.datenelementgruppen.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.implementierte.NotAllowedDatenelement
import net.dankito.fints.messages.datenelemente.implementierte.signatur.BezeichnerFuerSicherheitspartei
import net.dankito.fints.messages.datenelemente.implementierte.signatur.IdentifizierungDerPartei
import net.dankito.fints.messages.datenelementgruppen.Datenelementgruppe
/**
* Identifikation der im Sicherheitsprozess involvierten Parteien. Dient zur Übermittlung
* der CID bei kartenbasierten Sicherheitsverfahren bzw. der Kundensystem-ID bei
* softwarebasierten Verfahren (z. B. Speicherung der Schlüssel in einer Schlüsseldatei).
*
* Wenn eine Synchronisierung der Kundensystem-ID durchgeführt wird, ist als Identifizierung der Partei 0 einzustellen.
*
*
* Abweichende Belegung für PIN/TAN Verfahren (Dokument Sicherheitsverfahren PIN/TAN, B.9.3 DEG Sicherheitsidentifikation, Details, S. 58):
*
* CID
* Dieses Feld darf nicht belegt werden.
*
* Identifizierung der Partei
* Dieses Feld muss eine gültige, zuvor vom Banksystem angeforderte Kundensystem-ID enthalten
* (analog zu RAH-/RDH-Verfahren). Dies gilt auch für Zweit- und Drittsignaturen.
*/
open class SicherheitsidentifikationDetails(partyIdentification: String)
: Datenelementgruppe(listOf(
BezeichnerFuerSicherheitspartei(),
NotAllowedDatenelement(),
IdentifizierungDerPartei(partyIdentification)
), Existenzstatus.Mandatory)

View File

@ -0,0 +1,33 @@
package net.dankito.fints.messages.datenelementgruppen.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsverfahren
import net.dankito.fints.messages.datenelemente.implementierte.signatur.SicherheitsverfahrenCode
import net.dankito.fints.messages.datenelemente.implementierte.signatur.VersionDesSicherheitsverfahrens
import net.dankito.fints.messages.datenelemente.implementierte.signatur.VersionDesSicherheitsverfahrensDatenelement
import net.dankito.fints.messages.datenelementgruppen.Datenelementgruppe
/**
* Verfahren zur Absicherung der Transaktionen, das zwischen Kunde und Kreditinstitut
* vereinbar wurde. Das Sicherheitsprofil wird anhand der Kombination der beiden Elemente
* Sicherheitsverfahren und Version bestimmt (z. B. RDH-9). Für das Sicherheitsverfahren
* PINTAN ist als Code der Wert PIN und als Version der Wert 1 einzustellen.
*
*
* Abweichende Belegung für PIN/TAN Verfahren (Dokument Sicherheitsverfahren PIN/TAN, B.9.1 DEG Sicherheitsprofil, S. 58):
*
* Sicherheitsverfahren, Code
* - PIN : bei allen Nachrichten
*
* Version des Sicherheitsverfahrens
* - 1 : bei allen Nachrichten, wenn Dialog im Einschritt-Verfahren
* - 2 : bei allen Nachrichten, wenn Dialog im Zwei-Schritt-Verfahren
*/
open class Sicherheitsprofil(
method: Sicherheitsverfahren,
version: VersionDesSicherheitsverfahrens
) : Datenelementgruppe(listOf(
SicherheitsverfahrenCode(method),
VersionDesSicherheitsverfahrensDatenelement(version)
), Existenzstatus.Mandatory)

View File

@ -0,0 +1,26 @@
package net.dankito.fints.messages.datenelementgruppen.implementierte.signatur
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.implementierte.signatur.*
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 Signaturbildung über RAH.
*
*
* Abweichende Belegung für PIN/TAN Verfahren (Dokument Sicherheitsverfahren PIN/TAN, B.9.6 DEG Signaturalgorithmus, S. 58):
*
* Signaturalgorithmus, kodiert
* FinTS-Füllwert, z. B. 10
*
* Operationsmodus, kodiert
* FinTS-Füllwert, z. B. 16
*/
open class SignaturalgorithmusDatenelementgruppe (algorithm: Signaturalgorithmus, mode: Operationsmodus)
: Datenelementgruppe(listOf(
VerwendungDesSignaturalgorithmusKodiert(),
SignaturalgorithmusKodiert(algorithm),
OperationsmodusKodiert(mode)
), Existenzstatus.Mandatory)

View File

@ -1,6 +1,7 @@
package net.dankito.fints.messages.nachrichten.implementierte package net.dankito.fints.messages.nachrichten.implementierte
import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte
import net.dankito.fints.messages.datenelemente.implementierte.Nachrichtennummer.Companion.FirstMessageNumber import net.dankito.fints.messages.datenelemente.implementierte.Nachrichtennummer.Companion.FirstMessageNumber
import net.dankito.fints.messages.nachrichten.Nachricht import net.dankito.fints.messages.nachrichten.Nachricht
import net.dankito.fints.messages.segmente.implementierte.IdentifikationsSegment import net.dankito.fints.messages.segmente.implementierte.IdentifikationsSegment
@ -23,7 +24,7 @@ open class Dialoginitialisierung(
) )
: Nachricht(listOf( : Nachricht(listOf(
Nachrichtenkopf(1, messageSize, "0", FirstMessageNumber), Nachrichtenkopf(1, messageSize, "0", FirstMessageNumber),
IdentifikationsSegment(2, bankCountryCode, bankCode, customerId, customerSystemId), IdentifikationsSegment(2, bankCountryCode, bankCode, customerId, customerSystemId, KundensystemStatusWerte.NichtBenoetigt), // TODO: KundensystemStatusWerte
Verarbeitungsvorbereitung(3, bpdVersion, updVersion, language, productName, productVersion), Verarbeitungsvorbereitung(3, bpdVersion, updVersion, language, productName, productVersion),
Nachrichtenabschluss(4, FirstMessageNumber) Nachrichtenabschluss(4, FirstMessageNumber)
)) ))

View File

@ -1,9 +1,10 @@
package net.dankito.fints.messages.segmente.implementierte package net.dankito.fints.messages.segmente.implementierte
import net.dankito.fints.messages.Existenzstatus import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.implementierte.FinTsKundensystemStatus
import net.dankito.fints.messages.datenelemente.implementierte.KundenID import net.dankito.fints.messages.datenelemente.implementierte.KundenID
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatus
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte
import net.dankito.fints.messages.datenelementgruppen.implementierte.Kreditinstitutskennung import net.dankito.fints.messages.datenelementgruppen.implementierte.Kreditinstitutskennung
import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf
import net.dankito.fints.messages.segmente.Segment import net.dankito.fints.messages.segmente.Segment
@ -14,12 +15,13 @@ open class IdentifikationsSegment(
bankCountryCode: Int, bankCountryCode: Int,
bankCode: String, bankCode: String,
customerId: String, customerId: String,
customerSystemId: String customerSystemId: String,
status: KundensystemStatusWerte
) : Segment(listOf( ) : Segment(listOf(
Segmentkopf("HKIDN", segmentNumber, 2), Segmentkopf("HKIDN", 2, segmentNumber),
Kreditinstitutskennung(bankCountryCode, bankCode), Kreditinstitutskennung(bankCountryCode, bankCode),
KundenID(customerId), KundenID(customerId),
KundensystemID(customerSystemId), KundensystemID(customerSystemId),
FinTsKundensystemStatus() KundensystemStatus(status, Existenzstatus.Mandatory)
), Existenzstatus.Mandatory) ), Existenzstatus.Mandatory)

View File

@ -9,10 +9,10 @@ import net.dankito.fints.messages.segmente.Segment
/** /**
* Dieses Segment beendet alle Kunden- und Kreditinstitutsnachrichten. * Dieses Segment beendet alle Kunden- und Kreditinstitutsnachrichten.
*/ */
class Nachrichtenabschluss( open class Nachrichtenabschluss(
segmentNumber: Int, segmentNumber: Int,
messageNumber: Int messageNumber: Int
) : Segment(listOf( ) : Segment(listOf(
Segmentkopf("HNHBS", segmentNumber, 1), Segmentkopf("HNHBS", 1, segmentNumber),
Nachrichtennummer(messageNumber) Nachrichtennummer(messageNumber)
), Existenzstatus.Mandatory) ), Existenzstatus.Mandatory)

View File

@ -13,7 +13,7 @@ open class Nachrichtenkopf(
messageNumber: Int messageNumber: Int
) : Segment(listOf( ) : Segment(listOf(
Segmentkopf("HNHBK", segmentNumber, 3), Segmentkopf("HNHBK", 3, segmentNumber),
Nachrichtengroesse(messageSize), Nachrichtengroesse(messageSize),
HbciVersionDatenelement(HbciVersion.FinTs_3_0_0), HbciVersionDatenelement(HbciVersion.FinTs_3_0_0),
DialogId(dialogId), DialogId(dialogId),

View File

@ -0,0 +1,36 @@
package net.dankito.fints.messages.segmente.implementierte
import net.dankito.fints.messages.datenelemente.implementierte.signatur.*
class PinTanSignaturkopf(
segmentNumber: Int,
securityFunction: Sicherheitsfunktion,
securityControlReference: String,
/**
* Wenn eine Synchronisierung der Kundensystem-ID durchgeführt wird, ist als Identifizierung der Partei 0 einzustellen.
*/
partyIdentification: String,
date: Int,
time: Int,
bankCountryCode: Int,
bankCode: String,
userIdentification: String
) : Signaturkopf(
segmentNumber,
Sicherheitsverfahren.PIN_TAN_Verfahren,
VersionDesSicherheitsverfahrens.PIN_Zwei_Schritt,
securityFunction,
securityControlReference,
partyIdentification,
date,
time,
SignaturalgorithmusKodiert.FinTsMockValue,
OperationsmodusKodiert.FinTsMockValue,
bankCountryCode,
bankCode,
userIdentification,
Schluesselnummer.FinTsMockValue,
Schluesselversion.FinTsMockValue
)

View File

@ -0,0 +1,25 @@
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.signatur.Sicherheitskontrollreferenz
import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf
import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.BenutzerdefinierteSignatur
import net.dankito.fints.messages.segmente.Segment
/**
* Der Signaturabschluss stellt die Verbindung mit dem dazugehörigen Signaturkopf
* her und enthält als "Validierungsresultat" die elektronische Signatur.
*/
open class Signaturabschluss(
segmentNumber: Int,
securityControlReference: String,
pinOrTan: String
)
: Segment(listOf(
Segmentkopf("HNSHA", 2, segmentNumber),
Sicherheitskontrollreferenz(securityControlReference), // has to be the same as in Signaturkopf
NotAllowedDatenelement(), // only used for HBCI, not allowed for PIN/TAN
BenutzerdefinierteSignatur(pinOrTan)
), Existenzstatus.Mandatory)

View File

@ -0,0 +1,56 @@
package net.dankito.fints.messages.segmente.implementierte
import net.dankito.fints.messages.Existenzstatus
import net.dankito.fints.messages.datenelemente.implementierte.signatur.*
import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf
import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.*
import net.dankito.fints.messages.segmente.Segment
/**
* Der Signaturkopf enthält Informationen über den damit verbundenen Sicherheitsservice, sowie über den Absender.
*
*
* Abweichende Belegung für PIN/TAN Verfahren (Dokument Sicherheitsverfahren PIN/TAN, B.9.4 Segment Signaturkopf, S. 58):
*
* Sicherheitsfunktion, kodiert
* Beim Ein-Schritt-Verfahren ist der Wert 999 einzustellen, beim Zwei-Schritt-Verfahren der entsprechende
* in der BPD mitgeteilte Wert für das konkrete Verfahren 900 bis 997 (vgl. Kapitel B.8.2).
*
* Zertifikat
* Dieses Feld darf nicht belegt werden.
*/
open class Signaturkopf(
segmentNumber: Int,
method: Sicherheitsverfahren,
version: VersionDesSicherheitsverfahrens,
securityFunction: Sicherheitsfunktion,
securityControlReference: String,
/**
* Wenn eine Synchronisierung der Kundensystem-ID durchgeführt wird, ist als Identifizierung der Partei 0 einzustellen.
*/
partyIdentification: String,
date: Int,
time: Int,
algorithm: Signaturalgorithmus,
mode: Operationsmodus,
bankCountryCode: Int,
bankCode: String,
userIdentification: String,
keyNumber: Int,
keyVersion: Int
) : Segment(listOf(
Segmentkopf("HNSHK", 4, segmentNumber), // allowed
Sicherheitsprofil(method, version), // allowed: method: RAH, PIN;
SicherheitsfunktionKodiert(securityFunction), // allowed: 1, 2
Sicherheitskontrollreferenz(securityControlReference), // allowed: <>0
BereichDerSicherheitsapplikationKodiert(BereichDerSicherheitsapplikation.SignaturkopfUndHBCINutzdaten), // allowed: 1 ?
RolleDesSicherheitslieferantenKodiert(), // allowed: 1
SicherheitsidentifikationDetails(partyIdentification),
Sicherheitsreferenznummer(1), // TODO: is this always 1?
SicherheitsdatumUndUhrzeit(date, time),
HashalgorithmusDatenelementgruppe(),
SignaturalgorithmusDatenelementgruppe(algorithm, mode),
Schluesselname(bankCountryCode, bankCode, userIdentification, Schluesselart.Signierschluessel, keyNumber, keyVersion)
), Existenzstatus.Mandatory)

View File

@ -6,7 +6,7 @@ import net.dankito.fints.messages.datenelementgruppen.implementierte.Segmentkopf
import net.dankito.fints.messages.segmente.Segment import net.dankito.fints.messages.segmente.Segment
class Verarbeitungsvorbereitung( open class Verarbeitungsvorbereitung(
segmentNumber: Int, segmentNumber: Int,
bpdVersion: Int, bpdVersion: Int,
updVersion: Int, updVersion: Int,
@ -14,7 +14,7 @@ class Verarbeitungsvorbereitung(
productName: String, productName: String,
productVersion: String productVersion: String
) : Segment(listOf( ) : Segment(listOf(
Segmentkopf("HKVVB", segmentNumber, 3), Segmentkopf("HKVVB", 3, segmentNumber),
BPDVersion(bpdVersion, Existenzstatus.Mandatory), BPDVersion(bpdVersion, Existenzstatus.Mandatory),
UPDVersion(updVersion, Existenzstatus.Mandatory), UPDVersion(updVersion, Existenzstatus.Mandatory),
DialogspracheDatenelement(language, Existenzstatus.Mandatory), DialogspracheDatenelement(language, Existenzstatus.Mandatory),

View File

@ -0,0 +1,56 @@
package net.dankito.fints.util
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Datum
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Uhrzeit
import java.text.SimpleDateFormat
import java.util.*
open class FinTsUtils {
companion object {
val HbciDateFormat = SimpleDateFormat(Datum.HbciDateFormat)
val HbciTimeFormat = SimpleDateFormat(Uhrzeit.HbciTimeFormat)
}
open fun formatDateToday(): String {
return formatDate(Date())
}
open fun formatDate(date: Date): String {
return HbciDateFormat.format(date)
}
open fun formatDateTodayAsInt(): Int {
return convertToInt(formatDateToday())
}
open fun formatDateAsInt(date: Date): Int {
return convertToInt(formatDate(date))
}
open fun formatTimeNow(): String {
return formatTime(Date())
}
open fun formatTime(time: Date): String {
return HbciTimeFormat.format(time)
}
open fun formatTimeNowAsInt(): Int {
return convertToInt(formatTimeNow())
}
open fun formatTimeAsInt(time: Date): Int {
return convertToInt(formatTime(time))
}
protected fun convertToInt(string: String): Int {
return string.toInt()
}
}

View File

@ -2,6 +2,7 @@ package net.dankito.fints.messages.segmente.implementierte
import net.dankito.fints.messages.datenelemente.implementierte.KundenID import net.dankito.fints.messages.datenelemente.implementierte.KundenID
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID 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.Laenderkennzeichen
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
@ -13,7 +14,7 @@ class IdentifikationsSegmentTest {
fun format() { fun format() {
// given // given
val underTest = IdentifikationsSegment(2, Laenderkennzeichen.Germany, "12345678", KundenID.Anonymous, KundensystemID.Anonymous) val underTest = IdentifikationsSegment(2, Laenderkennzeichen.Germany, "12345678", KundenID.Anonymous, KundensystemID.Anonymous, KundensystemStatusWerte.NichtBenoetigt)
// when // when
val result = underTest.format() val result = underTest.format()

View File

@ -0,0 +1,25 @@
package net.dankito.fints.messages.segmente.implementierte
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
class SignaturabschlussTest {
@Test
fun format() {
// given
val segmentNumber = 7
val controlReference = "1902675680"
val pin = "MyPin"
val underTest = Signaturabschluss(segmentNumber, controlReference, pin)
// when
val result = underTest.format()
// then
assertThat(result).isEqualTo("HNSHA:$segmentNumber:2+$controlReference++$pin")
}
}

View File

@ -0,0 +1,36 @@
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 net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
class SignaturkopfTest {
@Test
fun format() {
// given
val securityFunction = Sicherheitsfunktion.PIN_TAN_911
val controlReference = "1902675680"
val partyIdentification = IdentifizierungDerPartei.SynchronizingCustomerSystemId
val date = 20191002
val time = 212757
val bankCode = "12345678"
val customerId = "0987654321"
val keyNumber = 0
val keyVersion = 0
val underTest = PinTanSignaturkopf(2, securityFunction, controlReference, partyIdentification,
date, time, Laenderkennzeichen.Germany, bankCode, customerId)
// when
val result = underTest.format()
// then
assertThat(result).isEqualTo("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:$keyNumber:$keyVersion")
}
}

View File

@ -0,0 +1,91 @@
package net.dankito.fints.util
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.util.*
class FinTsUtilsTest {
private val underTest = FinTsUtils()
@Test
fun formatDate() {
// given
val date = Date(88, 2, 27)
// when
val result = underTest.formatDate(date)
// then
assertThat(result).isEqualTo("19880327")
}
@Test
fun formatDateAsInt() {
// given
val date = Date(88, 2, 27)
// when
val result = underTest.formatDateAsInt(date)
// then
assertThat(result).isEqualTo(19880327)
}
@Test
fun formatTime_AM() {
// given
val date = Date(119, 9, 1, 8, 2, 1)
// when
val result = underTest.formatTime(date)
// then
assertThat(result).isEqualTo("080201")
}
@Test
fun formatTime_PM() {
// given
val date = Date(119, 9, 1, 18, 22, 51)
// when
val result = underTest.formatTime(date)
// then
assertThat(result).isEqualTo("182251")
}
@Test
fun formatTimeAsInt_AM() {
// given
val date = Date(119, 9, 1, 8, 2, 1)
// when
val result = underTest.formatTimeAsInt(date)
// then
assertThat(result).isEqualTo(80201)
}
@Test
fun formatTimeAsInt_PM() {
// given
val date = Date(119, 9, 1, 18, 22, 51)
// when
val result = underTest.formatTimeAsInt(date)
// then
assertThat(result).isEqualTo(182251)
}
}