Version in base suite: 3.0.22+dfsg-1 Base version: hbci4java_3.0.22+dfsg-1 Target version: hbci4java_3.1.29+dfsg-1+deb10u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/h/hbci4java/hbci4java_3.0.22+dfsg-1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/h/hbci4java/hbci4java_3.1.29+dfsg-1+deb10u1.dsc .gitignore | 1 debian/changelog | 6 pom.xml | 8 readme.md | 13 src/main/java/org/kapott/cryptalgs/CryptAlgs4JavaProvider.java | 10 src/main/java/org/kapott/hbci/GV/GVAccInfo.java | 9 src/main/java/org/kapott/hbci/GV/GVCardList.java | 11 src/main/java/org/kapott/hbci/GV/GVDauerList.java | 9 src/main/java/org/kapott/hbci/GV/GVDauerSEPAList.java | 11 src/main/java/org/kapott/hbci/GV/GVDauerSEPANew.java | 2 src/main/java/org/kapott/hbci/GV/GVFestCondList.java | 9 src/main/java/org/kapott/hbci/GV/GVFestList.java | 9 src/main/java/org/kapott/hbci/GV/GVFestListAll.java | 9 src/main/java/org/kapott/hbci/GV/GVInfoList.java | 9 src/main/java/org/kapott/hbci/GV/GVInstUebSEPA.java | 75 src/main/java/org/kapott/hbci/GV/GVKUmsAll.java | 12 src/main/java/org/kapott/hbci/GV/GVKUmsAllCamt.java | 11 src/main/java/org/kapott/hbci/GV/GVKontoauszug.java | 9 src/main/java/org/kapott/hbci/GV/GVKontoauszugPdf.java | 9 src/main/java/org/kapott/hbci/GV/GVMultiUebSEPA.java | 15 src/main/java/org/kapott/hbci/GV/GVReceipt.java | 9 src/main/java/org/kapott/hbci/GV/GVSEPAInfo.java | 24 src/main/java/org/kapott/hbci/GV/GVSaldoReq.java | 12 src/main/java/org/kapott/hbci/GV/GVStatus.java | 12 src/main/java/org/kapott/hbci/GV/GVTAN2Step.java | 266 + src/main/java/org/kapott/hbci/GV/GVTANList.java | 12 src/main/java/org/kapott/hbci/GV/GVTANMediaList.java | 30 src/main/java/org/kapott/hbci/GV/GVTermUebSEPA.java | 2 src/main/java/org/kapott/hbci/GV/GVTermUebSEPAEdit.java | 2 src/main/java/org/kapott/hbci/GV/GVUebSEPA.java | 16 src/main/java/org/kapott/hbci/GV/GVUmbSEPA.java | 2 src/main/java/org/kapott/hbci/GV/GVWPDepotList.java | 8 src/main/java/org/kapott/hbci/GV/GVWPDepotUms.java | 8 src/main/java/org/kapott/hbci/GV/HBCIJobImpl.java | 153 src/main/java/org/kapott/hbci/GV/generators/GenKUmsAllCamt05200107.java | 19 src/main/java/org/kapott/hbci/GV/parsers/AbstractCamtParser.java | 19 src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200101.java | 22 src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200102.java | 22 src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200103.java | 22 src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200104.java | 22 src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200105.java | 22 src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200106.java | 22 src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200107.java | 22 src/main/java/org/kapott/hbci/GV_Result/GVRInstUebSEPA.java | 61 src/main/java/org/kapott/hbci/GV_Result/GVRKUms.java | 7 src/main/java/org/kapott/hbci/callback/HBCICallbackIOStreams.java | 44 src/main/java/org/kapott/hbci/comm/CommStandard.java | 2 src/main/java/org/kapott/hbci/datatypes/SyntaxCtr.java | 5 src/main/java/org/kapott/hbci/dialog/AbstractRawHBCIDialog.java | 214 + src/main/java/org/kapott/hbci/dialog/AbstractRawHBCIDialogInit.java | 61 src/main/java/org/kapott/hbci/dialog/DialogContext.java | 258 + src/main/java/org/kapott/hbci/dialog/DialogEvent.java | 53 src/main/java/org/kapott/hbci/dialog/HBCIDialogEnd.java | 140 src/main/java/org/kapott/hbci/dialog/HBCIDialogFirstKeyRequest.java | 78 src/main/java/org/kapott/hbci/dialog/HBCIDialogInit.java | 65 src/main/java/org/kapott/hbci/dialog/HBCIDialogLockKeys.java | 87 src/main/java/org/kapott/hbci/dialog/HBCIDialogSepaInfo.java | 176 src/main/java/org/kapott/hbci/dialog/HBCIDialogSync.java | 127 src/main/java/org/kapott/hbci/dialog/HBCIDialogTanMedia.java | 166 src/main/java/org/kapott/hbci/dialog/HBCIMessage.java | 83 src/main/java/org/kapott/hbci/dialog/HBCIMessageQueue.java | 162 src/main/java/org/kapott/hbci/dialog/HBCIProcess.java | 43 src/main/java/org/kapott/hbci/dialog/HBCIProcessSepaInfo.java | 81 src/main/java/org/kapott/hbci/dialog/HBCIProcessTanMedia.java | 132 src/main/java/org/kapott/hbci/dialog/KnownDialogTemplate.java | 101 src/main/java/org/kapott/hbci/dialog/KnownReturncode.java | 171 src/main/java/org/kapott/hbci/dialog/KnownTANProcess.java | 144 src/main/java/org/kapott/hbci/dialog/RawHBCIDialog.java | 62 src/main/java/org/kapott/hbci/dialog/SCARequest.java | 107 src/main/java/org/kapott/hbci/examples/UmsatzAbrufPinTan.java | 5 src/main/java/org/kapott/hbci/manager/ChallengeInfo.java | 4 src/main/java/org/kapott/hbci/manager/Feature.java | 87 src/main/java/org/kapott/hbci/manager/FlickerCode.java | 141 src/main/java/org/kapott/hbci/manager/HBCIDialog.java | 491 +- src/main/java/org/kapott/hbci/manager/HBCIHandler.java | 139 src/main/java/org/kapott/hbci/manager/HBCIInstitute.java | 222 - src/main/java/org/kapott/hbci/manager/HBCIKernelImpl.java | 12 src/main/java/org/kapott/hbci/manager/HBCIUser.java | 589 +-- src/main/java/org/kapott/hbci/manager/HBCIUtils.java | 4 src/main/java/org/kapott/hbci/manager/HHDVersion.java | 2 src/main/java/org/kapott/hbci/manager/IHandlerData.java | 11 src/main/java/org/kapott/hbci/manager/MatrixCode.java | 17 src/main/java/org/kapott/hbci/manager/QRCode.java | 18 src/main/java/org/kapott/hbci/manager/TanMethod.java | 77 src/main/java/org/kapott/hbci/passport/AbstractHBCIPassport.java | 197 - src/main/java/org/kapott/hbci/passport/AbstractPinTanPassport.java | 1693 +++++---- src/main/java/org/kapott/hbci/passport/AbstractRDHPassport.java | 64 src/main/java/org/kapott/hbci/passport/AbstractRDHSWFileBasedPassport.java | 34 src/main/java/org/kapott/hbci/passport/AbstractRDHSWPassport.java | 2 src/main/java/org/kapott/hbci/passport/HBCIPassportAnonymous.java | 474 +- src/main/java/org/kapott/hbci/passport/HBCIPassportDDV.java | 298 - src/main/java/org/kapott/hbci/passport/HBCIPassportDDVPCSC.java | 148 src/main/java/org/kapott/hbci/passport/HBCIPassportInternal.java | 27 src/main/java/org/kapott/hbci/passport/HBCIPassportPinTan.java | 336 - src/main/java/org/kapott/hbci/passport/HBCIPassportRDH.java | 228 - src/main/java/org/kapott/hbci/passport/HBCIPassportRDH10File.java | 46 src/main/java/org/kapott/hbci/passport/HBCIPassportRDH2File.java | 41 src/main/java/org/kapott/hbci/passport/HBCIPassportRDHNew.java | 464 -- src/main/java/org/kapott/hbci/passport/HBCIPassportRDHXFile.java | 30 src/main/java/org/kapott/hbci/passport/HBCIPassportRSA.java | 434 +- src/main/java/org/kapott/hbci/passport/HBCIPassportSIZRDHFile.java | 12 src/main/java/org/kapott/hbci/passport/storage/PassportData.java | 129 src/main/java/org/kapott/hbci/passport/storage/PassportStorage.java | 311 + src/main/java/org/kapott/hbci/passport/storage/format/AESFormat.java | 328 + src/main/java/org/kapott/hbci/passport/storage/format/AbstractFormat.java | 115 src/main/java/org/kapott/hbci/passport/storage/format/LegacyFormat.java | 202 + src/main/java/org/kapott/hbci/passport/storage/format/PassportFormat.java | 45 src/main/java/org/kapott/hbci/passport/storage/format/legacy/AbstractConverter.java | 25 src/main/java/org/kapott/hbci/passport/storage/format/legacy/AbstractConverterXML.java | 357 + src/main/java/org/kapott/hbci/passport/storage/format/legacy/Converter.java | 49 src/main/java/org/kapott/hbci/passport/storage/format/legacy/ConverterAnonymous.java | 73 src/main/java/org/kapott/hbci/passport/storage/format/legacy/ConverterDDV.java | 78 src/main/java/org/kapott/hbci/passport/storage/format/legacy/ConverterPinTan.java | 102 src/main/java/org/kapott/hbci/passport/storage/format/legacy/ConverterRDHNew.java | 109 src/main/java/org/kapott/hbci/passport/storage/format/legacy/ConverterRSA.java | 69 src/main/java/org/kapott/hbci/security/Sig.java | 1 src/main/java/org/kapott/hbci/sepa/PainVersion.java | 4 src/main/java/org/kapott/hbci/smartcardio/ChipTanCardService.java | 57 src/main/java/org/kapott/hbci/smartcardio/HBCICardService.java | 19 src/main/java/org/kapott/hbci/smartcardio/SmartCardService.java | 16 src/main/java/org/kapott/hbci/status/HBCIMsgStatus.java | 22 src/main/java/org/kapott/hbci/status/HBCIStatus.java | 2 src/main/java/org/kapott/hbci/swift/Swift.java | 14 src/main/java/org/kapott/hbci/tools/DigestUtils.java | 106 src/main/java/org/kapott/hbci/tools/HBCIBatch.java | 4 src/main/java/org/kapott/hbci/tools/IOUtils.java | 207 + src/main/java/org/kapott/hbci/tools/NumberUtil.java | 57 src/main/java/org/kapott/hbci/tools/ParameterFinder.java | 325 + src/main/java/org/kapott/hbci/tools/StringUtil.java | 70 src/main/resources/META-INF/services/org.kapott.hbci.passport.storage.format.PassportFormat | 7 src/main/resources/META-INF/services/org.kapott.hbci.passport.storage.format.legacy.Converter | 7 src/main/resources/blz.properties | 22 src/main/resources/challengedata.xml | 1804 +++++----- src/main/resources/hbci-210.xml | 81 src/main/resources/hbci-300.xml | 448 ++ src/main/resources/hbci-plus.xml | 48 src/main/resources/hbci4java-messages_de.properties | 22 src/test/java/org/kapott/hbci4java/AbstractTestGV.java | 39 src/test/java/org/kapott/hbci4java/bpd/TestParseSync.java | 48 src/test/java/org/kapott/hbci4java/secmech/FlickerTest.java | 23 src/test/java/org/kapott/hbci4java/secmech/TestHHDVersion.java | 40 src/test/java/org/kapott/hbci4java/sepa/TestGVKUmsAll.java | 47 src/test/java/org/kapott/hbci4java/sepa/TestGVKUmsAllCamt.java | 2 src/test/java/org/kapott/hbci4java/swift/TestBrokenMT940.java | 12 src/test/java/org/kapott/hbci4java/tools/TestIOUtils.java | 59 src/test/java/org/kapott/hbci4java/tools/TestParameterFinder.java | 133 src/test/resources/org/kapott/hbci4java/bpd/bpd-psd2-consors.txt | 27 147 files changed, 11145 insertions(+), 4592 deletions(-) diff -Nru hbci4java-3.0.22+dfsg/.gitignore hbci4java-3.1.29+dfsg/.gitignore --- hbci4java-3.0.22+dfsg/.gitignore 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/.gitignore 2019-11-27 09:04:22.000000000 +0000 @@ -2,3 +2,4 @@ .project .settings/ target +/bin/ diff -Nru hbci4java-3.0.22+dfsg/debian/changelog hbci4java-3.1.29+dfsg/debian/changelog --- hbci4java-3.0.22+dfsg/debian/changelog 2019-01-30 19:55:27.000000000 +0000 +++ hbci4java-3.1.29+dfsg/debian/changelog 2019-12-21 17:44:47.000000000 +0000 @@ -1,3 +1,9 @@ +hbci4java (3.1.29+dfsg-1+deb10u1) buster; urgency=medium + + * New upstream version 3.1.29+dfsg + + -- Jochen Sprickerhof Sat, 21 Dec 2019 18:44:47 +0100 + hbci4java (3.0.22+dfsg-1) unstable; urgency=medium * New upstream version 3.0.22+dfsg diff -Nru hbci4java-3.0.22+dfsg/pom.xml hbci4java-3.1.29+dfsg/pom.xml --- hbci4java-3.0.22+dfsg/pom.xml 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/pom.xml 2019-11-27 09:04:22.000000000 +0000 @@ -3,7 +3,7 @@ 4.0.0 com.github.hbci4j hbci4j-core - 3.0.22 + 3.1.29 jar ${project.artifactId} HBCI4j - Home Banking Computer Interface for Java @@ -31,12 +31,12 @@ scm:git:git@github.com:hbci4j/hbci4java.git scm:git:git@github.com:hbci4j/hbci4java.git scm:git:git@github.com:hbci4j/hbci4java.git - hbci4j-core-3.0.22 + hbci4j-core-3.1.29 UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 2.8.2 2.10.4 3.0.1 diff -Nru hbci4java-3.0.22+dfsg/readme.md hbci4java-3.1.29+dfsg/readme.md --- hbci4java-3.0.22+dfsg/readme.md 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/readme.md 2019-11-27 09:04:22.000000000 +0000 @@ -15,14 +15,15 @@ Seither wurden umfangreiche neue Features hinzugefügt wie etwa: -- Die Unterstützung der neuen TAN-Verfahren (smsTAN, photoTAN, chipTAN - incl. Implementierung des HHD-Standards mit Flicker-Code) +- Unterstützung für die neuen EU-weiten PSD2-Anforderungen (SCA), die ab Mitte September 2019 für FinTS verpflichtend sind +- Abruf von Umsätzen im CAMT-Format (HKCAZ) +- Unterstützung für chipTAN USB +- Abruf des elektronischen Kontoauszuges (HKEKA und HKEKP) +- SEPA-Überweisungen und -Lastschriften (jeweils Einzel- und Sammelaufträge) sowie SEPA-Daueraufträge +- Support für alle aktuellen SEPA-PAIN-Versionen - Unterstützung von PC/SC-Kartenlesern via javax.smartcardio API - Eine aktuelle Bankenliste (mit BLZ, Server-Adresse, HBCI-Version,...) -- Support für alle aktuellen SEPA-PAIN-Versionen -- SEPA-Überweisungen und -Lastschriften (jeweils Einzel- und Sammelaufträge) sowie SEPA-Daueraufträge -- Abruf des elektronischen Kontoauszuges (HKEKA und HKEKP) -- Unterstützung für chipTAN USB -- Abruf von Umsätzen im CAMT-Format (HKCAZ) +- Die Unterstützung der neuen TAN-Verfahren (smsTAN, photoTAN, chipTAN - incl. Implementierung des HHD-Standards mit Flicker-Code) ## Lizenz diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/cryptalgs/CryptAlgs4JavaProvider.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/cryptalgs/CryptAlgs4JavaProvider.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/cryptalgs/CryptAlgs4JavaProvider.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/cryptalgs/CryptAlgs4JavaProvider.java 2019-11-27 09:04:22.000000000 +0000 @@ -23,11 +23,15 @@ import java.util.logging.Logger; -public final class CryptAlgs4JavaProvider - extends java.security.Provider +public final class CryptAlgs4JavaProvider extends java.security.Provider { private static final long serialVersionUID=1; + /** + * Der Provider-Name. + */ + public final static String NAME = "CryptAlgs4Java"; + protected Logger getLogger() { return Logger.getLogger(this.getClass().getName()); @@ -35,7 +39,7 @@ public CryptAlgs4JavaProvider() { - super("CryptAlgs4Java", 1.5, "Some hand-coded algorithms for special use cases"); + super(NAME, 1.5, "Some hand-coded algorithms for special use cases"); put("MessageDigest.RIPEMD160", "org.kapott.cryptalgs.RIPEMD160"); put("MessageDigest.MDC2", "org.kapott.cryptalgs.MDC2"); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVAccInfo.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVAccInfo.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVAccInfo.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVAccInfo.java 2019-11-27 09:04:22.000000000 +0000 @@ -52,6 +52,15 @@ addConstraint("all","allaccounts","N", LogFilter.FILTER_NONE); } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + public void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { Properties result=msgstatus.getData(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVCardList.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVCardList.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVCardList.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVCardList.java 2019-11-27 09:04:22.000000000 +0000 @@ -48,7 +48,16 @@ addConstraint("my.number","KTV.number",null, LogFilter.FILTER_IDS); addConstraint("my.subnumber","KTV.subnumber","", LogFilter.FILTER_MOST); } - + + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + public void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { Properties result=msgstatus.getData(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVDauerList.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVDauerList.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVDauerList.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVDauerList.java 2019-11-27 09:04:22.000000000 +0000 @@ -53,6 +53,15 @@ addConstraint("maxentries","maxentries","", LogFilter.FILTER_NONE); } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + protected void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { Properties result=msgstatus.getData(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVDauerSEPAList.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVDauerSEPAList.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVDauerSEPAList.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVDauerSEPAList.java 2019-11-27 09:04:22.000000000 +0000 @@ -96,6 +96,15 @@ } /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + + /** * @see org.kapott.hbci.GV.HBCIJobImpl#extractResults(org.kapott.hbci.status.HBCIMsgStatus, java.lang.String, int) */ @Override @@ -104,7 +113,7 @@ Properties result=msgstatus.getData(); GVRDauerList.Dauer entry=new GVRDauerList.Dauer(); - HBCIUtils.log("parsing SEPA standing orders from msg data [size: " + result.size() + "]",HBCIUtils.LOG_INFO); + HBCIUtils.log("parsing SEPA standing orders from msg data [size: " + result.size() + "]",HBCIUtils.LOG_DEBUG); entry.my=new Konto(); entry.my.country=result.getProperty(header+".My.KIK.country"); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVDauerSEPANew.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVDauerSEPANew.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVDauerSEPANew.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVDauerSEPANew.java 2019-11-27 09:04:22.000000000 +0000 @@ -74,7 +74,7 @@ addConstraint("src.bic", "sepa.src.bic", null, LogFilter.FILTER_MOST); addConstraint("src.iban", "sepa.src.iban", null, LogFilter.FILTER_IDS); addConstraint("src.name", "sepa.src.name", null, LogFilter.FILTER_IDS); - addConstraint("dst.bic", "sepa.dst.bic", null, LogFilter.FILTER_MOST); + addConstraint("dst.bic", "sepa.dst.bic", "", LogFilter.FILTER_MOST); // Kann eventuell entfallen, da BIC optional addConstraint("dst.iban", "sepa.dst.iban", null, LogFilter.FILTER_IDS); addConstraint("dst.name", "sepa.dst.name", null, LogFilter.FILTER_IDS); addConstraint("btg.value", "sepa.btg.value", null, LogFilter.FILTER_NONE); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVFestCondList.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVFestCondList.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVFestCondList.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVFestCondList.java 2019-11-27 09:04:22.000000000 +0000 @@ -48,6 +48,15 @@ addConstraint("maxentries","maxentries","", LogFilter.FILTER_NONE); } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + protected void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { Properties result=msgstatus.getData(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVFestList.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVFestList.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVFestList.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVFestList.java 2019-11-27 09:04:22.000000000 +0000 @@ -61,6 +61,15 @@ // TODO: maxentries fehlen } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + protected void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { Properties result=msgstatus.getData(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVFestListAll.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVFestListAll.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVFestListAll.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVFestListAll.java 2019-11-27 09:04:22.000000000 +0000 @@ -32,6 +32,15 @@ return "FestList"; } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + public GVFestListAll(HBCIHandler handler) { super(getLowlevelName(),handler); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVInfoList.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVInfoList.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVInfoList.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVInfoList.java 2019-11-27 09:04:22.000000000 +0000 @@ -46,6 +46,15 @@ addConstraint("maxentries","maxentries","", LogFilter.FILTER_NONE); } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + protected void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { Properties result=msgstatus.getData(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVInstUebSEPA.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVInstUebSEPA.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVInstUebSEPA.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVInstUebSEPA.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,75 @@ + +/* $Id: GVInstUebSEPA.java,v 1.0 2019/11/17 12:34:56 styppo Exp $ + + This file is part of HBCI4Java + Copyright (C) 2001-2008 Stefan Palme + + HBCI4Java is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + HBCI4Java is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.kapott.hbci.GV; + +import org.kapott.hbci.GV_Result.GVRInstUebSEPA; +import org.kapott.hbci.manager.HBCIHandler; +import org.kapott.hbci.status.HBCIMsgStatus; + +import java.util.Properties; + +/** + * Job-Implementierung fuer SEPA-Instant Ueberweisungen. + */ +public class GVInstUebSEPA extends GVUebSEPA { + /** + * Liefert den Lowlevel-Namen des Jobs. + * @return der Lowlevel-Namen des Jobs. + */ + public static String getLowlevelName() { + return "InstUebSEPA"; + } + + /** + * @see AbstractSEPAGV#getPainJobName() + */ + @Override + public String getPainJobName() { + return "UebSEPA"; + } + + /** + * ct. + * @param handler + */ + public GVInstUebSEPA(HBCIHandler handler) { + this(handler, getLowlevelName()); + } + + /** + * ct. + * @param handler + * @param name + */ + public GVInstUebSEPA(HBCIHandler handler, String name) { + super(handler, name, new GVRInstUebSEPA()); + } + + @Override + protected void extractResults(HBCIMsgStatus msgstatus, String header, int idx) { + final Properties data = msgstatus.getData(); + final GVRInstUebSEPA result = (GVRInstUebSEPA) jobResult; + result.setOrderId(data.getProperty(header + ".orderid")); + result.setOrderStatus(data.getProperty(header + ".orderstatus")); + result.setCancellationCode(data.getProperty(header + ".ccode")); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVKUmsAll.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVKUmsAll.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVKUmsAll.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVKUmsAll.java 2019-11-27 09:04:22.000000000 +0000 @@ -106,6 +106,18 @@ addConstraint("dummy","allaccounts","N", LogFilter.FILTER_NONE); } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#extractResults(org.kapott.hbci.status.HBCIMsgStatus, java.lang.String, int) + */ protected void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { Properties result=msgstatus.getData(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVKUmsAllCamt.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVKUmsAllCamt.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVKUmsAllCamt.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVKUmsAllCamt.java 2019-11-27 09:04:22.000000000 +0000 @@ -125,11 +125,20 @@ cal.add(Calendar.DATE,-Integer.parseInt(days)); date = HBCIUtils.date2StringISO(cal.getTime()); } - HBCIUtils.log("earliest start date according to BPD: " + (date != null && date.length() > 0 ? date : ""),HBCIUtils.LOG_INFO); + HBCIUtils.log("earliest start date according to BPD: " + (date != null && date.length() > 0 ? date : ""),HBCIUtils.LOG_DEBUG); return date; } /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + + /** * @see org.kapott.hbci.GV.HBCIJobImpl#extractResults(org.kapott.hbci.status.HBCIMsgStatus, java.lang.String, int) */ protected void extractResults(HBCIMsgStatus msgstatus,String header,int idx) diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVKontoauszug.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVKontoauszug.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVKontoauszug.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVKontoauszug.java 2019-11-27 09:04:22.000000000 +0000 @@ -101,6 +101,15 @@ } /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + + /** * @see org.kapott.hbci.GV.HBCIJobImpl#extractResults(org.kapott.hbci.status.HBCIMsgStatus, java.lang.String, int) */ protected void extractResults(HBCIMsgStatus msgstatus,String header,int idx) diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVKontoauszugPdf.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVKontoauszugPdf.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVKontoauszugPdf.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVKontoauszugPdf.java 2019-11-27 09:04:22.000000000 +0000 @@ -71,6 +71,15 @@ } /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + + /** * @see org.kapott.hbci.GV.HBCIJobImpl#extractResults(org.kapott.hbci.status.HBCIMsgStatus,java.lang.String, int) */ protected void extractResults(HBCIMsgStatus msgstatus, String header, int idx) diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVMultiUebSEPA.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVMultiUebSEPA.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVMultiUebSEPA.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVMultiUebSEPA.java 2019-11-27 09:04:22.000000000 +0000 @@ -69,6 +69,21 @@ addConstraint("Total.value", "Total.value", null, LogFilter.FILTER_MOST); addConstraint("Total.curr", "Total.curr", null, LogFilter.FILTER_NONE); } + + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#getChallengeParam(java.lang.String) + */ + @Override + public String getChallengeParam(String path) + { + if (path.equals("sepa.btg.value")) { + return getLowlevelParam(getName()+".Total.value"); + } + else if (path.equals("sepa.btg.curr")) { + return getLowlevelParam(getName()+".Total.curr"); + } + return null; + } @Override protected void createSEPAFromParams() { diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVReceipt.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVReceipt.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVReceipt.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVReceipt.java 2019-11-27 09:04:22.000000000 +0000 @@ -27,6 +27,15 @@ super(handler, getLowlevelName(), new HBCIJobResultImpl()); addConstraint("receipt","receipt","", LogFilter.FILTER_NONE); } + + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } /** * @see org.kapott.hbci.GV.HBCIJobImpl#setParam(java.lang.String, java.lang.String) diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVSEPAInfo.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVSEPAInfo.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVSEPAInfo.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVSEPAInfo.java 2019-11-27 09:04:22.000000000 +0000 @@ -27,7 +27,9 @@ import org.kapott.hbci.manager.HBCIHandler; import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.manager.HBCIUtilsInternal; +import org.kapott.hbci.passport.HBCIPassport; import org.kapott.hbci.status.HBCIMsgStatus; +import org.kapott.hbci.tools.StringUtil; public class GVSEPAInfo extends HBCIJobImpl @@ -42,10 +44,23 @@ super(handler,getLowlevelName(), new HBCIJobResultImpl()); } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#extractResults(org.kapott.hbci.status.HBCIMsgStatus, java.lang.String, int) + */ public void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { Properties result=msgstatus.getData(); - Properties upd=getParentHandler().getPassport().getUPD(); + HBCIPassport p = getParentHandler().getPassport(); + Properties upd =p.getUPD(); for (int i=0;;i++) { String subheader=HBCIUtilsInternal.withCounter(header+".Acc", i); @@ -87,8 +102,11 @@ temp_blz.equals(blz) && temp_number.equals(number)) { - upd.setProperty(temp_header+".KTV.iban", iban); - upd.setProperty(temp_header+".KTV.bic", bic); + if (StringUtil.hasText(iban)) + upd.setProperty(temp_header+".KTV.iban", iban); + + if (StringUtil.hasText(bic)) + upd.setProperty(temp_header+".KTV.bic", bic); } } } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVSaldoReq.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVSaldoReq.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVSaldoReq.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVSaldoReq.java 2019-11-27 09:04:22.000000000 +0000 @@ -59,6 +59,18 @@ addConstraint("maxentries","maxentries","", LogFilter.FILTER_NONE); } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#extractResults(org.kapott.hbci.status.HBCIMsgStatus, java.lang.String, int) + */ protected void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { Properties result=msgstatus.getData(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVStatus.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVStatus.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVStatus.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVStatus.java 2019-11-27 09:04:22.000000000 +0000 @@ -53,6 +53,18 @@ addConstraint("jobid",null,"", LogFilter.FILTER_NONE); } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#extractResults(org.kapott.hbci.status.HBCIMsgStatus, java.lang.String, int) + */ protected void extractResults(HBCIMsgStatus msgstatus, String header, int idx) { Properties result=msgstatus.getData(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVTAN2Step.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVTAN2Step.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVTAN2Step.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVTAN2Step.java 2019-11-27 09:04:22.000000000 +0000 @@ -25,37 +25,89 @@ import java.util.Properties; import org.kapott.hbci.GV_Result.GVRSaldoReq; +import org.kapott.hbci.dialog.KnownReturncode; +import org.kapott.hbci.dialog.KnownTANProcess; import org.kapott.hbci.manager.HBCIHandler; import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.manager.LogFilter; +import org.kapott.hbci.passport.AbstractPinTanPassport; +import org.kapott.hbci.passport.HBCIPassportInternal; import org.kapott.hbci.status.HBCIMsgStatus; +import org.kapott.hbci.tools.StringUtil; /** * @author stefan.palme */ -public class GVTAN2Step - extends HBCIJobImpl +public class GVTAN2Step extends HBCIJobImpl { - private GVTAN2Step otherTAN2StepTask; - private HBCIJobImpl origTask; + // Wenn der Wert NULL ist, ist "this" also das zweite HKTAN + private GVTAN2Step step2; + + private KnownTANProcess process = null; + + // Wenn der Wert NULL ist, ist "this" also das erste HKTAN, da die Task-Referenz nur im zweiten HKTAN enthalten ist. + private HBCIJobImpl task; + + private HBCIJobImpl redo; public static String getLowlevelName() { return "TAN2Step"; } + /** + * Speichert den Prozess-Schritt des HKTAN. + * @param p der Prozess-Schritt. + */ + public void setProcess(KnownTANProcess p) + { + this.process = p; + this.setParam("process",p.getCode()); + } + + /** + * ct. + * @param handler + */ public GVTAN2Step(HBCIHandler handler) { super(handler,getLowlevelName(),new GVRSaldoReq()); + int version = 5; + try + { + version = Integer.parseInt(this.getSegVersion()); + } + catch (Exception e) + { + HBCIUtils.log(e); + } + addConstraint("process","process",null, LogFilter.FILTER_NONE); + addConstraint("ordersegcode", "ordersegcode","", LogFilter.FILTER_NONE); + addConstraint("orderaccount.bic","OrderAccount.bic",null, LogFilter.FILTER_MOST); + addConstraint("orderaccount.iban","OrderAccount.iban",null, LogFilter.FILTER_IDS); + addConstraint("orderaccount.number","OrderAccount.number",null, LogFilter.FILTER_IDS); + addConstraint("orderaccount.subnumber","OrderAccount.subnumber","", LogFilter.FILTER_MOST); + addConstraint("orderaccount.blz","OrderAccount.KIK.blz",null, LogFilter.FILTER_MOST); + addConstraint("orderaccount.country","OrderAccount.KIK.country","DE", LogFilter.FILTER_NONE); addConstraint("orderhash","orderhash","", LogFilter.FILTER_NONE); addConstraint("orderref","orderref","", LogFilter.FILTER_NONE); - addConstraint("listidx","listidx","", LogFilter.FILTER_NONE); + + if (version < 6) + addConstraint("listidx","listidx","", LogFilter.FILTER_NONE); + addConstraint("notlasttan","notlasttan","N", LogFilter.FILTER_NONE); - addConstraint("info","info","", LogFilter.FILTER_NONE); + if (version <= 1) // Gabs nur in HKTAN 1 + addConstraint("info","info","", LogFilter.FILTER_NONE); + addConstraint("storno","storno","", LogFilter.FILTER_NONE); + // willuhn 2011-05-17 wird noch nicht genutzt + // addConstraint("smsaccount.number","SMSAccount.number",null, LogFilter.FILTER_IDS); + // addConstraint("smsaccount.subnumber","SMSAccount.subnumber","", LogFilter.FILTER_MOST); + // addConstraint("smsaccount.blz","SMSAccount.KIK.blz",null, LogFilter.FILTER_MOST); + // addConstraint("smsaccount.country","SMSAccount.KIK.country","DE", LogFilter.FILTER_NONE); addConstraint("challengeklass","challengeklass","", LogFilter.FILTER_NONE); addConstraint("ChallengeKlassParam1", "ChallengeKlassParams.param1","", LogFilter.FILTER_IDS); addConstraint("ChallengeKlassParam2", "ChallengeKlassParams.param2","", LogFilter.FILTER_IDS); @@ -68,23 +120,18 @@ addConstraint("ChallengeKlassParam9", "ChallengeKlassParams.param9","", LogFilter.FILTER_IDS); addConstraint("tanmedia", "tanmedia","", LogFilter.FILTER_IDS); + + addConstraint("HHDUCAnswer", "HHDUCAnswer.atc","", LogFilter.FILTER_IDS); + addConstraint("HHDUCAnswer", "HHDUCAnswer.appcrypto_ac","", LogFilter.FILTER_IDS); + addConstraint("HHDUCAnswer", "HHDUCAnswer.ef_id_data","", LogFilter.FILTER_IDS); + addConstraint("HHDUCAnswer", "HHDUCAnswer.cvr","", LogFilter.FILTER_IDS); + addConstraint("HHDUCAnswer", "HHDUCAnswer.versioninfo","", LogFilter.FILTER_IDS); - addConstraint("ordersegcode", "ordersegcode","", LogFilter.FILTER_NONE); - - addConstraint("orderaccount.bic","OrderAccount.bic",null, LogFilter.FILTER_MOST); - addConstraint("orderaccount.iban","OrderAccount.iban",null, LogFilter.FILTER_IDS); - addConstraint("orderaccount.number","OrderAccount.number",null, LogFilter.FILTER_IDS); - addConstraint("orderaccount.subnumber","OrderAccount.subnumber","", LogFilter.FILTER_MOST); - addConstraint("orderaccount.blz","OrderAccount.KIK.blz",null, LogFilter.FILTER_MOST); - addConstraint("orderaccount.country","OrderAccount.KIK.country","DE", LogFilter.FILTER_NONE); - - // willuhn 2011-05-17 wird noch nicht genutzt - // addConstraint("smsaccount.number","SMSAccount.number",null, LogFilter.FILTER_IDS); - // addConstraint("smsaccount.subnumber","SMSAccount.subnumber","", LogFilter.FILTER_MOST); - // addConstraint("smsaccount.blz","SMSAccount.KIK.blz",null, LogFilter.FILTER_MOST); - // addConstraint("smsaccount.country","SMSAccount.KIK.country","DE", LogFilter.FILTER_NONE); } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#setParam(java.lang.String, java.lang.String) + */ public void setParam(String paramName, String value) { if (paramName.equals("orderhash")) { @@ -93,83 +140,146 @@ super.setParam(paramName,value); } - public void storeOtherTAN2StepTask(GVTAN2Step other) + /** + * Speichert die Referenz auf das zweite HKTAN im ersten HKTAN. + * Wird fuer Prozess-Variante 2 benoetigt. + * @param step2 die Referenz auf den ersten HKTAN. + */ + public void setStep2(GVTAN2Step step2) { - this.otherTAN2StepTask=other; + this.step2 = step2; } - public void storeOriginalTask(HBCIJobImpl task) + /** + * Speichert eine Referenz auf den eigentlichen Geschaeftsvorfall. + * @param task + */ + public void setTask(HBCIJobImpl task) { - this.origTask=task; + this.task = task; } - protected void saveReturnValues(HBCIMsgStatus status, int sref) { + protected void saveReturnValues(HBCIMsgStatus status, int sref) + { super.saveReturnValues(status, sref); - if (origTask!=null) { - int orig_segnum=Integer.parseInt(origTask.getJobResult().getSegNum()); + if (this.task != null) + { + int orig_segnum=Integer.parseInt(task.getJobResult().getSegNum()); HBCIUtils.log("storing return values in orig task (segnum="+orig_segnum+")", HBCIUtils.LOG_DEBUG); - origTask.saveReturnValues(status,orig_segnum); + task.saveReturnValues(status,orig_segnum); } } - + + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redo() + */ + @Override + public HBCIJobImpl redo() + { + return this.redo; + } + + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#haveTan() + */ + @Override + public boolean haveTan() + { + // Das HKTAN selbst kann nie ein HKTAN benoetigen. + // Das ist hier nur zur Sicherheit. Eigentlich sollte HIPINS niemals fuer HKTAN=J liefern. + return true; + } + + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#extractResults(org.kapott.hbci.status.HBCIMsgStatus, java.lang.String, int) + */ protected void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { - Properties result=msgstatus.getData(); - String segcode=result.getProperty(header+".SegHead.code"); - HBCIUtils.log("found HKTAN response with segcode "+segcode,HBCIUtils.LOG_DEBUG); - - if (origTask!=null && new StringBuffer(origTask.getHBCICode()).replace(1,2,"I").toString().equals(segcode)) { - // das ist für PV#2, wenn nach dem nachträglichen versenden der TAN das - // antwortsegment des jobs aus der vorherigen Nachricht zurückommt - HBCIUtils.log("this is a response segment for the original task - storing results in the original job",HBCIUtils.LOG_DEBUG); - origTask.extractResults(msgstatus,header,idx); - } else { - HBCIUtils.log("this is a \"real\" HKTAN response - analyzing HITAN data",HBCIUtils.LOG_DEBUG); - - String challenge=result.getProperty(header+".challenge"); - if (challenge!=null) { - HBCIUtils.log("found challenge '"+challenge+"' in HITAN - saving it temporarily in passport",HBCIUtils.LOG_DEBUG); - // das ist für PV#1 (die antwort auf das einreichen des auftrags-hashs) oder - // für PV#2 (die antwort auf das einreichen des auftrages) - // in jedem fall muss mit der nächsten nachricht die TAN übertragen werden - getMainPassport().setPersistentData("pintan_challenge",challenge); - - // External-ID des originalen Jobs durchreichen - getMainPassport().setPersistentData("externalid",this.getExternalId()); - - // TODO: es muss hier evtl. noch überprüft werden, ob - // der zurückgegebene auftragshashwert mit dem ursprünglich versandten - // übereinstimmt - // für pv#1 gilt: hitan_orderhash == sent_orderhash (from previous hktan) - // für pv#2 gilt: hitan_orderhash == orderhash(gv from previous GV segment) - - // TODO: hier noch die optionale DEG ChallengeValidity bereitstellen + final Properties result = msgstatus.getData(); + final String segCode = result.getProperty(header+".SegHead.code"); // HITAN oder das HI** des GV + HBCIUtils.log("found HKTAN response with segcode " + segCode,HBCIUtils.LOG_DEBUG); + + /////////////////////////////////////////////////////////////////////// + // Die folgenden Sonderbehandlungen sind nur bei Prozess-Variante 2 in Schritt 2 noetig, + // weil wir dort ein Response auf einen GV erhalten, wir selbst aber gar nicht der GV sind sondern das HKTAN Step2 + if (this.process == KnownTANProcess.PROCESS2_STEP2 && this.task != null) + { + // Pruefen, ob die Bank eventuell ein 3040 gesendet hat - sie also noch weitere Daten braucht. + // Das 3040 bezieht sich dann aber nicht auf unser HKTAN sondern auf den eigentlichen GV + // In dem Fall muessen wir dem eigentlichen Task mitteilen, dass er erneut ausgefuehrt werden soll. + if (StringUtil.toInsCode(this.getHBCICode()).equals(segCode) && KnownReturncode.W3040.searchReturnValue(msgstatus.segStatus.getWarnings()) != null) + { + HBCIUtils.log("found status code 3040, need to repeat task " + this.task.getHBCICode(),HBCIUtils.LOG_DEBUG); + HBCIUtils.log("Weitere Daten folgen",HBCIUtils.LOG_INFO); + this.redo = this.task; } - // willuhn 2011-05-27 Challenge HHDuc aus dem Reponse holen und im Passport zwischenspeichern - String hhdUc = result.getProperty(header + ".challenge_hhd_uc"); - if (hhdUc != null) + // Das ist das Response auf den eigentlichen GV - an den Task durchreichen + // Muessen wir extra pruefen, weil das hier auch das HITAN sein koennte. Das schauen wir aber nicht an + if (StringUtil.toInsCode(this.task.getHBCICode()).equals(segCode)) { - HBCIUtils.log("found Challenge HHDuc '" + hhdUc + "' in HITAN - saving it temporarily in passport",HBCIUtils.LOG_DEBUG); - getMainPassport().setPersistentData("pintan_challenge_hhd_uc",hhdUc); + HBCIUtils.log("this is a response segment for the original task - storing results in the original job",HBCIUtils.LOG_DEBUG); + this.task.extractResults(msgstatus,header,idx); } - String orderref=result.getProperty(header+".orderref"); - if (orderref!=null) { - // orderref ist nur für PV#2 relevant - HBCIUtils.log("found orderref '"+orderref+"' in HITAN",HBCIUtils.LOG_DEBUG); - if (otherTAN2StepTask!=null) { - // hier sind wir ganz sicher in PV#2. das hier ist die antwort auf das - // erste HKTAN (welches mit dem eigentlichen auftrag verschickt wird) - // die orderref muss im zweiten HKTAN-job gespeichert werden, weil in - // dieser zweiten nachricht dann die TAN mit übertragen werden muss - HBCIUtils.log("storing it in following HKTAN task",HBCIUtils.LOG_DEBUG); - otherTAN2StepTask.setParam("orderref",orderref); - } else { - HBCIUtils.log("no other HKTAN task known - ignoring orderref",HBCIUtils.LOG_DEBUG); - } + // Wir haben hier nichts weiter zu tun + return; + } + // + /////////////////////////////////////////////////////////////////////// + + this.redo = null; + final HBCIPassportInternal p = this.getMainPassport(); + + /////////////////////////////////////////////////////////////////////// + // SCA-Ausnahme checken. Wenn wir in der Auswertung des ersten HKTAN sind, pruefen wir, ob die Bank einen 3076 geschickt + // hat. Wenn das der Fall ist, koennen wir das zweite HKTAN weglassen und muessen auch beim User keine TAN erfragen + if ((this.process == KnownTANProcess.PROCESS1 || this.process == KnownTANProcess.PROCESS2_STEP1) && (KnownReturncode.W3076.searchReturnValue(msgstatus.segStatus.getWarnings()) != null || KnownReturncode.W3076.searchReturnValue(msgstatus.globStatus.getWarnings()) != null)) + { + HBCIUtils.log("found status code 3076, no SCA required",HBCIUtils.LOG_DEBUG); + p.setPersistentData(AbstractPinTanPassport.KEY_PD_SCA,"true"); // Bewirkt, dass die TAN-Abfrage nicht erscheint + if (this.step2 != null) + { + // Bewirkt, dass das zweite HKTAN bei Prozess-Variante 2 nicht mehr gesendet wird + this.step2.skip(); } + return; + } + // + /////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + // Daten fuer die TAN-Abfrage einsammeln + + // Prozess-Variante 1: + final String challenge = result.getProperty(header+".challenge"); + if (challenge != null) + { + HBCIUtils.log("found challenge '" + challenge + "' in HITAN - saving it temporarily in passport",HBCIUtils.LOG_DEBUG); + p.setPersistentData(AbstractPinTanPassport.KEY_PD_CHALLENGE,challenge); + } + + // External-ID des originalen Jobs durchreichen + p.setPersistentData("externalid",this.getExternalId()); + + // Challenge HHDuc aus dem Reponse holen und im Passport zwischenspeichern + String hhdUc = result.getProperty(header + ".challenge_hhd_uc"); + if (hhdUc != null) + { + HBCIUtils.log("found Challenge HHDuc '" + hhdUc + "' in HITAN - saving it temporarily in passport",HBCIUtils.LOG_DEBUG); + p.setPersistentData(AbstractPinTanPassport.KEY_PD_HHDUC,hhdUc); + } + + // Die Auftragsreferenz aus dem ersten HITAN bei Prozessvariante 2. Die muessen wir bei dem HKTAN#2 mitschicken, damit die Bank + // weiss, auf welchen Auftrag sich die TAN bezieht + final String orderref = result.getProperty(header+".orderref"); + if (step2 != null && orderref != null) + { + HBCIUtils.log("found orderref '" + orderref + "' in HITAN",HBCIUtils.LOG_DEBUG); + step2.setParam("orderref",orderref); } + // + /////////////////////////////////////////////////////////////////////// } } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVTANList.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVTANList.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVTANList.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVTANList.java 2019-11-27 09:04:22.000000000 +0000 @@ -43,6 +43,18 @@ super(handler,getLowlevelName(),new GVRTANList()); } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#extractResults(org.kapott.hbci.status.HBCIMsgStatus, java.lang.String, int) + */ public void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { Properties result=msgstatus.getData(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVTANMediaList.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVTANMediaList.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVTANMediaList.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVTANMediaList.java 2019-11-27 09:04:22.000000000 +0000 @@ -4,9 +4,11 @@ import org.kapott.hbci.GV_Result.GVRTANMediaList; import org.kapott.hbci.manager.HBCIHandler; +import org.kapott.hbci.manager.HBCIUser; import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.manager.HBCIUtilsInternal; import org.kapott.hbci.manager.LogFilter; +import org.kapott.hbci.passport.HBCIPassportInternal; import org.kapott.hbci.status.HBCIMsgStatus; public class GVTANMediaList extends HBCIJobImpl { @@ -19,10 +21,22 @@ public GVTANMediaList(HBCIHandler handler) { super(handler,getLowlevelName(),new GVRTANMediaList()); - addConstraint("mediatype","mediatype","0", LogFilter.FILTER_NONE); // "1" gibts nicht. Siehe FinTS_3.0_Security_Sicherheitsverfahren_PINTAN_Rel_20101027_final_version.pdf "TAN-Medium-Art" + addConstraint("mediatype","mediatype","0", LogFilter.FILTER_NONE); addConstraint("mediacategory", "mediacategory", "A", LogFilter.FILTER_NONE); } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } + + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#extractResults(org.kapott.hbci.status.HBCIMsgStatus, java.lang.String, int) + */ public void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { Properties result=msgstatus.getData(); @@ -99,9 +113,17 @@ String names = mediaNames.toString(); if (names.length() > 0) { - HBCIUtils.log("adding TAN media names to UPD: " + names, HBCIUtils.LOG_INFO); - Properties upd = getParentHandler().getPassport().getUPD(); - upd.setProperty("tanmedia.names",names); + HBCIUtils.log("TAN-Medienbezeichnungen empfangen: " + names, HBCIUtils.LOG_INFO); + HBCIUtils.log("adding TAN media names to UPD: " + names, HBCIUtils.LOG_DEBUG); + HBCIPassportInternal p = (HBCIPassportInternal) getParentHandler().getPassport(); + Properties upd = p.getUPD(); + if (upd == null) + { + // Fuer den Fall, dass wir das zu einem Zeitpunkt aufgerufen haben, wo wir noch gar keine UPD haben + upd = new Properties(); + p.setUPD(upd); + } + upd.setProperty(HBCIUser.UPD_KEY_TANMEDIA,names); } } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVTermUebSEPA.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVTermUebSEPA.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVTermUebSEPA.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVTermUebSEPA.java 2019-11-27 09:04:22.000000000 +0000 @@ -93,7 +93,7 @@ addConstraint("src.bic", "sepa.src.bic", null, LogFilter.FILTER_MOST); addConstraint("src.iban", "sepa.src.iban", null, LogFilter.FILTER_IDS); addConstraint("src.name", "sepa.src.name", null, LogFilter.FILTER_IDS); - addConstraint("dst.bic", "sepa.dst.bic", null, LogFilter.FILTER_MOST); + addConstraint("dst.bic", "sepa.dst.bic", "", LogFilter.FILTER_MOST); // Kann eventuell entfallen, da BIC optional addConstraint("dst.iban", "sepa.dst.iban", null, LogFilter.FILTER_IDS); addConstraint("dst.name", "sepa.dst.name", null, LogFilter.FILTER_IDS); addConstraint("btg.value", "sepa.btg.value", null, LogFilter.FILTER_NONE); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVTermUebSEPAEdit.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVTermUebSEPAEdit.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVTermUebSEPAEdit.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVTermUebSEPAEdit.java 2019-11-27 09:04:22.000000000 +0000 @@ -70,7 +70,7 @@ addConstraint("src.bic", "sepa.src.bic", null, LogFilter.FILTER_MOST); addConstraint("src.iban", "sepa.src.iban", null, LogFilter.FILTER_IDS); addConstraint("src.name", "sepa.src.name", null, LogFilter.FILTER_IDS); - addConstraint("dst.bic", "sepa.dst.bic", null, LogFilter.FILTER_MOST); + addConstraint("dst.bic", "sepa.dst.bic", "", LogFilter.FILTER_MOST); // Kann eventuell entfallen, da BIC optional addConstraint("dst.iban", "sepa.dst.iban", null, LogFilter.FILTER_IDS); addConstraint("dst.name", "sepa.dst.name", null, LogFilter.FILTER_IDS); addConstraint("btg.value", "sepa.btg.value", null, LogFilter.FILTER_NONE); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVUebSEPA.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVUebSEPA.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVUebSEPA.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVUebSEPA.java 2019-11-27 09:04:22.000000000 +0000 @@ -21,6 +21,7 @@ package org.kapott.hbci.GV; +import org.kapott.hbci.GV_Result.HBCIJobResultImpl; import org.kapott.hbci.manager.HBCIHandler; import org.kapott.hbci.manager.LogFilter; import org.kapott.hbci.sepa.SepaVersion; @@ -76,7 +77,18 @@ */ public GVUebSEPA(HBCIHandler handler, String name) { - super(handler, name); + this(handler, name, new HBCIJobResultImpl()); + } + + /** + * ct. + * @param handler + * @param name + * @param jobResult + */ + public GVUebSEPA(HBCIHandler handler, String name, HBCIJobResultImpl jobResult) + { + super(handler, name, jobResult); addConstraint("src.bic", "My.bic", null, LogFilter.FILTER_MOST); addConstraint("src.iban", "My.iban", null, LogFilter.FILTER_IDS); @@ -98,7 +110,7 @@ addConstraint("src.bic", "sepa.src.bic", null, LogFilter.FILTER_MOST); addConstraint("src.iban", "sepa.src.iban", null, LogFilter.FILTER_IDS); addConstraint("src.name", "sepa.src.name", null, LogFilter.FILTER_IDS); - addConstraint("dst.bic", "sepa.dst.bic", "", LogFilter.FILTER_MOST, true); // Kann eventuell entfallen, da BIC optional + addConstraint("dst.bic", "sepa.dst.bic", "", LogFilter.FILTER_MOST, true); // Kann eventuell entfallen, da BIC optional addConstraint("dst.iban", "sepa.dst.iban", null, LogFilter.FILTER_IDS, true); addConstraint("dst.name", "sepa.dst.name", null, LogFilter.FILTER_IDS, true); addConstraint("btg.value", "sepa.btg.value", null, LogFilter.FILTER_NONE, true); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVUmbSEPA.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVUmbSEPA.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVUmbSEPA.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVUmbSEPA.java 2019-11-27 09:04:22.000000000 +0000 @@ -98,7 +98,7 @@ addConstraint("src.bic", "sepa.src.bic", null, LogFilter.FILTER_MOST); addConstraint("src.iban", "sepa.src.iban", null, LogFilter.FILTER_IDS); addConstraint("src.name", "sepa.src.name", null, LogFilter.FILTER_IDS); - addConstraint("dst.bic", "sepa.dst.bic", null, LogFilter.FILTER_MOST, true); + addConstraint("dst.bic", "sepa.dst.bic", "", LogFilter.FILTER_MOST, true); // Kann eventuell entfallen, da BIC optional addConstraint("dst.iban", "sepa.dst.iban", null, LogFilter.FILTER_IDS, true); addConstraint("dst.name", "sepa.dst.name", null, LogFilter.FILTER_IDS, true); addConstraint("btg.value", "sepa.btg.value", null, LogFilter.FILTER_NONE, true); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVWPDepotList.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVWPDepotList.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVWPDepotList.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVWPDepotList.java 2019-11-27 09:04:22.000000000 +0000 @@ -63,6 +63,14 @@ addConstraint("maxentries","maxentries","", LogFilter.FILTER_NONE); } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } protected void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVWPDepotUms.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVWPDepotUms.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/GVWPDepotUms.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/GVWPDepotUms.java 2019-11-27 09:04:22.000000000 +0000 @@ -99,6 +99,14 @@ saldo_type); } + /** + * @see org.kapott.hbci.GV.HBCIJobImpl#redoAllowed() + */ + @Override + protected boolean redoAllowed() + { + return true; + } protected void extractResults(HBCIMsgStatus msgstatus,String header,int idx) { diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/HBCIJobImpl.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/HBCIJobImpl.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/HBCIJobImpl.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/HBCIJobImpl.java 2019-11-27 09:04:22.000000000 +0000 @@ -36,6 +36,7 @@ import org.kapott.hbci.GV_Result.HBCIJobResult; import org.kapott.hbci.GV_Result.HBCIJobResultImpl; import org.kapott.hbci.callback.HBCICallback; +import org.kapott.hbci.dialog.KnownReturncode; import org.kapott.hbci.exceptions.HBCI_Exception; import org.kapott.hbci.exceptions.InvalidArgumentException; import org.kapott.hbci.exceptions.InvalidUserDataException; @@ -88,6 +89,9 @@ über den logfilter-Mechanimus geschützt werden soll */ private String externalId; + private int loopCount = 0; + private boolean haveTan = false; + private boolean skip = false; private HashSet indexedConstraints; @@ -206,7 +210,7 @@ // willuhn 2011-06-06 Segment-Versionen ueberspringen, die groesser als die max. zulaessige sind if (maxAllowedVersion > 0 && version > maxAllowedVersion) { - HBCIUtils.log("skipping segment version " + version + " for task " + jobnameLL + ", larger than allowed version " + maxAllowedVersion, HBCIUtils.LOG_INFO); + HBCIUtils.log("skipping segment version " + version + " for task " + jobnameLL + ", larger than allowed version " + maxAllowedVersion, HBCIUtils.LOG_DEBUG); continue; } // merken der größten jemals aufgetretenen versionsnummer @@ -260,7 +264,7 @@ if (version.equals(this.segVersion)) return; - HBCIUtils.log("changing segment version for task " + this.jobName + " explicit from " + this.segVersion + " to " + version,HBCIUtils.LOG_INFO); + HBCIUtils.log("changing segment version for task " + this.jobName + " explicitly from " + this.segVersion + " to " + version,HBCIUtils.LOG_DEBUG); // Der alte Name String oldName = this.name; @@ -724,10 +728,48 @@ } } - public void setContinueOffset(int loop) + /** + * Setzt den Offset-Parameter mit dem aktuellen Loop-Count. + * Der Counter wird jedesmal in fillJobResult erhoeht - also mit jedem neuen Ergebnis. + */ + public void applyOffset() + { + final String offset = this.getContinueOffset(); + this.setLowlevelParam(this.getName() + ".offset",(offset != null) ? offset : ""); + } + + /** + * Liefert true, wenn fuer den Auftrag ein HKTAN erzeugt wurde. + * @return true, wenn fuer den Auftrag ein HKTAN erzeugt wurde. + */ + public boolean haveTan() + { + return this.haveTan; + } + + /** + * Vermerkt den Auftrag als "HKTAN erzeugt". + */ + public void tanApplied() + { + this.haveTan = true; + } + + /** + * Markiert den Auftrag als zu ueberspringend. + */ + public void skip() + { + this.skip = true; + } + + /** + * Prueft, ob der Auftrag uebersprungen werden soll. + * @return true, wenn der Auftrag uebersprungen werden soll. + */ + public boolean skipped() { - String offset=getContinueOffset(loop); - setLowlevelParam(getName()+".offset",(offset!=null)?offset:""); + return this.skip; } protected void setLowlevelParam(String key,String value) @@ -761,50 +803,62 @@ return this.segVersion; } - /* stellt fest, ob für diesen Task ein neues Auftragssegment generiert werden muss. - Das ist in zwei Fällen der Fall: der Task wurde noch nie ausgeführt; oder der Task - wurde bereits ausgeführt, hat aber eine "offset"-Meldung zurückgegeben */ - public boolean needsContinue(int loop) - { - boolean needs=false; - - if (executed) { - HBCIRetVal retval=null; - int num=jobResult.getRetNumber(); - - for (int i=0;i 0 && p[0] != null && p[0].length() > 0 && (--loop) == 0) + return retval; } - - return ret; + + return null; } + + /* füllt das Objekt mit den Rückgabedaten. Dazu wird zuerst eine Liste aller Segmente erstellt, die Rückgabedaten für diesen Task enthalten. Anschließend werden die HBCI-Rückgabewerte (RetSegs) im outStore gespeichert. Danach werden @@ -812,7 +866,10 @@ public void fillJobResult(HBCIMsgStatus status,int offset) { try { - executed=true; + this.executed = true; + this.haveTan = false; + this.skip = false; + this.loopCount++; Properties result=status.getData(); // nachsehen, welche antwortsegmente ueberhaupt @@ -1225,17 +1282,17 @@ if (o != null) { String s = o.toString(); - HBCIUtils.log("value of \"cannationalacc\" overwritten in passport, value: " + s,HBCIUtils.LOG_INFO); + HBCIUtils.log("value of \"cannationalacc\" overwritten in passport, value: " + s,HBCIUtils.LOG_DEBUG); return s.equalsIgnoreCase("J"); } } - HBCIUtils.log("searching for value of \"cannationalacc\" in HISPAS",HBCIUtils.LOG_INFO); + HBCIUtils.log("searching for value of \"cannationalacc\" in HISPAS",HBCIUtils.LOG_DEBUG); // Ansonsten suchen wir in HISPAS - aber nur, wenn wir die Daten schon haben if (handler.getSupportedLowlevelJobs().getProperty("SEPAInfo") == null) { - HBCIUtils.log("no HISPAS data found",HBCIUtils.LOG_INFO); + HBCIUtils.log("no HISPAS data found",HBCIUtils.LOG_DEBUG); return false; // Ne, noch nicht. Dann lassen wir das erstmal weg } @@ -1243,7 +1300,7 @@ // SEPAInfo laden und darüber iterieren Properties props = handler.getLowlevelJobRestrictions("SEPAInfo"); String value = props.getProperty("cannationalacc"); - HBCIUtils.log("cannationalacc=" + value,HBCIUtils.LOG_INFO); + HBCIUtils.log("cannationalacc=" + value,HBCIUtils.LOG_DEBUG); return value != null && value.equalsIgnoreCase("J"); } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/generators/GenKUmsAllCamt05200107.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/generators/GenKUmsAllCamt05200107.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/generators/GenKUmsAllCamt05200107.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/generators/GenKUmsAllCamt05200107.java 2019-11-27 09:04:22.000000000 +0000 @@ -1,10 +1,21 @@ /********************************************************************** * + * This file is part of HBCI4Java. * Copyright (c) 2018 Olaf Willuhn - * All rights reserved. - * - * This software is copyrighted work licensed under the terms of the - * Jameica License. Please consult the file "LICENSE" for details. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * **********************************************************************/ diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/AbstractCamtParser.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/AbstractCamtParser.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/AbstractCamtParser.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/AbstractCamtParser.java 2019-11-27 09:04:22.000000000 +0000 @@ -1,10 +1,21 @@ /********************************************************************** * + * This file is part of HBCI4Java. * Copyright (c) 2018 Olaf Willuhn - * All rights reserved. - * - * This software is copyrighted work licensed under the terms of the - * Jameica License. Please consult the file "LICENSE" for details. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * **********************************************************************/ diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200101.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200101.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200101.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200101.java 2019-11-27 09:04:22.000000000 +0000 @@ -1,10 +1,21 @@ /********************************************************************** * + * This file is part of HBCI4Java. * Copyright (c) 2018 Olaf Willuhn - * All rights reserved. - * - * This software is copyrighted work licensed under the terms of the - * Jameica License. Please consult the file "LICENSE" for details. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * **********************************************************************/ @@ -139,6 +150,7 @@ { line.id = trim(ref.getPrtry() != null ? ref.getPrtry().getRef() : null); line.endToEndId = trim(ref.getEndToEndId()); + line.mandateId = trim(ref.getMndtId()); } //////////////////////////////////////////////////////////////////////// @@ -168,7 +180,7 @@ { BranchAndFinancialInstitutionIdentification3 bank = haben ? banks.getDbtrAgt() : banks.getCdtrAgt(); FinancialInstitutionIdentification5Choice bic = bank != null ? bank.getFinInstnId() : null; - line.other.bic = trim(bank != null ? bic.getBIC() : null); + line.other.bic = trim(bic != null ? bic.getBIC() : null); } // //////////////////////////////////////////////////////////////////////// diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200102.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200102.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200102.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200102.java 2019-11-27 09:04:22.000000000 +0000 @@ -1,10 +1,21 @@ /********************************************************************** * + * This file is part of HBCI4Java. * Copyright (c) 2018 Olaf Willuhn - * All rights reserved. - * - * This software is copyrighted work licensed under the terms of the - * Jameica License. Please consult the file "LICENSE" for details. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * **********************************************************************/ @@ -148,6 +159,7 @@ { line.id = trim(ref.getPrtry() != null ? ref.getPrtry().getRef() : null); line.endToEndId = trim(ref.getEndToEndId()); + line.mandateId = trim(ref.getMndtId()); } //////////////////////////////////////////////////////////////////////// @@ -178,7 +190,7 @@ { BranchAndFinancialInstitutionIdentification4 bank = haben ? banks.getDbtrAgt() : banks.getCdtrAgt(); FinancialInstitutionIdentification7 bic = bank != null ? bank.getFinInstnId() : null; - line.other.bic = trim(bank != null ? bic.getBIC() : null); + line.other.bic = trim(bic != null ? bic.getBIC() : null); } // //////////////////////////////////////////////////////////////////////// diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200103.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200103.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200103.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200103.java 2019-11-27 09:04:22.000000000 +0000 @@ -1,10 +1,21 @@ /********************************************************************** * + * This file is part of HBCI4Java. * Copyright (c) 2018 Olaf Willuhn - * All rights reserved. - * - * This software is copyrighted work licensed under the terms of the - * Jameica License. Please consult the file "LICENSE" for details. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * **********************************************************************/ @@ -148,6 +159,7 @@ { line.id = trim(ref.getPrtry() != null && ref.getPrtry().size() > 0 ? ref.getPrtry().get(0).getRef() : null); line.endToEndId = trim(ref.getEndToEndId()); + line.mandateId = trim(ref.getMndtId()); } //////////////////////////////////////////////////////////////////////// @@ -177,7 +189,7 @@ { BranchAndFinancialInstitutionIdentification5 bank = haben ? banks.getDbtrAgt() : banks.getCdtrAgt(); FinancialInstitutionIdentification8 bic = bank != null ? bank.getFinInstnId() : null; - line.other.bic = trim(bank != null ? bic.getBICFI() : null); + line.other.bic = trim(bic != null ? bic.getBICFI() : null); } // //////////////////////////////////////////////////////////////////////// diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200104.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200104.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200104.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200104.java 2019-11-27 09:04:22.000000000 +0000 @@ -1,10 +1,21 @@ /********************************************************************** * + * This file is part of HBCI4Java. * Copyright (c) 2018 Olaf Willuhn - * All rights reserved. - * - * This software is copyrighted work licensed under the terms of the - * Jameica License. Please consult the file "LICENSE" for details. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * **********************************************************************/ @@ -148,6 +159,7 @@ { line.id = trim(ref.getPrtry() != null && ref.getPrtry().size() > 0 ? ref.getPrtry().get(0).getRef() : null); line.endToEndId = trim(ref.getEndToEndId()); + line.mandateId = trim(ref.getMndtId()); } //////////////////////////////////////////////////////////////////////// @@ -177,7 +189,7 @@ { BranchAndFinancialInstitutionIdentification5 bank = haben ? banks.getDbtrAgt() : banks.getCdtrAgt(); FinancialInstitutionIdentification8 bic = bank != null ? bank.getFinInstnId() : null; - line.other.bic = trim(bank != null ? bic.getBICFI() : null); + line.other.bic = trim(bic != null ? bic.getBICFI() : null); } // //////////////////////////////////////////////////////////////////////// diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200105.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200105.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200105.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200105.java 2019-11-27 09:04:22.000000000 +0000 @@ -1,10 +1,21 @@ /********************************************************************** * + * This file is part of HBCI4Java. * Copyright (c) 2018 Olaf Willuhn - * All rights reserved. - * - * This software is copyrighted work licensed under the terms of the - * Jameica License. Please consult the file "LICENSE" for details. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * **********************************************************************/ @@ -148,6 +159,7 @@ { line.id = trim(ref.getPrtry() != null && ref.getPrtry().size() > 0 ? ref.getPrtry().get(0).getRef() : null); line.endToEndId = trim(ref.getEndToEndId()); + line.mandateId = trim(ref.getMndtId()); } //////////////////////////////////////////////////////////////////////// @@ -177,7 +189,7 @@ { BranchAndFinancialInstitutionIdentification5 bank = haben ? banks.getDbtrAgt() : banks.getCdtrAgt(); FinancialInstitutionIdentification8 bic = bank != null ? bank.getFinInstnId() : null; - line.other.bic = trim(bank != null ? bic.getBICFI() : null); + line.other.bic = trim(bic != null ? bic.getBICFI() : null); } // //////////////////////////////////////////////////////////////////////// diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200106.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200106.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200106.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200106.java 2019-11-27 09:04:22.000000000 +0000 @@ -1,10 +1,21 @@ /********************************************************************** * + * This file is part of HBCI4Java. * Copyright (c) 2018 Olaf Willuhn - * All rights reserved. - * - * This software is copyrighted work licensed under the terms of the - * Jameica License. Please consult the file "LICENSE" for details. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * **********************************************************************/ @@ -148,6 +159,7 @@ { line.id = trim(ref.getPrtry() != null && ref.getPrtry().size() > 0 ? ref.getPrtry().get(0).getRef() : null); line.endToEndId = trim(ref.getEndToEndId()); + line.mandateId = trim(ref.getMndtId()); } //////////////////////////////////////////////////////////////////////// @@ -177,7 +189,7 @@ { BranchAndFinancialInstitutionIdentification5 bank = haben ? banks.getDbtrAgt() : banks.getCdtrAgt(); FinancialInstitutionIdentification8 bic = bank != null ? bank.getFinInstnId() : null; - line.other.bic = trim(bank != null ? bic.getBICFI() : null); + line.other.bic = trim(bic != null ? bic.getBICFI() : null); } // //////////////////////////////////////////////////////////////////////// diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200107.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200107.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200107.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV/parsers/ParseCamt05200107.java 2019-11-27 09:04:22.000000000 +0000 @@ -1,10 +1,21 @@ /********************************************************************** * + * This file is part of HBCI4Java. * Copyright (c) 2018 Olaf Willuhn - * All rights reserved. - * - * This software is copyrighted work licensed under the terms of the - * Jameica License. Please consult the file "LICENSE" for details. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * **********************************************************************/ @@ -148,6 +159,7 @@ { line.id = trim(ref.getPrtry() != null && ref.getPrtry().size() > 0 ? ref.getPrtry().get(0).getRef() : null); line.endToEndId = trim(ref.getEndToEndId()); + line.mandateId = trim(ref.getMndtId()); } //////////////////////////////////////////////////////////////////////// @@ -179,7 +191,7 @@ { BranchAndFinancialInstitutionIdentification5 bank = haben ? banks.getDbtrAgt() : banks.getCdtrAgt(); FinancialInstitutionIdentification8 bic = bank != null ? bank.getFinInstnId() : null; - line.other.bic = trim(bank != null ? bic.getBICFI() : null); + line.other.bic = trim(bic != null ? bic.getBICFI() : null); } // //////////////////////////////////////////////////////////////////////// diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV_Result/GVRInstUebSEPA.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV_Result/GVRInstUebSEPA.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV_Result/GVRInstUebSEPA.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV_Result/GVRInstUebSEPA.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,61 @@ + +/* $Id: GVRInstUebSEPA.java,v 1.0 2019/11/18 12:34:56 styppo Exp $ + + This file is part of HBCI4Java + Copyright (C) 2001-2008 Stefan Palme + + HBCI4Java is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + HBCI4Java is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package org.kapott.hbci.GV_Result; + + +/** + * Rückgabedaten für das Einreichen einer SEPA-Instant Überweisung. + */ +public class GVRInstUebSEPA extends HBCIJobResultImpl { + private String orderId; + private String orderStatus; + private String cancellationCode; + + public String getOrderId() { + return orderId; + } + + public void setOrderId(String orderId) { + this.orderId = orderId; + } + + public String getOrderStatus() { + return orderStatus; + } + + public void setOrderStatus(String orderStatus) { + this.orderStatus = orderStatus; + } + + public String getCancellationCode() { + return cancellationCode; + } + + public void setCancellationCode(String cancellationCode) { + this.cancellationCode = cancellationCode; + } + + public String toString() { + return String.format("GVRInstUebSepa{orderId=%s, orderStatus=%s, cancellationCode=%s}", + orderId, orderStatus, cancellationCode); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV_Result/GVRKUms.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV_Result/GVRKUms.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/GV_Result/GVRKUms.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/GV_Result/GVRKUms.java 2019-11-27 09:04:22.000000000 +0000 @@ -137,6 +137,11 @@ * NUR BEI CAMT: Der Purpose-Code der Buchung. */ public String purposecode; + + /** + * NUR BEI CAMT: Mandats-Referenz (MREF) + */ + public String mandateId; public UmsLine() { @@ -428,7 +433,7 @@ // extract konto data String konto_info=Swift.getTagValue(st_tag,"25",0); - int pos=konto_info.indexOf("/"); + int pos = konto_info != null ? konto_info.indexOf("/") : -1; String blz; String number; String iban; diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/callback/HBCICallbackIOStreams.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/callback/HBCICallbackIOStreams.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/callback/HBCICallbackIOStreams.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/callback/HBCICallbackIOStreams.java 2019-11-27 09:04:22.000000000 +0000 @@ -22,6 +22,7 @@ package org.kapott.hbci.callback; import java.io.BufferedReader; +import java.io.IOException; import java.io.PrintStream; import java.util.Date; import java.util.StringTokenizer; @@ -79,6 +80,21 @@ return outStream; } + /** + * NPE-sichere Variante zum Lesen einer Zeile aus dem Eingabestream (normalerweise STDIN). + * Wenn der NULL ist, weil der Prozess von der Konsole abgekoppelt ist, dann wird ein Leerstring geliefert. + * @return den auf der Konsole eingegebenen Text oder einen Leerstring, wenn kein Eingabestream vorhanden ist. + * @throws IOException + */ + protected String readLine() throws IOException + { + BufferedReader r = this.getInStream(); + if (r == null) + return ""; + final String s = r.readLine(); + return s != null ? s : ""; + } + /** Schreiben von Logging-Ausgaben in einen PrintStream. Diese Methode implementiert die Logging-Schnittstelle des {@link org.kapott.hbci.callback.HBCICallback}-Interfaces. Die Log-Informationen, die dieser Methode übergeben werden, werden formatiert auf dem jeweiligen outStream ausgegeben. In dem @@ -114,12 +130,12 @@ getOutStream().print(msg+": "); getOutStream().flush(); - st=getInStream().readLine(); + st=this.readLine(); if (reason==NEED_PASSPHRASE_SAVE) { getOutStream().print(msg+" (again): "); getOutStream().flush(); - String st2=getInStream().readLine(); + String st2=this.readLine(); if (!st.equals(st2)) throw new InvalidUserDataException(HBCIUtilsInternal.getLocMsg("EXCMSG_PWDONTMATCH")); } @@ -143,7 +159,7 @@ case NEED_PROXY_PASS: getOutStream().print(msg+": "); getOutStream().flush(); - String secret=getInStream().readLine(); + String secret=this.readLine(); logfilter.addSecretData(secret,"X",LogFilter.FILTER_SECRETS); retData.replace(0,retData.length(),secret); break; @@ -166,7 +182,7 @@ case NEED_PROXY_USER: getOutStream().print(msg+" ["+retData.toString()+"]: "); getOutStream().flush(); - st=getInStream().readLine(); + st=this.readLine(); if (st.length()==0) st=retData.toString(); @@ -187,7 +203,7 @@ getOutStream().println(HBCIUtilsInternal.getLocMsg("HASH")+": "+HBCIUtils.data2hex(iniletter.getKeyHashDisplay())); getOutStream().print("=OK, \"ERR\"=ERROR: "); getOutStream().flush(); - retData.replace(0, retData.length(), getInStream().readLine()); + retData.replace(0, retData.length(), this.readLine()); break; case HAVE_NEW_MY_KEYS: @@ -209,7 +225,7 @@ getOutStream().println(msg); getOutStream().println(HBCIUtilsInternal.getLocMsg("CONTINUE")); getOutStream().flush(); - getInStream().readLine(); + this.readLine(); break; case NEED_REMOVE_CHIPCARD: @@ -225,14 +241,14 @@ getOutStream().print(HBCIUtilsInternal.getLocMsg("BLZ")+" ["+blz+"]: "); getOutStream().flush(); - String s=getInStream().readLine(); + String s=this.readLine(); if (s.length()==0) s=blz; blz=s; getOutStream().print(HBCIUtilsInternal.getLocMsg("ACCNUMBER")+" ["+number+"]: "); getOutStream().flush(); - s=getInStream().readLine(); + s=this.readLine(); if (s.length()==0) s=number; number=s; @@ -249,7 +265,7 @@ String iban=retData.toString(); getOutStream().print(HBCIUtilsInternal.getLocMsg("IBAN")+" ["+iban+"]: "); getOutStream().flush(); - String newiban=getInStream().readLine(); + String newiban=this.readLine(); if (newiban.length()!=0 && !newiban.equals(iban)) { retData.replace(0,retData.length(),newiban); logfilter.addSecretData(newiban,"X",LogFilter.FILTER_IDS); @@ -260,7 +276,7 @@ getOutStream().println(msg); getOutStream().print("=OK, \"ERR\"=ERROR: "); getOutStream().flush(); - retData.replace(0,retData.length(), getInStream().readLine()); + retData.replace(0,retData.length(), this.readLine()); break; case NEED_SIZENTRY_SELECT: @@ -277,7 +293,7 @@ } getOutStream().print(HBCIUtilsInternal.getLocMsg("CALLB_SELECT_ENTRY")+": "); getOutStream().flush(); - retData.replace(0,retData.length(),getInStream().readLine()); + retData.replace(0,retData.length(),this.readLine()); break; case NEED_PT_SECMECH: @@ -291,7 +307,7 @@ } getOutStream().print(HBCIUtilsInternal.getLocMsg("CALLB_SELECT_ENTRY")+": "); getOutStream().flush(); - retData.replace(0,retData.length(),getInStream().readLine()); + retData.replace(0,retData.length(),this.readLine()); break; case NEED_PT_TANMEDIA: @@ -312,7 +328,7 @@ } getOutStream().print(HBCIUtilsInternal.getLocMsg("CALLB_SELECT_ENTRY")+": "); getOutStream().flush(); - retData.replace(0,retData.length(),getInStream().readLine()); + retData.replace(0,retData.length(),this.readLine()); } break; @@ -321,7 +337,7 @@ getOutStream().println(msg); getOutStream().println(HBCIUtilsInternal.getLocMsg("CONTINUE")); getOutStream().flush(); - getInStream().readLine(); + this.readLine(); break; case USERID_CHANGED: diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/comm/CommStandard.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/comm/CommStandard.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/comm/CommStandard.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/comm/CommStandard.java 2019-11-27 09:04:22.000000000 +0000 @@ -106,7 +106,7 @@ boolean sizeknown = false; int msgsize=-1; - HBCIUtils.log("waiting for response",HBCIUtils.LOG_INFO); + HBCIUtils.log(HBCIUtilsInternal.getLocMsg("STATUS_MSG_RECV"),HBCIUtils.LOG_INFO); try { StringBuffer res=new StringBuffer(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/datatypes/SyntaxCtr.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/datatypes/SyntaxCtr.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/datatypes/SyntaxCtr.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/datatypes/SyntaxCtr.java 2019-11-27 09:04:22.000000000 +0000 @@ -21,8 +21,8 @@ package org.kapott.hbci.datatypes; -import org.kapott.hbci.exceptions.InvalidArgumentException; import org.kapott.hbci.exceptions.InvalidUserDataException; +import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.manager.HBCIUtilsInternal; /* @brief class for storing data of type "country" @@ -199,7 +199,8 @@ } else if (x.equals("978")) { ret="EU"; } else { - throw new InvalidArgumentException(HBCIUtilsInternal.getLocMsg("EXC_DT_UNNKOWN_CTR",x)); + HBCIUtils.log("unable to determine country code for: \"" + x + "\", fallback to DE",HBCIUtils.LOG_DEBUG); + ret = "DE"; } return ret; diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/AbstractRawHBCIDialog.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/AbstractRawHBCIDialog.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/AbstractRawHBCIDialog.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/AbstractRawHBCIDialog.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,214 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; + +import org.kapott.hbci.dialog.KnownTANProcess.Variant; +import org.kapott.hbci.exceptions.HBCI_Exception; +import org.kapott.hbci.manager.HBCIKernelImpl; +import org.kapott.hbci.manager.HBCIUtils; +import org.kapott.hbci.passport.HBCIPassportInternal; +import org.kapott.hbci.status.HBCIMsgStatus; +import org.kapott.hbci.tools.StringUtil; + +/** + * Abstrakte Basis-Klasse fuer "rohe" HBCI-Dialoge. + */ +public abstract class AbstractRawHBCIDialog implements RawHBCIDialog +{ + private KnownDialogTemplate template = null; + private AtomicInteger executions = new AtomicInteger(0); + + /** + * ct. + * @param template das zu verwendende Template. + */ + AbstractRawHBCIDialog(KnownDialogTemplate template) + { + this.template = template; + } + + /** + * @see org.kapott.hbci.dialog.RawHBCIDialog#execute(org.kapott.hbci.dialog.DialogContext) + */ + public final HBCIMsgStatus execute(final DialogContext ctx) + { + HBCIMsgStatus status = null; + + do + { + if (ctx.isDialogEnd()) + { + ctx.setDialogEnd(false); + final HBCIDialogEnd end = new HBCIDialogEnd(); + end.execute(ctx); + } + // Sicherstellen, dass das Flag false ist, wenn wir starten. Kann von einem Event wieder aktiviert werden + ctx.setRepeat(false); + + // Checken, ob ein Neustart noch moeglich ist: + if (this.executions.get() > 2) + { + HBCIUtils.log("dialog loop detected for " + this.getTemplate() + ", id " + ctx.getDialogId() + ", message number: " + ctx.getMsgNum() + ", execution count: " + this.executions.get(),HBCIUtils.LOG_ERR); + throw new HBCI_Exception("dialog loop detected for " + this.getTemplate()); + } + + ctx.setDialogInit(this); + + final HBCIPassportInternal p = ctx.getPassport(); + final HBCIKernelImpl k = ctx.getKernel(); + + /////////////////////////////////////////////////////// + // Erstellung der Nachricht + p.onDialogEvent(DialogEvent.MSG_CREATE,ctx); + final String dialog = this.getActualTemplate(ctx); + HBCIUtils.log("creating dialog " + dialog + ", id " + ctx.getDialogId() + ", message number: " + ctx.getMsgNum() + ", execution count: " + this.executions.get(),HBCIUtils.LOG_DEBUG); + k.rawNewMsg(dialog); + this.applyData(ctx); + p.onDialogEvent(DialogEvent.MSG_CREATED,ctx); + // + /////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////// + // Versand der Nachricht + HBCIUtils.log("sending message using dialog " + dialog + ", id " + ctx.getDialogId() + ", message number: " + ctx.getMsgNum(),HBCIUtils.LOG_DEBUG); + status = this.sendData(ctx); + // + /////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////// + // Ergebnis-Auswertung + ctx.update(status); + this.executions.incrementAndGet(); + p.onDialogEvent(DialogEvent.MSG_SENT,ctx); + this.checkResult(ctx); + // + /////////////////////////////////////////////////////// + } + while (ctx.isRepeat()); + + return status; + } + + /** + * Default-Implementierung fuer den Versand. Verschluesselung und Signierung findet nur statt, wenn es kein anonymer Dialog ist. + * @param ctx der Kontext. + * @return die Ergebnis-Daten. + */ + protected HBCIMsgStatus sendData(final DialogContext ctx) + { + final boolean a = ctx.isAnonymous(); + return ctx.getKernel().rawDoIt(!a && HBCIKernelImpl.SIGNIT, + !a && HBCIKernelImpl.CRYPTIT, + !a && HBCIKernelImpl.NEED_CRYPT); + } + + /** + * Befuellt die Daten fuer die Nachricht. + * @param ctx der Kontext. + */ + protected void applyData(final DialogContext ctx) + { + final HBCIKernelImpl k = ctx.getKernel(); + + k.rawSet("MsgHead.dialogid",ctx.getDialogId()); + k.rawSet("MsgHead.msgnum",Integer.toString(ctx.getMsgNum())); + k.rawSet("MsgTail.msgnum",Integer.toString(ctx.getMsgNum())); + } + + /** + * Kann implementiert werden, um das Ergebnis des Dialogs zu pruefen. + * @param ctx der Kontext. + */ + protected void checkResult(final DialogContext ctx) + { + } + + /** + * @see org.kapott.hbci.dialog.RawHBCIDialog#createSCARequest(java.util.Properties, int) + */ + @Override + public SCARequest createSCARequest(Properties secmechInfo, int hktanVersion) + { + SCARequest r = new SCARequest(); + r.setVersion(hktanVersion); + r.setVariant(Variant.determine(secmechInfo != null ? secmechInfo.getProperty("process") : null)); + return r; + } + + /** + * @see org.kapott.hbci.dialog.RawHBCIDialog#getTemplate() + */ + @Override + public KnownDialogTemplate getTemplate() + { + return this.template; + } + + /** + * @see org.kapott.hbci.dialog.RawHBCIDialog#setTemplate(org.kapott.hbci.dialog.KnownDialogTemplate) + */ + @Override + public void setTemplate(KnownDialogTemplate t) + { + this.template = t; + } + + /** + * Liefert das tatsaechlich zu verwendende Message-Template basierend auf dem Kontext. + * @param ctx der Kontext. + * @return template das zu verwendende Message-Template. + */ + protected String getActualTemplate(final DialogContext ctx) + { + return this.getTemplate().getName(); + } + + /** + * Liefert die hoechste bei der Bank verfuegbare Segment-Version. + * @param ctx der Kontext. + * @param gvName der Name des Geschaeftsvorfalls. + * @param defaultVersion die Default-Version, wenn keine gefunden wurde. + * @return die Segment-Version oder NULL, wenn keine brauchbare Version unterstuetzt wird + */ + protected Integer getSegmentVersion(DialogContext ctx, String gvName, Integer defaultVersion) + { + final HBCIPassportInternal p = ctx.getPassport(); + final Properties props = p.getParamSegmentNames(); + final String version = props.getProperty(gvName); + + if (!StringUtil.hasText(version)) + return defaultVersion; + + try + { + return Integer.valueOf(version); + } catch (Exception e) + { + HBCIUtils.log("invalid segment version: " + version, HBCIUtils.LOG_WARN); + return defaultVersion; + } + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/AbstractRawHBCIDialogInit.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/AbstractRawHBCIDialogInit.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/AbstractRawHBCIDialogInit.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/AbstractRawHBCIDialogInit.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,61 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import org.kapott.hbci.manager.HBCIKernelImpl; +import org.kapott.hbci.manager.HBCIUtils; +import org.kapott.hbci.passport.HBCIPassportInternal; + +/** + * Abstrakte Basis-Klasse fuer "rohe" HBCI-Init-Dialoge. + */ +public abstract class AbstractRawHBCIDialogInit extends AbstractRawHBCIDialog +{ + /** + * ct. + * @param template das Template. + */ + AbstractRawHBCIDialogInit(KnownDialogTemplate template) + { + super(template); + } + + /** + * @see org.kapott.hbci.dialog.AbstractRawHBCIDialog#applyData(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected void applyData(final DialogContext ctx) + { + super.applyData(ctx); + + final HBCIPassportInternal p = ctx.getPassport(); + final HBCIKernelImpl k = ctx.getKernel(); + + k.rawSet("Idn.KIK.blz", p.getBLZ()); + k.rawSet("Idn.KIK.country", p.getCountry()); + k.rawSet("ProcPrep.BPD", p.getBPDVersion()); + k.rawSet("ProcPrep.UPD", p.getUPDVersion()); + k.rawSet("ProcPrep.lang",p.getLang()); + k.rawSet("ProcPrep.prodName",HBCIUtils.getParam("client.product.name",HBCIUtils.PRODUCT_ID)); + k.rawSet("ProcPrep.prodVersion",HBCIUtils.getParam("client.product.version","3")); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/DialogContext.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/DialogContext.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/DialogContext.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/DialogContext.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,258 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import org.kapott.hbci.manager.HBCIDialog; +import org.kapott.hbci.manager.HBCIKernelImpl; +import org.kapott.hbci.manager.HBCIUtils; +import org.kapott.hbci.passport.HBCIPassportInternal; +import org.kapott.hbci.status.HBCIMsgStatus; + +/** + * Diese Klasse kapselt die Context-Daten. + */ +public class DialogContext +{ + /** + * Die initiale Dialog-ID. + */ + private final static String DIALOGID_INITIAL = "0"; + + /** + * Initiale Nachrichtennumer. + */ + private final static int MSGNUM_INITIAL = 1; + + private HBCIKernelImpl kernel; + private HBCIPassportInternal passport; + private HBCIMsgStatus msgStatus; + private RawHBCIDialog init; + private HBCIDialog dialog; + private Map meta = new HashMap(); + + private AtomicInteger msgNum = new AtomicInteger(MSGNUM_INITIAL); + private String dialogId; + private boolean anonymous = false; + + private final AtomicBoolean repeat = new AtomicBoolean(false); + private final AtomicBoolean dialogEnd = new AtomicBoolean(false); + + /** + * Erzeugt einen neuen Dialog-Context. + * @param kernel der Kernel. + * @param passport der Passport. + * @return der neue Context. + */ + public static DialogContext create(HBCIKernelImpl kernel, HBCIPassportInternal passport) + { + DialogContext ctx = new DialogContext(); + ctx.kernel = kernel; + ctx.passport = passport; + return ctx; + } + + /** + * ct. + */ + private DialogContext() + { + } + + /** + * Liefert den Kernel. + * @return der Kernel. Kann NULL sein. + */ + public HBCIKernelImpl getKernel() + { + return kernel; + } + + /** + * Liefert die Dialog-Initialisierung. + * @return die Dialog-Initialisierung. + */ + public RawHBCIDialog getDialogInit() + { + return init; + } + + /** + * Speichert die Dialog-Initialisierung. + * @param dialog die Dialog-Initialisierung. + */ + void setDialogInit(RawHBCIDialog dialog) + { + this.init = dialog; + } + + /** + * Liefert den Message-Status. + * @return der Message-Status. Kann NULL sein. + */ + public HBCIMsgStatus getMsgStatus() + { + return msgStatus; + } + + /** + * Aktualisiert den Kontext mit dem aktuellen Nachrichten-Status. + * @param msgStatus der Message-Status. + */ + void update(HBCIMsgStatus msgStatus) + { + this.msgStatus = msgStatus; + this.msgNum.incrementAndGet(); // Nachrichtennummer erhoehen + + // Wir uebernehmen bei der Gelegenheit gleich noch den aktuellen Status. + if (this.msgStatus.isOK()) + { + final Properties result = this.msgStatus.getData(); + this.dialogId = (result != null ? result.getProperty("MsgHead.dialogid",null) : null); + + if (this.dialogId != null) + HBCIUtils.log("new dialog-id: " + this.dialogId,HBCIUtils.LOG_DEBUG); + } + } + + /** + * Liefert den aktuellen Dialog. + * @return der aktuelle Dialog. Kann NULL sein. + */ + public HBCIDialog getDialog() + { + return dialog; + } + + /** + * Speichert den aktuellen Dialog. + * @param dialog der aktuelle Dialog. + */ + public void setDialog(HBCIDialog dialog) + { + this.dialog = dialog; + } + + /** + * Liefert den Passport. + * @return der Passport. + */ + public HBCIPassportInternal getPassport() + { + return passport; + } + + /** + * Liefert true, wenn der Dialog anonym ist. + * @return true, wenn der Dialog anonym ist. + */ + public boolean isAnonymous() + { + return this.anonymous; + } + + /** + * Speichert, ob der Dialog anonym ist. + * @param anonymous true, wenn der Dialog anonym ist. + */ + public void setAnonymous(boolean anonymous) + { + this.anonymous = anonymous; + } + + /** + * Map mit frei definierbaren Meta-Daten. + * @return meta frei definierbare Meta-Daten. + */ + public Map getMeta() + { + return meta; + } + + /** + * Liefert die aktuelle Dialog-ID. + * @return die aktuelle Dialog-ID. + */ + public String getDialogId() + { + return this.dialogId != null ? this.dialogId : DIALOGID_INITIAL; + } + + /** + * Speichert die aktuelle Dialog-ID. + * @param dialogId die aktuelle Dialog-ID. + */ + void setDialogId(String dialogId) + { + this.dialogId = dialogId; + } + + /** + * Liefert die aktuelle Nachrichtennummer. + * @return die aktuelle Nachrichtennummer. + */ + public int getMsgNum() + { + return this.msgNum.get(); + } + + /** + * Legt fest, ob vor dem Repeat ein Dialog-Ende gesendet werden soll. + * @param end true, wenn vor dem Repeat ein Dialog-Ende gesendet werden soll. + */ + public void setDialogEnd(boolean end) + { + this.dialogEnd.set(end); + } + + /** + * Liefert true, wenn vor dem Repeat ein Dialog-Ende gesendet werden soll. + * @return true, wenn vor dem Repeat ein Dialog-Ende gesendet werden soll. + */ + public boolean isDialogEnd() + { + return this.dialogEnd.get(); + } + + /** + * Teilt dem Dialog mit, dass er erneut ausgefuehrt werden soll. + * @param repeat true, wenn der Dialog wiederholt werden soll. + */ + public void setRepeat(boolean repeat) + { + this.repeat.set(repeat); + } + + /** + * Prueft, ob der Dialog erneut ausgefuehrt werden soll. + * @return true, wenn der Dialog erneut ausgefuehrt werden soll. + */ + public boolean isRepeat() + { + return this.repeat.get(); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/DialogEvent.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/DialogEvent.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/DialogEvent.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/DialogEvent.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,53 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +/** + * Diese Klasse enthaelt die verschiedenen Event-Arten. + */ +public enum DialogEvent +{ + /** + * Wird gesendet, bevor HBCI4Java die Nachricht erzeugt aber noch nicht an die Bank gesendet hat. + * Kann mehrfach auftreten, wenn z.Bsp. die Dialog-Initialisierung nach einer Neuwahl des TAN-Verfahrens (aus Code 3920) wiederholt werden muss. + */ + MSG_CREATE, + + /** + * Wird versendet, bevor HBCI4Java die Nachricht an die Bank gesendet hat und das Response vorliegt. + * Kann mehrfach auftreten, wenn z.Bsp. die Dialog-Initialisierung nach einer Neuwahl des TAN-Verfahrens (aus Code 3920) wiederholt werden muss. + */ + MSG_CREATED, + + /** + * Wird versendet, nachdem HBCI4Java die Nachricht an die Bank gesendet hat und das Response vorliegt. + * Kann mehrfach auftreten, wenn z.Bsp. die Dialog-Initialisierung nach einer Neuwahl des TAN-Verfahrens (aus Code 3920) wiederholt werden muss. + */ + MSG_SENT, + + /** + * Wird gesendet, nachdem die Dialog-Initialisierung abgeschlossen ist und bevor die eigentlichen Geschaeftsvorfaelle an die Bank gesendet werden. + */ + JOBS_CREATED, +} + + diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogEnd.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogEnd.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogEnd.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogEnd.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,140 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.kapott.hbci.callback.HBCICallback; +import org.kapott.hbci.exceptions.ProcessException; +import org.kapott.hbci.manager.HBCIKernelImpl; +import org.kapott.hbci.manager.HBCIUtils; +import org.kapott.hbci.manager.HBCIUtilsInternal; +import org.kapott.hbci.passport.HBCIPassportInternal; +import org.kapott.hbci.status.HBCIMsgStatus; + +/** + * Diese Klasse enthaelt das Dialog-Ende. + */ +public class HBCIDialogEnd extends AbstractRawHBCIDialog +{ + /** + * Parametrisierende Flags fuer das Dialog-Ende. + */ + public enum Flag + { + /** + * Dialogende fuer Sync-SigID. + */ + SIG_ID, + + /** + * Fehler werden nicht geworfen sondern nur geloggt. + * Muss nur explizit angegeben werden, wenn es kein anonymer Dialog ist oder das Werfen per "client.errors.ignoreDialogEndErrors" abgeschaltet ist. + */ + ACCEPT_ERROR, + + } + + private List flags = new ArrayList(); + + /** + * ct. + * @param flags optionale Flags. + */ + public HBCIDialogEnd(Flag... flags) + { + super(KnownDialogTemplate.END); + if (flags != null) + this.flags.addAll(Arrays.asList(flags)); + } + + /** + * @see org.kapott.hbci.dialog.AbstractHBCIDialogInit#applyData(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected void applyData(DialogContext ctx) + { + super.applyData(ctx); + + final HBCIKernelImpl k = ctx.getKernel(); + k.rawSet("DialogEndS.dialogid",ctx.getDialogId()); + } + + /** + * @see org.kapott.hbci.dialog.AbstractRawHBCIDialog#sendData(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected HBCIMsgStatus sendData(DialogContext ctx) + { + if (this.flags.contains(Flag.SIG_ID)) + { + final HBCIPassportInternal p = ctx.getPassport(); + return ctx.getKernel().rawDoIt(p.hasMySigKey(),HBCIKernelImpl.CRYPTIT,p.hasMyEncKey()); + } + return super.sendData(ctx); + } + + /** + * @see org.kapott.hbci.dialog.AbstractRawHBCIDialog#checkResult(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected void checkResult(DialogContext ctx) + { + super.checkResult(ctx); + + final HBCIMsgStatus ret = ctx.getMsgStatus(); + + HBCIUtilsInternal.getCallback().status(ctx.getPassport(),HBCICallback.STATUS_DIALOG_END_DONE,ret); + + if (ret.isOK()) + return; + + final String msg = HBCIUtilsInternal.getLocMsg("ERR_INST_ENDFAILED"); + + // Checken, ob es ein anonymer Dialog war. Der kann fehlschlagen. Daher tolerieren wir hier auch fehlgeschlagene Dialog-Enden + // Ausserdem ignorieren wir den Fehler, wenn es explizit so konfiguriert ist oder per Flag angegeben ist. + final boolean ignore = ctx.isAnonymous() || + this.flags.contains(Flag.ACCEPT_ERROR) || + HBCIUtilsInternal.ignoreError(null,"client.errors.ignoreDialogEndErrors",msg+": "+ret.getErrorString()); + + // Loggen + HBCIUtils.log("dialog end failed: "+ret.getErrorString(),ignore ? HBCIUtils.LOG_WARN : HBCIUtils.LOG_ERR); + + ProcessException e = new ProcessException(msg,ret); + ret.addException(e); + + // Fehler werfen, wenn er nicht ignoriert werden darf + if (!ignore) + throw e; + } + + /** + * @see org.kapott.hbci.dialog.AbstractHBCIDialogInit#getActualTemplate(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected String getActualTemplate(DialogContext ctx) + { + return this.getTemplate().getName() + (ctx.isAnonymous() ? "Anon" : ""); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogFirstKeyRequest.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogFirstKeyRequest.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogFirstKeyRequest.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogFirstKeyRequest.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,78 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import org.kapott.hbci.manager.HBCIKernelImpl; +import org.kapott.hbci.passport.HBCIPassportInternal; +import org.kapott.hbci.status.HBCIMsgStatus; + +/** + * Diese Klasse enthaelt den Request fuer die Abfrage der Bankensignatur. + */ +public class HBCIDialogFirstKeyRequest extends AbstractRawHBCIDialogInit +{ + /** + * ct. + */ + public HBCIDialogFirstKeyRequest() + { + super(KnownDialogTemplate.FIRSTKEYREQUEST); + } + + /** + * @see org.kapott.hbci.dialog.AbstractRawHBCIDialog#applyData(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected void applyData(DialogContext ctx) + { + super.applyData(ctx); + + final HBCIPassportInternal p = ctx.getPassport(); + final HBCIKernelImpl k = ctx.getKernel(); + + final String country = p.getCountry(); + final String blz = p.getBLZ(); + + k.rawSet("KeyReq.SecProfile.method",p.getProfileMethod()); + k.rawSet("KeyReq.SecProfile.version",p.getProfileVersion()); + k.rawSet("KeyReq.KeyName.keytype", "V"); + k.rawSet("KeyReq.KeyName.KIK.blz", blz); + k.rawSet("KeyReq.KeyName.KIK.country", country); + k.rawSet("KeyReq_2.SecProfile.method",p.getProfileMethod()); + k.rawSet("KeyReq_2.SecProfile.version",p.getProfileVersion()); + k.rawSet("KeyReq_2.KeyName.keytype", "S"); + k.rawSet("KeyReq_2.KeyName.KIK.blz", blz); + k.rawSet("KeyReq_2.KeyName.KIK.country", country); + } + + /** + * @see org.kapott.hbci.dialog.AbstractHBCIDialogInit#sendData(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected HBCIMsgStatus sendData(DialogContext ctx) + { + // Generell ohne Signatur und ohne Verschluesselung + return ctx.getKernel().rawDoIt(HBCIKernelImpl.DONT_SIGNIT, + HBCIKernelImpl.DONT_CRYPTIT, + HBCIKernelImpl.DONT_NEED_CRYPT); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogInit.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogInit.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogInit.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogInit.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,65 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import org.kapott.hbci.manager.HBCIKernelImpl; +import org.kapott.hbci.passport.HBCIPassportInternal; + +/** + * Diese Klasse enthaelt die Dialog-Initialisierung. + */ +public class HBCIDialogInit extends AbstractRawHBCIDialogInit +{ + /** + * ct. + */ + public HBCIDialogInit() + { + super(KnownDialogTemplate.INIT); + } + + /** + * @see org.kapott.hbci.dialog.AbstractHBCIDialogInit#applyData(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected void applyData(DialogContext ctx) + { + super.applyData(ctx); + + final HBCIPassportInternal p = ctx.getPassport(); + final HBCIKernelImpl k = ctx.getKernel(); + final boolean a = ctx.isAnonymous(); + + k.rawSet("Idn.customerid", a ? "9999999999" : p.getCustomerId()); + k.rawSet("Idn.sysid", a ? "0" : p.getSysId()); + k.rawSet("Idn.sysStatus",a ? "0" : p.getSysStatus()); + } + + /** + * @see org.kapott.hbci.dialog.AbstractHBCIDialogInit#getActualTemplate(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected String getActualTemplate(DialogContext ctx) + { + return this.getTemplate().getName() + (ctx.isAnonymous() ? "Anon" : ""); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogLockKeys.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogLockKeys.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogLockKeys.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogLockKeys.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,87 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import org.kapott.hbci.exceptions.ProcessException; +import org.kapott.hbci.manager.HBCIKernelImpl; +import org.kapott.hbci.manager.HBCIUtilsInternal; +import org.kapott.hbci.passport.HBCIPassportInternal; +import org.kapott.hbci.status.HBCIMsgStatus; + +/** + * Diese Klasse enthaelt die Message zum Sperren von Schluesseln. + */ +public class HBCIDialogLockKeys extends AbstractRawHBCIDialog +{ + /** + * ct. + */ + public HBCIDialogLockKeys() + { + super(KnownDialogTemplate.LOCKKEYS); + } + + /** + * @see org.kapott.hbci.dialog.AbstractHBCIDialogInit#applyData(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected void applyData(DialogContext ctx) + { + super.applyData(ctx); + + final HBCIPassportInternal p = ctx.getPassport(); + final HBCIKernelImpl k = ctx.getKernel(); + + k.rawSet("KeyLock.KeyName.KIK.country",p.getCountry()); + k.rawSet("KeyLock.KeyName.KIK.blz",p.getBLZ()); + k.rawSet("KeyLock.KeyName.userid",p.getMySigKeyName()); + k.rawSet("KeyLock.KeyName.keynum",p.getMySigKeyNum()); + k.rawSet("KeyLock.KeyName.keyversion",p.getMySigKeyVersion()); + k.rawSet("KeyLock.SecProfile.method", p.getProfileMethod()); + k.rawSet("KeyLock.SecProfile.version", p.getProfileVersion()); + k.rawSet("KeyLock.locktype","999"); + } + + /** + * @see org.kapott.hbci.dialog.AbstractRawHBCIDialog#sendData(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected HBCIMsgStatus sendData(DialogContext ctx) + { + return ctx.getKernel().rawDoIt(HBCIKernelImpl.SIGNIT, + HBCIKernelImpl.CRYPTIT, + HBCIKernelImpl.DONT_NEED_CRYPT); + } + + /** + * @see org.kapott.hbci.dialog.AbstractRawHBCIDialog#checkResult(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected void checkResult(DialogContext ctx) + { + super.checkResult(ctx); + + final HBCIMsgStatus ret = ctx.getMsgStatus(); + if (!ret.isOK()) + throw new ProcessException(HBCIUtilsInternal.getLocMsg("EXCMSG_LOCKFAILED"),ret); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogSepaInfo.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogSepaInfo.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogSepaInfo.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogSepaInfo.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,176 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import java.util.Date; +import java.util.Objects; +import java.util.Properties; + +import org.kapott.hbci.manager.HBCIUser; +import org.kapott.hbci.manager.HBCIUtils; +import org.kapott.hbci.manager.HBCIUtilsInternal; +import org.kapott.hbci.passport.HBCIPassportInternal; +import org.kapott.hbci.status.HBCIMsgStatus; +import org.kapott.hbci.tools.StringUtil; + +/** + * Diese Klasse enthaelt den Dialog fuer den Abruf der SEPA-Informationen der Konten. + */ +public class HBCIDialogSepaInfo extends AbstractRawHBCIDialog +{ + private final static String GVNAME = "SEPAInfo"; + + /** + * ct. + */ + public HBCIDialogSepaInfo() + { + super(KnownDialogTemplate.SEPAINFO); + } + + /** + * Prueft, ob der Dialog noetig ist. + * @param ctx der Context. + * @return true, wenn er noetig ist. + */ + public boolean required(DialogContext ctx) + { + HBCIPassportInternal p = ctx.getPassport(); + final Properties upd = p.getUPD(); + if (upd == null) + return true; + + return !upd.containsKey(HBCIUser.UPD_KEY_FETCH_SEPAINFO); + } + + /** + * Prueft, ob der Dialog moeglich ist. + * @param ctx der Context. + * @return true, wenn er moeglich ist. + */ + public boolean supported(DialogContext ctx) + { + return getSegmentVersion(ctx,GVNAME,null) != null; + } + + /** + * @see org.kapott.hbci.dialog.AbstractRawHBCIDialog#checkResult(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected void checkResult(DialogContext ctx) + { + final HBCIPassportInternal p = ctx.getPassport(); + final Properties upd = p.getUPD(); + + try + { + super.checkResult(ctx); + + final HBCIMsgStatus ret = ctx.getMsgStatus(); + if (!ret.isOK()) + return; + + final Properties result = ret.getData(); + if (result == null) + return; + + // Wenn wir noch keine UPD haben, koennen wir uns die Auswertung schenken. Wir + // wissen dann eh nicht, wo wir die Daten einsortieren sollen. + if (upd == null) + { + HBCIUtils.log("suspect, got SEPAInfo result but have no UPD",HBCIUtils.LOG_INFO); + return; + } + + int count = 0; + + for (int i=0;i<500;i++) + { + final String header = HBCIUtilsInternal.withCounter("SEPAInfoRes1.Acc", i); + final String cansepa = result.getProperty(header + ".sepa"); + + // Ende + if (!StringUtil.hasText(cansepa)) + break; + + // Konto kann kein Sepa + if (cansepa.equals("N")) + continue; + + final String iban = result.getProperty(header + ".iban"); + final String bic = result.getProperty(header + ".bic"); + final String country = result.getProperty(header + ".KIK.country"); + final String blz = result.getProperty(header + ".KIK.blz"); + final String number = result.getProperty(header + ".number"); + + // keine IBAN erhalten + if (!StringUtil.hasText(iban)) + continue; + + HBCIUtils.log("found BIC/IBAN = " + bic + "/" + iban + " for account " + country + "/" + blz + "/" + number,HBCIUtils.LOG_DEBUG); + + // Konto in den UPD suchen und UPD-Informationen aktualisieren + for (int j=0;j<500;j++) + { + final String h = HBCIUtilsInternal.withCounter("KInfo",j); + final String n = upd.getProperty(h + ".KTV.number"); + final String c = upd.getProperty(h + ".KTV.KIK.country"); + final String b = upd.getProperty(h + ".KTV.KIK.blz"); + + // Ende + if (!StringUtil.hasText(n)) + break; + + // Land, BLZ und Konto stimmen ueberein - wir uebernehmen IBAN und BIC in das Konto + if (Objects.equals(country,c) && Objects.equals(blz,b) && Objects.equals(number,n)) + { + count++; + + HBCIUtils.log("updating BIC/IBAN = " + bic + "/" + iban + " for account " + country + "/" + blz + "/" + number,HBCIUtils.LOG_DEBUG); + + // uebernehmen wir nur, wenn wir eine haben + if (StringUtil.hasText(iban)) + upd.setProperty(h + ".KTV.iban", iban); + + // uebernehmen wir nur, wenn wir eine haben + if (StringUtil.hasText(bic)) + upd.setProperty(h + ".KTV.bic", bic); + + break; + } + } + } + final String name = (count == 1 ? "Konto" : "Konten"); + HBCIUtils.log("IBAN/BIC für " + count + " " + name + " empfangen", HBCIUtils.LOG_INFO); + } + finally + { + // Egal, wie das Abrufen der SEPA-Infos ausgegangen ist, wir vermerken es als erledigt, + // damit es nicht immer wieder wiederholt wird. + if (p != null && upd != null) + { + upd.setProperty(HBCIUser.UPD_KEY_FETCH_SEPAINFO,new Date().toString()); + p.saveChanges(); // Sicherstellen, dass die Aenderungen sofort gespeichert sind + } + } + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogSync.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogSync.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogSync.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogSync.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,127 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import org.kapott.hbci.exceptions.ProcessException; +import org.kapott.hbci.manager.HBCIKernelImpl; +import org.kapott.hbci.manager.HBCIUtilsInternal; +import org.kapott.hbci.passport.HBCIPassportInternal; +import org.kapott.hbci.status.HBCIMsgStatus; + +/** + * Diese Klasse enthaelt die Synchronisierung. + */ +public class HBCIDialogSync extends AbstractRawHBCIDialogInit +{ + /** + * Legt fest, ob die System-ID oder die Signatur-ID synchronisiert werden soll. + */ + public enum Mode + { + /** + * System-ID. + */ + SYS_ID, + + /** + * Signatur-ID. + */ + SIG_ID + } + + private Mode mode = null; + + /** + * ct. + * @param mode der Modus. + */ + public HBCIDialogSync(Mode mode) + { + super(KnownDialogTemplate.SYNC); + this.mode = mode; + } + + /** + * @see org.kapott.hbci.dialog.AbstractHBCIDialogInit#applyData(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected void applyData(DialogContext ctx) + { + super.applyData(ctx); + + final HBCIPassportInternal p = ctx.getPassport(); + final HBCIKernelImpl k = ctx.getKernel(); + + k.rawSet("Idn.customerid", p.getCustomerId()); + + if (this.mode == Mode.SYS_ID) + { + k.rawSet("Idn.sysid", "0"); + k.rawSet("Idn.sysStatus", "1"); + k.rawSet("Sync.mode", "0"); + } + else + { + k.rawSet("Idn.sysid", p.getSysId()); + k.rawSet("Idn.sysStatus", p.getSysStatus()); + k.rawSet("Sync.mode", "2"); + } + + // PSD2: Beim Synchronisieren senden wir der Bank, dass wir angeblich nur BPD-Version 0 haben. + // Das forciert, dass die Bank uns die BPD neu schickt. Diesmal aber nicht die anoyme Version sondern + // die mit SCA. Dort sind im HIPINS-Segment dann naemlich auch HKKAZ & Co. TAN-pflichtig + if (!ctx.isAnonymous()) + k.rawSet("ProcPrep.BPD", "0"); + } + + /** + * @see org.kapott.hbci.dialog.AbstractHBCIDialogInit#sendData(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected HBCIMsgStatus sendData(DialogContext ctx) + { + if (this.mode == Mode.SIG_ID) + { + final HBCIPassportInternal p = ctx.getPassport(); + return ctx.getKernel().rawDoIt(p.hasMySigKey(), + HBCIKernelImpl.CRYPTIT, + p.hasMyEncKey()); + } + + return super.sendData(ctx); + } + + /** + * @see org.kapott.hbci.dialog.AbstractRawHBCIDialog#checkResult(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected void checkResult(DialogContext ctx) + { + super.checkResult(ctx); + + final HBCIMsgStatus ret = ctx.getMsgStatus(); + if (ret.isOK()) + return; + + throw new ProcessException(HBCIUtilsInternal.getLocMsg((this.mode == Mode.SIG_ID) ? "EXCMSG_SYNCSIGIDFAIL" : "EXCMSG_SYNCSYSIDFAIL"),ret); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogTanMedia.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogTanMedia.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogTanMedia.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIDialogTanMedia.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,166 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import java.util.Date; +import java.util.Objects; +import java.util.Properties; + +import org.kapott.hbci.manager.HBCIKernelImpl; +import org.kapott.hbci.manager.HBCIUser; +import org.kapott.hbci.manager.HBCIUtils; +import org.kapott.hbci.manager.HBCIUtilsInternal; +import org.kapott.hbci.passport.AbstractPinTanPassport; +import org.kapott.hbci.passport.HBCIPassportInternal; +import org.kapott.hbci.status.HBCIMsgStatus; +import org.kapott.hbci.tools.StringUtil; + +/** + * Diese Klasse enthaelt den Dialog fuer den Abruf der TAN-Medien. + */ +public class HBCIDialogTanMedia extends AbstractRawHBCIDialog +{ + private final static String GVNAME = "TANMediaList"; + + /** + * ct. + */ + public HBCIDialogTanMedia() + { + super(KnownDialogTemplate.TANMEDIA); + } + + /** + * Prueft, ob der Dialog noetig ist. + * @param ctx der Context. + * @return true, wenn er noetig ist. + */ + public boolean required(DialogContext ctx) + { + HBCIPassportInternal p = ctx.getPassport(); + final Properties upd = p.getUPD(); + if (upd == null) + return true; + + return !upd.containsKey(HBCIUser.UPD_KEY_FETCH_TANMEDIA); + } + + /** + * Prueft, ob der Dialog moeglich ist. + * @param ctx der Context. + * @return true, wenn er moeglich ist. + */ + public boolean supported(DialogContext ctx) + { + HBCIPassportInternal p = ctx != null ? ctx.getPassport() : null; + if (p == null || !(p instanceof AbstractPinTanPassport)) + return false; + + return getSegmentVersion(ctx,GVNAME,0) >= 2; + } + + /** + * @see org.kapott.hbci.dialog.AbstractHBCIDialogInit#applyData(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected void applyData(DialogContext ctx) + { + super.applyData(ctx); + + final HBCIKernelImpl k = ctx.getKernel(); + final Integer version = getSegmentVersion(ctx,GVNAME,5); + k.rawSet(GVNAME + version + ".mediatype","0"); // Eigentlich wollen wir nur 1 (also nur die aktiven). Aber die SPK akzeptiert das nicht + k.rawSet(GVNAME + version + ".mediacategory","A"); // Wir wollen alle Medien-Arten + } + + /** + * @see org.kapott.hbci.dialog.AbstractRawHBCIDialog#checkResult(org.kapott.hbci.dialog.DialogContext) + */ + @Override + protected void checkResult(DialogContext ctx) + { + final HBCIPassportInternal p = ctx.getPassport(); + Properties upd = null; + + try + { + super.checkResult(ctx); + + final HBCIMsgStatus ret = ctx.getMsgStatus(); + if (!ret.isOK()) + return; + + final Properties result = ret.getData(); + if (result == null) + return; + + final Integer version = getSegmentVersion(ctx,GVNAME,5); + + final StringBuilder sb = new StringBuilder(); + for (int i=0;i<100;i++) + { + final String header = HBCIUtilsInternal.withCounter("TANMediaListRes" + version + ".MediaInfo",i); + + if (result.getProperty(header + ".mediacategory") == null) + break; + + // Nur aktive + final String status = result.getProperty(header + ".status"); + if (!Objects.equals(status,"1")) + continue; + + final String name = result.getProperty(header + ".medianame"); + if (!StringUtil.hasText(name)) + continue; + + if (sb.length() != 0) + sb.append("|"); + + sb.append(name); + } + + final String names = sb.toString(); + if (!StringUtil.hasText(names)) + return; + + HBCIUtils.log("TAN-Medienbezeichnungen empfangen: " + names, HBCIUtils.LOG_INFO); + upd = p.getUPD(); + if (upd == null) + { + // Fuer den Fall, dass wir das zu einem Zeitpunkt aufgerufen haben, wo wir noch gar keine UPD haben + upd = new Properties(); + p.setUPD(upd); + } + upd.setProperty(HBCIUser.UPD_KEY_TANMEDIA,names); + } + finally + { + // Egal, wie das Abrufen der SEPA-Infos ausgegangen ist, wir vermerken es als erledigt, + // damit es nicht immer wieder wiederholt wird. + if (p != null && upd != null) + { + upd.setProperty(HBCIUser.UPD_KEY_FETCH_TANMEDIA,new Date().toString()); + p.saveChanges(); // Sicherstellen, dass die Aenderungen sofort gespeichert sind + } + } + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIMessage.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIMessage.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIMessage.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIMessage.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,83 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import java.util.ArrayList; +import java.util.List; + +import org.kapott.hbci.GV.HBCIJobImpl; + +/** + * Kapselt die fachlichen Jobs in einr HBCI-Nachricht an die Bank. + */ +public class HBCIMessage +{ + private List tasks = new ArrayList(); + + /** + * Liefert die Kopie der Task-Liste. + * Aenderungen an der Liste wirken sich nicht auf die Nachricht aus. Die Tasks darin koennen jedoch geaendert werden. + * @return die Kopie der Task-Liste. + */ + public List getTasks() + { + return new ArrayList(this.tasks); + } + + /** + * Liefert die Anzahl aller Tasks in der Naxchricht. + * @return die Anzahl aller Tasks in der Naxchricht. + */ + public int getTaskCount() + { + return this.tasks.size(); + } + + /** + * Sucht in der Nachricht nach einem Task mit dem angegebenen HBCI-Code. + * @param hbciCode der HBCI-Code. + * @return der Task oder NULL, wenn er nicht gefunden wurde. + */ + public HBCIJobImpl findTask(String hbciCode) + { + if (hbciCode == null) + return null; + + for (HBCIJobImpl task:this.tasks) + { + if (hbciCode.equals(task.getHBCICode())) + return task; + } + return null; + } + + /** + * Fuegt einen neuen Job zur Nachricht hinzu. + * @param task der neue Job. + */ + public void append(HBCIJobImpl task) + { + this.tasks.add(task); + } +} + + diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIMessageQueue.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIMessageQueue.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIMessageQueue.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIMessageQueue.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,162 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import java.util.ArrayList; +import java.util.List; + +import org.kapott.hbci.GV.HBCIJobImpl; + +/** + * Kapselt die Liste der Nachrichten, die innerhalb eines Dialogs an die Bank gesendet werden sollen. + */ +public class HBCIMessageQueue +{ + private List messages = new ArrayList(); + + /** + * ct. + * Erzeugt die Queue und befuellt sie gleich mit der ersten Nachricht. + */ + public HBCIMessageQueue() + { + this.append(new HBCIMessage()); + } + + /** + * Liefert die Kopie der Nachrichten-Liste. + * Aenderungen an der Liste wirken sich nicht auf die Queue aus. Die Nachrichten darin koennen jedoch geaendert werden. + * @return die Kopie der Nachrichten-Liste. + */ + public List getMessages() + { + return new ArrayList(this.messages); + } + + /** + * Liefert die naechste auszufuehrende Nachricht mit Tasks aus der Queue. + * @return die naechste auszufuehrende Nachricht mit Tasks aus der Queue oder NULL, wenn keine weitere mehr mit Tasks existiert. + */ + public HBCIMessage poll() + { + while (this.messages.size() > 0) + { + HBCIMessage m = this.messages.remove(0); + if (m.getTaskCount() > 0) + return m; + } + return null; + } + + /** + * Liefert die Anzahl aller Tasks in allen Naxchrichten. + * @return die Anzahl aller Tasks in allen Naxchrichten. + */ + public int getTaskCount() + { + int count = 0; + for (HBCIMessage msg:this.messages) + { + count += msg.getTaskCount(); + } + + return count; + } + + /** + * Sucht in der ganzen Queue nach einem Task mit dem angegebenen HBCI-Code. + * @param hbciCode der HBCI-Code. + * @return der Task oder NULL, wenn er nicht gefunden wurde. + */ + public HBCIJobImpl findTask(String hbciCode) + { + if (hbciCode == null) + return null; + + for (HBCIMessage msg:this.messages) + { + HBCIJobImpl task = msg.findTask(hbciCode); + if (task != null) + return task; + } + + return null; + } + + /** + * Liefert die letzte Nachricht. + * @return die letzte Nachricht. + */ + public HBCIMessage getLast() + { + return this.messages.get(this.messages.size()-1); + } + + /** + * Fuegt eine neue Nachricht am Ende der Queue hinzu. + * @param message die neue Nachricht. + */ + public void append(HBCIMessage message) + { + this.messages.add(message); + } + + /** + * Fuegt vor der angegebenen Nachricht noch eine neue hinzu und liefert sie zurueck. + * @param message die Nachricht, vor der noch eine neue eingfuegt werden soll. + * @return die neue Nachricht. + */ + public HBCIMessage insertBefore(HBCIMessage message) + { + if (message == null) + throw new IllegalArgumentException("no message given"); + + final int pos = this.messages.indexOf(message); + + if (pos == -1) + throw new IllegalArgumentException("message unknown to queue"); + + HBCIMessage m = new HBCIMessage(); + this.messages.add(pos,m); + return m; + } + + /** + * Fuegt nach der angegebenen Nachricht noch eine neue hinzu und liefert sie zurueck. + * @param message die Nachricht, vor der noch eine neue eingfuegt werden soll. + * @return die neue Nachricht. + */ + public HBCIMessage insertAfter(HBCIMessage message) + { + if (message == null) + throw new IllegalArgumentException("no message given"); + + final int pos = this.messages.indexOf(message); + + if (pos == -1) + throw new IllegalArgumentException("message unknown to queue"); + + HBCIMessage m = new HBCIMessage(); + this.messages.add(pos+1,m); + return m; + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIProcess.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIProcess.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIProcess.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIProcess.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,43 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + + +package org.kapott.hbci.dialog; + +import org.kapott.hbci.status.HBCIMsgStatus; + +/** + * Kapselt mehrere HBCI-Nachrichten zu einem kompletten Dialog. + * Eigentlich muesste das Interface "RawHBCIDialog" "RawHBCIMessage" heissen, weil es nur + * eine einzelne Nachricht kapselt und keinen kompletten Dialog. Ich lass das aber jetzt mal so. + */ +public interface HBCIProcess +{ + /** + * Fuehrt die Dialoge mit der Bank aus. + * @param ctx der Dialog-Context. + * @return der Ausfuehrungsstatus. + */ + public HBCIMsgStatus execute(final DialogContext ctx); + +} + + diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIProcessSepaInfo.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIProcessSepaInfo.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIProcessSepaInfo.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIProcessSepaInfo.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,81 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import org.kapott.hbci.manager.HBCIUtils; +import org.kapott.hbci.status.HBCIMsgStatus; + +/** + * Kapselt die HBCI-Nachrichten zum Abruf der SEPA-Infos. + */ +public class HBCIProcessSepaInfo implements HBCIProcess +{ + private HBCIDialogSepaInfo dialog = null; + private boolean force = false; + + /** + * ct. + * @param force true, wenn der Abruf der TAN-Medien forciert werden soll, auch wenn er eigentlich nicht noetig ist. + */ + public HBCIProcessSepaInfo(boolean force) + { + this.dialog = new HBCIDialogSepaInfo(); + this.force = force; + } + + /** + * @see org.kapott.hbci.dialog.HBCIProcess#execute(org.kapott.hbci.dialog.DialogContext) + */ + @Override + public HBCIMsgStatus execute(final DialogContext ctx) + { + if (!this.dialog.supported(ctx)) + return null; + + // Weder erzwungen noch noetig + if (!this.force && !this.dialog.required(ctx)) + return null; + + try + { + HBCIUtils.log("trying to fetch SEPA infos",HBCIUtils.LOG_INFO); + + final HBCIDialogInit init = new HBCIDialogInit(); + init.execute(ctx); + + this.dialog.execute(ctx); + + final HBCIDialogEnd end = new HBCIDialogEnd(); + return end.execute(ctx); + } + catch (Exception e) + { + // Wir werfen das nicht hoch. Wenn es fehlschlaegt, dann haben wir halt keine SEPA-Infos. Davon geht die Welt nicht unter + HBCIUtils.log("failed: " + e.getMessage(),HBCIUtils.LOG_INFO); + HBCIUtils.log(e,HBCIUtils.LOG_DEBUG); + + return null; + } + } +} + + diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIProcessTanMedia.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIProcessTanMedia.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIProcessTanMedia.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/HBCIProcessTanMedia.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,132 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import java.util.Objects; +import java.util.Properties; + +import org.kapott.hbci.manager.Feature; +import org.kapott.hbci.manager.HBCIUtils; +import org.kapott.hbci.status.HBCIMsgStatus; + +/** + * Kapselt die HBCI-Nachrichten zum Abruf der TAN-Medien. + */ +public class HBCIProcessTanMedia implements HBCIProcess +{ + private HBCIDialogTanMedia dialog = null; + private boolean force = false; + + /** + * ct. + * @param force true, wenn der Abruf der TAN-Medien forciert werden soll, auch wenn er eigentlich nicht noetig ist. + */ + public HBCIProcessTanMedia(boolean force) + { + this.dialog = new HBCIDialogTanMedia(); + this.force = force; + } + + /** + * @see org.kapott.hbci.dialog.HBCIProcess#execute(org.kapott.hbci.dialog.DialogContext) + */ + @Override + public HBCIMsgStatus execute(final DialogContext ctx) + { + if (!this.dialog.supported(ctx)) + return null; + + if (!this.force && !this.dialog.required(ctx)) + return null; + + boolean skip = Feature.PINTAN_INIT_SKIPONESTEPSCA.isEnabled(); + // Wir versuchen es erstmal mit dem Einschritt-TAN-Verfahren und ohne SCA - wenn es per Feature erlaubt ist + // Das unterstuetzen aber nicht alle Banken. Die Deutsche Bank z.Bsp. hier ein konkretes Zweischritt-Verfahren, + // was voellig absurd ist. Denn fuer das Zweischritt-Verfahren brauche ich ja die Medienbezeichnung. + // Und die will ich ja gerade erst abrufen. + try + { + return this.execute(ctx,skip); + } + catch (Exception e) + { + HBCIUtils.log("failed: " + e.getMessage(),HBCIUtils.LOG_INFO); + HBCIUtils.log(e,HBCIUtils.LOG_DEBUG); + + // Den erneuten Versuch mit SCA brauchen wir natuerlich machen, wenn wir ihn beim ersten weggelassen haben + if (skip) + { + try + { + return this.execute(ctx,false); + } + catch (Exception e2) + { + // Wir werfen das nicht hoch. Wenn es fehlschlaegt, dann haben wir halt keine TAN-Medien. Davon geht die Welt nicht unter + HBCIUtils.log("fetching of TAN media names failed: " + e.getMessage(),HBCIUtils.LOG_INFO); + HBCIUtils.log(e,HBCIUtils.LOG_DEBUG); + } + } + + return null; + } + } + + /** + * Fuehrt die HBCI-Dialoge aus. + * @param ctx der Context. + * @param skipSCA true, wenn die SCA weggelassen werden soll. + * @return der Status der Dialoge. + */ + private HBCIMsgStatus execute(final DialogContext ctx, boolean skipSCA) + { + HBCIUtils.log("trying to fetch TAN media names [skip sca: " + skipSCA + "]",HBCIUtils.LOG_INFO); + + final HBCIDialogInit init = new HBCIDialogInit() + { + /** + * @see org.kapott.hbci.dialog.AbstractRawHBCIDialog#createSCARequest(java.util.Properties, int) + */ + @Override + public SCARequest createSCARequest(Properties secmechInfo, int hktanVersion) + { + if (!skipSCA) + return null; + + // Anpassen des SCA-Requests fuer das Abfragen der TAN-Medien per HKTAB + SCARequest r = super.createSCARequest(secmechInfo, hktanVersion); + r.setTanReference("HKTAB"); + final String needed = secmechInfo != null ? secmechInfo.getProperty("needtanmedia","") : ""; + r.setTanMedia(Objects.equals(needed,"2") ? "noref" : ""); + return r; + } + }; + init.execute(ctx); + + this.dialog.execute(ctx); + + final HBCIDialogEnd end = new HBCIDialogEnd(); + return end.execute(ctx); + } +} + + diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/KnownDialogTemplate.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/KnownDialogTemplate.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/KnownDialogTemplate.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/KnownDialogTemplate.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,101 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import java.util.Arrays; +import java.util.List; + +/** + * Die Namen der bekannten Dialog-Templates. + */ +public enum KnownDialogTemplate +{ + /** + * Dialog-Initialisierung. + */ + INIT("DialogInit"), + + /** + * Dialog-Initialisierung. + */ + INIT_SCA("DialogInitSCA"), + + /** + * Synchronisierung. + */ + SYNC("Synch"), + + /** + * Abruf der TAN-Medien. + */ + TANMEDIA("TanMedia"), + + /** + * Abruf der SEPA-Infos. + */ + SEPAINFO("SepaInfo"), + + /** + * Abfrage der Bankensignatur. + */ + FIRSTKEYREQUEST("FirstKeyReq"), + + /** + * Sperren von Schluesseln. + */ + LOCKKEYS("LockKeys"), + + /** + * Dialog-Ende. + */ + END("DialogEnd"), + + ; + + private String name = null; + + /** + * Liste der Dialoge, bei denen ein SCA-Request per HKTAN gesendet werden soll. + * Auch bei der anonymen - Siehe 'FinTS_3.0_Security_Sicherheitsverfahren_PINTAN_2018-02-23_final_version.pdf', letzter Hinweis in B.4.3.1 + * FIRSTKEYREQUEST ist zwar auch eine Dialog-Initialisierung. Da es die aber nur bei Schluesseldateien + * und nicht bei PIN/TAN gibt, wuerde ein HKTAN hier gar keinen Sinn machen. + */ + public static List LIST_SEND_SCA = Arrays.asList(INIT,INIT_SCA,SYNC); + + /** + * ct. + * @param name der Name des Template. + */ + private KnownDialogTemplate(String name) + { + this.name = name; + } + + /** + * Liefert den Namen des Dialogs. + * @return der Name des Dialogs. + */ + public String getName() + { + return this.name; + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/KnownReturncode.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/KnownReturncode.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/KnownReturncode.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/KnownReturncode.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,171 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import java.util.ArrayList; +import java.util.List; + +import org.kapott.hbci.status.HBCIRetVal; + +/** + * Liste von bekannten Returncodes. + */ +public enum KnownReturncode +{ + /** + * Es liegen weitere Informationen vor - mit Aufsetzpunkt. + */ + W3040("3040"), + + /** + * Geaenderte Benutzerdaten. + */ + W3072("3072"), + + /** + * SCA-Ausnahme. + */ + W3076("3076"), + + /** + * Die Liste der zugelassenen Zweischritt-Verfahren. + */ + W3920("3920"), + + /** + * Signatur falsch (generisch) + */ + E9340("9340"), + + /** + * PIN ist gesperrt + */ + E9930("9930"), + + /** + * Konto gesperrt wegen falscher PIN + */ + E9931("9931"), + + /** + * PIN falsch (konkret) + */ + E9942("9942"), + + ; + + /** + * Die Liste der Return-Codes, die als "PIN falsch" interpretiert werden sollen. + */ + public final static KnownReturncode[] LIST_AUTH_FAIL = new KnownReturncode[]{E9340,E9930,E9931,E9942}; + + private String code = null; + + /** + * ct. + * @param code der Code. + */ + private KnownReturncode(String code) + { + this.code = code; + } + + /** + * Prueft der angegebene Code identisch ist. + * @param code der zu pruefende Code. + * @return true, wenn der Code identisch ist. + */ + public boolean is(String code) + { + return code != null && this.code.equals(code); + } + + /** + * Prueft, ob der angegebene Code in der Liste enthalten ist. + * @param code der zu pruefende Code. + * @param codes die Liste der Codes. + * @return true, wenn er in der Liste enthalten ist. + */ + public static boolean contains(String code, KnownReturncode... codes) + { + return find(code,codes) != null; + } + + /** + * Prueft, ob der angegebene Code in der Liste enthalten ist. + * @param code der zu pruefende Code. + * @param codes die Liste der Codes. + * @return true, wenn er in der Liste enthalten ist. + */ + public static KnownReturncode find(String code, KnownReturncode... codes) + { + if (code == null || code.length() == 0 || codes == null || codes.length == 0) + return null; + + for (KnownReturncode c:codes) + { + if (c.is(code)) + return c; + } + + return null; + } + + /** + * Sucht nach dem angegebenen Status-Code in den Rueckmeldungen und liefert den Code zurueck. + * @param rets die Rueckmeldungen. + * @return der gesuchte Rueckmeldecode oder NULL, wenn er nicht existiert. + * Es wird der erste gefundene verwendet. + */ + public HBCIRetVal searchReturnValue(HBCIRetVal[] rets) + { + if (rets == null || rets.length == 0) + return null; + + for (HBCIRetVal ret:rets) + { + if (this.is(ret.code)) + return ret; + } + return null; + } + + /** + * Sucht nach dem angegebenen Status-Code in den Rueckmeldungen und liefert den Code zurueck. + * @param rets die Rueckmeldungen. + * @return die gesuchten Rueckmeldecodes oder NULL, wenn sie nicht existieren. + * Es werden alle gefundenen geliefert. + */ + public List searchReturnValues(HBCIRetVal[] rets) + { + if (rets == null || rets.length == 0) + return null; + + List result = new ArrayList(); + for (HBCIRetVal ret:rets) + { + if (this.is(ret.code)) + result.add(ret); + } + return result; + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/KnownTANProcess.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/KnownTANProcess.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/KnownTANProcess.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/KnownTANProcess.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,144 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import java.util.Objects; + +import org.kapott.hbci.tools.StringUtil; + +/** + * Enthaelt die Liste der bekannten TAN-Prozesse. + */ +public enum KnownTANProcess +{ + /** + * Prozess-Variante 1. + */ + PROCESS1("1"), + + /** + * Prozess-Variante 2, Schritt 1. + */ + PROCESS2_STEP1("4"), + + /** + * Prozess-Variante 2, Schritt 2. + */ + PROCESS2_STEP2("2"), + + ; + + /** + * Prozess-Variante. + */ + public enum Variant + { + /** + * Prozess-Variante 1. + */ + V1("1"), + + /** + * Prozess-Variante 2. + */ + V2("2"), + + ; + + private final static Variant DEFAULT = V2; + + private String code = null; + + /** + * ct. + * @param code der Code der Prozess-Variante. + */ + private Variant(String code) + { + this.code = code; + } + + /** + * Liefert die zu verwendende Prozessvariante. + * @param code der Code der Variante. Nie NULL sondern hoechstens die Default-Variante. + * @return die Prozess-Variante. + */ + public final static Variant determine(String code) + { + if (!StringUtil.hasText(code)) + return DEFAULT; + + for (Variant v:values()) + { + if (Objects.equals(code,v.code)) + return v; + } + + return DEFAULT; + } + } + + private String code = null; + + /** + * ct. + * @param code der Prozess-Schritt. + */ + private KnownTANProcess(String code) + { + this.code = code; + } + + /** + * Prueft der angegebene Code identisch ist. + * @param code der zu pruefende Code. + * @return true, wenn der Code identisch ist. + */ + public boolean is(String code) + { + return code != null && this.code.equals(code); + } + + /** + * Liefert den Code des TAN-Prozess-Schrittes. + * @return der Code des TAN-Prozess-Schrittes. + */ + public String getCode() + { + return this.code; + } + + /** + * Ermittelt den passenden TAN-Prozess fuer die Variante und die Schritt-Nummer. + * @param v die Prozess-Variante. + * @param step die Schritt-Nummer. + * @return der TAN-Prozess. Nie NULL sondern im Zweifel {@link KnownTANProcess#PROCESS2_STEP1}. + */ + public static KnownTANProcess get(Variant v, int step) + { + // Hier gibts nur einen + if (v == Variant.V1) + return PROCESS1; + + return step == 2 ? PROCESS2_STEP2 : PROCESS2_STEP1; + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/RawHBCIDialog.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/RawHBCIDialog.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/RawHBCIDialog.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/RawHBCIDialog.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,62 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import java.util.Properties; + +import org.kapott.hbci.status.HBCIMsgStatus; + +/** + * Bei der Ausfuehrung von HBCI-Dialogen kommt es an mehreren Stellen zu Callbacks in Paspports, weil dort + * abhaengig vom Zugangsverfahren Sonderbehandlungen ergeben (im Wesentlichen PIN/TAN - Stichwort SCA sowie bei Schlüsseldateien). + * Da diese Callbacks im Laufe der Zeit zu unuebersichtlich geworden sind, gibt es jetzt generische Events und eine Kapselung der rohen HBCI-Dialoge. + * Interface fuer die rohen HBCI-Dialoge. + */ +public interface RawHBCIDialog +{ + /** + * Sendet die Dialog-Initialisierung an die Bank. + * @param ctx der Dialog-Context. + * @return der Ausfuehrungsstatus. Darf niemals NULL sein. In dem Fall muss die Methode eine Exception werfen. + */ + public HBCIMsgStatus execute(final DialogContext ctx); + + /** + * Liefert das Template. + * @return das Template. + */ + public KnownDialogTemplate getTemplate(); + + /** + * Speichert das Template. + * @param t das Template. + */ + public void setTemplate(KnownDialogTemplate t); + + /** + * Erzeugt einen SCA-Request. + * @param secmechInfo die TAN-Verfahren-Parameter. + * @param hktanVersion die HKTAN-Version. + * @return der SCA-Request. + */ + public SCARequest createSCARequest(Properties secmechInfo, int hktanVersion); +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/SCARequest.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/SCARequest.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/dialog/SCARequest.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/dialog/SCARequest.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,107 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.dialog; + +import org.kapott.hbci.dialog.KnownTANProcess.Variant; + +/** + * Kapselt die Eckdaten des SCA-Requests in der Dialog-Initialisierung. + */ +public class SCARequest +{ + private int version; + private String tanReference; + private Variant variant; + private String tanMedia; + + /** + * Liefert die Prozess-Variante. + * @return variant die Prozess-Variante. + */ + public Variant getVariant() + { + return variant; + } + + /** + * Speichert die Prozess-Variante. + * @param variant die Prozess-Variante. + */ + public void setVariant(Variant variant) + { + this.variant = variant; + } + + /** + * Liefert die Segment-Version fuer das HKTAN. + * @return die Segment-Version fuer das HKTAN. + */ + public int getVersion() + { + return version; + } + + /** + * Speichert die Segment-Version fuer das HKTAN. + * @param version die Segment-Version fuer das HKTAN. + */ + public void setVersion(int version) + { + this.version = version; + } + + /** + * Liefert den Geschaeftsvorfall-Code, der im HKTAN als Referenz verwendet werden soll. + * @return de Geschaeftsvorfall-Code, der im HKTAN als Referenz verwendet werden soll. + */ + public String getTanReference() + { + return tanReference; + } + + /** + * Speichert den Geschaeftsvorfall-Code, der im HKTAN als Referenz verwendet werden soll. + * @param tanReference der Geschaeftsvorfall-Code, der im HKTAN als Referenz verwendet werden soll. + */ + public void setTanReference(String tanReference) + { + this.tanReference = tanReference; + } + + /** + * Liefert die TAN-Medienbezeichnung. + * @return die TAN-Medienbezeichnung. + */ + public String getTanMedia() + { + return tanMedia; + } + + /** + * Speichert die TAN-Medienbezeichnung. + * @param tanMedia die TAN-Medienbezeichnung. + */ + public void setTanMedia(String tanMedia) + { + this.tanMedia = tanMedia; + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/examples/UmsatzAbrufPinTan.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/examples/UmsatzAbrufPinTan.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/examples/UmsatzAbrufPinTan.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/examples/UmsatzAbrufPinTan.java 2019-11-27 09:04:22.000000000 +0000 @@ -74,11 +74,10 @@ // Wir setzen die Kernel-Parameter zur Laufzeit. Wir koennten sie alternativ // auch oben in "props" setzen. HBCIUtils.setParam("client.passport.default","PinTan"); // Legt als Verfahren PIN/TAN fest. - HBCIUtils.setParam("client.passport.PinTan.filename",passportFile.getAbsolutePath()); - HBCIUtils.setParam("client.passport.PinTan.init","1"); + HBCIUtils.setParam("client.passport.PinTan.init","1"); // Stellt sicher, dass der Passport initialisiert wird // Erzeugen des Passport-Objektes. - HBCIPassport passport = AbstractHBCIPassport.getInstance(); + HBCIPassport passport = AbstractHBCIPassport.getInstance(passportFile); // Konfigurieren des Passport-Objektes. // Das kann alternativ auch alles ueber den Callback unten geschehen diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/ChallengeInfo.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/ChallengeInfo.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/ChallengeInfo.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/ChallengeInfo.java 2019-11-27 09:04:22.000000000 +0000 @@ -147,7 +147,7 @@ // auch keine Challenge-Parameter setzen if (job == null) { - HBCIUtils.log("have no challenge data for " + code + ", will not apply challenge params", HBCIUtils.LOG_INFO); + HBCIUtils.log("have no challenge data for " + code + ", will not apply challenge params", HBCIUtils.LOG_DEBUG); return; } @@ -160,7 +160,7 @@ // Wir haben keine Parameter fuer diese HHD-Version if (hhd == null) { - HBCIUtils.log("have no challenge data for " + code + " in " + version + ", will not apply challenge params", HBCIUtils.LOG_INFO); + HBCIUtils.log("have no challenge data for " + code + " in " + version + ", will not apply challenge params", HBCIUtils.LOG_DEBUG); return; } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/Feature.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/Feature.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/Feature.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/Feature.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,87 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.manager; + +/** + * Kapselt verschiedene Feature-Flags. + */ +public enum Feature +{ + /** + * Feature, mit dem festgelegt werden kann, ob die personalisierte Dialog-Initialisierung ohne HKTAN gesendet werden soll, wenn als TAN-Verfahren 999 verwendet wird. + */ + PINTAN_INIT_SKIPONESTEPSCA(true), + + /** + * Feature, mit dem festgelegt werden kann, ob HBCI4Java versuchen soll, das TAN-Verfahren automatisch zu ermitteln, wenn es noch keine per 3920 erhalten hat. + * Leider geht das bei einigen Banken (wie Deutsche Bank) nicht, da die keine personalisierte Dialog-Initialisierung mit TAN-Verfahren 999 erlauben. + */ + PINTAN_INIT_AUTOMETHOD(true), + + ; + + private boolean enabled = false; + + /** + * ct. + * @param enabled Legt fest, ob das Feature per Default aktiviert sein soll. + */ + private Feature(boolean enabled) + { + this.enabled = enabled; + } + + /** + * Liefert true, wenn das Feature per Default aktiv sein soll. + * @return true, wenn das Feature per Default aktiv sein soll. + */ + public boolean getDefault() + { + return this.enabled; + } + + /** + * Liefert den aktuellen Zustand des Features. + * @return true, wenn das Feature aktiv ist. + */ + public boolean isEnabled() + { + return Boolean.parseBoolean(HBCIUtils.getParam("feature." + this,Boolean.toString(this.getDefault()))); + } + + /** + * Setzt den Status des Features zur Laufzeit. + * @param b true, wenn das Feature aktiv sein soll. + */ + public void setEnabled(boolean b) + { + HBCIUtils.setParam("feature." + this,Boolean.toString(b)); // Hier nicht "NULL" bei false, weil das den Default-Zustand wieder herstellen koennte. Und der kann true sein + } + + /** + * Setzt den Status des Features zur Laufzeit auf die Werksvorgabe zurueck. + */ + public void reset() + { + HBCIUtils.setParam("feature." + this,null); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/FlickerCode.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/FlickerCode.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/FlickerCode.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/FlickerCode.java 2019-11-27 09:04:22.000000000 +0000 @@ -12,6 +12,7 @@ package org.kapott.hbci.manager; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -31,12 +32,44 @@ /** * HHD-Version 1.4 */ - HHD14, + HHD14(org.kapott.hbci.manager.HHDVersion.QR_1_4, + org.kapott.hbci.manager.HHDVersion.HHD_1_4, + org.kapott.hbci.manager.HHDVersion.MS_1), /** * HHD-Version 1.3 */ - HHD13 + HHD13(org.kapott.hbci.manager.HHDVersion.QR_1_3, + org.kapott.hbci.manager.HHDVersion.HHD_1_3, + org.kapott.hbci.manager.HHDVersion.HHD_1_2), + + ; + + private List assigned = new ArrayList<>(); + + /** + * ct. + * @param assigned Liste der zugeordneten HHD-Versionen. + */ + private HHDVersion(org.kapott.hbci.manager.HHDVersion... assigned) + { + this.assigned = Arrays.asList(assigned); + } + + /** + * Versucht die HHD-Version zu ermitteln. + * @param h die HHD-Version. + * @return das korrespondierende interne Enum. + */ + private static HHDVersion find(org.kapott.hbci.manager.HHDVersion h) + { + for (HHDVersion v:values()) + { + if (v.assigned.contains(h)) + return v; + } + return null; + } } /** @@ -130,6 +163,80 @@ public String rest = null; /** + * Versucht, aus Challenge und Challenge HHDuc den Flicker-Code zu extrahieren + * und ihn in einen flickerfaehigen Code umzuwandeln. + * Nur wenn tatsaechlich ein gueltiger Code enthalten ist, der als + * HHDuc-Code geparst und in einen Flicker-Code umgewandelt werden konnte, + * liefert die Funktion den Code. Sonst immer NULL. + * @param challenge der Challenge-Text. Das DE "Challenge HHDuc" gibt es + * erst seit HITAN4. Einige Banken haben aber schon vorher optisches chipTAN + * gemacht. Die haben das HHDuc dann direkt im Freitext des Challenge + * mitgeschickt (mit String-Tokens zum Extrahieren markiert). Die werden vom + * FlickerCode-Parser auch unterstuetzt. + * @param hhduc das echte Challenge HHDuc. + * @return der geparste Flickercode oder NULL. + */ + public static FlickerCode tryParse(String challenge, String hhduc) + { + return tryParse(null,challenge,hhduc); + } + + /** + * Versucht, aus Challenge und Challenge HHDuc den Flicker-Code zu extrahieren + * und ihn in einen flickerfaehigen Code umzuwandeln. + * Nur wenn tatsaechlich ein gueltiger Code enthalten ist, der als + * HHDuc-Code geparst und in einen Flicker-Code umgewandelt werden konnte, + * liefert die Funktion den Code. Sonst immer NULL. + * @param hhd die HHD-Version. Kann NULL sein. + * @param challenge der Challenge-Text. Das DE "Challenge HHDuc" gibt es + * erst seit HITAN4. Einige Banken haben aber schon vorher optisches chipTAN + * gemacht. Die haben das HHDuc dann direkt im Freitext des Challenge + * mitgeschickt (mit String-Tokens zum Extrahieren markiert). Die werden vom + * FlickerCode-Parser auch unterstuetzt. + * @param hhduc das echte Challenge HHDuc. + * @return der geparste Flickercode oder NULL. + */ + public static FlickerCode tryParse(org.kapott.hbci.manager.HHDVersion hhd, String challenge, String hhduc) + { + // 1. Prioritaet hat hhduc. Gibts aber erst seit HITAN4 + if (hhduc != null && hhduc.trim().length() > 0) + { + try + { + FlickerCode code = new FlickerCode(hhd,hhduc); + code.render(); // testweise rendern + return code; + } + catch (Exception e) + { + HBCIUtils.log("unable to parse Challenge HHDuc " + hhduc + ":" + HBCIUtils.exception2String(e),HBCIUtils.LOG_DEBUG); + } + } + + // 2. Checken, ob im Freitext-Challenge was parse-faehiges steht. + // Kann seit HITAN1 auftreten + if (challenge != null && challenge.trim().length() > 0) + { + try + { + FlickerCode code = new FlickerCode(hhd,challenge); + code.render(); // testweise rendern + return code; + } + catch (Exception e) + { + // Das darf durchaus vorkommen, weil das Challenge auch bei manuellem + // chipTAN- und smsTAN Verfahren verwendet wird, wo gar kein Flicker-Code enthalten ist. + // Wir loggen es aber trotzdem - fuer den Fall, dass tatsaechlich ein Flicker-Code + // enthalten ist. Sonst koennen wir das nicht debuggen. + HBCIUtils.log("challenge contains no HHDuc (no problem in most cases):" + HBCIUtils.exception2String(e),HBCIUtils.LOG_DEBUG2); + } + } + // Ne, definitiv kein Flicker-Code. + return null; + } + + /** * ct. * Parameterloser Konstruktor zum manuellen Zusammenstecken eines Codes. */ @@ -144,6 +251,36 @@ */ public FlickerCode(String code) { + this(null,code); + } + + /** + * ct. + * Parst den HHDuc-Code aus dem uebergebenen Code. + * @param hhd die HHD-Version. Kann NULL sein. + * @param code der zu parsende Code. + */ + public FlickerCode(org.kapott.hbci.manager.HHDVersion hhd, String code) + { + // Wenn eine Version angegeben ist, versuchen wir es mit der + if (hhd != null) + { + try + { + HHDVersion v = HHDVersion.find(hhd); + if (v != null) + parse(code,v); + + // Wenn keine Exception geflogen ist, koennte es die richtige Version gewesen sein. + return; + } + catch (Exception e) + { + // Die HHD-Version war explizit angegeben, liess sich damit aber nicht parsen? Sehr verdaechtig + HBCIUtils.log("unable to parse code " + code + " as " + hhd + ": " + HBCIUtils.exception2String(e),HBCIUtils.LOG_DEBUG); + } + } + // Wir versuchen es erstmal als HHD 1.4 try { diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HBCIDialog.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HBCIDialog.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HBCIDialog.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HBCIDialog.java 2019-11-27 09:04:22.000000000 +0000 @@ -23,12 +23,16 @@ import java.util.ArrayList; import java.util.Enumeration; -import java.util.Iterator; import java.util.List; import java.util.Properties; import org.kapott.hbci.GV.HBCIJobImpl; import org.kapott.hbci.callback.HBCICallback; +import org.kapott.hbci.dialog.DialogContext; +import org.kapott.hbci.dialog.DialogEvent; +import org.kapott.hbci.dialog.HBCIDialogInit; +import org.kapott.hbci.dialog.HBCIMessage; +import org.kapott.hbci.dialog.HBCIMessageQueue; import org.kapott.hbci.exceptions.HBCI_Exception; import org.kapott.hbci.passport.HBCIPassportInternal; import org.kapott.hbci.passport.HBCIPassportList; @@ -59,12 +63,8 @@ private String anonSuffix; private String dialogid; /* The dialogID for this dialog (unique for each dialog) */ private long msgnum; /* An automatically managed message counter. */ - private List> msgs; /* this array contains all messages to be sent (excluding - dialogInit and dialogEnd); each element of the arrayList - is again an ArrayList, where each element is one - task (GV) to be sent with this specific message */ - private Properties listOfGVs; // liste aller GVs in der aktuellen msg; key ist der hbciCode des jobs, - // value ist die anzahl dieses jobs in der aktuellen msg + private HBCIMessageQueue queue; + private Properties listOfGVs = new Properties(); private HBCIHandler parentHandler; public HBCIDialog(HBCIHandler parentHandler) @@ -74,9 +74,8 @@ this.parentHandler=parentHandler; this.isAnon=((HBCIPassportInternal)parentHandler.getPassport()).isAnonymous(); this.anonSuffix=isAnon?"Anon":""; - this.msgs=new ArrayList>(); - this.msgs.add(new ArrayList()); - this.listOfGVs=new Properties(); + + this.reset(); } public HBCIHandler getParentHandler() @@ -93,7 +92,7 @@ update their internal state with the data received from the institute. */ private HBCIMsgStatus doDialogInit() { - HBCIMsgStatus ret=new HBCIMsgStatus(); + HBCIMsgStatus ret = null; try { HBCIPassportInternal mainPassport=(HBCIPassportInternal)getParentHandler().getPassport(); @@ -106,85 +105,41 @@ HBCIUtils.log(HBCIUtilsInternal.getLocMsg("STATUS_DIALOG_INIT"),HBCIUtils.LOG_INFO); HBCIUtilsInternal.getCallback().status(mainPassport,HBCICallback.STATUS_DIALOG_INIT,null); - String country=mainPassport.getCountry(); - String blz=mainPassport.getBLZ(); - boolean restarted=false; - while (true) { - kernel.rawNewMsg("DialogInit"+anonSuffix); - kernel.rawSet("Idn.KIK.blz", blz); - kernel.rawSet("Idn.KIK.country", country); - if (!isAnon) { - kernel.rawSet("Idn.customerid", mainPassport.getCustomerId()); - kernel.rawSet("Idn.sysid", mainPassport.getSysId()); - String sysstatus=mainPassport.getSysStatus(); - kernel.rawSet("Idn.sysStatus",sysstatus); - if (mainPassport.needInstKeys()) { - kernel.rawSet("KeyReq.SecProfile.method",mainPassport.getProfileMethod()); - kernel.rawSet("KeyReq.SecProfile.version",mainPassport.getProfileVersion()); - kernel.rawSet("KeyReq.KeyName.keytype", "V"); - kernel.rawSet("KeyReq.KeyName.KIK.country", country); - kernel.rawSet("KeyReq.KeyName.KIK.blz", blz); - kernel.rawSet("KeyReq.KeyName.userid", mainPassport.getInstEncKeyName()); - kernel.rawSet("KeyReq.KeyName.keynum", mainPassport.getInstEncKeyNum()); - kernel.rawSet("KeyReq.KeyName.keyversion", mainPassport.getInstEncKeyVersion()); - - if (mainPassport.hasInstSigKey()) { - kernel.rawSet("KeyReq_2.SecProfile.method",mainPassport.getProfileMethod()); - kernel.rawSet("KeyReq_2.SecProfile.version",mainPassport.getProfileVersion()); - kernel.rawSet("KeyReq_2.KeyName.keytype", "S"); - kernel.rawSet("KeyReq_2.KeyName.KIK.country", country); - kernel.rawSet("KeyReq_2.KeyName.KIK.blz", blz); - kernel.rawSet("KeyReq_2.KeyName.userid", mainPassport.getInstSigKeyName()); - kernel.rawSet("KeyReq_2.KeyName.keynum", mainPassport.getInstSigKeyNum()); - kernel.rawSet("KeyReq_2.KeyName.keyversion", mainPassport.getInstSigKeyVersion()); - } - } - } - kernel.rawSet("ProcPrep.BPD", mainPassport.getBPDVersion()); - kernel.rawSet("ProcPrep.UPD", mainPassport.getUPDVersion()); - kernel.rawSet("ProcPrep.lang",mainPassport.getDefaultLang()); - kernel.rawSet("ProcPrep.prodName",HBCIUtils.getParam("client.product.name",HBCIUtils.PRODUCT_ID)); - kernel.rawSet("ProcPrep.prodVersion",HBCIUtils.getParam("client.product.version","3")); - ret=kernel.rawDoIt(!isAnon && HBCIKernelImpl.SIGNIT, - !isAnon && HBCIKernelImpl.CRYPTIT, - !isAnon && HBCIKernelImpl.NEED_SIG, - !isAnon && HBCIKernelImpl.NEED_CRYPT); - - boolean need_restart=mainPassport.postInitResponseHook(ret,isAnon); - if (need_restart) { - HBCIUtils.log("for some reason we have to restart this dialog", HBCIUtils.LOG_INFO); - if (restarted) { - HBCIUtils.log("this dialog already has been restarted once - to avoid endless loops we stop here", HBCIUtils.LOG_WARN); - throw new HBCI_Exception("*** restart loop - aborting"); - } - restarted=true; - } else { - break; - } - } + // Dialog-Context erzeugen + final DialogContext ctx = DialogContext.create(kernel,mainPassport); + ctx.setDialog(this); + ctx.setAnonymous(this.isAnon); + + // Dialog-Initialisierung senden + final HBCIDialogInit init = new HBCIDialogInit(); + ret = init.execute(ctx); - Properties result=ret.getData(); - if (ret.isOK()) { - HBCIInstitute inst=new HBCIInstitute(kernel,mainPassport,false); + if (ret.isOK()) + { + final Properties result = ret.getData(); + final HBCIInstitute inst = new HBCIInstitute(kernel,mainPassport,false); inst.updateBPD(result); inst.extractKeys(result); - HBCIUser user=new HBCIUser(kernel,mainPassport,false); + final HBCIUser user = new HBCIUser(kernel,mainPassport,false); user.updateUPD(result); mainPassport.saveChanges(); - msgnum=2; - dialogid=result.getProperty("MsgHead.dialogid"); - HBCIUtils.log("dialog-id set to "+dialogid,HBCIUtils.LOG_DEBUG); + this.msgnum = ctx.getMsgNum(); + this.dialogid = ctx.getDialogId(); HBCIInstMessage msg=null; - for (int i=0;true;i++) { - try { + for (int i=0;true;i++) + { + try + { String header=HBCIUtilsInternal.withCounter("KIMsg",i); msg=new HBCIInstMessage(result,header); - } catch (Exception e) { + } + catch (Exception e) + { break; } HBCIUtilsInternal.getCallback().callback(mainPassport, @@ -196,163 +151,242 @@ } HBCIUtilsInternal.getCallback().status(mainPassport,HBCICallback.STATUS_DIALOG_INIT_DONE,new Object[] {ret,dialogid}); - } catch (Exception e) { + } + catch (Exception e) + { + if (ret == null) + ret = new HBCIMsgStatus(); ret.addException(e); } return ret; } + /** + * Fuehrt die eigentlichen Geschaeftsvorfaelle aus. + * @return + */ private HBCIMsgStatus[] doJobs() { HBCIUtils.log(HBCIUtilsInternal.getLocMsg("LOG_PROCESSING_JOBS"),HBCIUtils.LOG_INFO); - ArrayList msgstatus_a=new ArrayList(); - HBCIPassportList msgPassports=new HBCIPassportList(); + final HBCIHandler h = this.getParentHandler(); + final HBCIKernelImpl k = (HBCIKernelImpl) h.getKernel(); + final HBCIPassportInternal p = (HBCIPassportInternal) h.getPassport(); - HBCIKernelImpl kernel=(HBCIKernelImpl)getParentHandler().getKernel(); - HBCIPassportInternal mainPassport=(HBCIPassportInternal)getParentHandler().getPassport(); + final DialogContext ctx = DialogContext.create(k,p); + ctx.setDialog(this); + ctx.setAnonymous(this.isAnon); - // durch die liste aller auszuführenden nachrichten durchloopen - int nof_messages=msgs.size(); - for (int j=0;j tasks= msgs.get(j); - - // loop wird benutzt, um zu zählen, wie oft bereits "nachgehakt" wurde, - // falls ein bestimmter job nicht mit einem einzigen nachrichtenaustausch - // abgearbeitet werden konnte (z.b. abholen kontoauszüge) - int loop=0; - HBCIMsgStatus msgstatus=new HBCIMsgStatus(); - - // diese schleife loopt solange, bis alle jobs der aktuellen nachricht - // tatsächlich abgearbeitet wurden (also inclusive "nachhaken") - while (true) { - boolean addMsgStatus=true; - - try { - HBCIUtils.log("generating custom msg #"+(j+1)+" (loop "+(loop+1)+")", - HBCIUtils.LOG_DEBUG); - - int taskNum=0; + final ArrayList allStatuses = new ArrayList(); - msgPassports.clear(); - kernel.rawNewMsg("CustomMsg"); - - // durch alle jobs loopen, die eigentlich in der aktuellen - // nachricht abgearbeitet werden müssten - for (Iterator i=tasks.iterator();i.hasNext();) { - HBCIJobImpl task=i.next(); - - // wenn der Task entweder noch gar nicht ausgeführt wurde - // oder in der letzten Antwortnachricht ein entsprechendes - // Offset angegeben wurde - if (task.needsContinue(loop)) { - task.setContinueOffset(loop); - - Properties p=task.getLowlevelParams(); - String header=HBCIUtilsInternal.withCounter("GV",taskNum); - - String taskName=task.getName(); - HBCIUtils.log("adding task "+taskName,HBCIUtils.LOG_DEBUG); - HBCIUtilsInternal.getCallback().status(mainPassport,HBCICallback.STATUS_SEND_TASK,task); - task.setIdx(taskNum); - - // Daten für den Task festlegen - for (Enumeration e=p.keys();e.hasMoreElements();) { - String key=(String)(e.nextElement()); - kernel.rawSet(header+"."+key,p.getProperty(key)); - } - - // additional passports für diesen task ermitteln - // und zu den passports für die aktuelle nachricht - // hinzufügen; - // doppelgänger werden schon von - // HBCIPassportList.addPassport() herausgefiltert - msgPassports.addAll(task.getSignaturePassports()); - - taskNum++; - } - } - - // wenn keine jobs für die aktuelle message existieren - if (taskNum==0) { - HBCIUtils.log( - "loop "+(loop+1)+" aborted, because there are no more tasks to be executed", - HBCIUtils.LOG_DEBUG); - addMsgStatus=false; - break; - } + int msgCount = 0; + HBCIMessage msg = null; + + while (true) + { + p.onDialogEvent(DialogEvent.JOBS_CREATED,ctx); + msg = this.queue.poll(); + if (msg == null) + { + HBCIUtils.log("dialog completed after " + msgCount + " messages",HBCIUtils.LOG_DEBUG); + break; + } + + final List tasks = msg.getTasks(); + + if (tasks.size() == 0) + { + HBCIUtils.log("no tasks in message #" + msgCount + ", skipping",HBCIUtils.LOG_WARN); + continue; + } + + msgCount++; + HBCIMsgStatus msgstatus = null; + + try + { + //////////////////////////////////////////////////////////////////// + // Basis-Daten der Nachricht + final HBCIPassportList msgPassports = new HBCIPassportList(); + HBCIUtils.log("generating msg #" + msgCount,HBCIUtils.LOG_DEBUG); - kernel.rawSet("MsgHead.dialogid", dialogid); - kernel.rawSet("MsgHead.msgnum", getMsgNum()); - kernel.rawSet("MsgTail.msgnum", getMsgNum()); - nextMsgNum(); + k.rawNewMsg("CustomMsg"); + k.rawSet("MsgHead.dialogid", dialogid); + k.rawSet("MsgHead.msgnum", this.getMsgNum()); + k.rawSet("MsgTail.msgnum", this.getMsgNum()); + // + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // Jobs hinzufuegen + int taskNum = 0; + for (HBCIJobImpl task:tasks) + { + if (task.skipped()) + continue; - // nachrichtenaustausch durchführen - msgstatus=kernel.rawDoIt(msgPassports,HBCIKernelImpl.SIGNIT,HBCIKernelImpl.CRYPTIT,HBCIKernelImpl.NEED_SIG,HBCIKernelImpl.NEED_CRYPT); - Properties result=msgstatus.getData(); + final String name = task.getName(); + HBCIUtils.log("adding task " + name,HBCIUtils.LOG_DEBUG); + HBCIUtilsInternal.getCallback().status(p,HBCICallback.STATUS_SEND_TASK,task); + + // Uebernimmt den aktuellen loop-Wert in die Lowlevel-Parameter + task.applyOffset(); + task.setIdx(taskNum); - // searching for first segment number that belongs to the custom_msg - // we look for entries like {"1","CustomMsg.MsgHead"} and so - // on (this data is inserted from the HBCIKernelImpl.rawDoIt() method), - // until we find the first segment containing a task - int offset=0; // this specifies, how many segments precede the first task segment - for (offset=1;true;offset++) { - String path=result.getProperty(Integer.toString(offset)); - if (path==null || path.startsWith("CustomMsg.GV")) { - if (path==null) { // wenn kein entsprechendes Segment gefunden, dann offset auf 0 setzen - offset=0; - } - break; + // Daten des Tasks in den Kernel uebernehmen + { + final String header = HBCIUtilsInternal.withCounter("GV",taskNum); + final Properties props = task.getLowlevelParams(); + for (Enumeration e = props.keys(); e.hasMoreElements();) + { + String key = (String) e.nextElement(); + k.rawSet(header + "." + key,props.getProperty(key)); } } - if (offset!=0) { - // für jeden Task die entsprechenden Rückgabedaten-Klassen füllen - // in fillOutStore wird auch "executed" fuer den jeweiligen Task auf true gesetzt. - for (Iterator i=tasks.iterator();i.hasNext();) { - HBCIJobImpl task=i.next(); - if (task.needsContinue(loop)) { - // nur wenn der auftrag auch tatsaechlich gesendet werden musste - try { - task.fillJobResult(msgstatus,offset); - HBCIUtilsInternal.getCallback().status(mainPassport,HBCICallback.STATUS_SEND_TASK_DONE,task); - } catch (Exception e) { - msgstatus.addException(e); - } - } - } - } + // additional passports für diesen task ermitteln und zu den passports für die aktuelle nachricht + // hinzufügen; doppelgänger werden schon von HBCIPassportList.addPassport() herausgefiltert + msgPassports.addAll(task.getSignaturePassports()); + taskNum++; + } + // + //////////////////////////////////////////////////////////////////// + + // Das passiert immer dann, wenn wir in der Message nur ein HKTAN#2 aus Prozess-Variante 2 hatten. + // Dieses aufgrund einer 3076-SCA-Ausnahme aber nicht benoetigt wird. + if (taskNum == 0) + { + HBCIUtils.log("no tasks in message #" + msgCount + ", skipping",HBCIUtils.LOG_DEBUG); + continue; + } - if (msgstatus.hasExceptions()) { - HBCIUtils.log("aborting current loop because of errors",HBCIUtils.LOG_ERR); - break; + //////////////////////////////////////////////////////////////////// + // Nachricht an die Bank senden + msgstatus = k.rawDoIt(msgPassports,HBCIKernelImpl.SIGNIT,HBCIKernelImpl.CRYPTIT,HBCIKernelImpl.NEED_CRYPT); + this.nextMsgNum(); + // + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // Antworten auswerten + final int segnum = this.findTaskSegment(msgstatus); + if (segnum != 0) + { + // für jeden Task die entsprechenden Rückgabedaten-Klassen füllen + for (HBCIJobImpl task:tasks) + { + if (task.skipped()) + continue; + + try + { + task.fillJobResult(msgstatus,segnum); + HBCIUtilsInternal.getCallback().status(p,HBCICallback.STATUS_SEND_TASK_DONE,task); + } + catch (Exception e) + { + msgstatus.addException(e); + } } + } + // + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // Wenn wir Fehler haben, brechen wir den kompletten Dialog ab + if (msgstatus.hasExceptions()) + { + HBCIUtils.log("aborting current loop because of errors",HBCIUtils.LOG_ERR); + break; + } + // + //////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////// + // Jobs erneut ausfuehren, falls noetig. + HBCIMessage newMsg = null; + for (HBCIJobImpl task:tasks) + { + if (task.skipped()) + continue; - loop++; - } catch (Exception e) { - msgstatus.addException(e); - } finally { - if (addMsgStatus) { - msgstatus_a.add(msgstatus); + HBCIJobImpl redo = task.redo(); + if (redo != null) + { + // Nachricht bei Bedarf erstellen und an die Queue haengen + if (newMsg == null) + { + newMsg = new HBCIMessage(); + queue.append(newMsg); + } + + // Task hinzufuegen + HBCIUtils.log("repeat task " + redo.getName(),HBCIUtils.LOG_DEBUG); + newMsg.append(redo); } } + // + //////////////////////////////////////////////////////////////////// + } + catch (Exception e) + { + HBCIUtils.log(e); + if (msgstatus != null) + msgstatus.addException(e); + } + finally + { + if (msgstatus != null) + allStatuses.add(msgstatus); } } - HBCIMsgStatus[] ret=new HBCIMsgStatus[0]; - if (msgstatus_a.size()!=0) - ret=(msgstatus_a.toArray(ret)); + return allStatuses.size() > 0 ? allStatuses.toArray(new HBCIMsgStatus[allStatuses.size()]) : new HBCIMsgStatus[0]; + } + + /** + * Sucht in den Ergebnis-Daten des Kernels nach der ersten Segment-Nummer mit einem Task-Response. + * @param msgstatus die Ergebnis-Daten des Kernels. + * @return die Nummer des Segments oder -1, wenn keines gefunden wurde. + */ + private int findTaskSegment(HBCIMsgStatus msgstatus) + { + final Properties result = msgstatus.getData(); + + // searching for first segment number that belongs to the custom_msg + // we look for entries like {"1","CustomMsg.GV*"} and so on (this data is inserted from the HBCIKernelImpl.rawDoIt() method), + // until we find the first segment containing a task + int segnum = 1; + while (segnum < 1000) // Wir brauchen ja nicht endlos suchen + { + final String path = result.getProperty(Integer.toString(segnum)); + + // Wir sind am Ende der Segmente angekommen + if (path == null) + return -1; + + // Wir haben ein GV-Antwort-Segment gefunden + if (path.startsWith("CustomMsg.GV")) + return segnum; - return ret; + // naechstes Segment + segnum++; + } + + return -1; } /** @brief Processes the DialogEnd stage of an HBCIDialog (mid-level API). - Works similarily to doDialogInit(). */ + @Deprecated private HBCIMsgStatus doDialogEnd() { + + // Neues Dialog-Ende: Koennen wir noch nicht verwenden, weil wir hier noch nicht die aktuelle Nachrichten-Nummer haben. +// HBCIDialogEnd end = new HBCIDialogEnd(Flag.ACCEPT_ERROR); +// end.execute(ctx); + HBCIMsgStatus ret=new HBCIMsgStatus(); HBCIHandler handler=getParentHandler(); @@ -371,7 +405,6 @@ nextMsgNum(); ret=kernel.rawDoIt(!isAnon && HBCIKernelImpl.SIGNIT, !isAnon && HBCIKernelImpl.CRYPTIT, - !isAnon && HBCIKernelImpl.NEED_SIG, !isAnon && HBCIKernelImpl.NEED_CRYPT); HBCIUtilsInternal.getCallback().status(mainPassport,HBCICallback.STATUS_DIALOG_END_DONE,ret); @@ -392,21 +425,13 @@ { try { HBCIUtils.log("executing dialog",HBCIUtils.LOG_DEBUG); - HBCIDialogStatus ret=new HBCIDialogStatus(); - - HBCIPassportInternal passport=(HBCIPassportInternal)getParentHandler().getPassport(); - - // first call passports's before-dialog-hook - passport.beforeCustomDialogHook(this); + final HBCIDialogStatus ret = new HBCIDialogStatus(); - HBCIMsgStatus initStatus=doDialogInit(); + HBCIMsgStatus initStatus = this.doDialogInit(); ret.setInitStatus(initStatus); - // so that e.g. pintan-passports can patch the list of messages to - // be executed (needed for twostep-mech) - passport.afterCustomDialogInitHook(this); - - if (initStatus.isOK()) { + if (initStatus.isOK()) + { ret.setMsgStatus(doJobs()); ret.setEndStatus(doDialogEnd()); } @@ -419,13 +444,15 @@ private void reset() { - try { + try + { dialogid=null; msgnum=1; - msgs=new ArrayList>(); - msgs.add(new ArrayList()); + this.queue = new HBCIMessageQueue(); listOfGVs.clear(); - } catch (Exception e) { + } + catch (Exception e) + { HBCIUtils.log(e); } } @@ -465,7 +492,7 @@ // signatur-anforderungen (anzahl) in einer msg stehen try { - HBCIUtils.log(HBCIUtilsInternal.getLocMsg("EXCMSG_ADDJOB",job.getName()),HBCIUtils.LOG_INFO); + HBCIUtils.log(HBCIUtilsInternal.getLocMsg("EXCMSG_ADDJOB",job.getName()),HBCIUtils.LOG_DEBUG); job.verifyConstraints(); // check bpd.numgva here @@ -511,8 +538,7 @@ } listOfGVs.setProperty(hbciCode,Integer.toString(gv_counter)); - - msgs.get(msgs.size()-1).add(job); + this.queue.getLast().append(job); } catch (Exception e) { String msg=HBCIUtilsInternal.getLocMsg("EXCMSG_CANTADDJOB",job.getName()); if (!HBCIUtilsInternal.ignoreError(null,"client.errors.ignoreAddJobErrors", @@ -524,26 +550,23 @@ } } - public List getAllTasks() + /** + * Liefert die Nachrichten-Queue des Dialogs. + * @return die Nachrichten-Queue des Dialogs. + */ + public HBCIMessageQueue getMessageQueue() { - List tasks=new ArrayList(); - - for (Iterator i=msgs.iterator();i.hasNext();) { - tasks.addAll((List)i.next()); - } - - return tasks; + return this.queue; } - + + /** + * Erzeugt explizit eine neue Message. + */ public void newMsg() { HBCIUtils.log("starting new message",HBCIUtils.LOG_DEBUG); - msgs.add(new ArrayList()); + this.queue.append(new HBCIMessage()); listOfGVs.clear(); } - public List> getMessages() - { - return this.msgs; - } } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HBCIHandler.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HBCIHandler.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HBCIHandler.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HBCIHandler.java 2019-11-27 09:04:22.000000000 +0000 @@ -23,7 +23,6 @@ import java.lang.reflect.Constructor; import java.security.KeyPair; -import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; @@ -141,125 +140,11 @@ } catch (Exception e) { throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_CANT_CREATE_HANDLE"),e); } - - // wenn in den UPD noch keine SEPA- und TAN-Medien-Informationen ueber die Konten enthalten - // sind, versuchen wir, diese zu holen - Properties upd=passport.getUPD(); - if (upd!=null && !upd.containsKey("_fetchedMetaInfo")) - { - // wir haben UPD, in denen aber nicht "_fetchedMetaInfo" drinsteht - updateMetaInfo(); - } } /** - * Ruft die SEPA-Infos der Konten sowie die TAN-Medienbezeichnungen ab. - * unterstuetzt wird und speichert diese Infos in den UPD. + * Macht die anonyme Initialisierung und ruft die BPD ab. */ - public void updateMetaInfo() - { - Properties bpd = passport.getBPD(); - if (bpd == null) - { - HBCIUtils.log("have no bpd, skip fetching of meta info", HBCIUtils.LOG_WARN); - return; - } - - try - { - final Properties lowlevel = this.getSupportedLowlevelJobs(); - - // SEPA-Infos abrufen - if (lowlevel.getProperty("SEPAInfo") != null) - { - HBCIUtils.log("fetching SEPA information", HBCIUtils.LOG_INFO); - HBCIJob sepainfo = this.newJob("SEPAInfo"); - sepainfo.addToQueue(); - } - - // TAN-Medien abrufen - aber nur bei PIN/TAN-Verfahren - if (lowlevel.getProperty("TANMediaList") != null && (this.passport instanceof AbstractPinTanPassport)) - { - HBCIUtils.log("fetching TAN media list", HBCIUtils.LOG_INFO); - HBCIJob tanMedia = this.newJob("TANMediaList"); - tanMedia.addToQueue(); - } - - HBCIExecStatus status = this.execute(); - if (status.isOK()) - { - HBCIUtils.log("successfully fetched meta info", HBCIUtils.LOG_INFO); - passport.getUPD().setProperty("_fetchedMetaInfo",new Date().toString()); - passport.saveChanges(); - } - else - { - HBCIUtils.log("error while fetching meta info: " + status.toString(), HBCIUtils.LOG_ERR); - } - } - catch (Exception e) - { - // Wir werfen das nicht als Exception. Unschoen, wenn das nicht klappt. - // Aber kein Grund zur Panik. - HBCIUtils.log(e); - } - } - - - /** - * Wenn der GV SEPAInfo unterstützt wird, heißt das, dass die Bank mit - * SEPA-Konten umgehen kann. In diesem Fall holen wir die SEPA-Informationen - * über die Konten von der Bank ab - für jedes SEPA-fähige Konto werden u.a. - * BIC/IBAN geliefert - * @deprecated Bitte updateMetaInfo verwenden. Das aktualisiert auch die TAN-Medien. - */ - public void updateSEPAInfo() - { - Properties bpd = passport.getBPD(); - if (bpd == null) - { - HBCIUtils.log("have no bpd, skipping SEPA information fetching", HBCIUtils.LOG_WARN); - return; - } - - // jetzt noch zusaetzliche die SEPA-Informationen abholen - try { - if (getSupportedLowlevelJobs().getProperty("SEPAInfo")!=null) { - HBCIUtils.log("trying to fetch SEPA information from institute", HBCIUtils.LOG_INFO); - - // HKSPA wird unterstuetzt - HBCIJob sepainfo=newJob("SEPAInfo"); - sepainfo.addToQueue(); - HBCIExecStatus status=execute(); - if (status.isOK()) { - HBCIUtils.log("successfully fetched information about SEPA accounts from institute", HBCIUtils.LOG_INFO); - - passport.getUPD().setProperty("_fetchedSEPA","1"); - passport.saveChanges(); - } else { - HBCIUtils.log("error while fetching information about SEPA accounts from institute:", HBCIUtils.LOG_ERR); - HBCIUtils.log(status.toString(), HBCIUtils.LOG_ERR); - } - /* beim execute() werden die Job-Result-Objekte automatisch - * gefuellt. Der GV-Klasse fuer SEPAInfo haengt sich in diese - * Logik rein, um gleich die UPD mit den SEPA-Konto-Daten - * zu aktualisieren, so dass an dieser Stelle die UPD um - * die SEPA-Informationen erweitert wurden. - */ - } else { - HBCIUtils.log("institute does not support SEPA accounts, so we skip fetching information about SEPA", HBCIUtils.LOG_DEBUG); - } - } - catch (HBCI_Exception he) - { - throw he; - } - catch (Exception e) - { - throw new HBCI_Exception(e); - } - } - private void registerInstitute() { try { @@ -271,6 +156,9 @@ } } + /** + * Ruft die UPD ab. + */ private void registerUser() { try { @@ -282,6 +170,18 @@ } } + /** + * @see org.kapott.hbci.manager.IHandlerData#sync(boolean) + */ + public void sync(boolean force) + { + HBCIInstitute inst = new HBCIInstitute(kernel,passport,false); + inst.sync(force); + + HBCIUser user = new HBCIUser(kernel,passport,false); + user.sync(force); + } + /**

Schließen des Handlers. Diese Methode sollte immer dann aufgerufen werden, wenn die entsprechende HBCI-Verbindung nicht mehr benötigt wird.

Beim Schließen des Handlers wird das Passport ebenfalls geschlossen. @@ -418,9 +318,9 @@ customerId=fixUnspecifiedCustomerId(customerId); - HBCIDialog dialog=null; + HBCIDialog dialog = null; try { - dialog=getDialogFor(customerId); + dialog = getDialogFor(customerId); dialog.addTask((HBCIJobImpl)job); } finally { // wenn beim hinzufügen des jobs ein fehler auftrat, und wenn der @@ -429,7 +329,8 @@ // auszuführender dialoge entfernt werden if (dialog!=null) { - if (dialog.getAllTasks().size()==0) { + if (dialog.getMessageQueue().getTaskCount() == 0) + { HBCIUtils.log("removing empty dialog for customerid "+customerId+" from list of dialogs",HBCIUtils.LOG_DEBUG); dialogs.remove(customerId); } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HBCIInstitute.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HBCIInstitute.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HBCIInstitute.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HBCIInstitute.java 2019-11-27 09:04:22.000000000 +0000 @@ -33,6 +33,10 @@ import org.kapott.hbci.callback.HBCICallback; import org.kapott.hbci.comm.Comm; +import org.kapott.hbci.dialog.DialogContext; +import org.kapott.hbci.dialog.HBCIDialogEnd; +import org.kapott.hbci.dialog.HBCIDialogFirstKeyRequest; +import org.kapott.hbci.dialog.HBCIDialogInit; import org.kapott.hbci.exceptions.HBCI_Exception; import org.kapott.hbci.exceptions.InvalidUserDataException; import org.kapott.hbci.exceptions.ProcessException; @@ -89,7 +93,7 @@ p.setProperty(BPD_KEY_HBCIVERSION,kernel.getHBCIVersion()); p.setProperty(BPD_KEY_LASTUPDATE,String.valueOf(System.currentTimeMillis())); passport.setBPD(p); - HBCIUtils.log("installed new BPD with version "+passport.getBPDVersion(),HBCIUtils.LOG_INFO); + HBCIUtils.log("installed new BPD with version "+passport.getBPDVersion(),HBCIUtils.LOG_DEBUG); HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_INST_BPD_INIT_DONE,passport.getBPD()); } } @@ -117,7 +121,7 @@ HBCIUtils.log("found key "+ keyCountry+"_"+keyBLZ+"_"+keyUserId+"_"+keyType+"_"+ keyNum+"_"+keyVersion, - HBCIUtils.LOG_INFO); + HBCIUtils.LOG_DEBUG); byte[] keyExponent=result.getProperty(head+".PubKey.exponent").getBytes(Comm.ENCODING); byte[] keyModulus=result.getProperty(head+".PubKey.modulus").getBytes(Comm.ENCODING); @@ -171,29 +175,6 @@ } } - private void doDialogEnd(String dialogid,boolean needSig) - { - HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_DIALOG_END,null); - - kernel.rawNewMsg("DialogEndAnon"); - kernel.rawSet("MsgHead.dialogid",dialogid); - kernel.rawSet("MsgHead.msgnum","2"); - kernel.rawSet("DialogEndS.dialogid",dialogid); - kernel.rawSet("MsgTail.msgnum","2"); - HBCIMsgStatus status=kernel.rawDoIt(HBCIKernelImpl.DONT_SIGNIT,HBCIKernelImpl.DONT_CRYPTIT,needSig,HBCIKernelImpl.DONT_NEED_CRYPT); - HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_DIALOG_END_DONE,status); - - if (!status.isOK()) { - HBCIUtils.log("dialog end failed: "+status.getErrorString(),HBCIUtils.LOG_ERR); - - String msg=HBCIUtilsInternal.getLocMsg("ERR_INST_ENDFAILED"); - if (!HBCIUtilsInternal.ignoreError(null,"client.errors.ignoreDialogEndErrors", - msg+": "+status.getErrorString())) { - throw new ProcessException(msg,status); - } - } - } - /** * Prueft, ob die BPD abgelaufen sind und neu geladen werden muessen. * @return true, wenn die BPD abgelaufen sind. @@ -202,7 +183,7 @@ { Properties bpd = passport.getBPD(); String maxAge = HBCIUtils.getParam("bpd.maxage.days","7"); - HBCIUtils.log("[BPD] max age: " + maxAge + " days",HBCIUtils.LOG_INFO); + HBCIUtils.log("[BPD] max age: " + maxAge + " days",HBCIUtils.LOG_DEBUG); long maxMillis = -1L; try @@ -210,7 +191,7 @@ int days = Integer.parseInt(maxAge); if (days == 0) { - HBCIUtils.log("[BPD] auto-expiry disabled",HBCIUtils.LOG_INFO); + HBCIUtils.log("[BPD] auto-expiry disabled",HBCIUtils.LOG_DEBUG); return false; } @@ -236,13 +217,13 @@ HBCIUtils.log(e); return false; } - HBCIUtils.log("[BPD] last update: " + (lastUpdate == 0 ? "never" : new Date(lastUpdate)),HBCIUtils.LOG_INFO); + HBCIUtils.log("[BPD] last update: " + (lastUpdate == 0 ? "never" : new Date(lastUpdate)),HBCIUtils.LOG_DEBUG); } long now = System.currentTimeMillis(); if (maxMillis < 0 || (now - lastUpdate) > maxMillis) { - HBCIUtils.log("[BPD] expired, will be updated now",HBCIUtils.LOG_INFO); + HBCIUtils.log("[BPD] expired, will be updated now",HBCIUtils.LOG_DEBUG); return true; } @@ -278,57 +259,37 @@ // gelandet sind. if (!version.equals("0")) { - HBCIUtils.log("resetting BPD version from " + version + " to 0",HBCIUtils.LOG_INFO); + HBCIUtils.log("resetting BPD version from " + version + " to 0",HBCIUtils.LOG_DEBUG); passport.getBPD().setProperty("BPA.version","0"); passport.saveChanges(); } HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_INST_BPD_INIT,null); - HBCIUtils.log("fetching BPD",HBCIUtils.LOG_INFO); + HBCIUtils.log("Aktualisiere Bankparameter (BPD)",HBCIUtils.LOG_INFO); - HBCIMsgStatus status=null; - boolean restarted=false; - while (true) { - kernel.rawNewMsg("DialogInitAnon"); - kernel.rawSet("Idn.KIK.blz", passport.getBLZ()); - kernel.rawSet("Idn.KIK.country", passport.getCountry()); - kernel.rawSet("ProcPrep.BPD", "0"); - kernel.rawSet("ProcPrep.UPD", passport.getUPDVersion()); - kernel.rawSet("ProcPrep.lang", "0"); - kernel.rawSet("ProcPrep.prodName", HBCIUtils.getParam("client.product.name",HBCIUtils.PRODUCT_ID)); - kernel.rawSet("ProcPrep.prodVersion", HBCIUtils.getParam("client.product.version","3")); - - status=kernel.rawDoIt(HBCIKernelImpl.DONT_SIGNIT,HBCIKernelImpl.DONT_CRYPTIT, - HBCIKernelImpl.DONT_NEED_SIG,HBCIKernelImpl.DONT_NEED_CRYPT); - - boolean need_restart=passport.postInitResponseHook(status, true); - if (need_restart) { - HBCIUtils.log("for some reason we have to restart this dialog", HBCIUtils.LOG_INFO); - if (restarted) { - HBCIUtils.log("this dialog already has been restarted once - to avoid endless loops we stop here", HBCIUtils.LOG_WARN); - throw new HBCI_Exception("*** restart loop - aborting"); - } - restarted=true; - } else { - break; - } - } + // Dialog-Context erzeugen + final DialogContext ctx = DialogContext.create(this.kernel,this.passport); + ctx.setAnonymous(true); + + // Dialog-Initialisierung senden + final HBCIDialogInit init = new HBCIDialogInit(); + final HBCIMsgStatus ret = init.execute(ctx); - Properties result=status.getData(); - updateBPD(result); + final Properties result = ret.getData(); + this.updateBPD(result); passport.saveChanges(); + + final HBCIDialogEnd end = new HBCIDialogEnd(); + end.execute(ctx); - try { - doDialogEnd(result.getProperty("MsgHead.dialogid"),HBCIKernelImpl.DONT_NEED_SIG); - } catch (Exception ex) { - HBCIUtils.log(ex); - } - - if (!status.isOK()) { - HBCIUtils.log("fetching BPD failed: "+status.getErrorString(),HBCIUtils.LOG_ERR); - throw new ProcessException(HBCIUtilsInternal.getLocMsg("ERR_INST_BPDFAILED"),status); + if (!ret.isOK()) + { + HBCIUtils.log("fetching BPD failed: "+ret.getErrorString(),HBCIUtils.LOG_ERR); + throw new ProcessException(HBCIUtilsInternal.getLocMsg("ERR_INST_BPDFAILED"),ret); } - } catch (Exception e) { + } + catch (Exception e) + { if (e instanceof HBCI_Exception) { HBCI_Exception he = (HBCI_Exception) e; @@ -336,10 +297,12 @@ throw he; } HBCIUtils.log(e,HBCIUtils.LOG_INFO); - // Viele Kreditinstitute unterstützen den anonymen Login nicht. Dass sollte nicht als Fehler den Anwender beunruhigen + // Viele Kreditinstitute unterstützen den anonymen Login nicht. Wir tolerieren den Fehler daher HBCIUtils.log("FAILED! - maybe this institute does not support anonymous logins",HBCIUtils.LOG_INFO); HBCIUtils.log("we will nevertheless go on",HBCIUtils.LOG_INFO); - } finally { + } + finally + { passport.closeComm(); } } @@ -367,76 +330,41 @@ public void fetchKeys() { // bei RDH institut-keys abholen (wenn nicht vorhanden) - if (passport.needInstKeys() && !passport.hasInstEncKey()) { - // TODO: hasInstEncKey(): bei Bankensignatur für HKTAN gibt es - // hier kollisionen, weil hasInstEncKey() für PINTAN eigentlich - // *immer* true zurückgibt + if (!passport.needInstKeys() || passport.hasInstEncKey()) + return; + + // TODO: hasInstEncKey(): bei Bankensignatur für HKTAN gibt es + // hier kollisionen, weil hasInstEncKey() für PINTAN eigentlich + // *immer* true zurückgibt - try { - HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_INST_GET_KEYS,null); - HBCIUtils.log("fetching institute keys",HBCIUtils.LOG_INFO); - - String country=passport.getCountry(); - String blz=passport.getBLZ(); - - HBCIMsgStatus status=null; - boolean restarted=false; - while (true) { - kernel.rawNewMsg("FirstKeyReq"); - kernel.rawSet("Idn.KIK.blz", blz); - kernel.rawSet("Idn.KIK.country", country); - kernel.rawSet("KeyReq.SecProfile.method",passport.getProfileMethod()); - kernel.rawSet("KeyReq.SecProfile.version",passport.getProfileVersion()); - kernel.rawSet("KeyReq.KeyName.keytype", "V"); - kernel.rawSet("KeyReq.KeyName.KIK.blz", blz); - kernel.rawSet("KeyReq.KeyName.KIK.country", country); - kernel.rawSet("KeyReq_2.SecProfile.method",passport.getProfileMethod()); - kernel.rawSet("KeyReq_2.SecProfile.version",passport.getProfileVersion()); - kernel.rawSet("KeyReq_2.KeyName.keytype", "S"); - kernel.rawSet("KeyReq_2.KeyName.KIK.blz", blz); - kernel.rawSet("KeyReq_2.KeyName.KIK.country", country); - kernel.rawSet("ProcPrep.BPD", passport.getBPDVersion()); - kernel.rawSet("ProcPrep.UPD", passport.getUPDVersion()); - kernel.rawSet("ProcPrep.lang", "0"); - kernel.rawSet("ProcPrep.prodName", HBCIUtils.getParam("client.product.name",HBCIUtils.PRODUCT_ID)); - kernel.rawSet("ProcPrep.prodVersion", HBCIUtils.getParam("client.product.version","3")); - - status = kernel.rawDoIt(HBCIKernelImpl.DONT_SIGNIT,HBCIKernelImpl.DONT_CRYPTIT, - HBCIKernelImpl.DONT_NEED_SIG,HBCIKernelImpl.DONT_NEED_CRYPT); - - boolean need_restart=passport.postInitResponseHook(status, true); - if (need_restart) { - HBCIUtils.log("for some reason we have to restart this dialog", HBCIUtils.LOG_INFO); - if (restarted) { - HBCIUtils.log("this dialog already has been restarted once - to avoid endless loops we stop here", HBCIUtils.LOG_WARN); - throw new HBCI_Exception("*** restart loop - aborting"); - } - restarted=true; - } else { - break; - } - } + try { + HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_INST_GET_KEYS,null); + HBCIUtils.log("Rufe Institutsschlüssel ab",HBCIUtils.LOG_INFO); + + // Dialog-Context erzeugen + final DialogContext ctx = DialogContext.create(this.kernel,this.passport); + ctx.setAnonymous(true); + + // Dialog-Initialisierung senden + final HBCIDialogFirstKeyRequest init = new HBCIDialogFirstKeyRequest(); + final HBCIMsgStatus ret = init.execute(ctx); + + final Properties result = ret.getData(); + updateBPD(result); + extractKeys(result); + passport.saveChanges(); + + final HBCIDialogEnd end = new HBCIDialogEnd(); + end.execute(ctx); - Properties result=status.getData(); - updateBPD(result); - extractKeys(result); - passport.saveChanges(); - - try { - doDialogEnd(result.getProperty("MsgHead.dialogid"),HBCIKernelImpl.DONT_NEED_SIG); - } catch (Exception ex) { - HBCIUtils.log(ex); - } - - if (!status.isOK()) { - HBCIUtils.log("fetching institute keys failed: "+status.getErrorString(),HBCIUtils.LOG_ERR); - throw new ProcessException(HBCIUtilsInternal.getLocMsg("ERR_INST_GETKEYSFAILED"),status); - } - } catch (Exception e) { - throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_FETCH_IKEYS_ERR"),e); - } finally { - passport.closeComm(); + if (!ret.isOK()) { + HBCIUtils.log("fetching institute keys failed: "+ret.getErrorString(),HBCIUtils.LOG_ERR); + throw new ProcessException(HBCIUtilsInternal.getLocMsg("ERR_INST_GETKEYSFAILED"),ret); } + } catch (Exception e) { + throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_FETCH_IKEYS_ERR"),e); + } finally { + passport.closeComm(); } } @@ -444,7 +372,21 @@ { fetchBPD(); fetchKeys(); - passport.setPersistentData("_registered_institute", Boolean.TRUE); + } + + /** + * @see org.kapott.hbci.manager.IHandlerData#sync(boolean) + */ + @Override + public void sync(boolean force) + { + if (force) + { + Properties bpd = this.passport.getBPD(); + if (bpd != null) + bpd.remove(BPD_KEY_LASTUPDATE); + } + this.register(); } public MsgGen getMsgGen() diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HBCIKernelImpl.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HBCIKernelImpl.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HBCIKernelImpl.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HBCIKernelImpl.java 2019-11-27 09:04:22.000000000 +0000 @@ -54,8 +54,6 @@ public final static boolean DONT_SIGNIT=false; public final static boolean CRYPTIT=true; public final static boolean DONT_CRYPTIT=false; - public final static boolean NEED_SIG=true; - public final static boolean DONT_NEED_SIG=false; public final static boolean NEED_CRYPT=true; public final static boolean DONT_NEED_CRYPT=false; @@ -170,12 +168,12 @@ } } - public HBCIMsgStatus rawDoIt(boolean signit,boolean cryptit,boolean needSig,boolean needCrypt) + public HBCIMsgStatus rawDoIt(boolean signit,boolean cryptit,boolean needCrypt) { HBCIPassportList passports=new HBCIPassportList(); HBCIPassportInternal passport=(HBCIPassportInternal)getParentHandlerData().getPassport(); passports.addPassport(passport,HBCIPassport.ROLE_ISS); - return rawDoIt(passports,signit,cryptit,needSig,needCrypt); + return rawDoIt(passports,signit,cryptit,needCrypt); } /* Processes the current message (mid-level API). @@ -196,7 +194,7 @@ @param cryptit A boolean value specifying, if the message to be sent should be encrypted. @return A Properties object that contains a path-value-pair for each dataelement of the received message. */ - public HBCIMsgStatus rawDoIt(HBCIPassportList passports,boolean signit,boolean cryptit,boolean needSig,boolean needCrypt) + public HBCIMsgStatus rawDoIt(HBCIPassportList passports,boolean signit,boolean cryptit,boolean needCrypt) { HBCIMsgStatus ret=new HBCIMsgStatus(); MSG msg=null; @@ -209,15 +207,13 @@ // plaintextnachricht erzeugen msg=gen.generate(currentMsgName); - + // alle daten für den rewriter setzen Rewrite.setData("passports",passports); Rewrite.setData("msgStatus",ret); Rewrite.setData("msgName",currentMsgName); Rewrite.setData("signIt",Boolean.valueOf(signit)); Rewrite.setData("cryptIt",Boolean.valueOf(cryptit)); - Rewrite.setData("needSig",Boolean.valueOf(needSig)); - Rewrite.setData("needCrypt",Boolean.valueOf(needCrypt)); // liste der rewriter erzeugen String rewriters_st=HBCIUtils.getParam("kernel.rewriter"); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HBCIUser.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HBCIUser.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HBCIUser.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HBCIUser.java 2019-11-27 09:04:22.000000000 +0000 @@ -24,11 +24,28 @@ import java.security.KeyFactory; import java.security.KeyPair; import java.security.spec.RSAPublicKeySpec; +import java.util.Arrays; import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.kapott.hbci.callback.HBCICallback; import org.kapott.hbci.comm.Comm; +import org.kapott.hbci.dialog.DialogContext; +import org.kapott.hbci.dialog.HBCIDialogEnd; +import org.kapott.hbci.dialog.HBCIDialogEnd.Flag; +import org.kapott.hbci.dialog.HBCIDialogInit; +import org.kapott.hbci.dialog.HBCIDialogLockKeys; +import org.kapott.hbci.dialog.HBCIDialogSync; +import org.kapott.hbci.dialog.HBCIDialogSync.Mode; +import org.kapott.hbci.dialog.HBCIProcess; +import org.kapott.hbci.dialog.HBCIProcessSepaInfo; +import org.kapott.hbci.dialog.HBCIProcessTanMedia; import org.kapott.hbci.exceptions.HBCI_Exception; import org.kapott.hbci.exceptions.NeedKeyAckException; import org.kapott.hbci.exceptions.ProcessException; @@ -36,11 +53,30 @@ import org.kapott.hbci.passport.HBCIPassportInternal; import org.kapott.hbci.status.HBCIMsgStatus; -/* @brief Instances of this class represent a certain user in combination with - a certain institute. */ -public final class HBCIUser - implements IHandlerData +/** + * Kapselt die authentifizierten Initialisierungsdialoge. Also im Wesentlichen alles, was mit den UPD zu tun hat. + */ +public final class HBCIUser implements IHandlerData { + public final static String UPD_KEY_HBCIVERSION = "_hbciversion"; + + /** + * In dem UPD-Property sind die TAN-Medienbezeichnungen gespeichert + */ + public final static String UPD_KEY_TANMEDIA = "tanmedia.names"; + + /** + * In dem UPD-Property ist gespeichert, wann wir die SEPA-Infos (IBAN, BIC) abgerufen haben + */ + public final static String UPD_KEY_FETCH_SEPAINFO = "_fetchedSepaInfo"; + + /** + * In dem UPD-Property ist gespeichert, wann wir die TAN-Medienbezeichnungen abgerufen haben + */ + public final static String UPD_KEY_FETCH_TANMEDIA = "_fetchedTanMedia"; + + private final static List UPD_PROTECT_KEYS = Arrays.asList(UPD_KEY_FETCH_TANMEDIA,UPD_KEY_FETCH_SEPAINFO,UPD_KEY_TANMEDIA); + private HBCIPassportInternal passport; private HBCIKernelImpl kernel; private boolean isAnon; @@ -67,6 +103,10 @@ this.anonSuffix=isAnon?"Anon":""; } + /** + * @deprecated Stattdessen die Klasse "HBCIDialogEnd" verwenden. + */ + @Deprecated private void doDialogEnd(String dialogid,String msgnum,boolean signIt,boolean cryptIt,boolean needCrypt) { HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_DIALOG_END,null); @@ -78,7 +118,6 @@ kernel.rawSet("MsgTail.msgnum",msgnum); HBCIMsgStatus status=kernel.rawDoIt(!isAnon && signIt, !isAnon && cryptIt, - !isAnon && HBCIKernelImpl.NEED_SIG, !isAnon && needCrypt); HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_DIALOG_END_DONE,status); @@ -95,7 +134,7 @@ private void sendAndActivateNewUserKeys(HBCIKey[] sigKey,HBCIKey[] encKey) { try { - HBCIUtils.log("sending user keys to institute",HBCIUtils.LOG_INFO); + HBCIUtils.log("Sende neue Benutzerschlüssel",HBCIUtils.LOG_INFO); String country=passport.getCountry(); String blz=passport.getBLZ(); @@ -182,10 +221,8 @@ // TODO: setMyDigKey passport.saveChanges(); - HBCIMsgStatus ret=kernel.rawDoIt(HBCIKernelImpl.SIGNIT,HBCIKernelImpl.CRYPTIT, - HBCIKernelImpl.NEED_SIG,HBCIKernelImpl.DONT_NEED_CRYPT); + HBCIMsgStatus ret=kernel.rawDoIt(HBCIKernelImpl.SIGNIT,HBCIKernelImpl.CRYPTIT,HBCIKernelImpl.DONT_NEED_CRYPT); - passport.postInitResponseHook(ret, passport.isAnonymous()); Properties result=ret.getData(); HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_SEND_KEYS_DONE,ret); @@ -204,59 +241,38 @@ throw new ProcessException(HBCIUtilsInternal.getLocMsg("EXCMSG_SENDKEYERR"),ret); } - try { - doDialogEnd(result.getProperty("MsgHead.dialogid"),"2",HBCIKernelImpl.DONT_SIGNIT,HBCIKernelImpl.CRYPTIT, - HBCIKernelImpl.DONT_NEED_CRYPT); - } catch (Exception e) { + try + { + doDialogEnd(result.getProperty("MsgHead.dialogid"),"2",HBCIKernelImpl.DONT_SIGNIT,HBCIKernelImpl.CRYPTIT,HBCIKernelImpl.DONT_NEED_CRYPT); + } + catch (Exception e) + { HBCIUtils.log(e); } triggerNewKeysEvent(); - } else { + } + else + { // aendern der aktuellen Nutzerschluessel HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_DIALOG_INIT,null); - // als erstes Dialog-Initialisierung - HBCIMsgStatus ret=null; - boolean restarted=false; - while (true) { - kernel.rawNewMsg("DialogInit"); - kernel.rawSet("Idn.KIK.blz", blz); - kernel.rawSet("Idn.KIK.country", country); - kernel.rawSet("Idn.customerid", passport.getCustomerId()); - kernel.rawSet("Idn.sysid", passport.getSysId()); - String sysstatus=passport.getSysStatus(); - kernel.rawSet("Idn.sysStatus",sysstatus); - kernel.rawSet("ProcPrep.BPD",passport.getBPDVersion()); - kernel.rawSet("ProcPrep.UPD",passport.getUPDVersion()); - kernel.rawSet("ProcPrep.lang",passport.getLang()); - kernel.rawSet("ProcPrep.prodName",HBCIUtils.getParam("client.product.name",HBCIUtils.PRODUCT_ID)); - kernel.rawSet("ProcPrep.prodVersion",HBCIUtils.getParam("client.product.version","3")); - ret=kernel.rawDoIt(HBCIKernelImpl.SIGNIT,HBCIKernelImpl.CRYPTIT, - HBCIKernelImpl.NEED_SIG,HBCIKernelImpl.NEED_CRYPT); - - boolean need_restart=passport.postInitResponseHook(ret, passport.isAnonymous()); - if (need_restart) { - HBCIUtils.log("for some reason we have to restart this dialog", HBCIUtils.LOG_INFO); - if (restarted) { - HBCIUtils.log("this dialog already has been restarted once - to avoid endless loops we stop here", HBCIUtils.LOG_WARN); - throw new HBCI_Exception("*** restart loop - aborting"); - } - restarted=true; - } else { - break; - } - } - - Properties result=ret.getData(); - + // Dialog-Context erzeugen + final DialogContext ctx = DialogContext.create(this.kernel,this.passport); + + // Dialog-Initialisierung senden + final HBCIDialogInit init = new HBCIDialogInit(); + HBCIMsgStatus ret = init.execute(ctx); + if (!ret.isOK()) throw new ProcessException(HBCIUtilsInternal.getLocMsg("EXCMSG_GETUPDFAIL"),ret); - + + Properties result = ret.getData(); + // evtl. Passport-Daten aktualisieren - HBCIInstitute inst=new HBCIInstitute(kernel,passport,false); + final HBCIInstitute inst=new HBCIInstitute(kernel,passport,false); inst.updateBPD(result); - updateUPD(result); + this.updateUPD(result); passport.saveChanges(); HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_DIALOG_INIT_DONE,new Object[] {ret,result.getProperty("MsgHead.dialogid")}); @@ -303,8 +319,7 @@ passport.setMyPrivateEncKey(encKey[1]); passport.saveChanges(); - ret=kernel.rawDoIt(HBCIKernelImpl.SIGNIT,HBCIKernelImpl.CRYPTIT, - HBCIKernelImpl.NEED_SIG,HBCIKernelImpl.NEED_CRYPT); + ret=kernel.rawDoIt(HBCIKernelImpl.SIGNIT,HBCIKernelImpl.CRYPTIT,HBCIKernelImpl.NEED_CRYPT); if (!ret.isOK()) { // TODO: hier muessen am besten beide schluessel im passport // gesichert werden, damit spaeter ueberprueft werden @@ -331,7 +346,7 @@ // TODO: setDigKey() passport.saveChanges(); - result=ret.getData(); + result = ret.getData(); HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_SEND_KEYS_DONE,ret); doDialogEnd(result.getProperty("MsgHead.dialogid"),"3",HBCIKernelImpl.SIGNIT,HBCIKernelImpl.CRYPTIT, HBCIKernelImpl.NEED_CRYPT); @@ -372,7 +387,7 @@ HBCIKey[] newEncKey=null; try { - HBCIUtils.log("manually setting new user keys",HBCIUtils.LOG_INFO); + HBCIUtils.log("Speichere neue Benutzerschlüssel",HBCIUtils.LOG_INFO); String blz=passport.getBLZ(); String country=passport.getCountry(); @@ -408,130 +423,71 @@ { try { HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_INIT_SYSID,null); - HBCIUtils.log("fetching new sys-id from institute",HBCIUtils.LOG_INFO); + HBCIUtils.log("Rufe neue System-ID ab",HBCIUtils.LOG_INFO); - // autosecmech HBCIUtils.log("checking whether passport is supported (but ignoring result)",HBCIUtils.LOG_DEBUG); boolean s=passport.isSupported(); HBCIUtils.log("passport supported: "+s,HBCIUtils.LOG_DEBUG); - String blz=passport.getBLZ(); - String country=passport.getCountry(); - passport.setSigId(new Long(1)); passport.setSysId("0"); - - HBCIMsgStatus ret=null; - boolean restarted=false; - while (true) { - kernel.rawNewMsg("Synch"); - kernel.rawSet("Idn.KIK.blz", blz); - kernel.rawSet("Idn.KIK.country", country); - kernel.rawSet("Idn.customerid", passport.getCustomerId()); - kernel.rawSet("Idn.sysid", "0"); - kernel.rawSet("Idn.sysStatus", "1"); - kernel.rawSet("MsgHead.dialogid", "0"); - kernel.rawSet("MsgHead.msgnum", "1"); - kernel.rawSet("MsgTail.msgnum", "1"); - kernel.rawSet("ProcPrep.BPD", passport.getBPDVersion()); - kernel.rawSet("ProcPrep.UPD", passport.getUPDVersion()); - kernel.rawSet("ProcPrep.lang", "0"); - kernel.rawSet("ProcPrep.prodName", HBCIUtils.getParam("client.product.name",HBCIUtils.PRODUCT_ID)); - kernel.rawSet("ProcPrep.prodVersion", HBCIUtils.getParam("client.product.version","3")); - kernel.rawSet("Sync.mode", "0"); - ret=kernel.rawDoIt(HBCIKernelImpl.SIGNIT,HBCIKernelImpl.CRYPTIT, - HBCIKernelImpl.NEED_SIG,HBCIKernelImpl.NEED_CRYPT); - - boolean need_restart=passport.postInitResponseHook(ret, passport.isAnonymous()); - if (need_restart) { - HBCIUtils.log("for some reason we have to restart this dialog", HBCIUtils.LOG_INFO); - if (restarted) { - HBCIUtils.log("this dialog already has been restarted once - to avoid endless loops we stop here", HBCIUtils.LOG_WARN); - throw new HBCI_Exception("*** restart loop - aborting"); - } - restarted=true; - } else { - break; - } - } - - Properties result=ret.getData(); - - if (!ret.isOK()) - throw new ProcessException(HBCIUtilsInternal.getLocMsg("EXCMSG_SYNCSYSIDFAIL"),ret); - - HBCIInstitute inst=new HBCIInstitute(kernel,passport,false); - inst.updateBPD(result); - updateUPD(result); - passport.setSysId(result.getProperty("SyncRes.sysid")); - passport.saveChanges(); - - HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_INIT_SYSID_DONE,new Object[] {ret,passport.getSysId()}); - HBCIUtils.log("new sys-id is "+passport.getSysId(),HBCIUtils.LOG_DEBUG); - doDialogEnd(result.getProperty("MsgHead.dialogid"),"2",HBCIKernelImpl.SIGNIT,HBCIKernelImpl.CRYPTIT, - HBCIKernelImpl.NEED_CRYPT); - } catch (Exception e) { + + //////////////////////////////////////// + // Sync + { + // Dialog-Synchronisierung senden + final DialogContext ctx = DialogContext.create(this.kernel,this.passport); + final HBCIDialogSync sync = new HBCIDialogSync(Mode.SYS_ID); + final HBCIMsgStatus ret = sync.execute(ctx); + final Properties result = ret.getData(); + + HBCIInstitute inst = new HBCIInstitute(kernel,passport,false); + inst.updateBPD(result); + updateUPD(result); + passport.setSysId(result.getProperty("SyncRes.sysid")); + passport.saveChanges(); + + HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_INIT_SYSID_DONE,new Object[] {ret,passport.getSysId()}); + HBCIUtils.log("new sys-id is "+passport.getSysId(),HBCIUtils.LOG_DEBUG); + + final HBCIDialogEnd end = new HBCIDialogEnd(); + end.execute(ctx); + + } + // + //////////////////////////////////////// + } + catch (Exception e) + { throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_SYNCSYSIDFAIL"),e); - } finally { + } + finally + { passport.closeComm(); } } - + public void fetchSigId() { try { HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_INIT_SIGID,null); - HBCIUtils.log("syncing signature id",HBCIUtils.LOG_INFO); + HBCIUtils.log("Synchronisiere Signatur-ID",HBCIUtils.LOG_INFO); // autosecmech HBCIUtils.log("checking whether passport is supported (but ignoring result)",HBCIUtils.LOG_DEBUG); boolean s=passport.isSupported(); HBCIUtils.log("passport supported: "+s,HBCIUtils.LOG_DEBUG); - String blz=passport.getBLZ(); - String country=passport.getCountry(); - passport.setSigId(new Long("9999999999999999")); - HBCIMsgStatus ret=null; - boolean restarted=false; - while (true) { - kernel.rawNewMsg("Synch"); - kernel.rawSet("Idn.KIK.blz", blz); - kernel.rawSet("Idn.KIK.country", country); - kernel.rawSet("Idn.customerid", passport.getCustomerId()); - kernel.rawSet("Idn.sysid", passport.getSysId()); - kernel.rawSet("Idn.sysStatus", passport.getSysStatus()); - kernel.rawSet("MsgHead.dialogid", "0"); - kernel.rawSet("MsgHead.msgnum", "1"); - kernel.rawSet("MsgTail.msgnum", "1"); - kernel.rawSet("ProcPrep.BPD", passport.getBPDVersion()); - kernel.rawSet("ProcPrep.UPD", passport.getUPDVersion()); - kernel.rawSet("ProcPrep.lang", "0"); - kernel.rawSet("ProcPrep.prodName", HBCIUtils.getParam("client.product.name",HBCIUtils.PRODUCT_ID)); - kernel.rawSet("ProcPrep.prodVersion", HBCIUtils.getParam("client.product.version","3")); - kernel.rawSet("Sync.mode", "2"); - ret=kernel.rawDoIt(passport.hasMySigKey(),HBCIKernelImpl.CRYPTIT, - HBCIKernelImpl.NEED_SIG,passport.hasMyEncKey()); - - boolean need_restart=passport.postInitResponseHook(ret, passport.isAnonymous()); - if (need_restart) { - HBCIUtils.log("for some reason we have to restart this dialog", HBCIUtils.LOG_INFO); - if (restarted) { - HBCIUtils.log("this dialog already has been restarted once - to avoid endless loops we stop here", HBCIUtils.LOG_WARN); - throw new HBCI_Exception("*** restart loop - aborting"); - } - restarted=true; - } else { - break; - } - } - - Properties result=ret.getData(); + // Dialog-Context erzeugen + final DialogContext ctx = DialogContext.create(this.kernel,this.passport); + + // Dialog-Synchronisierung senden + final HBCIDialogSync sync = new HBCIDialogSync(Mode.SIG_ID); + final HBCIMsgStatus ret = sync.execute(ctx); + final Properties result = ret.getData(); - if (!ret.isOK()) - throw new ProcessException(HBCIUtilsInternal.getLocMsg("EXCMSG_SYNCSIGIDFAIL"),ret); - HBCIInstitute inst=new HBCIInstitute(kernel,passport,false); inst.updateBPD(result); updateUPD(result); @@ -541,154 +497,210 @@ HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_INIT_SIGID_DONE,new Object[] {ret,passport.getSigId()}); HBCIUtils.log("signature id set to "+passport.getSigId(),HBCIUtils.LOG_DEBUG); - doDialogEnd(result.getProperty("MsgHead.dialogid"),"2",passport.hasMySigKey(),HBCIKernelImpl.CRYPTIT, - passport.hasMyEncKey()); - } catch (Exception e) { + + final HBCIDialogEnd end = new HBCIDialogEnd(Flag.SIG_ID); + end.execute(ctx); + } + catch (Exception e) + { throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_SYNCSIGIDFAIL"),e); - } finally { + } + finally + { passport.closeComm(); } } - + + /** + * Uebernimmt die aktualisierten UPD in den Passport. + * @param result die Ergebnis-Daten mit den UPD. + */ public void updateUPD(Properties result) { HBCIUtils.log("extracting UPD from results",HBCIUtils.LOG_DEBUG); - Properties p = new Properties(); - + /////////////////////////////////////////////////////////////////// + // Wir fischen alle UPD aus den Ergebnisdaten raus + final Properties p = new Properties(); for (Enumeration e = result.keys(); e.hasMoreElements(); ) { String key = (String)(e.nextElement()); if (key.startsWith("UPD.")) { p.setProperty(key.substring(("UPD.").length()), result.getProperty(key)); } } - - if (p.size()!=0) { - p.setProperty("_hbciversion",kernel.getHBCIVersion()); - - // Wir sichern wenigstens noch die TAN-Media-Infos, die vom HBCIHandler vorher abgerufen wurden - // Das ist etwas unschoen. Sinnvollerweise sollten die SEPA-Infos und TAN-Medien nicht in den - // UPD gespeichert werden. Dann gehen die auch nicht jedesmal wieder verloren und muessen nicht - // dauernd neu abgerufen werden. Das wuerde aber einen groesseren Umbau erfordern - Properties upd = passport.getUPD(); - if (upd != null) + // + /////////////////////////////////////////////////////////////////// + + // Keine UPD enthalten + if (p.size() == 0) + return; + + p.setProperty(UPD_KEY_HBCIVERSION,kernel.getHBCIVersion()); + + /////////////////////////////////////////////////////////////////// + // Die UPD-Keys sicher, die nicht direkt von der Bank kommen sondern + // von uns. Eigentlich sollten die nicht direkt in den UPD-Properties + // gespeichert sondern separat. Dazu muesste ich aber die Datenstruktur + // von PassportData erweitern und das auch bei allen anderen Passports + // nachziehen sowie eine Migration einbauen. Dafuer lohnt sich das nicht. + final Map protectedKeys = new HashMap(); + final Properties upd = passport.getUPD(); + if (upd != null && upd.size() > 0) + { + for (String key:UPD_PROTECT_KEYS) { - String mediaInfo = upd.getProperty("tanmedia.names"); - if (mediaInfo != null) - { - HBCIUtils.log("rescued TAN media info to new UPD: " + mediaInfo,HBCIUtils.LOG_INFO); - p.setProperty("tanmedia.names",mediaInfo); - } + String value = upd.getProperty(key); + if (value != null) // Achtung: In Properties darf es keine NULL-Keys geben + protectedKeys.put(key,value); } - String oldVersion = passport.getUPDVersion(); - passport.setUPD(p); - - HBCIUtils.log("installed new UPD [old version: " + oldVersion + ", new version: " + passport.getUPDVersion() + "]",HBCIUtils.LOG_INFO); - HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_INIT_UPD_DONE,passport.getUPD()); + p.putAll(protectedKeys); + } + + // Wenn die UPD-Keys keine BIC und IBAN mehr enthalten, verwende + // die bekannten einfach weiter, solange die Kontonummer identisch ist. + // Manche Banken schicken in den UPDs scheinbar die Konto-Daten nicht mehr immer mit. Daher merken wird uns die vorherigen + // Werte, wenn keine neuen uebertragen wurden + if (upd != null && upd.size() > 0) { + final Pattern pattern = Pattern.compile("(KInfo(.*?)\\.KTV)\\.(bic|iban)"); + for (final Object okey : upd.keySet()) { + final String key = okey.toString(); + final Matcher m = pattern.matcher(key); + if (m.matches() && !result.contains("UPD." + key)) { + final String kinfo = m.group(1); + if (Objects.equals(result.getProperty("UPD." + kinfo + ".KIK.country"), upd.getProperty(kinfo + ".KIK.country")) + && Objects.equals(result.getProperty("UPD." + kinfo + ".KIK.blz"), upd.getProperty(kinfo + ".KIK.blz")) + && Objects.equals(result.getProperty("UPD." + kinfo + ".number"), upd.getProperty(kinfo + ".number"))) { + HBCIUtils.log(key + " is missing, using the previous UPD's value", HBCIUtils.LOG_DEBUG); + p.put(okey, upd.getProperty(key)); + } + } + } } + + // Wir aktualisieren unabhaengig davon, ob sich die Versionsnummer erhoeht hat oder nicht, + // da nicht alle Banken die Versionsnummern erhoehen, wenn es Aenderungen gibt. Manche bleiben + // einfach pauschal immer bei Version 0. Daher aktualisieren wir immer dann, wenn wir neue + // UPDs erhalten haben. + final String oldVersion = passport.getUPDVersion(); + passport.setUPD(p); + final String newVersion = passport.getUPDVersion(); + + HBCIUtils.log("Benutzerparameter (UPD) aktualisiert [Bisherige Version: " + oldVersion + ", neue Version: " + newVersion + "]",HBCIUtils.LOG_INFO); + HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_INIT_UPD_DONE,passport.getUPD()); } + /** + * Ruft die UPD von der Bank ab. + */ public void fetchUPD() { try { HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_INIT_UPD,null); - HBCIUtils.log("fetching UPD (BPD-Version: " + passport.getBPDVersion() + ")",HBCIUtils.LOG_INFO); + HBCIUtils.log("updating UPD (BPD-Version: " + passport.getBPDVersion() + ")",HBCIUtils.LOG_DEBUG); + HBCIUtils.log("Aktualisiere Benutzerparameter (UPD)",HBCIUtils.LOG_INFO); // autosecmech HBCIUtils.log("checking whether passport is supported (but ignoring result)",HBCIUtils.LOG_DEBUG); boolean s=passport.isSupported(); HBCIUtils.log("passport supported: "+s,HBCIUtils.LOG_DEBUG); - - String blz=passport.getBLZ(); - String country=passport.getCountry(); - - HBCIMsgStatus ret=null; - boolean restarted=false; - while (true) { - kernel.rawNewMsg("DialogInit"+anonSuffix); - kernel.rawSet("Idn.KIK.blz", blz); - kernel.rawSet("Idn.KIK.country", country); - if (!isAnon) { - kernel.rawSet("Idn.customerid", passport.getCustomerId()); - kernel.rawSet("Idn.sysid", passport.getSysId()); - String sysstatus=passport.getSysStatus(); - kernel.rawSet("Idn.sysStatus",sysstatus); - } - kernel.rawSet("ProcPrep.BPD",passport.getBPDVersion()); - kernel.rawSet("ProcPrep.UPD","0"); - kernel.rawSet("ProcPrep.lang",passport.getLang()); - kernel.rawSet("ProcPrep.prodName",HBCIUtils.getParam("client.product.name",HBCIUtils.PRODUCT_ID)); - kernel.rawSet("ProcPrep.prodVersion",HBCIUtils.getParam("client.product.version","3")); - ret=kernel.rawDoIt(!isAnon && HBCIKernelImpl.SIGNIT, - !isAnon && HBCIKernelImpl.CRYPTIT, - !isAnon && HBCIKernelImpl.NEED_SIG, - !isAnon && HBCIKernelImpl.NEED_CRYPT); - - boolean need_restart=passport.postInitResponseHook(ret, passport.isAnonymous()); - if (need_restart) { - HBCIUtils.log("for some reason we have to restart this dialog", HBCIUtils.LOG_INFO); - if (restarted) { - HBCIUtils.log("this dialog already has been restarted once - to avoid endless loops we stop here", HBCIUtils.LOG_WARN); - throw new HBCI_Exception("*** restart loop - aborting"); - } - restarted=true; - } else { - break; - } - } - Properties result=ret.getData(); + final String version = passport.getUPDVersion(); + if (!version.equals("0")) + { + HBCIUtils.log("resetting UPD version from " + version + " to 0",HBCIUtils.LOG_DEBUG); + passport.getBPD().setProperty("UPA.version","0"); + passport.saveChanges(); + } + // Dialog-Context erzeugen + final DialogContext ctx = DialogContext.create(this.kernel,this.passport); + ctx.setAnonymous(this.isAnon); + + // Dialog-Initialisierung senden + final HBCIDialogInit init = new HBCIDialogInit(); + final HBCIMsgStatus ret = init.execute(ctx); + if (!ret.isOK()) throw new ProcessException(HBCIUtilsInternal.getLocMsg("EXCMSG_GETUPDFAIL"),ret); - + + Properties result = ret.getData(); + HBCIInstitute inst=new HBCIInstitute(kernel,passport,false); inst.updateBPD(result); - updateUPD(result); + this.updateUPD(result); passport.saveChanges(); - - doDialogEnd(result.getProperty("MsgHead.dialogid"),"2",HBCIKernelImpl.SIGNIT,HBCIKernelImpl.CRYPTIT, - HBCIKernelImpl.NEED_CRYPT); - } catch (Exception e) { + + final HBCIDialogEnd end = new HBCIDialogEnd(); + end.execute(ctx); + } + catch (Exception e) + { throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_GETUPDFAIL"),e); - } finally { + } + finally + { passport.closeComm(); } } - private void updateUserData() + /** + * @see org.kapott.hbci.manager.IHandlerData#sync(boolean) + */ + public void sync(boolean force) { - if (passport.getSysStatus().equals("1")) { + if (passport.getSysStatus().equals("1")) + { if (passport.getSysId().equals("0")) - fetchSysId(); + this.fetchSysId(); if (passport.getSigId().longValue()==-1) - fetchSigId(); + this.fetchSigId(); } - Properties upd=passport.getUPD(); - Properties bpd=passport.getBPD(); - String hbciVersionOfUPD=(upd!=null)?upd.getProperty("_hbciversion"):null; - - // Wir haben noch keine BPD. Offensichtlich unterstuetzt die Bank - // das Abrufen von BPDs ueber einen anonymen Dialog nicht. Also machen - // wir das jetzt hier mit einem nicht-anonymen Dialog gleich mit - if (bpd == null || passport.getUPD() == null || - hbciVersionOfUPD==null || - !hbciVersionOfUPD.equals(kernel.getHBCIVersion())) + Properties upd = passport.getUPD(); + Properties bpd = passport.getBPD(); + String hbciVersion = (upd != null) ? upd.getProperty(UPD_KEY_HBCIVERSION) : null; + + //////////////////////////////////////// + // TAN-Medienbezeichnung abrufen - machen wir noch vor den UPD. Weil wir dafuer ja ggf. bereits das TAN-Verfahren brauchen (wir rufen dort ja auch KInfo ab) + { + final DialogContext ctx = DialogContext.create(this.kernel,this.passport); + HBCIProcess p = new HBCIProcessTanMedia(force); + p.execute(ctx); + } + // + //////////////////////////////////////// + + //////////////////////////////////////// + // UPD abrufen, falls noetig + if (force || bpd == null || passport.getUPD() == null || hbciVersion==null || !hbciVersion.equals(kernel.getHBCIVersion())) { fetchUPD(); } + // + //////////////////////////////////////// + + //////////////////////////////////////// + // Zum Schluss noch die SEPA-Infos abrufen + { + final DialogContext ctx = DialogContext.create(this.kernel,this.passport); + HBCIProcess p = new HBCIProcessSepaInfo(force); + p.execute(ctx); + } + // + //////////////////////////////////////// } + /** + * Registriert den User. + */ public void register() { - if (passport.needUserKeys() && !passport.hasMySigKey()) { + if (passport.needUserKeys() && !passport.hasMySigKey()) generateNewKeys(); - } - updateUserData(); - passport.setPersistentData("_registered_user", Boolean.TRUE); + + this.sync(false); } public void lockKeys() @@ -704,68 +716,26 @@ try { HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_DIALOG_INIT,null); - HBCIUtils.log("locking user keys",HBCIUtils.LOG_INFO); + HBCIUtils.log("Sperre Benutzerschlüssel",HBCIUtils.LOG_INFO); - String blz=passport.getBLZ(); - String country=passport.getCountry(); - - HBCIMsgStatus status=null; - boolean restarted=false; - while (true) { - kernel.rawNewMsg("DialogInit"); - kernel.rawSet("Idn.KIK.blz", blz); - kernel.rawSet("Idn.KIK.country", country); - kernel.rawSet("Idn.customerid", passport.getCustomerId()); - kernel.rawSet("Idn.sysid", passport.getSysId()); - kernel.rawSet("Idn.sysStatus",passport.getSysStatus()); - kernel.rawSet("ProcPrep.BPD",passport.getBPDVersion()); - kernel.rawSet("ProcPrep.UPD",passport.getUPDVersion()); - kernel.rawSet("ProcPrep.lang",passport.getLang()); - kernel.rawSet("ProcPrep.prodName",HBCIUtils.getParam("client.product.name",HBCIUtils.PRODUCT_ID)); - kernel.rawSet("ProcPrep.prodVersion",HBCIUtils.getParam("client.product.version","3")); - - status=kernel.rawDoIt(HBCIKernelImpl.SIGNIT,HBCIKernelImpl.CRYPTIT, - HBCIKernelImpl.NEED_SIG,HBCIKernelImpl.NEED_CRYPT); - - boolean need_restart=passport.postInitResponseHook(status, passport.isAnonymous()); - if (need_restart) { - HBCIUtils.log("for some reason we have to restart this dialog", HBCIUtils.LOG_INFO); - if (restarted) { - HBCIUtils.log("this dialog already has been restarted once - to avoid endless loops we stop here", HBCIUtils.LOG_WARN); - throw new HBCI_Exception("*** restart loop - aborting"); - } - restarted=true; - } else { - break; - } - } - - Properties result=status.getData(); - - if (!status.isOK()) - throw new ProcessException(HBCIUtilsInternal.getLocMsg("EXCMSG_LOCKFAILED"),status); + // Dialog-Context erzeugen + final DialogContext ctx = DialogContext.create(this.kernel,this.passport); + + // Dialog-Initialisierung senden + final HBCIDialogInit init = new HBCIDialogInit(); + HBCIMsgStatus ret = init.execute(ctx); + + if (!ret.isOK()) + throw new ProcessException(HBCIUtilsInternal.getLocMsg("EXCMSG_LOCKFAILED"),ret); + + final Properties result = ret.getData(); String dialogid=result.getProperty("MsgHead.dialogid"); - HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_DIALOG_INIT_DONE,new Object[] {status,dialogid}); - + HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_DIALOG_INIT_DONE,new Object[] {ret,dialogid}); HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_LOCK_KEYS,null); - kernel.rawNewMsg("LockKeys"); - kernel.rawSet("MsgHead.dialogid",dialogid); - kernel.rawSet("MsgHead.msgnum","2"); - kernel.rawSet("MsgTail.msgnum","2"); - kernel.rawSet("KeyLock.KeyName.KIK.country",country); - kernel.rawSet("KeyLock.KeyName.KIK.blz",blz); - kernel.rawSet("KeyLock.KeyName.userid",passport.getMySigKeyName()); - kernel.rawSet("KeyLock.KeyName.keynum",passport.getMySigKeyNum()); - kernel.rawSet("KeyLock.KeyName.keyversion",passport.getMySigKeyVersion()); - kernel.rawSet("KeyLock.SecProfile.method", passport.getProfileMethod()); - kernel.rawSet("KeyLock.SecProfile.version", passport.getProfileVersion()); - kernel.rawSet("KeyLock.locktype","999"); - - status=kernel.rawDoIt(HBCIKernelImpl.SIGNIT,HBCIKernelImpl.CRYPTIT, - HBCIKernelImpl.NEED_SIG,HBCIKernelImpl.DONT_NEED_CRYPT); - if (!status.isOK()) - throw new ProcessException(HBCIUtilsInternal.getLocMsg("EXCMSG_LOCKFAILED"),status); + + final HBCIDialogLockKeys lock = new HBCIDialogLockKeys(); + ret = lock.execute(ctx); passport.clearMyDigKey(); passport.clearMySigKey(); @@ -773,9 +743,10 @@ passport.setSigId(new Long(1)); passport.saveChanges(); - - HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_LOCK_KEYS_DONE,status); - doDialogEnd(dialogid,"3",HBCIKernelImpl.DONT_SIGNIT,HBCIKernelImpl.CRYPTIT,HBCIKernelImpl.DONT_NEED_CRYPT); + + HBCIUtilsInternal.getCallback().status(passport,HBCICallback.STATUS_LOCK_KEYS_DONE,ret); + final HBCIDialogEnd end = new HBCIDialogEnd(); + end.execute(ctx); } catch (Exception e) { throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_LOCKFAILED"),e); } finally { diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HBCIUtils.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HBCIUtils.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HBCIUtils.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HBCIUtils.java 2019-11-27 09:04:22.000000000 +0000 @@ -837,7 +837,7 @@ refreshBLZList(HBCIUtils.class.getClassLoader()); - if (Security.getProvider("CryptAlgs4Java") == null) + if (Security.getProvider(CryptAlgs4JavaProvider.NAME) == null) { Security.addProvider(new CryptAlgs4JavaProvider()); } @@ -2230,7 +2230,7 @@ // Im Zweifel lassen wir die Bankverbindung lieber durch if (alg == null || alg.length() != 2) { - HBCIUtils.log("no crc information about " + blz + " in database", HBCIUtils.LOG_WARN); + HBCIUtils.log("no crc information about " + blz + " in database", HBCIUtils.LOG_DEBUG); return true; } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HHDVersion.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HHDVersion.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/HHDVersion.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/HHDVersion.java 2019-11-27 09:04:22.000000000 +0000 @@ -18,7 +18,7 @@ * QR-Code in HHD-Version 1.3 - die Sparkasse verwendet das so. * Muss als erstes hier stehen, weil es sonst falsch als "HHD_1_3" erkannt wird (ID beginnt genauso). */ - QR_1_3(Type.QRCODE,"HHD1\\.3\\..*?QR","1.3",-1,"hhd13"), + QR_1_3(Type.QRCODE,"HHD1\\.3\\..*?QR",null,-1,"hhd13"), /** * QR-Code. diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/IHandlerData.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/IHandlerData.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/IHandlerData.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/IHandlerData.java 2019-11-27 09:04:22.000000000 +0000 @@ -32,13 +32,14 @@ // Im Moment ist bei allen Implementierungen dieses Interfaces getMsgGen() // implementiert, indem this.kernel.getMsgGen() zurückgegeben wird - aber // das muss nicht so sein! -// -// TODO: das sieht aus wie ein typisches DesignPattern - das evtl. mal -// irgendwie verallgemeinern (double-linked-parent-child-connection) und überall -// wo nötig einsetzen (evtl. inklusive asserts oder automatischer Setter) - public interface IHandlerData { public HBCIPassport getPassport(); public MsgGen getMsgGen(); + + /** + * Fuehrt eine Neu-Synchronisierung durch. + * @param force true, wenn die Neu-Synchronisierung forciert werden soll. + */ + public void sync(boolean force); } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/MatrixCode.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/MatrixCode.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/MatrixCode.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/MatrixCode.java 2019-11-27 09:04:22.000000000 +0000 @@ -18,6 +18,23 @@ private byte[] image = null; /** + * Versucht den Text als Matrix-Code zu parsen. + * @param data die zu parsenden Daten. + * @return der Matrix-Code, wenn er lesbar war, sonst NULL. + */ + public static MatrixCode tryParse(String data) + { + try + { + return new MatrixCode(data); + } + catch (Exception e) + { + return null; + } + } + + /** * ct. * @param data die Rohdaten aus dem HHDuc als String. * @throws Exception wenn die Daten nicht als Bild geparst werden konnten. diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/QRCode.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/QRCode.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/QRCode.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/QRCode.java 2019-11-27 09:04:22.000000000 +0000 @@ -19,6 +19,24 @@ private byte[] image = null; /** + * Versucht die Daten als QR-Code zu parsen. + * @param hhd der HHDuc. + * @param msg die Nachricht. + * @return der QR-Code oder NULL. + */ + public static QRCode tryParse(String hhd, String msg) + { + try + { + return new QRCode(hhd,msg); + } + catch (Exception e) + { + return null; + } + } + + /** * ct. * @param hhd die Rohdaten aus dem HHDuc als String. * @param msg Die Sparkassen verwenden QR-Code in HHD 1.3 und uebertragen dort (wie beim Flickercode auch) die diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/TanMethod.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/TanMethod.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/manager/TanMethod.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/manager/TanMethod.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,77 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.manager; + +/** + * Kapselt die Informationen zu einem TAN-Verfahren. + */ +public class TanMethod +{ + /** + * Das Einschritt-Verfahren. + */ + public final static TanMethod ONESTEP = new TanMethod("999","Einschritt-Verfahren"); + + private String id = null; + private String name = null; + + /** + * ct. + * @param id ID des TAN-Verfahrens. + * @param name Name des TAN-Verfahrens. + */ + public TanMethod(String id, String name) + { + this.id = id; + this.name = name; + } + + /** + * Liefert die dreistellige numerische ID. + * @return id die dreistellige numerische ID. + */ + public String getId() + { + return id; + } + + /** + * Liefert die sprechende Bezeichnung. + * @return name die sprechende Bezeichnung. + */ + public String getName() + { + return name; + } + + /** + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return this.id + ": " + this.name; + } + +} + + diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/AbstractHBCIPassport.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/AbstractHBCIPassport.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/AbstractHBCIPassport.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/AbstractHBCIPassport.java 2019-11-27 09:04:22.000000000 +0000 @@ -21,7 +21,6 @@ package org.kapott.hbci.passport; -import java.io.File; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -31,22 +30,18 @@ import java.util.Hashtable; import java.util.Properties; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.PBEKeySpec; - import org.kapott.hbci.callback.HBCICallback; import org.kapott.hbci.comm.Comm; import org.kapott.hbci.comm.Filter; +import org.kapott.hbci.dialog.DialogContext; +import org.kapott.hbci.dialog.DialogEvent; import org.kapott.hbci.exceptions.HBCI_Exception; import org.kapott.hbci.exceptions.InvalidUserDataException; -import org.kapott.hbci.manager.HBCIDialog; import org.kapott.hbci.manager.HBCIKey; import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.manager.HBCIUtilsInternal; import org.kapott.hbci.manager.IHandlerData; import org.kapott.hbci.manager.LogFilter; -import org.kapott.hbci.status.HBCIMsgStatus; import org.kapott.hbci.structures.Konto; import org.kapott.hbci.structures.Limit; import org.kapott.hbci.structures.Value; @@ -79,9 +74,6 @@ private IHandlerData parentHandlerData; - protected static final boolean FOR_SAVE=true; - protected static final boolean FOR_LOAD=false; - public AbstractHBCIPassport(Object init) { persistentData=new Hashtable(); @@ -186,7 +178,7 @@ return comm; } - protected abstract Comm getCommInstance(); + public abstract Comm getCommInstance(); public final Filter getCommFilter() { @@ -284,33 +276,36 @@ return ret.toArray(new Konto[ret.size()]); } + /** + * @see org.kapott.hbci.passport.HBCIPassport#fillAccountInfo(org.kapott.hbci.structures.Konto) + */ public final void fillAccountInfo(Konto account) { - String number=HBCIUtilsInternal.stripLeadingZeroes(account.number); - String iban=HBCIUtilsInternal.stripLeadingZeroes(account.iban); - boolean haveNumber=(number!=null && number.length()!=0); - boolean haveIBAN=(iban!=null && iban.length()!=0); + String number = HBCIUtilsInternal.stripLeadingZeroes(account.number); + String iban = HBCIUtilsInternal.stripLeadingZeroes(account.iban); + boolean haveNumber = (number!=null && number.length()!=0); + boolean haveIBAN = (iban!=null && iban.length()!=0); - Konto[] accounts=getAccounts(); + Konto[] accounts = getAccounts(); - for (int i=0;i 0) account.blz=accounts[i].blz; + if (accounts[i].country != null && accounts[i].country.length() > 0) account.country=accounts[i].country; + if (accounts[i].number != null && accounts[i].number.length() > 0) account.number=accounts[i].number; + if (accounts[i].subnumber != null && accounts[i].subnumber.length() > 0) account.subnumber=accounts[i].subnumber; + if (accounts[i].type != null && accounts[i].type.length() > 0) account.type=accounts[i].type; + if (accounts[i].curr != null && accounts[i].curr.length() > 0) account.curr=accounts[i].curr; + if (accounts[i].customerid != null && accounts[i].customerid.length() > 0) account.customerid=accounts[i].customerid; + if (accounts[i].name != null && accounts[i].name.length() > 0) account.name=accounts[i].name; + if (accounts[i].bic != null && accounts[i].bic.length() > 0) account.bic=accounts[i].bic; + if (accounts[i].iban != null && accounts[i].iban.length() > 0) account.iban=accounts[i].iban; + if (accounts[i].acctype != null && accounts[i].acctype.length() > 0) account.acctype=accounts[i].acctype; break; } } @@ -774,40 +769,6 @@ closeComm(); } - /** - * Fragt den User per Callback nach dem Passwort fuer die Passport-Datei. - * @param forSaving true, wenn das Passwort zum Speichern erfragt werden soll. - * @return der Secret-Key. - */ - protected SecretKey calculatePassportKey(boolean forSaving) - { - try { - StringBuffer passphrase=new StringBuffer(); - HBCIUtilsInternal.getCallback().callback(this, - forSaving?HBCICallback.NEED_PASSPHRASE_SAVE - :HBCICallback.NEED_PASSPHRASE_LOAD, - forSaving?HBCIUtilsInternal.getLocMsg("CALLB_NEED_PASS_NEW") - :HBCIUtilsInternal.getLocMsg("CALLB_NEED_PASS"), - HBCICallback.TYPE_SECRET, - passphrase); - if (passphrase.length()==0) { - throw new InvalidUserDataException(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSZERO")); - } - LogFilter.getInstance().addSecretData(passphrase.toString(),"X",LogFilter.FILTER_SECRETS); - - String provider = HBCIUtils.getParam("kernel.security.provider"); - SecretKeyFactory fac = provider==null ? SecretKeyFactory.getInstance("PBEWithMD5AndDES") : SecretKeyFactory.getInstance("PBEWithMD5AndDES", provider); - PBEKeySpec keyspec=new PBEKeySpec(passphrase.toString().toCharArray()); - SecretKey passportKey=fac.generateSecret(keyspec); - keyspec.clearPassword(); - passphrase=null; - - return passportKey; - } catch (Exception ex) { - throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSPORT_KEYCALCERR"),ex); - } - } - public Properties getParamSegmentNames() { Properties ret=new Properties(); @@ -908,6 +869,7 @@ public void changePassphrase() { + persistentData.clear(); // Stellt sicher, dass auch gecachte Passwoerter geleert werden - siehe AbstractFormat resetPassphrase(); saveChanges(); } @@ -995,104 +957,23 @@ return result; } - - public boolean postInitResponseHook(HBCIMsgStatus msgStatus, boolean anonDialog) - { - // die default-Implementierung tut nichts und verlangt auch keine - // erneute Dialog-Initialisierung - return false; - } - - public void beforeCustomDialogHook(HBCIDialog dialog) - { - // default implementation does nothing - } - public void afterCustomDialogInitHook(HBCIDialog dialog) + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#onDialogEvent(org.kapott.hbci.dialog.DialogEvent, org.kapott.hbci.dialog.DialogContext) + */ + @Override + public void onDialogEvent(DialogEvent event, DialogContext ctx) { - // default implementation does nothing - only PinTan variant will override this + // Default-Implementierung macht nichts. } - + + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getMaxGVSegsPerMsg() + */ + @Override public int getMaxGVSegsPerMsg() { return 0; } - /** - * Ersetzt die Datei origFile gegen tmpFile. - * Nach dem Loeschen der Datei origFile wartet die Methode jedoch maximal 20 Sekunden, - * um sicherzustellen, dass z.Bsp. Virenscanner die Datei wieder losgelassen haben und - * sie wirklich verschwunden ist, bevor tmpFile auf den Namen von origFile umbenannt wird. - * Wichtig ist, dass zum Zeitpunkt des Aufrufes dieser Methode alle Streams auf die - * Dateien bereits geschlossen wurden. Die Schreibvorgaenge auf die Dateien muessen also - * abgeschlossen sein. Heisst: "os.close()" nicht erst im finally-Block machen sondern - * VOR dem Aufruf dieser Methode. - * @param origFile die originale zu ersetzende Datei. - * @param tmpFile die neue Datei, welche die originale ersetzen soll. - */ - protected void safeReplace(File origFile, File tmpFile) - { - HBCIUtils.log("saving passport file " + origFile, HBCIUtils.LOG_DEBUG); - - if (origFile.exists()) // Nur loeschen, wenn es ueberhaupt existiert - { - HBCIUtils.log("deleting old passport file " + origFile, HBCIUtils.LOG_DEBUG); - if (!origFile.delete()) - HBCIUtils.log("delete method for " + origFile + " returned false", HBCIUtils.LOG_ERR); - } - - // Wenn die Datei noch existiert, warten wir noch etwas - int retry = 0; - while (origFile.exists() && retry++ < 20) - { - try - { - HBCIUtils.log("wait a little bit, maybe another thread (antivirus scanner) holds a lock, file still exists", HBCIUtils.LOG_WARN); - Thread.sleep(1000L); - } - catch (InterruptedException e) - { - HBCIUtils.log("interrupted", HBCIUtils.LOG_WARN); - break; - } - if (!origFile.exists()) - { - HBCIUtils.log("passport file now gone: " + origFile, HBCIUtils.LOG_INFO); - break; - } - } - - // Datei existiert immer noch, dann brauchen wir das Rename gar nicht erst versuchen - if (origFile.exists()) - throw new HBCI_Exception("could not delete " + origFile); - - // Das Rename versuchen wir jetzt auch wiederholt mehrfach - retry = 0; - HBCIUtils.log("renaming " + tmpFile.getName() + " to " + origFile.getName(), HBCIUtils.LOG_DEBUG); - while (!origFile.exists() && retry++ < 20) - { - if (!tmpFile.renameTo(origFile)) - HBCIUtils.log("rename method for " + tmpFile + " to " + origFile + " returned false", HBCIUtils.LOG_ERR); - - if (origFile.exists()) - { - HBCIUtils.log("new passport file now exists: " + origFile, HBCIUtils.LOG_DEBUG); - break; - } - - try - { - HBCIUtils.log("wait a little bit, maybe another thread (antivirus scanner) holds a lock, file still not renamed", HBCIUtils.LOG_WARN); - Thread.sleep(1000L); - } - catch (InterruptedException e) - { - HBCIUtils.log("interrupted", HBCIUtils.LOG_WARN); - break; - } - } - - if (!origFile.exists()) - throw new HBCI_Exception("could not rename " + tmpFile.getName() + " to " + origFile.getName()); - } } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/AbstractPinTanPassport.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/AbstractPinTanPassport.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/AbstractPinTanPassport.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/AbstractPinTanPassport.java 2019-11-27 09:04:22.000000000 +0000 @@ -21,27 +21,42 @@ package org.kapott.hbci.passport; -import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; +import java.util.HashSet; import java.util.Hashtable; -import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.Properties; +import java.util.Set; import org.kapott.hbci.GV.GVTAN2Step; import org.kapott.hbci.GV.HBCIJobImpl; import org.kapott.hbci.callback.HBCICallback; import org.kapott.hbci.comm.Comm; +import org.kapott.hbci.dialog.DialogContext; +import org.kapott.hbci.dialog.DialogEvent; +import org.kapott.hbci.dialog.HBCIMessage; +import org.kapott.hbci.dialog.HBCIMessageQueue; +import org.kapott.hbci.dialog.KnownDialogTemplate; +import org.kapott.hbci.dialog.KnownReturncode; +import org.kapott.hbci.dialog.KnownTANProcess; +import org.kapott.hbci.dialog.KnownTANProcess.Variant; +import org.kapott.hbci.dialog.RawHBCIDialog; +import org.kapott.hbci.dialog.SCARequest; import org.kapott.hbci.exceptions.HBCI_Exception; import org.kapott.hbci.exceptions.InvalidUserDataException; import org.kapott.hbci.manager.ChallengeInfo; +import org.kapott.hbci.manager.Feature; import org.kapott.hbci.manager.HBCIDialog; import org.kapott.hbci.manager.HBCIHandler; +import org.kapott.hbci.manager.HBCIKernelImpl; import org.kapott.hbci.manager.HBCIKey; +import org.kapott.hbci.manager.HBCIUser; import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.manager.HBCIUtilsInternal; +import org.kapott.hbci.manager.TanMethod; import org.kapott.hbci.protocol.SEG; import org.kapott.hbci.protocol.factory.SEGFactory; import org.kapott.hbci.security.Crypt; @@ -49,39 +64,88 @@ import org.kapott.hbci.status.HBCIMsgStatus; import org.kapott.hbci.status.HBCIRetVal; import org.kapott.hbci.structures.Konto; - -public abstract class AbstractPinTanPassport - extends AbstractHBCIPassport +import org.kapott.hbci.tools.DigestUtils; +import org.kapott.hbci.tools.NumberUtil; +import org.kapott.hbci.tools.ParameterFinder; +import org.kapott.hbci.tools.ParameterFinder.Query; +import org.kapott.hbci.tools.StringUtil; + +/** + * Abstrakte Basis-Implementierung des PIN/TAN-Supports. + */ +public abstract class AbstractPinTanPassport extends AbstractHBCIPassport { - private String certfile; - private boolean checkCert; + /** + * Hier speichern wir zwischen, ob wir eine HKTAN-Anfrage in der Dialog-Initialisierung gesendet haben und wenn ja, welcher Prozess-Schritt es war + */ + private final static String CACHE_KEY_SCA_STEP = "__sca_step__"; + + /** + * Hier speichern wir, ob wir eine SCA-Ausnahme fuer einen GV von der Bank erhalten haben + */ + public final static String KEY_PD_SCA = "__pintan_sca___"; - private String proxy; - private String proxyuser; - private String proxypass; + /** + * Hier speichern wir den Challenge-Text der Bank fuer die TAN-Abfrage. + */ + public final static String KEY_PD_CHALLENGE = "__pintan_challenge___"; + + /** + * Hier speichern wir das HHDuc fuer die TAN-Abfrage. + */ + public final static String KEY_PD_HHDUC = "__pintan_hhduc___"; - private boolean verifyTANMode; + /** + * Hier speichern wir die Auftragsreferenz fuer die TAN-Abfrage. + */ + public final static String KEY_PD_ORDERREF = "__pintan_orderref___"; + + private String certfile; + private boolean checkCert; + + private String proxy; + private String proxyuser; + private String proxypass; + + private boolean verifyTANMode; - private Hashtable twostepMechanisms; - private List allowedTwostepMechanisms; + // Das aktuell ausgewaehlte TAN-Verfahren + private String tanMethod; - private String currentTANMethod; - private boolean currentTANMethodWasAutoSelected; + // True, wenn das aktuelle TAN-Verfahren automatisch gewaehlt wurde + private boolean tanMethodAutoSelected; + + // Die TAN-Verfahren fuer den User (aus dem 3920-Rueckmeldecode) + private List tanMethodsUser; - private String pin; + // Die TAN-Verfahren mit den Parametern aus den BPD + private Hashtable tanMethodsBank; + private String pin; + + /** + * ct. + * @param initObject + */ public AbstractPinTanPassport(Object initObject) { super(initObject); - this.twostepMechanisms=new Hashtable(); - this.allowedTwostepMechanisms=new ArrayList(); + + this.tanMethodsBank=new Hashtable(); + this.tanMethodsUser=new ArrayList(); } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getPassportTypeName() + */ public String getPassportTypeName() { return "PinTan"; } + /** + * @see org.kapott.hbci.passport.AbstractHBCIPassport#setBPD(java.util.Properties) + */ public void setBPD(Properties p) { super.setBPD(p); @@ -90,7 +154,7 @@ // hier die liste der verfügbaren sicherheitsverfahren aus den // bpd (HITANS) extrahieren - twostepMechanisms.clear(); + tanMethodsBank.clear(); // willuhn 2011-06-06 Maximal zulaessige HITANS-Segment-Version ermitteln // Hintergrund: Es gibt User, die nur HHD 1.3-taugliche TAN-Generatoren haben, @@ -123,21 +187,21 @@ // willuhn 2011-06-06 Segment-Versionen ueberspringen, die groesser als die max. zulaessige sind if (maxAllowedVersion > 0 && segVersion > maxAllowedVersion) { - HBCIUtils.log("skipping segversion " + segVersion + ", larger than allowed version " + maxAllowedVersion, HBCIUtils.LOG_INFO); + HBCIUtils.log("skipping segversion " + segVersion + ", larger than allowed version " + maxAllowedVersion, HBCIUtils.LOG_DEBUG); continue; } String secfunc=p.getProperty(key); // willuhn 2011-05-13 Checken, ob wir das Verfahren schon aus einer aktuelleren Segment-Version haben - Properties prev = twostepMechanisms.get(secfunc); + Properties prev = tanMethodsBank.get(secfunc); if (prev != null) { // Wir haben es schonmal. Mal sehen, welche Versionsnummer es hat int prevVersion = Integer.parseInt(prev.getProperty("segversion")); if (prevVersion > segVersion) { - HBCIUtils.log("found another twostepmech " + secfunc + " in segversion " + segVersion + ", allready have one in segversion " + prevVersion + ", ignoring segversion " + segVersion, HBCIUtils.LOG_DEBUG); + HBCIUtils.log("found another twostepmech " + secfunc + " in segversion " + segVersion + ", already have one in segversion " + prevVersion + ", ignoring segversion " + segVersion, HBCIUtils.LOG_DEBUG); continue; } } @@ -168,7 +232,7 @@ } // diesen mechanismus abspeichern - twostepMechanisms.put(secfunc,entry); + tanMethodsBank.put(secfunc,entry); } } } @@ -176,185 +240,440 @@ } } - private void searchFor3920s(HBCIRetVal[] rets) + /** + * @see org.kapott.hbci.passport.AbstractHBCIPassport#onDialogEvent(org.kapott.hbci.dialog.DialogEvent, org.kapott.hbci.dialog.DialogContext) + */ + @Override + public void onDialogEvent(DialogEvent event, DialogContext ctx) { - int l=rets.length; - for (int i=0; i0) { - newUserId = ret.params[0]; - newCustomerId = ret.params[0]; - } - if(l2>1) { - newCustomerId = ret.params[1]; - } - if(l2>0) { - HBCIUtils.log("autosecfunc: found 3072 in response - change user id", HBCIUtils.LOG_DEBUG); - // Aufrufer informieren, dass UserID und CustomerID geändert wurde - StringBuffer retData=new StringBuffer(); - retData.append(newUserId+"|"+newCustomerId); - HBCIUtilsInternal.getCallback().callback(this,HBCICallback.USERID_CHANGED,"*** User ID changed",HBCICallback.TYPE_TEXT,retData); - return true; - } - } - } - return false; + // Falsche PIN kann es bei einem anonymen Dialog nicht geben + if (ctx.isAnonymous()) + return; + + HBCIMsgStatus status = ctx.getMsgStatus(); + if (status == null) + return; + + if (status.isOK()) + return; + + HBCIRetVal ret = status.getInvalidPINCode(); + + if (ret == null) + return; + + HBCIUtils.log("PIN-Fehler erkannt, Meldung der Bank: " + ret.code + ": " + ret.text, HBCIUtils.LOG_INFO); + this.clearPIN(); + + // Aufrufer informieren, dass falsche PIN eingegeben wurde (um evtl. PIN aus Puffer zu löschen, etc.) + HBCIUtilsInternal.getCallback().callback(this,HBCICallback.WRONG_PIN,"*** invalid PIN entered",HBCICallback.TYPE_TEXT,new StringBuffer()); } - public boolean postInitResponseHook(HBCIMsgStatus msgStatus, boolean anonDialog) + /** + * Prueft, ob im Response der Code 3920 enthalten ist. + * Dort liefert die Bank neue Zweischritt-Verfahren. + * @param ctx der Kontext. + */ + private void check3920(DialogContext ctx) { - boolean restart_needed=super.postInitResponseHook(msgStatus, anonDialog); + // In einem anonymen Dialog koennen keine 3920 enthalten sein, da die User-spezifisch sind + if (ctx.isAnonymous()) + return; - if (!msgStatus.isOK()) { - HBCIUtils.log("dialog init ended with errors - searching for return code 'wrong PIN'", HBCIUtils.LOG_DEBUG); - - if (msgStatus.isInvalidPIN()) { - HBCIUtils.log("detected 'invalid PIN' error - clearing passport PIN", HBCIUtils.LOG_INFO); - clearPIN(); - - // Aufrufer informieren, dass falsche PIN eingegeben wurde (um evtl. PIN aus Puffer zu löschen, etc.) - StringBuffer retData=new StringBuffer(); - HBCIUtilsInternal.getCallback().callback(this,HBCICallback.WRONG_PIN,"*** invalid PIN entered",HBCICallback.TYPE_TEXT,retData); + HBCIMsgStatus status = ctx.getMsgStatus(); + if (status == null) + return; + + //////////////////////////////////////////////////// + // TAN-Verfahren ermitteln und uebernehmen + HBCIRetVal[] glob = status.globStatus != null ? status.globStatus.getWarnings() : null; + HBCIRetVal[] seg = status.segStatus != null ? status.segStatus.getWarnings() : null; + + if (glob == null && seg == null) + return; + + HBCIUtils.log("autosecfunc: search for 3920 in response to detect allowed twostep secmechs", HBCIUtils.LOG_DEBUG); + + List globRet = KnownReturncode.W3920.searchReturnValues(glob); + List segRet = KnownReturncode.W3920.searchReturnValues(seg); + + if (globRet == null && segRet == null) + return; + + final List oldList = new ArrayList(this.tanMethodsUser); + final Set newSet = new HashSet(); // Damit doppelte nicht doppelt in der Liste landen + + if (globRet != null) + { + for (HBCIRetVal r:globRet) + { + if (r.params != null) + newSet.addAll(Arrays.asList(r.params)); + } + } + if (segRet != null) + { + for (HBCIRetVal r:segRet) + { + if (r.params != null) + newSet.addAll(Arrays.asList(r.params)); } } - - HBCIUtils.log("autosecfunc: search for 3920s in response to detect allowed twostep secmechs", HBCIUtils.LOG_DEBUG); - - searchFor3920s(msgStatus.globStatus.getWarnings()); - searchFor3920s(msgStatus.segStatus.getWarnings()); - searchFor3072s(msgStatus.segStatus.getWarnings()); + final List newList = new ArrayList(newSet); - if (!anonDialog) { - setPersistentData("_authed_dialog_executed", Boolean.TRUE); - - // aktuelle secmech merken und neue auswählen (basierend auf evtl. gerade - // neu empfangenen informationen (3920s)) - String oldTANMethod=currentTANMethod; - String updatedTANMethod=getCurrentTANMethod(true); - - if (!oldTANMethod.equals(updatedTANMethod)) { - // wenn sich das ausgewählte secmech geändert hat, müssen wir - // einen dialog-restart fordern, weil während eines dialoges - // das secmech nicht gewechselt werden darf - restart_needed=true; - HBCIUtils.log("autosecfunc: after this dialog-init we had to change selected pintan method from "+oldTANMethod+" to "+updatedTANMethod+", so a restart of this dialog is needed", HBCIUtils.LOG_INFO); - } + if (newList.size() > 0 && !newList.equals(oldList)) + { + this.tanMethodsUser.clear(); + this.tanMethodsUser.addAll(newList); + HBCIUtils.log("autosecfunc: found 3920 in response - updated list of allowed twostepmechs - old: " + oldList + ", new: " + this.tanMethodsUser, HBCIUtils.LOG_DEBUG); } + // + //////////////////////////////////////////////////// - return restart_needed; + if (this.isAnonymous()) + return; + + //////////////////////////////////////////////////// + // Dialog neu starten, wenn das Verfahren sich geaendert hat + // aktuelle secmech merken und neue auswählen (basierend auf evtl. gerade neu empfangenen informationen (3920s)) + final String oldMethod = this.tanMethod; + final String newMethod = this.getCurrentTANMethod(true); + + if (Objects.equals(oldMethod,newMethod)) + return; + + // Wenn es eine Synchronisierung ist, lassen wir das Repeat weg. + // Die Postbank kommt nicht damit klar, wenn man eine neue Synchronisierung mit dem anderen TAN-Verfahren direkt hinterher sendet + if (ctx.getDialogInit().getTemplate() == KnownDialogTemplate.SYNC) + return; + + // wenn sich das ausgewählte secmech geändert hat, müssen wir + // einen dialog-restart fordern, weil während eines dialoges + // das secmech nicht gewechselt werden darf + HBCIUtils.log("autosecfunc: after this dialog-init we had to change selected pintan method from " + oldMethod + " to " + newMethod + ", so a restart of this dialog is needed", HBCIUtils.LOG_DEBUG); + HBCIUtils.log("Derzeitiges TAN-Verfahren aktualisiert, starte Dialog neu", HBCIUtils.LOG_INFO); + + ctx.setRepeat(true); + // + //////////////////////////////////////////////////// + } - public Comm getCommInstance() + /** + * Prueft, ob im Response der Code 3072 enthalten ist. + * Dort liefert die Bank ggf. aktualisierte Zugangsdaten. + * @param ctx der Kontext. + */ + private void check3072(DialogContext ctx) { - return Comm.getInstance("PinTan",this); + // Neue Zugangsdaten kann es anonym nicht geben. + if (ctx.isAnonymous()) + return; + + HBCIMsgStatus status = ctx.getMsgStatus(); + if (status == null) + return; + + HBCIRetVal[] seg = status.segStatus != null ? status.segStatus.getWarnings() : null; + if (seg == null) + return; + + final HBCIRetVal ret = KnownReturncode.W3072.searchReturnValue(seg); + if (ret == null) + return; + + String newCustomerId = ""; + String newUserId = ""; + int l2=ret.params.length; + if(l2>0) { + newUserId = ret.params[0]; + newCustomerId = ret.params[0]; + } + if(l2>1) { + newCustomerId = ret.params[1]; + } + if(l2>0) { + HBCIUtils.log("autosecfunc: found 3072 in response - change user id", HBCIUtils.LOG_DEBUG); + // Aufrufer informieren, dass UserID und CustomerID geändert wurde + StringBuffer retData=new StringBuffer(); + retData.append(newUserId+"|"+newCustomerId); + HBCIUtilsInternal.getCallback().callback(this,HBCICallback.USERID_CHANGED,"*** User ID changed",HBCICallback.TYPE_TEXT,retData); + } } - - public boolean isSupported() + + /** + * Prueft, ob die Dialog-Initialisierung um ein HKTAN erweitert werden muss. + * @param ctx der Kontext. + */ + private void checkSCARequest(DialogContext ctx) { - boolean ret=false; - Properties bpd=getBPD(); + final SCARequest sca = this.getSCARequest(ctx); + if (sca == null) + return; - if (bpd!=null && bpd.size()!=0) { - // loop through bpd and search for PinTanPar segment - for (Enumeration e=bpd.propertyNames();e.hasMoreElements();) { - String key=(String)e.nextElement(); - - if (key.startsWith("Params")) { - int posi=key.indexOf("."); - if (key.substring(posi+1).startsWith("PinTanPar")) { - ret=true; - break; - } - } - } - - if (ret) { - // prüfen, ob gewähltes sicherheitsverfahren unterstützt wird - // autosecmech: hier wird ein flag uebergeben, das anzeigt, dass getCurrentTANMethod() - // hier evtl. automatisch ermittelte secmechs neu verifzieren soll - String current=getCurrentTANMethod(true); - - if (current.equals(Sig.SECFUNC_SIG_PT_1STEP)) { - // einschrittverfahren gewählt - if (!isOneStepAllowed()) { - HBCIUtils.log("not supported: onestep method not allowed by BPD",HBCIUtils.LOG_ERR); - ret=false; - } else { - HBCIUtils.log("supported: pintan-onestep",HBCIUtils.LOG_DEBUG); - } - } else { - // irgendein zweischritt-verfahren gewählt - Properties entry=twostepMechanisms.get(current); - if (entry==null) { - // es gibt keinen info-eintrag für das gewählte verfahren - HBCIUtils.log("not supported: twostep-method "+current+" selected, but this is not supported",HBCIUtils.LOG_ERR); - ret=false; - } else { - HBCIUtils.log("selected twostep-method "+current+" ("+entry.getProperty("name")+") is supported",HBCIUtils.LOG_DEBUG); - } - } - } - } else { - ret=true; - } + Integer step = (Integer) ctx.getMeta().get(CACHE_KEY_SCA_STEP); + // Wir haben noch kein HKTAN gesendet. Dann senden wir jetzt Schritt 1 + if (step == null) + step = 1; + + ctx.getMeta().put(CACHE_KEY_SCA_STEP,step); + + final Variant variant = sca.getVariant(); + final KnownTANProcess tp = KnownTANProcess.get(variant,step.intValue()); + final int version = sca.getVersion(); - return ret; + // wir fuegen die Daten des HKTAN ein + final HBCIKernelImpl k = ctx.getKernel(); + final String prefix = "TAN2Step" + version; + k.rawSet(prefix,"requested"); // forcieren, dass das Segment mit gesendet wird - auch wenn es eigentlich optional ist + k.rawSet(prefix + ".process",tp.getCode()); + + final String segcode = sca.getTanReference(); + HBCIUtils.log("creating HKTAN for SCA [process : " + tp + "], order code: " + segcode + "]",HBCIUtils.LOG_DEBUG); + + k.rawSet(prefix + ".ordersegcode",segcode); + k.rawSet(prefix + ".OrderAccount.bic",""); + k.rawSet(prefix + ".OrderAccount.iban",""); + k.rawSet(prefix + ".OrderAccount.number",""); + k.rawSet(prefix + ".OrderAccount.subnumber",""); + k.rawSet(prefix + ".OrderAccount.KIK.blz",""); + k.rawSet(prefix + ".OrderAccount.KIK.country",""); + k.rawSet(prefix + ".orderhash",(variant == Variant.V2) ? "" : ("B00000000")); + k.rawSet(prefix + ".orderref",(step == 2) ? (String) this.getPersistentData(KEY_PD_ORDERREF) : ""); + k.rawSet(prefix + ".notlasttan","N"); + k.rawSet(prefix + ".challengeklass",(variant == Variant.V2) ? "" : "99"); + k.rawSet(prefix + ".tanmedia",sca.getTanMedia()); } - private boolean isOneStepAllowed() + /** + * Erzeugt einen passenden SCA-Request fuer die Dialog-Initialisierung. + * @param ctx der Context. + * @return der SCA-Request oder NULL, wenn keiner noetig ist. + */ + private SCARequest getSCARequest(DialogContext ctx) { - // default ist true, weil entweder *nur* das einschritt-verfahren unter- - // stützt wird oder keine BPD vorhanden sind, um das zu entscheiden - boolean ret=true; - - Properties bpd=getBPD(); - if (bpd!=null) { - for (Enumeration e=bpd.propertyNames();e.hasMoreElements();) { - String key=(String)e.nextElement(); - - // TODO: willuhn 2011-05-13: Das nimmt einfach den ersten gefundenen Parameter, liefert - // jedoch faelschlicherweise false, wenn das erste gefundene kein Einschritt-Verfahren ist - // Hier muesste man durch alle iterieren und dann true liefern, wenn wenigstens - // eines den Wert "J" hat. + final RawHBCIDialog init = ctx.getDialogInit(); + if (init == null) + return null; - // p.getProperty("Params_x.TAN2StepParY.ParTAN2StepZ.can1step") - if (key.startsWith("Params")) { - String subkey=key.substring(key.indexOf('.')+1); - if (subkey.startsWith("TAN2StepPar") && - subkey.endsWith(".can1step")) + // Checken, ob es ein Dialog, in dem eine SCA gemacht werden soll + if (!KnownDialogTemplate.LIST_SEND_SCA.contains(init.getTemplate())) + return null; + + if (Feature.PINTAN_INIT_SKIPONESTEPSCA.isEnabled()) + { + // Wenn wir ein Einschritt-TAN-Verfahren haben und es die autorisierte Initialisierung ist, + // dann senden wir das Init ohne HKTAN. Im anonymen Init haben wir ja schon per HKTAN mitgeteilt, dass + // wir SCA koennen. Jetzt gehts uns nur darum, die TAN-Verfahren per 3920 zu kriegen + // Ist nach Abstimmung mit einem HBCI-Server-Experten so legitim und wird von allen so gemacht: + // Bei Dialog-Init Verfahren 999 nehmen und ohne HKTAN senden + // Laut https://homebanking-hilfe.de/forum/topic.php?p=149751#real149751 akzeptiert die DKB kein Sync mit 999 + // + // Dialog HKTAN weglassen? + // --------------------------------------------------------- + // DialogInitAnon nein + // DialogInit mit Einschritt-TAN ja + // DialogInit mit Zweischritt-TAN nein + // Sync nein + if (!ctx.isAnonymous() && Objects.equals(TanMethod.ONESTEP.getId(),this.getCurrentTANMethod(false)) && init.getTemplate() == KnownDialogTemplate.INIT) + { + HBCIUtils.log("skipping HKTAN for dialog init, since we are using a one-step tan method",HBCIUtils.LOG_DEBUG); + return null; + } + } + + // HKTAN-Version und Prozessvariante ermitteln - kann NULL sein + final int segversionDefault = 6; + final Properties secmechInfo = this.getCurrentSecMechInfo(); + + final int hktanVersion = secmechInfo != null ? NumberUtil.parseInt(secmechInfo.getProperty("segversion"),segversionDefault) : segversionDefault; + + // Erst ab HKTAN 6 noetig. Die Bank unterstuetzt es scheinbar noch nicht + // Siehe B.4.3.1 - Wenn die Bank HITAN < 6 geschickt hat, dann kann sie keine SCA + if (hktanVersion < 6) + return null; + + final SCARequest r = init.createSCARequest(secmechInfo,hktanVersion); + if (r == null) + return null; + + if (r.getTanReference() == null) + { + // Beim Bezug auf das Segment schicken wir per Default "HKIDN". Gemaess Kapitel B.4.3.1 muss das Bezugssegment aber + // bei PIN/TAN-Management-Geschaeftsvorfaellen mit dem GV des jeweiligen Geschaeftsvorfalls belegt werden. + // Daher muessen wir im Payload schauen, ob ein entsprechender Geschaeftsvorfall enthalten ist. + // Wird muessen nur nach HKPAE, HKTAB schauen - das sind die einzigen beiden, die wir unterstuetzen + String segcode = "HKIDN"; + HBCIDialog payload = ctx.getDialog(); + if (payload != null) + { + final HBCIMessageQueue queue = payload.getMessageQueue(); + for (String code:Arrays.asList("HKPAE","HKTAB")) // Das sind GVChangePIN und GVTANMediaList + { + if (queue.findTask(code) != null) { - String value=bpd.getProperty(key); - ret=value.equals("J"); + segcode = code; break; } } } + r.setTanReference(segcode); } + if (r.getTanMedia() == null) + r.setTanMedia(this.getTanMedia(hktanVersion)); - return ret; + return r; + } + + /** + * Prueft das Response auf Vorhandensein eines HITAN bzw Code. + * Hinweis: Wir haben das ganze HKTAN-Handling derzeit leider doppelt. Einmal fuer die Dialog-Initialisierung und einmal fuer + * die Nachrichten mit den eigentlichen Geschaeftsvorfaellen (in patchMessagesFor2StepMethods). Wenn auch HBCIDialog#doJobs irgendwann + * auf die neuen RawHBCIDialoge umgestellt ist, kann eigentlich patchMessagesFor2StepMethods entfallen. + * @param ctx der Kontext. + */ + private void checkSCAResponse(DialogContext ctx) + { + final RawHBCIDialog init = ctx.getDialogInit(); + if (init == null) + return; + + // Checken, ob es ein Dialog, in dem eine SCA gemacht werden soll + if (!KnownDialogTemplate.LIST_SEND_SCA.contains(init.getTemplate())) + return; + + // Wenn wir noch in der anonymen Dialog-Initialisierung sind, interessiert uns das nicht. + if (ctx.isAnonymous() || this.isAnonymous()) + { + HBCIUtils.log("anonymous dialog, skip SCA response analysis",HBCIUtils.LOG_DEBUG); + ctx.getMeta().remove(CACHE_KEY_SCA_STEP); + return; + } + + Integer scaStep = (Integer) ctx.getMeta().get(CACHE_KEY_SCA_STEP); + + // Wenn wir keinen SCA-Request gesendet haben, brauchen wir auch nicht nach dem Response suchen + if (scaStep == null) + return; + + // Ohne Status brauchen wir es gar nicht erst versuchen + final HBCIMsgStatus status = ctx.getMsgStatus(); + if (status == null) + return; + + // Bank hat uns eine Ausnahme erteilt - wir brauchen keine TAN + if (status.segStatus != null && (KnownReturncode.W3076.searchReturnValue(status.segStatus.getWarnings()) != null || KnownReturncode.W3076.searchReturnValue(status.globStatus.getWarnings()) != null)) + { + HBCIUtils.log("found status code 3076, no SCA required",HBCIUtils.LOG_DEBUG); + ctx.getMeta().remove(CACHE_KEY_SCA_STEP); + return; + } + + // Schritt 1: Wir haben eine HKTAN-Anfrage gesendet. Mal schauen, ob die Bank tatsaechlich eine TAN will + if (scaStep.intValue() == 1) + { + HBCIUtils.log("HKTAN step 1 for SCA sent, checking for HITAN response [step: " + scaStep + "]",HBCIUtils.LOG_DEBUG); + + Properties props = ParameterFinder.find(status.getData(),"TAN2StepRes*."); + if (props == null || props.size() == 0) + return; // Wir haben kein HITAN + + // HITAN erhalten - Daten uebernehmen + HBCIUtils.log("SCA HITAN response found, triggering TAN request",HBCIUtils.LOG_DEBUG); + final String challenge = props.getProperty("challenge"); + if (challenge != null && challenge.length() > 0) + this.setPersistentData(KEY_PD_CHALLENGE,challenge); + + final String hhdUc = props.getProperty("challenge_hhd_uc"); + if (hhdUc != null && hhdUc.length() > 0) + this.setPersistentData(KEY_PD_HHDUC,hhdUc); + + final String orderref = props.getProperty("orderref"); + if (orderref != null && orderref.length() > 0) + this.setPersistentData(KEY_PD_ORDERREF,orderref); + + ///////////////////////////////////////////////////// + // Dialog-Init wiederholen, um den zweiten HKTAN-Schritt durchzufuehren + // OK, wir senden jetzt das finale HKTAN. Die Message darf nichts anderes enthalten. Daher aendern wir das Template. + ctx.getMeta().put(CACHE_KEY_SCA_STEP,2); + ctx.getDialogInit().setTemplate(KnownDialogTemplate.INIT_SCA); + ctx.setRepeat(true); + // + ///////////////////////////////////////////////////// + + return; + } + + if (scaStep.intValue() == 2) + { + ctx.getMeta().remove(CACHE_KEY_SCA_STEP); // Geschafft + HBCIUtils.log("HKTAN step 2 for SCA sent, checking for HITAN response [step: " + scaStep + "]",HBCIUtils.LOG_DEBUG); + Properties props = ParameterFinder.find(status.getData(),"TAN2StepRes*."); + if (props.size() > 0) + HBCIUtils.log("final SCA HITAN response found",HBCIUtils.LOG_DEBUG); + } + } + + /** + * @see org.kapott.hbci.passport.AbstractHBCIPassport#getCommInstance() + */ + public Comm getCommInstance() + { + return Comm.getInstance("PinTan",this); + } + + /** + * @see org.kapott.hbci.passport.HBCIPassport#isSupported() + */ + public boolean isSupported() + { + final Properties bpd = this.getBPD(); + if (bpd == null) + return true; + + // Wir triggern hier nur einmal die Auswahl des TAN-Verfahrens + this.getCurrentTANMethod(true); + return true; + } + + /** + * Liefert true, wenn das TAN-Einschritt-Verfahren unterstuetzt wird. + * @return true, wenn das TAN-Einschritt-Verfahren unterstuetzt wird. + */ + private boolean isOneStepAllowed() + { + final Properties bpd = this.getBPD(); + if (bpd == null) + return true; + + return ParameterFinder.findAll(bpd,ParameterFinder.Query.BPD_PINTAN_CAN1STEP).containsValue("J"); } /** Kann vor new HBCIHandler() aufgerufen werden, um zu @@ -362,193 +681,306 @@ * neu vom Server abgeholt wird und evtl. neu vom Nutzer abgefragt wird. */ public void resetSecMechs() { - this.allowedTwostepMechanisms=new ArrayList(); - this.currentTANMethod=null; - this.currentTANMethodWasAutoSelected=false; + this.tanMethodsUser=new ArrayList(); + this.tanMethod=null; + this.tanMethodAutoSelected=false; } + /** + * Legt das aktuelle TAN-Verfahren fest. + * @param method das aktuelle TAN-Verfahren. + */ public void setCurrentTANMethod(String method) { - this.currentTANMethod=method; + this.tanMethod=method; } - - public String getCurrentTANMethod(boolean recheckSupportedSecMechs) + + /** + * Liefert das aktuelle TAN-Verfahren. + * @param recheck true, wenn die gespeicherte Auswahl auf Aktualitaet und Verfuegbarkeit geprueft werden soll. + * Die Funktion kann in dem Fall einen Callback ausloesen, wenn mehrere Optionen zur Wahl stehen. + * @return das TAN-Verfahren. + */ + public String getCurrentTANMethod(boolean recheck) { - // autosecmech: hier auch dann checken, wenn recheckSupportedSecMechs==true - // UND die vorherige auswahl AUTOMATISCH getroffen wurde (manuelle auswahl - // also in jedem fall weiter verwenden) (das AUTOMATISCH erkennt man daran, - // dass recheckCurrentTANMethodNeeded==true ist) - if (currentTANMethod==null || recheckSupportedSecMechs) { - HBCIUtils.log("autosecfunc: (re)checking selected pintan secmech", HBCIUtils.LOG_DEBUG); - - // es ist noch kein zweischrittverfahren ausgewaehlt, oder die - // aktuelle auswahl soll gegen die liste der tatsaechlich unterstuetzten - // verfahren validiert werden + // Wir haben ein aktuelles TAN-Verfahren und eine Neupruefung ist nicht noetig + if (this.tanMethod != null && !recheck) + return this.tanMethod; + + boolean auto = Feature.PINTAN_INIT_AUTOMETHOD.isEnabled(); + HBCIUtils.log("(re)checking selected pintan method using " + (auto ? "auto-determine" : "ask") + " strategy", HBCIUtils.LOG_DEBUG); + + if (auto) + return this.determineTanMethod(); + + return this.askForTanMethod(); + } + + /** + * Liefert das aktuelle TAN-Verfahren. + * Hierbei versucht HBCI4Java das Verfahren erst automatisch zu ermitteln, bevor es den User fragt. + * @return das TAN-Verfahren. + */ + private String determineTanMethod() + { + // Wenn der User noch keine TAN-Verfahren hat, bleibt und als Option nur 999 - also Einschritt-Verfahren, um an den 3920 + // mit den zulaessigen Verfahren zu kommen. Wir pruefen hier gar nicht erst per "isOneStepAllowed", ob die Bank ein + // Einschritt-Verfahren anbietet, weil wir gar keine andere Option haben + // Update 2019-11-02: Geht bei der Postbank leider nicht. Die erlauben kein Einschritt-Vefahren und wollen daher + // tatsaechlich bereits beim Abruf der verfuegbaren TAN-Verfahren ein Zweischritt-Verfahren haben. Siehe + // https://homebanking-hilfe.de/forum/topic.php?p=151725#real151725 + if (this.tanMethodsUser.size() == 0 && this.isOneStepAllowed()) + return TanMethod.ONESTEP.getId(); + + ///////////////////////////////////////// + // Die Liste der verfuegbaren Optionen ermitteln + final List userList = new ArrayList(); + final List bankList = new ArrayList(); + + String[] secfuncs= this.tanMethodsBank.keySet().toArray(new String[this.tanMethodsBank.size()]); + Arrays.sort(secfuncs); + for (String secfunc:secfuncs) + { + final Properties entry = this.tanMethodsBank.get(secfunc); + final TanMethod m = new TanMethod(secfunc,entry.getProperty("name")); + if (this.tanMethodsUser.contains(secfunc)) + userList.add(m); + bankList.add(m); + } + // + ///////////////////////////////////////// + + HBCIUtils.log("tan methods of institute: " + bankList, HBCIUtils.LOG_DEBUG); + HBCIUtils.log("tan methods for user: " + userList, HBCIUtils.LOG_DEBUG); + + ///////////////////////////////////////// + // Auswahl treffen + + if (userList.size() == 0) + { + if (this.isOneStepAllowed()) + { + final TanMethod m = TanMethod.ONESTEP; + HBCIUtils.log("no tan method available for user, using: " + m,HBCIUtils.LOG_DEBUG); + // Wir speichern das TAN-Verfahren nicht, das kann unmoeglich das finale Verfahren sein. + return m.getId(); + } + else + { + // Sonderfall HBCI4Java Testserver. Der liefert gar keine TAN-Verfahren + if (bankList.size() == 0) + { + final TanMethod m = TanMethod.ONESTEP; + HBCIUtils.log("no tan method available for bank, using: " + m,HBCIUtils.LOG_DEBUG); + // Wir speichern das TAN-Verfahren nicht, das kann unmoeglich das finale Verfahren sein. + return m.getId(); + } - List options=new ArrayList(); + // Das ist sicher die Postbank. Wir haben noch kein Verfahren per 3920 erhalten, die Bank erlaubt + // aber nicht, diese per Einschritt-Verfahren abzurufen. Also muessen wir den User bitten, die Auswahl + // aus der in den BPD verfuegbaren Verfahren zu treffen. Auch wenn diese Liste dann Eintraege enthaelt, + // die fuer den User u.U. gar nicht verfuegbar sind. + HBCIUtils.log("have no methods for user and institute doesn't allow one step method - asking user. available methods on institute: " + bankList,HBCIUtils.LOG_DEBUG); + this.setCurrentTANMethod(this.chooseTANMethod(bankList)); + HBCIUtils.log("selected pintan method by user: " + tanMethod, HBCIUtils.LOG_INFO); + return this.tanMethod; + } + } + + if (userList.size() == 1) + { + final TanMethod m = userList.get(0); + HBCIUtils.log("only one tan method available for user: " + m,HBCIUtils.LOG_DEBUG); + this.setCurrentTANMethod(m.getId()); // Ueberschreibt bei der Gelegenheit gleich die letzte Version + return this.tanMethod; + } + + // Wir haben mehrere zur Auswahl. Checken, ob wir schon eins hatten. Und wenn ja, ob es + // immer noch verfuegbar ist. Wenn ja, liefern wir einfach das zurueck. Wenn es nicht + // mehr verfuegbar ist, muessen wir den User neu fragen. + boolean reuse = this.tanMethod != null && userList.stream().anyMatch(m -> Objects.equals(m.getId(),this.tanMethod)); + if (reuse) + return this.tanMethod; + + // User fragen + HBCIUtils.log("asking user what tan method to use. available methods: " + userList,HBCIUtils.LOG_DEBUG); + this.setCurrentTANMethod(this.chooseTANMethod(userList)); + HBCIUtils.log("selected pintan method by user: " + tanMethod, HBCIUtils.LOG_INFO); + return this.tanMethod; + } + + /** + * Liefert das aktuelle TAN-Verfahren. + * Fragt hierbei im Zweifelsfall eher den User anstatt es selbst herauszufinden. + * @return das TAN-Verfahren. + */ + private String askForTanMethod() + { + ///////////////////////////////////////// + // Die Liste der verfuegbaren Optionen ermitteln + final List options = new ArrayList(); + final List fallback = new ArrayList(); + + // Einschritt-Verfahren optional hinzufuegen + if (this.isOneStepAllowed()) + { + TanMethod m = TanMethod.ONESTEP; + // Nur hinzufuegen, wenn wir entweder gar keine erlaubten haben oder es in der Liste der erlaubten drin ist + if (this.tanMethodsUser.size() == 0 || this.tanMethodsUser.contains(m.getId())) + options.add(m); + } + + // Die Zweischritt-Verfahren hinzufuegen + String[] secfuncs= this.tanMethodsBank.keySet().toArray(new String[this.tanMethodsBank.size()]); + Arrays.sort(secfuncs); + for (String secfunc:secfuncs) + { + final Properties entry = this.tanMethodsBank.get(secfunc); + final TanMethod m = new TanMethod(secfunc,entry.getProperty("name")); + if (this.tanMethodsUser.size() == 0 || this.tanMethodsUser.contains(secfunc)) + { + options.add(m); + } + fallback.add(m); + } + // + ///////////////////////////////////////// + + + ///////////////////////////////////////// + // 0 Optionen verfuegbar + + // Wir haben keine Optionen gefunden + if (options.size() == 0) + { + // wir lassen das hier mal noch auf true stehen, weil das bestimmt noch nicht final war. Schliesslich basierte die + // Auswahl des Verfahrens nicht auf den fuer den User freigeschalteten Verfahren sondern nur den allgemein von der + // Bank unterstuetzten + this.tanMethodAutoSelected = true; - if (isOneStepAllowed()) { - // wenn einschrittverfahren unterstützt, dass zur liste hinzufügen - if (allowedTwostepMechanisms.size()==0 || allowedTwostepMechanisms.contains(Sig.SECFUNC_SIG_PT_1STEP)) { - options.add(new String[] {Sig.SECFUNC_SIG_PT_1STEP,"Einschritt-Verfahren"}); - } + HBCIUtils.log("autosecfunc: no information about allowed pintan methods available", HBCIUtils.LOG_INFO); + // Wir haben keine TAN-Verfahren, die fuer den User per 3920 zugelassen sind. + // Wir schauen mal, ob die Bank wenigstens welche in HIPINS gemeldet hat. Wenn ja, dann soll der + // User eins von dort auswaehlen. Ob das dann aber ein erlaubtes ist, wissen wir nicht. + if (fallback.size() > 0) + { + HBCIUtils.log("autosecfunc: have some pintan methods in HIPINS, asking user, what to use from: " + fallback, HBCIUtils.LOG_INFO); + final String selected = this.chooseTANMethod(fallback); + this.setCurrentTANMethod(selected); + HBCIUtils.log("autosecfunc: manually selected pintan method from HIPINS " + tanMethod, HBCIUtils.LOG_DEBUG); + } + else + { + TanMethod m = TanMethod.ONESTEP; + HBCIUtils.log("autosecfunc: absolutly no information about allowed pintan methods available, fallback to " + m, HBCIUtils.LOG_WARN); + this.setCurrentTANMethod(m.getId()); } - // alle zweischritt-verfahren zur auswahlliste hinzufügen - String[] secfuncs= twostepMechanisms.keySet().toArray(new String[twostepMechanisms.size()]); - Arrays.sort(secfuncs); - int len=secfuncs.length; - for (int i=0;i1) { - // es werden mehrere verfahren unterstützt - - if (currentTANMethod!=null) { - // es ist schon ein verfahren ausgewaehlt. falls dieses verfahren - // nicht in der liste der unterstuetzten verfahren enthalten ist, - // setzen wir das auf "null" zurueck, damit das zu verwendende - // verfahren neu ermittelt wird - - boolean ok=false; - for (Iterator i=options.iterator();i.hasNext();) { - if (currentTANMethod.equals((i.next())[0])) { - ok=true; - break; - } - } - - if (!ok) { - HBCIUtils.log("autosecfunc: currently selected pintan method ("+currentTANMethod+") not in list of supported methods - resetting current selection", HBCIUtils.LOG_DEBUG); - currentTANMethod=null; - } - } - - if (currentTANMethod==null || this.currentTANMethodWasAutoSelected) { - // wenn noch kein verfahren ausgewaehlt ist, oder das bisherige - // verfahren automatisch ausgewaehlt wurde, muessen wir uns - // neu fuer eine method aus der liste entscheiden - - // TODO: damit das sinnvoll funktioniert, sollte die liste der - // allowedTwostepMechs mit im passport gespeichert werden. - if (allowedTwostepMechanisms.size()==0 && - getPersistentData("_authed_dialog_executed")==null) - { - // wir wählen einen secmech automatisch aus, wenn wir - // die liste der erlaubten secmechs nicht haben - // (entweder weil wir sie noch nie abgefragt haben oder weil - // diese daten einfach nicht geliefert werden). im fall - // "schon abgefragt, aber nicht geliefert" dürfen wir aber - // wiederum NICHT automatisch auswählen, so dass wir zusätzlich - // fragen, ob schon mal ein dialog gelaufen ist, bei dem - // diese daten hätten geliefert werden KÖNNEN (_authed_dialog_executed). - // nur wenn wir die liste der gültigen secmechs noch gar - // nicht haben KÖNNEN, wählen wir einen automatisch aus. - - String autoSelection=(options.get(0))[0]; - HBCIUtils.log("autosecfunc: there are "+options.size()+" pintan methods supported, but we don't know which of them are allowed for the current user, so we automatically choose "+autoSelection,HBCIUtils.LOG_DEBUG); - setCurrentTANMethod(autoSelection); - - // autosecmech: hier merken, dass dieses verfahren AUTOMATISCH - // ausgewaehlt wurde, so dass wir spaeter immer mal wieder pruefen - // muessen, ob inzwischen nicht mehr/andere unterstuetzte secmechs bekannt sind - // (passiert z.b. wenn das anonyme abholen der bpd fehlschlaegt) - this.currentTANMethodWasAutoSelected=true; - - } else { - // wir wissen schon, welche secmechs erlaubt sind (entweder - // durch einen vorhergehenden dialog oder aus den persistenten - // passport-daten), oder wir wissen es nicht (size==0), haben aber schonmal - // danach gefragt (ein authed_dialog ist schon gelaufen, bei dem - // diese daten aber nicht geliefert wurden). - // in jedem fall steht in "options" die liste der prinzipiell - // verfügbaren secmechs drin, u.U. gekürzt auf die tatsächlich - // erlaubten secmechs. - // wir fragen also via callback nach, welcher dieser secmechs - // denn nun verwendet werden soll - - HBCIUtils.log("autosecfunc: we have to callback to ask for pintan method to be used", HBCIUtils.LOG_DEBUG); - - // auswahlliste als string zusammensetzen - StringBuffer retData=new StringBuffer(); - for (Iterator i=options.iterator();i.hasNext();) { - if (retData.length()!=0) { - retData.append("|"); - } - String[] entry= i.next(); - retData.append(entry[0]).append(":").append(entry[1]); - } - - // callback erzeugen - HBCIUtilsInternal.getCallback().callback(this, - HBCICallback.NEED_PT_SECMECH, - "*** Select a pintan method from the list", - HBCICallback.TYPE_TEXT, - retData); - - // überprüfen, ob das gewählte verfahren einem aus der liste entspricht - String selected=retData.toString(); - boolean ok=false; - for (Iterator i=options.iterator();i.hasNext();) { - if (selected.equals((i.next())[0])) { - ok=true; - break; - } - } - - if (!ok) { - throw new InvalidUserDataException("*** selected pintan method not supported!"); - } - - setCurrentTANMethod(selected); - this.currentTANMethodWasAutoSelected=false; - - HBCIUtils.log("autosecfunc: manually selected pintan method "+currentTANMethod, HBCIUtils.LOG_DEBUG); - } - } - - } else { - // es wird scheinbar GAR KEIN verfahren unterstuetzt. also nehmen - // wir automatisch 999 - HBCIUtils.log("autosecfunc: absolutely no information about allowed pintan methods available - automatically falling back to 999", HBCIUtils.LOG_DEBUG); - setCurrentTANMethod("999"); - this.currentTANMethodWasAutoSelected=true; + if (!found) + { + HBCIUtils.log("autosecfunc: currently selected pintan method ("+this.tanMethod+") not in list of supported methods " + options + " - resetting current selection", HBCIUtils.LOG_DEBUG); + this.tanMethod = null; } } + // + ///////////////////////////////////////// + + // Wenn wir jetzt immer noch ein Verfahren haben und dieses nicht automatisch gewaehlt wurde, dann + // duerfen wir es verwenden. + if (this.tanMethod != null && !this.tanMethodAutoSelected) + return this.tanMethod; + + // User fragen - aber nur, wenn wir was zum Auswahlen haben + if (options != null && options.size() > 0) + { + HBCIUtils.log("autosecfunc: asking user what tan method to use. available methods: " + options,HBCIUtils.LOG_DEBUG); + final String selected = this.chooseTANMethod(options); + + this.setCurrentTANMethod(selected); + this.tanMethodAutoSelected = false; + HBCIUtils.log("autosecfunc: manually selected pintan method "+tanMethod, HBCIUtils.LOG_DEBUG); + } + return tanMethod; + } + + /** + * Fuehrt den Callback zur Auswahl des TAN-Verfahrens durch. + * @param options die verfuegbaren Optionen. + * @return das gewaehlte TAN-Verfahren. + */ + private String chooseTANMethod(List options) + { + final StringBuffer retData = new StringBuffer(); + for (TanMethod entry:options) + { + if (retData.length()!=0) + retData.append("|"); - return currentTANMethod; + retData.append(entry.getId()).append(":").append(entry.getName()); + } + + HBCIUtilsInternal.getCallback().callback(this,HBCICallback.NEED_PT_SECMECH,"*** Select a pintan method from the list",HBCICallback.TYPE_TEXT,retData); + + // Pruefen, ob das gewaehlte Verfahren einem aus der Liste entspricht + final String selected = retData.toString(); + + for (TanMethod entry:options) + { + if (selected.equals(entry.getId())) + return selected; + } + + throw new InvalidUserDataException("*** selected pintan method not supported: " + selected); } public Properties getCurrentSecMechInfo() { - return twostepMechanisms.get(getCurrentTANMethod(false)); + return tanMethodsBank.get(getCurrentTANMethod(false)); } public Hashtable getTwostepMechanisms() { - return twostepMechanisms; + return tanMethodsBank; } public String getProfileMethod() @@ -558,7 +990,7 @@ public String getProfileVersion() { - return getCurrentTANMethod(false).equals(Sig.SECFUNC_SIG_PT_1STEP)?"1":"2"; + return getCurrentTANMethod(false).equals(TanMethod.ONESTEP.getId())?"1":"2"; } public boolean needUserKeys() @@ -825,50 +1257,56 @@ return ret.toString(); } + /** + * Liefert "J" oder "N" aus den BPD des Geschaeftsvorfalls, ob fuer diesen eine TAN erforderlich ist. + * @param code der GV-Code. + * @return "J" oder "N". Oder "A", wenn es ein Admin-Segment ist, jedoch keine TAN noetig ist. + */ public String getPinTanInfo(String code) { String ret=""; - Properties bpd=getBPD(); + Properties bpd = getBPD(); - if (bpd!=null) { - boolean isGV=false; - StringBuffer paramCode=new StringBuffer(code).replace(1,2,"I").append("S"); - - for (Enumeration e=bpd.propertyNames();e.hasMoreElements();) { - String key=(String)e.nextElement(); + if (bpd == null) + return ret; + + boolean isGV = false; + final String paramCode = StringUtil.toParameterCode(code); + + for (Enumeration e=bpd.propertyNames();e.hasMoreElements();) { + String key=(String)e.nextElement(); - if (key.startsWith("Params")&& - key.substring(key.indexOf(".")+1).startsWith("PinTanPar") && - key.indexOf(".ParPinTan.PinTanGV")!=-1 && - key.endsWith(".segcode")) - { - String code2=bpd.getProperty(key); - if (code.equals(code2)) { - key=key.substring(0,key.length()-("segcode").length())+"needtan"; - ret=bpd.getProperty(key); - break; - } - } else if (key.startsWith("Params")&& - key.endsWith(".SegHead.code")) { + if (key.startsWith("Params")&& + key.substring(key.indexOf(".")+1).startsWith("PinTanPar") && + key.indexOf(".ParPinTan.PinTanGV")!=-1 && + key.endsWith(".segcode")) + { + String code2=bpd.getProperty(key); + if (code.equals(code2)) { + key=key.substring(0,key.length()-("segcode").length())+"needtan"; + ret=bpd.getProperty(key); + break; + } + } else if (key.startsWith("Params")&& + key.endsWith(".SegHead.code")) { - String code2=bpd.getProperty(key); - if (paramCode.equals(code2)) { - isGV=true; - } + String code2=bpd.getProperty(key); + if (paramCode.equals(code2)) { + isGV=true; } } + } - // wenn das kein GV ist, dann ist es ein Admin-Segment - if (ret.length()==0&&!isGV) { - if (verifyTANMode && code.equals("HKIDN")) { - // im TAN-verify-mode wird bei der dialog-initialisierung - // eine TAN mit versandt; die Dialog-Initialisierung erkennt - // man am HKIDN-segment - ret="J"; - deactivateTANVerifyMode(); - } else { - ret="A"; - } + // wenn das kein GV ist, dann ist es ein Admin-Segment + if (ret.length()==0&&!isGV) { + if (verifyTANMode && code.equals("HKIDN")) { + // im TAN-verify-mode wird bei der dialog-initialisierung + // eine TAN mit versandt; die Dialog-Initialisierung erkennt + // man am HKIDN-segment + ret="J"; + deactivateTANVerifyMode(); + } else { + ret="A"; } } @@ -895,9 +1333,9 @@ return certfile; } - protected void setCheckCert(boolean skip) + protected void setCheckCert(boolean doCheck) { - this.checkCert=skip; + this.checkCert=doCheck; } public boolean getCheckCert() @@ -935,323 +1373,242 @@ this.proxyuser = proxyuser; } + /** + * Liefert den Code fuer den Hash-Modus, mit dem bei der HKTAN-Prozessvariante 1 das Auftragssegment gehasht werden soll. + * @return der Order-Hashmode oder NULL, wenn er nicht ermittelbar ist. + * @throws HBCI_Exception wenn ein ungueltiger Wert fuer den Hash-Mode in den BPD angegeben ist. + */ private String getOrderHashMode() { - String ret=null; + final Properties bpd = this.getBPD(); + if (bpd == null) + return null; + + // Wir muessen auch bei der richtigen Segment-Version schauen + final Properties props = this.getCurrentSecMechInfo(); + final String segVersion = props.getProperty("segversion"); - Properties bpd=getBPD(); - if (bpd!=null) { - for (Enumeration e=bpd.propertyNames();e.hasMoreElements();) { - String key=(String)e.nextElement(); + final String s = ParameterFinder.getValue(bpd,Query.BPD_PINTAN_ORDERHASHMODE.withParameters((segVersion != null ? segVersion : "")),null); + + if ("1".equals(s)) + return DigestUtils.ALG_RIPE_MD160; + if ("2".equals(s)) + return DigestUtils.ALG_SHA1; + + throw new HBCI_Exception("unknown orderhash mode " + s); + } + + /** + * Patcht die TAN-Abfrage bei Bedarf in die Nachricht. + * Hinweis: Wir haben das ganze HKTAN-Handling derzeit leider doppelt. Einmal fuer die Dialog-Initialisierung (checkSCAResponse) und einmal fuer + * die Nachrichten mit den eigentlichen Geschaeftsvorfaellen (in patchMessagesFor2StepMethods). Wenn auch HBCIDialog#doJobs irgendwann + * auf die neuen RawHBCIDialoge umgestellt ist, kann eigentlich patchMessagesFor2StepMethods entfallen. + * @param dialog der Dialog. + * @param ret der aktuelle Dialog-Status. + */ + private void patchMessagesFor2StepMethods(DialogContext ctx) + { + final HBCIDialog dialog = ctx.getDialog(); + if (dialog == null) + return; + + final HBCIMessageQueue queue = dialog.getMessageQueue(); + if (queue == null) + return; + + // Einschritt-Verfahren - kein HKTAN erforderlich + final String tanMethod = this.getCurrentTANMethod(false); + if (tanMethod.equals(TanMethod.ONESTEP.getId())) + return; + + // wenn es sich um das pintan-verfahren im zweischritt-modus handelt, + // müssen evtl. zusätzliche nachrichten bzw. segmente eingeführt werden + HBCIUtils.log("patching message for twostep method",HBCIUtils.LOG_DEBUG); + + final HBCIHandler handler = (HBCIHandler) this.getParentHandlerData(); + final Properties secmechInfo = this.getCurrentSecMechInfo(); + final String segversion = secmechInfo.getProperty("segversion"); + final String process = secmechInfo.getProperty("process"); - Properties props = getCurrentSecMechInfo(); - String segVersion = ""; - try { - int value = Integer.parseInt(props.getProperty("segversion")); - segVersion += value; - } catch (NumberFormatException nfe) { - //Not an integer, hence ignored - } + for (HBCIMessage message:queue.getMessages()) + { + for (HBCIJobImpl task:message.getTasks()) + { + // Damit wir keine doppelten erzeugen + if (task.haveTan()) + continue; - // p.getProperty("Params_x.TAN2StepParY.ParTAN2StepZ.can1step") - if (key.startsWith("Params")) { - String subkey=key.substring(key.indexOf('.')+1); - if (subkey.startsWith("TAN2StepPar" + segVersion) && - subkey.endsWith(".orderhashmode")) - { - ret=bpd.getProperty(key); - break; - } + final String segcode = task.getHBCICode(); + + // Braucht der Job eine TAN? + if (!this.getPinTanInfo(segcode).equals("J")) + { + HBCIUtils.log("found task that does not require HKTAN: " + segcode + " - adding it to current msg",HBCIUtils.LOG_DEBUG); + continue; } - } - } - - return ret; - } - // das wird vor dialog.executeJobs() abstrakt aufgerufen - // (via beforeCustomDialogHook(dialog)) - private void patchMessagesFor2StepMethods(HBCIDialog dialog) - { - if (!getCurrentTANMethod(false).equals(Sig.SECFUNC_SIG_PT_1STEP)) { - // wenn es sich um das pintan-verfahren im zweischritt-modus handelt, - // müssen evtl. zusätzliche nachrichten bzw. segmente eingeführt werden - - HBCIUtils.log("afterCustomDialogInitHook: patching message queues for twostep method",HBCIUtils.LOG_DEBUG); - - HBCIHandler handler = (HBCIHandler)getParentHandlerData(); - Properties secmechInfo = getCurrentSecMechInfo(); - String segversion = secmechInfo.getProperty("segversion"); - String process = secmechInfo.getProperty("process"); - - List> msgs=dialog.getMessages(); - List> new_msgs=new ArrayList>(); - - // durch alle ursprünglichen nachrichten laufen - for (Iterator> i=msgs.iterator();i.hasNext();) { - ArrayList msg_tasks= i.next(); - ArrayList new_msg_tasks=new ArrayList(); + // OK, Task braucht vermutlich eine TAN - es handelt sich um einen tan-pflichtigen task + // Ob letztlich tatsaechlich beim User eine TAN-Abfrage ankommt, haengt davon ab, ob die Bank ggf. eine 3076 SCA-Ausnahme sendet + HBCIUtils.log("found task that probably requires HKTAN: " + segcode + " - have to patch message queue",HBCIUtils.LOG_DEBUG); - ArrayList additional_msg_tasks=null; + final GVTAN2Step hktan = (GVTAN2Step) handler.newJob("TAN2Step"); + hktan.setParam("ordersegcode",task.getHBCICode()); // Seit HKTAN auch bei HKTAN#6 Pflicht + hktan.setExternalId(task.getExternalId()); // externe ID durchreichen + hktan.setSegVersion(segversion); // muessen wir explizit setzen, damit wir das HKTAN in der gleichen Version schicken, in der das HITANS kam. + task.tanApplied(); - // jeden task einer nachricht ansehen - for (Iterator j=msg_tasks.iterator();j.hasNext();) { - HBCIJobImpl task= j.next(); - String segcode=task.getHBCICode(); - - if (getPinTanInfo(segcode).equals("J")) { - // es handelt sich um einen tan-pflichtigen task - HBCIUtils.log("found task that requires a TAN: "+segcode+" - have to patch message queue",HBCIUtils.LOG_DEBUG); - - if (process.equals("1")) { - // prozessvariante 1 - HBCIUtils.log("process #1: adding new message with HKTAN(p=1,hash=...) before current message",HBCIUtils.LOG_DEBUG); - - // neue msg erzeugen - additional_msg_tasks=new ArrayList(); - - GVTAN2Step hktan = (GVTAN2Step) handler.newJob("TAN2Step"); - hktan.setExternalId(task.getExternalId()); // externe ID durchreichen - - // muessen wir explizit setzen, damit wir das HKTAN in der gleichen Version - // schicken, in der das HITANS kam. - hktan.setSegVersion(segversion); - - hktan.setParam("process",process); - hktan.setParam("notlasttan","N"); - - // willuhn 2011-05-16 - // Siehe FinTS_3.0_Security_Sicherheitsverfahren_PINTAN_Rel_20101027_final_version.pdf, Seite 58 - int hktanVersion = Integer.parseInt(hktan.getSegVersion()); - if (hktanVersion >= 5) - { - // Bis HKTAN4/hhd1.3 wurde das noch als Challenge-Parameter uebermittelt. Jetzt hat es einen - // eigenen Platz in den Job-Parametern - hktan.setParam("ordersegcode",task.getHBCICode()); - - // Zitat aus HITANS5: Diese Funktion ermöglicht das Sicherstellen einer gültigen Kontoverbindung - // z. B. für die Abrechnung von SMS-Kosten bereits vor Erzeugen und Versenden einer - // (ggf. kostenpflichtigen!) TAN. - // 0: Auftraggeberkonto darf nicht angegeben werden - // 2: Auftraggeberkonto muss angegeben werden, wenn im Geschäftsvorfall enthalten - String noa = secmechInfo.getProperty("needorderaccount",""); - HBCIUtils.log("needorderaccount=" + noa,HBCIUtils.LOG_INFO); - if (noa.equals("2")) - { - Konto k = task.getOrderAccount(); - if (k != null) - { - HBCIUtils.log("applying orderaccount to HKTAN for " + task.getHBCICode(),HBCIUtils.LOG_INFO); - hktan.setParam("orderaccount",k); - } - else - { - HBCIUtils.log("orderaccount needed, but not found in " + task.getHBCICode(),HBCIUtils.LOG_WARN); - } - } - } - - // TODO: das für mehrfachsignaturen - // hktan.setParam("notlasttan","J"); - - // orderhash ermitteln - try { - // TODO: hier wird jetzt *immer* segnum=3 angenommen, - // kann in Einzelfällen evtl. auch anders sein (?) - SEG seg=task.createJobSegment(3); - seg.validate(); - String segdata=seg.toString(0); - HBCIUtils.log("calculating hash for jobsegment: "+segdata,HBCIUtils.LOG_DEBUG2); - - // zu verwendenden Hash-Algorithmus von dem Wert "orderhashmode" aus den BPD abhängig machen - String orderhashmode=getOrderHashMode(); - String alg=null; - String provider=null; - if (orderhashmode.equals("1")) { - alg="RIPEMD160"; - provider="CryptAlgs4Java"; - } else if (orderhashmode.equals("2")) { - alg="SHA-1"; - } - HBCIUtils.log("using "+alg+"/"+provider+" for generating order hash", HBCIUtils.LOG_DEBUG); - MessageDigest digest=MessageDigest.getInstance(alg,provider); - - digest.update(segdata.getBytes(Comm.ENCODING)); - byte[] hash=digest.digest(); - SEGFactory.getInstance().unuseObject(seg); - hktan.setParam("orderhash",new String(hash,Comm.ENCODING)); - } catch (Exception e) { - throw new HBCI_Exception(e); - } - - // TODO: evtl. listindex ermitteln - // hktan.setParam("listidx",""); - - // wenn needchallengeklass gesetzt ist: - if (secmechInfo.getProperty("needchallengeklass","N").equals("J")) - { - HBCIUtils.log("we are in PV #1, and a challenge klass is required",HBCIUtils.LOG_DEBUG); - ChallengeInfo cinfo = ChallengeInfo.getInstance(); - cinfo.applyParams(task,hktan,secmechInfo); - } - - // willuhn 2011-05-09: Bei Bedarf noch das TAN-Medium erfragen - applyTanMedia(hktan); - - // hktan-job zur neuen msg hinzufügen - additional_msg_tasks.add(hktan); - - // diese neue msg vor der aktuellen in die msg-queue einstellen - new_msgs.add(additional_msg_tasks); - // und gleich wieder auf null setzen, damit diese msg nicht - // später nochmal *nach* der aktuellen msg eingefügt wird - additional_msg_tasks=null; - - // den aktuellen task ganz normal zur aktuellen msg hinzufügen - new_msg_tasks.add(task); - } else { - // prozessvariante 2 - HBCIUtils.log("process #2: adding new task HKTAN(p=4) to current message",HBCIUtils.LOG_DEBUG); - - // den aktuellen task ganz normal zur aktuellen msg hinzufügen - new_msg_tasks.add(task); - - // dazu noch einen hktan-job hinzufügen - GVTAN2Step hktan1 = (GVTAN2Step) handler.newJob("TAN2Step"); - hktan1.setExternalId(task.getExternalId()); // externe ID durchreichen - - // muessen wir explizit setzen, damit wir das HKTAN in der gleichen Version - // schicken, in der das HITANS kam. - hktan1.setSegVersion(segversion); - - hktan1.setParam("process","4"); - // TODO: evtl. listindex ermitteln - // hktan1.setParam("listidx",""); - // TODO: das für mehrfachsignaturen - // hktan1.setParam("notlasttan","N"); - - // willuhn 2011-05-09: Bei Bedarf noch das TAN-Medium erfragen - applyTanMedia(hktan1); - - // den hktan-job zusätzlich zur aktuellen msg hinzufügen - new_msg_tasks.add(hktan1); - - // eine neue msg für das einreichen der tan erzeugen - HBCIUtils.log("creating new msg with HKTAN(p=2,orderref=DELAYED)",HBCIUtils.LOG_DEBUG); - additional_msg_tasks=new ArrayList(); - - // HKTAN-job für das einreichen der TAN erzeugen - GVTAN2Step hktan2 = (GVTAN2Step) handler.newJob("TAN2Step"); - hktan2.setExternalId(task.getExternalId()); // externe ID durchreichen - - // muessen wir explizit setzen, damit wir das HKTAN in der gleichen Version - // schicken, in der das HITANS kam. - hktan1.setSegVersion(segversion); - - hktan2.setParam("process","2"); - hktan2.setParam("notlasttan","N"); - // TODO: evtl. listindex ermitteln - // hktan2.setParam("listidx",""); - // TODO: das für mehrfachsignaturen - // hktan2.setParam("notlasttan","J"); - - // willuhn 2011-05-09 TAN-Media gibts nur bei Prozess 1,3,4 - also nicht in hktan2 - - // hktan-job zur neuen msg hinzufügen - additional_msg_tasks.add(hktan2); - - // in dem ersten HKTAN-job eine referenz auf den zweiten speichern, - // damit der erste die auftragsreferenz später im zweiten speichern kann - HBCIUtils.log("storing reference to this HKTAN in previous HKTAN segment",HBCIUtils.LOG_DEBUG); - hktan1.storeOtherTAN2StepTask(hktan2); - - // in dem zweiten HKTAN-job eine referenz auf den originalen job - // speichern, damit die antwortdaten für den job, die als antwortdaten - // für hktan2 ankommen, dem richtigen job zugeordnet werden können - HBCIUtils.log("storing reference to original job in new HKTAN segment",HBCIUtils.LOG_DEBUG); - hktan2.storeOriginalTask(task); - - // die neue msg wird später (nach der aktuellen) zur msg-queue hinzugefügt + final String tanMedia = this.getTanMedia(Integer.parseInt(hktan.getSegVersion())); + if (tanMedia != null && tanMedia.length() > 0) // tanmedia nur setzen, wenn vorhanden Sonst meckert HBCIJobIml + hktan.setParam("tanmedia",tanMedia); + + + //////////////////////////////////////////////////////////////////////////// + // Prozess-Variante 1: + // 1. Nur HKTAN mit dem Hash des Auftragssegments einreichen, dann per HITAN die TAN generieren + // 2. Auftrag + TAN (HNSHA) einreichen + if (process.equals("1")) + { + HBCIUtils.log("process variant 1: adding new message with HKTAN(p=1,hash=...) before current message",HBCIUtils.LOG_DEBUG); + hktan.setProcess(KnownTANProcess.PROCESS1); + hktan.setParam("notlasttan","N"); + + // willuhn 2011-05-16 + // Siehe FinTS_3.0_Security_Sicherheitsverfahren_PINTAN_Rel_20101027_final_version.pdf, Seite 58 + int hktanVersion = Integer.parseInt(hktan.getSegVersion()); + if (hktanVersion >= 5) + { + // Zitat aus HITANS5: Diese Funktion ermöglicht das Sicherstellen einer gültigen Kontoverbindung + // z. B. für die Abrechnung von SMS-Kosten bereits vor Erzeugen und Versenden einer + // (ggf. kostenpflichtigen!) TAN. + // 0: Auftraggeberkonto darf nicht angegeben werden + // 2: Auftraggeberkonto muss angegeben werden, wenn im Geschäftsvorfall enthalten + String noa = secmechInfo.getProperty("needorderaccount",""); + HBCIUtils.log("needorderaccount=" + noa,HBCIUtils.LOG_DEBUG); + if (noa.equals("2")) + { + Konto k = task.getOrderAccount(); + if (k != null) + { + HBCIUtils.log("applying orderaccount to HKTAN for " + task.getHBCICode(),HBCIUtils.LOG_DEBUG); + hktan.setParam("orderaccount",k); + } + else + { + HBCIUtils.log("orderaccount needed, but not found in " + task.getHBCICode(),HBCIUtils.LOG_WARN); } - } else { - // kein tan-pflichtiger task, also einfach zur gepatchten msg-queue hinzufügen - HBCIUtils.log("found task that does not require a TAN: "+segcode+" - adding it to current msg",HBCIUtils.LOG_DEBUG); - new_msg_tasks.add(task); + } } + + // Challenge-Klasse, wenn erforderlich + if (secmechInfo.getProperty("needchallengeklass","N").equals("J")) + { + ChallengeInfo cinfo = ChallengeInfo.getInstance(); + cinfo.applyParams(task,hktan,secmechInfo); + } + + // orderhash ermitteln + SEG seg = null; + try + { + seg = task.createJobSegment(3); // FIXME: hartcodierte Segment-Nummer. Zu dem Zeitpunkt wissen wir sie noch nicht. + seg.validate(); + final String segdata = seg.toString(0); + HBCIUtils.log("calculating hash for jobsegment: " + segdata,HBCIUtils.LOG_DEBUG2); + hktan.setParam("orderhash",DigestUtils.hash(segdata,this.getOrderHashMode())); + } + finally + { + SEGFactory.getInstance().unuseObject(seg); + } + + // HKTAN in einer neuen Nachricht *vor* dem eigentlichen Auftrag einreihen + HBCIMessage newMsg = queue.insertBefore(message); + newMsg.append(hktan); } + // + //////////////////////////////////////////////////////////////////////////// - msg_tasks.clear(); - msg_tasks.addAll(new_msg_tasks); - - new_msgs.add(msg_tasks); - if (additional_msg_tasks!=null) { - // wenn für prozessvariante 2 eine zusätzliche msg erzeugt - // wurde, diese jetzt mit anhängen - HBCIUtils.log("adding newly created message with HKTAN(p=2) after current one",HBCIUtils.LOG_DEBUG); - new_msgs.add(additional_msg_tasks); - additional_msg_tasks=null; + //////////////////////////////////////////////////////////////////////////// + // Prozess-Variante 2: + // 1. Auftrag + HKTAN einreichen, dann per HITAN die TAN generieren + // 2. HKTAN mit Referenz zum Auftrag und TAN(HNSHA) einreichen + else + { + HBCIUtils.log("process variant 2: adding new task HKTAN(p=4) to current message",HBCIUtils.LOG_DEBUG); + hktan.setProcess(KnownTANProcess.PROCESS2_STEP1); + + // das HKTAN direkt dahinter - in der selben Nachricht + message.append(hktan); + + // Neue Nachricht fuer das zweite HKTAN + HBCIUtils.log("process variant 2: creating new msg with HKTAN(p=2,orderref=DELAYED)",HBCIUtils.LOG_DEBUG); + + // HKTAN-job für das einreichen der TAN erzeugen + final GVTAN2Step hktan2 = (GVTAN2Step) handler.newJob("TAN2Step"); + hktan2.setProcess(KnownTANProcess.PROCESS2_STEP2); + hktan2.setExternalId(task.getExternalId()); // externe ID auch an HKTAN2 durchreichen + hktan2.setSegVersion(segversion); + hktan2.setParam("notlasttan","N"); + + // in dem zweiten HKTAN-Job eine referenz auf den originalen job + // speichern, damit die antwortdaten für den job, die als antwortdaten + // für hktan2 ankommen, dem richtigen job zugeordnet werden können + HBCIUtils.log("storing reference to original job in new HKTAN segment",HBCIUtils.LOG_DEBUG); + hktan2.setTask(task); + + // in dem ersten HKTAN-job eine referenz auf den zweiten speichern, + // damit der erste die auftragsreferenz später im zweiten speichern kann + hktan.setStep2(hktan2); + + // Dahinter eine neue Nachricht mit dem einzelnen HKTAN#2 + HBCIUtils.log("adding new message with HKTAN(p=2) after current one",HBCIUtils.LOG_DEBUG); + HBCIMessage newMsg = queue.insertAfter(message); + newMsg.append(hktan2); } } - - msgs.clear(); - msgs.addAll(new_msgs); } } /** - * Uebernimmt das Rueckfragen und Einsetzen der TAN-Medien-Bezeichung bei Bedarf. - * @param hktan der Job, in den der Parameter eingesetzt werden soll. - * @param secmechInfo + * Uebernimmt das Rueckfragen der TAN-Medien-Bezeichung bei Bedarf. + * @param segVersion die HKTAN-Versionsnummer. + * @return das ausgewaehlte TAN-Medium oder einen Leerstring, wenn keines verfuegbar war oder keines noetig ist (bei HKTAN < 3). */ - private void applyTanMedia(GVTAN2Step hktan) + private String getTanMedia(int segVersion) { - if (hktan == null) - return; - - // Gibts erst ab hhd1.3, siehe - // FinTS_3.0_Security_Sicherheitsverfahren_PINTAN_Rel_20101027_final_version.pdf, Kapitel B.4.3.1.1.1 - // Zitat: Ist in der BPD als Anzahl unterstützter aktiver TAN-Medien ein Wert > 1 - // angegeben und ist der BPD-Wert für Bezeichnung des TAN-Mediums erforderlich = 2, - // so muss der Kunde z. B. im Falle des mobileTAN-Verfahrens - // hier die Bezeichnung seines für diesen Auftrag zu verwendenden TAN- - // Mediums angeben. - // Ausserdem: "Nur bei TAN-Prozess=1, 3, 4". Das muess aber der Aufrufer pruefen. Ist mir - // hier zu kompliziert - - int hktan_version = Integer.parseInt(hktan.getSegVersion()); - HBCIUtils.log("hktan_version: " + hktan_version,HBCIUtils.LOG_DEBUG); - if (hktan_version >= 3) - { - Properties secmechInfo = getCurrentSecMechInfo(); - - // Anzahl aktiver TAN-Medien ermitteln - int num = Integer.parseInt(secmechInfo.getProperty("nofactivetanmedia","0")); - String needed = secmechInfo.getProperty("needtanmedia",""); - HBCIUtils.log("nofactivetanmedia: " + num + ", needtanmedia: " + needed,HBCIUtils.LOG_DEBUG); - - // Ich hab Mails von Usern erhalten, bei denen die Angabe des TAN-Mediums auch - // dann noetig war, wenn nur eine Handy-Nummer hinterlegt war. Daher logen wir - // "num" nur, bringen die Abfrage jedoch schon bei num<2 - insofern needed=2. - if (needed.equals("2")) - { - HBCIUtils.log("we have to add the tan media",HBCIUtils.LOG_DEBUG); - - StringBuffer retData=new StringBuffer(); - retData.append(this.getUPD().getProperty("tanmedia.names","")); - HBCIUtilsInternal.getCallback().callback(this,HBCICallback.NEED_PT_TANMEDIA, - "*** Enter the name of your TAN media", - HBCICallback.TYPE_TEXT, - retData); - - hktan.setParam("tanmedia",retData.toString()); + // Gibts erst ab hhd1.3, siehe + // FinTS_3.0_Security_Sicherheitsverfahren_PINTAN_Rel_20101027_final_version.pdf, Kapitel B.4.3.1.1.1 + HBCIUtils.log("HKTAN version: " + segVersion,HBCIUtils.LOG_DEBUG); + if (segVersion < 3) + return ""; + + final Properties secmechInfo = this.getCurrentSecMechInfo(); + + // Brauchen wir ein TAN-Medium? + final String needed = secmechInfo != null ? secmechInfo.getProperty("needtanmedia","") : ""; + HBCIUtils.log("needtanmedia: " + needed,HBCIUtils.LOG_DEBUG); + + final boolean tn = Objects.equals(needed,"2"); + if (tn) + { + HBCIUtils.log("we have to add the tan media",HBCIUtils.LOG_DEBUG); + + final StringBuffer retData = new StringBuffer(); + + // Namen der TAN-Medien als Auswahl anbieten, falls vorhanden + final Properties upd = this.getUPD(); + if (upd != null) + retData.append(upd.getProperty(HBCIUser.UPD_KEY_TANMEDIA,"")); + + HBCIUtilsInternal.getCallback().callback(this,HBCICallback.NEED_PT_TANMEDIA,"*** Enter the name of your TAN media",HBCICallback.TYPE_TEXT,retData); + final String result = retData.toString(); + if (StringUtil.hasText(result)) + return result; } - } - } - - public void afterCustomDialogInitHook(HBCIDialog dialog) - { - super.afterCustomDialogInitHook(dialog); - patchMessagesFor2StepMethods(dialog); + + // Seit HKTAN 6: Wenn die Angabe eines TAN-Mediennamens laut BPD erforderlich ist, wir aber gar keinen Namen haben, + // dann "noref" eintragen. + return tn ? "noref" : ""; } public void setPIN(String pin) @@ -1271,12 +1628,12 @@ public List getAllowedTwostepMechanisms() { - return this.allowedTwostepMechanisms; + return this.tanMethodsUser; } public void setAllowedTwostepMechanisms(List l) { - this.allowedTwostepMechanisms=l; + this.tanMethodsUser=l; } public int getMaxGVSegsPerMsg() diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/AbstractRDHPassport.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/AbstractRDHPassport.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/AbstractRDHPassport.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/AbstractRDHPassport.java 2019-11-27 09:04:22.000000000 +0000 @@ -32,9 +32,15 @@ import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESedeKeySpec; +import org.kapott.cryptalgs.CryptAlgs4JavaProvider; import org.kapott.cryptalgs.SignatureParamSpec; import org.kapott.hbci.comm.Comm; +import org.kapott.hbci.dialog.DialogContext; +import org.kapott.hbci.dialog.DialogEvent; +import org.kapott.hbci.dialog.KnownDialogTemplate; +import org.kapott.hbci.dialog.RawHBCIDialog; import org.kapott.hbci.exceptions.HBCI_Exception; +import org.kapott.hbci.manager.HBCIKernelImpl; import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.security.Crypt; import org.kapott.hbci.security.Sig; @@ -96,6 +102,10 @@ return "1"; } + /** + * @see org.kapott.hbci.passport.HBCIPassport#needInstKeys() + */ + @Override public boolean needInstKeys() { return true; @@ -267,11 +277,11 @@ switch (profile) { case 1: hashalg="RIPEMD160"; - hashprovider="CryptAlgs4Java"; + hashprovider=CryptAlgs4JavaProvider.NAME; break; case 2: hashalg="RIPEMD160"; - hashprovider="CryptAlgs4Java"; + hashprovider=CryptAlgs4JavaProvider.NAME; break; case 10: hashalg="SHA-256"; @@ -296,15 +306,15 @@ switch (profile) { case 1: sigalg="ISO9796p1"; - sigprovider="CryptAlgs4Java"; + sigprovider=CryptAlgs4JavaProvider.NAME; break; case 2: sigalg="ISO9796p2"; - sigprovider="CryptAlgs4Java"; + sigprovider=CryptAlgs4JavaProvider.NAME; break; case 10: sigalg="PKCS1_PSS"; - sigprovider="CryptAlgs4Java"; + sigprovider=CryptAlgs4JavaProvider.NAME; break; default: // TODO das später vom security profile abhängig machen @@ -366,5 +376,47 @@ return result; } - + + /** + * @see org.kapott.hbci.passport.AbstractHBCIPassport#onDialogEvent(org.kapott.hbci.dialog.DialogEvent, org.kapott.hbci.dialog.DialogContext) + */ + @Override + public void onDialogEvent(DialogEvent event, DialogContext ctx) + { + super.onDialogEvent(event, ctx); + + if (event != DialogEvent.MSG_CREATED) + return; + + // Wenn es eine nicht-anonyme Dialog-Initialisierung ist, rufen wir die Schluessel mit ab + RawHBCIDialog init = ctx.getDialogInit(); + if (init.getTemplate() != KnownDialogTemplate.INIT || ctx.isAnonymous()) + return; + + if (!this.needInstKeys()) + return; + + HBCIKernelImpl k = ctx.getKernel(); + + k.rawSet("KeyReq.SecProfile.method",this.getProfileMethod()); + k.rawSet("KeyReq.SecProfile.version",this.getProfileVersion()); + k.rawSet("KeyReq.KeyName.keytype", "V"); + k.rawSet("KeyReq.KeyName.KIK.country", this.getCountry()); + k.rawSet("KeyReq.KeyName.KIK.blz", this.getBLZ()); + k.rawSet("KeyReq.KeyName.userid", this.getInstEncKeyName()); + k.rawSet("KeyReq.KeyName.keynum", this.getInstEncKeyNum()); + k.rawSet("KeyReq.KeyName.keyversion", this.getInstEncKeyVersion()); + + if (this.hasInstSigKey()) + { + k.rawSet("KeyReq_2.SecProfile.method",this.getProfileMethod()); + k.rawSet("KeyReq_2.SecProfile.version",this.getProfileVersion()); + k.rawSet("KeyReq_2.KeyName.keytype", "S"); + k.rawSet("KeyReq_2.KeyName.KIK.country", this.getCountry()); + k.rawSet("KeyReq_2.KeyName.KIK.blz", this.getBLZ()); + k.rawSet("KeyReq_2.KeyName.userid", this.getInstSigKeyName()); + k.rawSet("KeyReq_2.KeyName.keynum", this.getInstSigKeyNum()); + k.rawSet("KeyReq_2.KeyName.keyversion", this.getInstSigKeyVersion()); + } + } } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/AbstractRDHSWFileBasedPassport.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/AbstractRDHSWFileBasedPassport.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/AbstractRDHSWFileBasedPassport.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/AbstractRDHSWFileBasedPassport.java 2019-11-27 09:04:22.000000000 +0000 @@ -21,42 +21,34 @@ package org.kapott.hbci.passport; -import javax.crypto.SecretKey; - -public abstract class AbstractRDHSWFileBasedPassport - extends AbstractRDHSWPassport +/** + * + */ +public abstract class AbstractRDHSWFileBasedPassport extends AbstractRDHSWPassport { private String filename; - private SecretKey passportKey; - protected final static byte[] CIPHER_SALT={(byte)0x26,(byte)0x19,(byte)0x38,(byte)0xa7, - (byte)0x99,(byte)0xbc,(byte)0xf1,(byte)0x55}; - protected final static int CIPHER_ITERATIONS=987; - protected AbstractRDHSWFileBasedPassport(Object init) { super(init); } + /** + * @return + */ public String getFilename() { return filename; } + /** + * @param filename + */ public void setFilename(String filename) { this.filename = filename; } - public SecretKey getPassportKey() { - return passportKey; - } - - public void setPassportKey(SecretKey passportKey) { - this.passportKey = passportKey; - } - - public void resetPassphrase() { - setPassportKey(null); - } - + /** + * @see org.kapott.hbci.passport.AbstractHBCIPassport#close() + */ public void close() { super.close(); resetPassphrase(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/AbstractRDHSWPassport.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/AbstractRDHSWPassport.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/AbstractRDHSWPassport.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/AbstractRDHSWPassport.java 2019-11-27 09:04:22.000000000 +0000 @@ -441,7 +441,7 @@ HBCIKey[] newSigKey=null; HBCIKey[] newEncKey=null; try { - HBCIUtils.log("generating new user keys",HBCIUtils.LOG_INFO); + HBCIUtils.log("Erzeuge neue Benutzerschlüssel",HBCIUtils.LOG_INFO); String blz=getBLZ(); String country=getCountry(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportAnonymous.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportAnonymous.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportAnonymous.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportAnonymous.java 2019-11-27 09:04:22.000000000 +0000 @@ -22,35 +22,14 @@ package org.kapott.hbci.passport; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.util.Enumeration; -import java.util.Properties; - -import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.CipherOutputStream; -import javax.crypto.SecretKey; -import javax.crypto.spec.PBEParameterSpec; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; import org.kapott.hbci.comm.Comm; import org.kapott.hbci.exceptions.HBCI_Exception; -import org.kapott.hbci.exceptions.InvalidPassphraseException; import org.kapott.hbci.manager.HBCIKey; import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.manager.HBCIUtilsInternal; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; +import org.kapott.hbci.passport.storage.PassportData; +import org.kapott.hbci.passport.storage.PassportStorage; /**

Passport-Implementation für anonyme Zugänge. Bei dieser Passport-Variante handelt es sich nicht um einen "echten" HBCI-Zugang. Statt dessen handelt @@ -74,16 +53,14 @@ HBCI-Dialog verwendet werden, der aus (anonymer) Dialog-Initialisierung und (anonymem) Dialog-Ende besteht. Damit kann zumindest die Verfügbarkeit des HBCI-Servers bzw. von anonymen Zugängen überprüft werden.

*/ -public class HBCIPassportAnonymous - extends AbstractHBCIPassport +public class HBCIPassportAnonymous extends AbstractHBCIPassport { - private String filename; - private SecretKey passportKey; + private String filename; - protected final static byte[] CIPHER_SALT={(byte)0x26,(byte)0x19,(byte)0x38,(byte)0xa7, - (byte)0x99,(byte)0xbc,(byte)0xf1,(byte)0x55}; - protected final static int CIPHER_ITERATIONS=987; - + /** + * ct. + * @param initObject + */ public HBCIPassportAnonymous(Object initObject) { super(initObject); @@ -92,265 +69,318 @@ String filename=HBCIUtils.getParam(header+"filename"); boolean init=HBCIUtils.getParam(header+"init","1").equals("1"); - if (filename==null) { + if (filename==null) throw new NullPointerException("*** client.passport.Anonymous.filename must not be null"); - } HBCIUtils.log("loading passport data from file "+filename,HBCIUtils.LOG_DEBUG); setFileName(filename); setFilterType("None"); - setPort(new Integer(3000)); + setPort(Integer.valueOf(3000)); - if (init) { + if (init) + { HBCIUtils.log("loading data from file "+filename,HBCIUtils.LOG_DEBUG); - if (!new File(filename).canRead()) { + if (!new File(filename).canRead()) + { HBCIUtils.log("have to create new passport file",HBCIUtils.LOG_WARN); askForMissingData(true,true,true,true,false,false,false); saveChanges(); } - try { - DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance(); - dbf.setValidating(false); - DocumentBuilder db=dbf.newDocumentBuilder(); - Element root=null; - - int retries=Integer.parseInt(HBCIUtils.getParam("client.retries.passphrase","3")); - - while (true) { // loop for entering the correct passphrase - if (passportKey==null) - passportKey=calculatePassportKey(FOR_LOAD); - - PBEParameterSpec paramspec=new PBEParameterSpec(CIPHER_SALT,CIPHER_ITERATIONS); - String provider = HBCIUtils.getParam("kernel.security.provider"); - Cipher cipher = provider == null ? Cipher.getInstance("PBEWithMD5AndDES") : Cipher.getInstance("PBEWithMD5AndDES", provider) ; - cipher.init(Cipher.DECRYPT_MODE,passportKey,paramspec); - - root=null; - CipherInputStream ci=null; - - try { - ci=new CipherInputStream(new FileInputStream(getFileName()),cipher); - root=db.parse(ci).getDocumentElement(); - } catch (SAXException e) { - passportKey=null; - - retries--; - if (retries<=0) - throw new InvalidPassphraseException(); - } finally { - if (ci!=null) - ci.close(); - } - - if (root!=null) - break; - } - - setBLZ(getElementValue(root,"blz")); - setCountry(getElementValue(root,"country")); - setHost(getElementValue(root,"host")); - setPort(new Integer(getElementValue(root,"port"))); - setHBCIVersion(getElementValue(root,"hbciversion")); + PassportData data = PassportStorage.load(this,new File(filename)); + this.setBLZ(data.blz); + this.setCountry(data.country); + this.setHost(data.host); + this.setPort(data.port); + this.setHBCIVersion(data.hbciVersion); - setBPD(getElementProps(root,"bpd")); - setUPD(getElementProps(root,"upd")); + this.setBPD(data.bpd); + this.setUPD(data.upd); - if (askForMissingData(true,true,true,true,false,false,false)) - saveChanges(); - } catch (Exception e) { - throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSPORT_READERR"),e); - } + if (askForMissingData(true,true,true,true,false,false,false)) + saveChanges(); } } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getPassportTypeName() + */ + @Override public String getPassportTypeName() { return "Anonymous"; } - private String getElementValue(Element root,String name) - { - String ret=null; - - NodeList list=root.getElementsByTagName(name); - if (list!=null && list.getLength()!=0) { - Node content=list.item(0).getFirstChild(); - if (content!=null) - ret=content.getNodeValue(); - } - - return ret; - } - - private Properties getElementProps(Element root,String name) - { - Properties ret=null; - - Node base=root.getElementsByTagName(name).item(0); - if (base!=null) { - ret=new Properties(); - NodeList entries=base.getChildNodes(); - int len=entries.getLength(); - - for (int i=0;iPassport-Klasse für Sicherheitsverfahren DDV mit Medium Chipkarte. Bei dieser Variante gibt die Bank eine Chipkarte aus, auf der die Zugangsdaten des @@ -71,8 +65,7 @@ Benutzen dieses HBCI-Zuganges diese Daten nicht erneut abfragen zu müssen. Diese zusätzliche Datei wird automatisch angelegt, der Dateiname setzt sich aus einem definierbaren Prefix (Pfad) und der Seriennummer der Chipkarte zusammen.

*/ -public class HBCIPassportDDV - extends AbstractDDVPassport +public class HBCIPassportDDV extends AbstractDDVPassport { private String paramHeader; @@ -85,7 +78,6 @@ private int useBio; private int useSoftPin; private byte[] softPin; - private SecretKey passportKey; private int entryIdx; protected native void initCT(); @@ -99,10 +91,11 @@ protected native byte[] ctDecrypt(byte[] cryptedKey); protected native void closeCT(); - protected final static byte[] CIPHER_SALT={(byte)0x56,(byte)0xbc,(byte)0x1c,(byte)0x88, - (byte)0x1f,(byte)0xe3,(byte)0x73,(byte)0xcc}; - protected final static int CIPHER_ITERATIONS=987; - + /** + * ct. + * @param init + * @param dummy + */ public HBCIPassportDDV(Object init,int dummy) { super(init); @@ -114,138 +107,77 @@ } } + /** + * ct. + * @param init + */ public HBCIPassportDDV(Object init) { this(init,0); - // get ddv-parameters - String path=HBCIUtils.getParam(paramHeader+".path","./"); - - // set parameters for initializing card - int comport=Integer.parseInt(HBCIUtils.getParam(paramHeader+".port","0")); - int ctnumber=Integer.parseInt(HBCIUtils.getParam(paramHeader+".ctnumber","0")); - String ddvLib=HBCIUtils.getParam(paramHeader+".libname.ddv"); + final int comport = Integer.parseInt(HBCIUtils.getParam(paramHeader+".port","0")); + final int ctnumber = Integer.parseInt(HBCIUtils.getParam(paramHeader+".ctnumber","0")); - if (ddvLib==null) { + final String ddvLib = HBCIUtils.getParam(paramHeader+".libname.ddv"); + if (ddvLib == null) throw new NullPointerException(paramHeader+".libname.ddv must not be null"); - } - setComPort(comport); - setCTNumber(ctnumber); - setUseBio(Integer.parseInt(HBCIUtils.getParam(paramHeader+".usebio","-1"))); - setUseSoftPin(Integer.parseInt(HBCIUtils.getParam(paramHeader+".softpin","-1"))); - setSoftPin(new byte[0]); - setPINEntered(false); - setEntryIdx(Integer.parseInt(HBCIUtils.getParam(paramHeader+".entryidx","1"))); + this.setComPort(comport); + this.setCTNumber(ctnumber); + this.setUseBio(Integer.parseInt(HBCIUtils.getParam(paramHeader+".usebio","-1"))); + this.setUseSoftPin(Integer.parseInt(HBCIUtils.getParam(paramHeader+".softpin","-1"))); + this.setSoftPin(new byte[0]); + this.setPINEntered(false); + this.setEntryIdx(Integer.parseInt(HBCIUtils.getParam(paramHeader+".entryidx","1"))); + this.setPort(new Integer(3000)); + this.setFilterType("None"); HBCIUtils.log("trying to load native DDV library "+ddvLib,HBCIUtils.LOG_DEBUG); System.load(ddvLib); - // init card - HBCIUtils.log("using chipcard terminal with port "+comport+" and terminal number "+ctnumber, - HBCIUtils.LOG_DEBUG); - try { - HBCIUtilsInternal.getCallback().callback(this, - HBCICallback.NEED_CHIPCARD, - HBCIUtilsInternal.getLocMsg("CALLB_NEED_CHIPCARD"), - HBCICallback.TYPE_NONE, - null); + HBCIUtils.log("using chipcard terminal with port "+comport+" and terminal number "+ctnumber,HBCIUtils.LOG_DEBUG); + try + { + HBCIUtilsInternal.getCallback().callback(this,HBCICallback.NEED_CHIPCARD,HBCIUtilsInternal.getLocMsg("CALLB_NEED_CHIPCARD"),HBCICallback.TYPE_NONE,null); initCT(); - } catch (Exception e) { - try { - closeCT(); - } catch (Exception e2) { - HBCIUtils.log(e2); - } - - throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_CTERR"),e); - } finally { - HBCIUtilsInternal.getCallback().callback(this, - HBCICallback.HAVE_CHIPCARD, - "", - HBCICallback.TYPE_NONE, - null); - } - - // init basic bank data - try { - setPort(new Integer(3000)); - setFilterType("None"); - ctReadBankData(); + HBCIUtilsInternal.getCallback().callback(this,HBCICallback.HAVE_CHIPCARD,"",HBCICallback.TYPE_NONE,null); + this.ctReadBankData(); + + // Wenn wir neue Daten haben, speichern wir die gleich auf der Karte if (askForMissingData(true,true,true,false,false,true,false)) - saveBankData(); - + this.saveBankData(); + + // Schluesseldaten von der Karte laden. ctReadKeyData(); - } catch (Exception e) { - try { - closeCT(); - } catch (Exception e2) { - HBCIUtils.log(e2); + + // Lokale Passport-Datei laden, wenn sie existiert + this.setFileName(HBCIUtilsInternal.withCounter(HBCIUtils.getParam(paramHeader+".path","./") + this.getCardId(),getEntryIdx()-1)); + File file = new File(this.getFileName()); + if (file.exists() && file.isFile() && file.canRead()) + { + PassportData data = PassportStorage.load(this,new File(this.getFileName())); + this.setBPD(data.bpd); + this.setUPD(data.upd); + this.setHBCIVersion(data.hbciVersion); } - - throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSPORT_INSTDATAERR"),e); } - - setFileName(HBCIUtilsInternal.withCounter(path+getCardId(),getEntryIdx()-1)); - HBCIUtils.log("loading passport data from file "+getFileName(),HBCIUtils.LOG_DEBUG); - - FileInputStream f=null; - try { - f=new FileInputStream(getFileName()); - } catch (Exception e) { - HBCIUtils.log("error while reading passport file",HBCIUtils.LOG_WARN); - } - - if (f!=null) { - ObjectInputStream o=null; - try { - f.close(); - int retries=Integer.parseInt(HBCIUtils.getParam("client.retries.passphrase","3")); - - while (true) { // loop for entering the correct passphrase - if (passportKey==null) - passportKey=calculatePassportKey(FOR_LOAD); - - PBEParameterSpec paramspec=new PBEParameterSpec(CIPHER_SALT,CIPHER_ITERATIONS); - String provider = HBCIUtils.getParam("kernel.security.provider"); - Cipher cipher = provider == null ? Cipher.getInstance("PBEWithMD5AndDES") : Cipher.getInstance("PBEWithMD5AndDES", provider); - cipher.init(Cipher.DECRYPT_MODE,passportKey,paramspec); - - o=null; - try { - o=new ObjectInputStream(new CipherInputStream(new FileInputStream(getFileName()),cipher)); - } catch (StreamCorruptedException e) { - passportKey=null; - - retries--; - if (retries<=0) - throw new InvalidPassphraseException(); - } - - if (o!=null) - break; - } - - setBPD((Properties)(o.readObject())); - setUPD((Properties)(o.readObject())); - setHBCIVersion((String)o.readObject()); - } catch (Exception e) { - try { - closeCT(); - } catch (Exception e2) { - HBCIUtils.log(e2); - } - - throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSPORT_READERR"),e); - } finally { - try { - if (o!=null) - o.close(); - } catch (Exception e) { - HBCIUtils.log(e); - } + catch (Exception e) + { + // Verbindung zum Kartenleser nur im Fehlerfall schliessen + try + { + closeCT(); } + catch (Exception e2) + { + HBCIUtils.log(e2); + } + + if (e instanceof HBCI_Exception) + throw (HBCI_Exception) e; + + throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_CTERR"),e); } } @@ -264,31 +196,7 @@ @Override public void setFileName(String filename) { - // Checken, ob der Dateiname ungueltige Zeichen enthaelt. Wenn das der Fall ist, schneiden wir die raus - // Siehe https://homebanking-hilfe.de/forum/topic.php?p=138325#real138325 - if (filename != null && filename.length() > 0) - { - File f = new File(filename); - String name = f.getName(); - - // Zeichen, die nicht enthalten sein sollten, entfernen wir - // Konkret sind nur Buchstaben, Zahlen und Unterstrich erlaubt - name = name.replaceAll("[^a-zA-Z0-9_-]",""); - - // Wenn die Datei laenger als 25 Zeichen ist, schneiden wir die ueberzaehligen ab - if (name.length() > 25) - name = name.substring(0,25); - - f = new File(f.getParentFile(),name); - String newName = f.getAbsolutePath(); - if (!newName.equals(filename)) - { - HBCIUtils.log("auto-fixed passport filename from " + filename + " to " + newName,HBCIUtils.LOG_INFO); - filename = newName; - } - } - - this.filename = filename; + this.filename = IOUtils.safeFilename(filename); } public void setComPort(int comport) @@ -633,7 +541,6 @@ */ public void resetPassphrase() { - passportKey=null; } /** @@ -642,42 +549,36 @@ public void saveChanges() { try { - checkPIN(); - ctSaveSigId(); + this.checkPIN(); + this.ctSaveSigId(); - if (passportKey==null) - passportKey=calculatePassportKey(FOR_SAVE); + final PassportData data = new PassportData(); + data.bpd = this.getBPD(); + data.upd = this.getUPD(); + data.hbciVersion = this.getHBCIVersion(); - File passportfile=new File(getFileName()); - File directory=passportfile.getAbsoluteFile().getParentFile(); - String prefix=passportfile.getName()+"_"; - File tempfile=File.createTempFile(prefix,"",directory); - - PBEParameterSpec paramspec=new PBEParameterSpec(CIPHER_SALT,CIPHER_ITERATIONS); - String provider = HBCIUtils.getParam("kernel.security.provider"); - Cipher cipher = provider == null ? Cipher.getInstance("PBEWithMD5AndDES") : Cipher.getInstance("PBEWithMD5AndDES", provider); - cipher.init(Cipher.ENCRYPT_MODE,passportKey,paramspec); - ObjectOutputStream o=new ObjectOutputStream(new CipherOutputStream(new FileOutputStream(tempfile),cipher)); - - o.writeObject(getBPD()); - o.writeObject(getUPD()); - o.writeObject(getHBCIVersion()); - - o.close(); - this.safeReplace(passportfile,tempfile); - - } catch (Exception e) { + PassportStorage.save(this,data,new File(this.getFileName())); + } + catch (HBCI_Exception he) + { + throw he; + } + catch (Exception e) + { throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSPORT_WRITEERR"),e); } } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#hash(byte[]) + */ public byte[] hash(byte[] data) { /* hash-before-sign creates a RMD-160 hash, which will encrypted by * the sign() method later */ MessageDigest dig; try { - dig = MessageDigest.getInstance("RIPEMD160","CryptAlgs4Java"); + dig = MessageDigest.getInstance("RIPEMD160",CryptAlgs4JavaProvider.NAME); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } catch (NoSuchProviderException e) { @@ -686,6 +587,9 @@ return dig.digest(data); } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#sign(byte[]) + */ public byte[] sign(byte[] data) { /* data is hash value calculated by the hash() method */ @@ -693,6 +597,9 @@ return ctSign(data); } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#verify(byte[], byte[]) + */ public boolean verify(byte[] data,byte[] sig) { /* data is the hash value calculated by the hash() method */ @@ -701,6 +608,9 @@ return Arrays.equals(sig,correctSig); } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#encrypt(byte[]) + */ public byte[][] encrypt(byte[] plainMsg) { try { @@ -735,6 +645,9 @@ } } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#decrypt(byte[], byte[]) + */ public byte[] decrypt(byte[] cryptedKey,byte[] cryptedMsg) { try { @@ -764,16 +677,21 @@ } } + /** + * @param pinEntered + */ public void setPINEntered(boolean pinEntered) { this.pinEntered=pinEntered; } + /** + * @see org.kapott.hbci.passport.AbstractHBCIPassport#close() + */ public void close() { super.close(); - passportKey=null; setPINEntered(false); closeCT(); } @@ -785,23 +703,19 @@ return HBCIUtils.getParam(paramHeader+".libname.ctapi"); } + /** + * @see org.kapott.hbci.passport.AbstractHBCIPassport#setParamHeader(java.lang.String) + */ protected void setParamHeader(String p) { this.paramHeader=p; } + /** + * @see org.kapott.hbci.passport.AbstractHBCIPassport#getParamHeader() + */ protected String getParamHeader() { return this.paramHeader; } - - protected void setPassportKey(SecretKey key) - { - this.passportKey=key; - } - - protected SecretKey getPassportKey() - { - return this.passportKey; - } } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportDDVPCSC.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportDDVPCSC.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportDDVPCSC.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportDDVPCSC.java 2019-11-27 09:04:22.000000000 +0000 @@ -22,22 +22,15 @@ package org.kapott.hbci.passport; import java.io.File; -import java.io.FileInputStream; -import java.io.ObjectInputStream; -import java.io.StreamCorruptedException; -import java.util.Properties; - -import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.spec.PBEParameterSpec; import org.kapott.hbci.callback.HBCICallback; import org.kapott.hbci.datatypes.SyntaxCtr; import org.kapott.hbci.exceptions.HBCI_Exception; -import org.kapott.hbci.exceptions.InvalidPassphraseException; import org.kapott.hbci.manager.HBCIKey; import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.manager.HBCIUtilsInternal; +import org.kapott.hbci.passport.storage.PassportData; +import org.kapott.hbci.passport.storage.PassportStorage; import org.kapott.hbci.smartcardio.DDVBankData; import org.kapott.hbci.smartcardio.DDVCardService; import org.kapott.hbci.smartcardio.DDVKeyData; @@ -68,132 +61,61 @@ */ public HBCIPassportDDVPCSC(Object init) { - this(init,0); + this(init,0); - ObjectInputStream is = null; - - try - { - //////////////////////////////////////////////////////////////////////// - // set parameters for initializing card this.setUseBio(Integer.parseInt(HBCIUtils.getParam(getParamHeader()+".usebio","-1"))); this.setUseSoftPin(Integer.parseInt(HBCIUtils.getParam(getParamHeader()+".softpin","-1"))); this.setSoftPin(new byte[0]); this.setPINEntered(false); this.setEntryIdx(Integer.parseInt(HBCIUtils.getParam(getParamHeader()+".entryidx","1"))); - // - //////////////////////////////////////////////////////////////////////// + this.setPort(new Integer(3000)); + this.setFilterType("None"); - //////////////////////////////////////////////////////////////////////// - // init card - HBCIUtils.log("initializing javax.smartcardio",HBCIUtils.LOG_DEBUG); - HBCIUtilsInternal.getCallback().callback(this,HBCICallback.NEED_CHIPCARD,HBCIUtilsInternal.getLocMsg("CALLB_NEED_CHIPCARD"),HBCICallback.TYPE_NONE,null); - - this.initCT(); - HBCIUtilsInternal.getCallback().callback(this,HBCICallback.HAVE_CHIPCARD,"",HBCICallback.TYPE_NONE,null); - // - //////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////// - // init basic bank data - try { - this.setPort(new Integer(3000)); - this.setFilterType("None"); + try + { + HBCIUtilsInternal.getCallback().callback(this,HBCICallback.NEED_CHIPCARD,HBCIUtilsInternal.getLocMsg("CALLB_NEED_CHIPCARD"),HBCICallback.TYPE_NONE,null); + HBCIUtils.log("initializing javax.smartcardio",HBCIUtils.LOG_DEBUG); + this.initCT(); + HBCIUtilsInternal.getCallback().callback(this,HBCICallback.HAVE_CHIPCARD,"",HBCICallback.TYPE_NONE,null); + this.ctReadBankData(); - + + // Wenn wir neue Daten haben, speichern wir die gleich auf der Karte if (this.askForMissingData(true,true,true,false,false,true,false)) this.saveBankData(); - + + // Schluesseldaten von der Karte laden. this.ctReadKeyData(); - } - catch (HBCI_Exception e1) - { - throw e1; + + // Lokale Passport-Datei laden, wenn sie existiert + final String path = HBCIUtils.getParam(getParamHeader()+".path","./"); + this.setFileName(HBCIUtilsInternal.withCounter(path + "pcsc" + getCardId(),getEntryIdx()-1)); + File file = new File(this.getFileName()); + if (file.exists() && file.isFile() && file.canRead()) + { + PassportData data = PassportStorage.load(this,new File(this.getFileName())); + this.setBPD(data.bpd); + this.setUPD(data.upd); + this.setHBCIVersion(data.hbciVersion); + } } catch (Exception e) { - throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSPORT_INSTDATAERR"),e); - } - // - //////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////// - // read passport file - String path = HBCIUtils.getParam(getParamHeader()+".path","./"); - this.setFileName(HBCIUtilsInternal.withCounter(path+"pcsc"+getCardId(),getEntryIdx()-1)); - HBCIUtils.log("loading passport data from file "+getFileName(),HBCIUtils.LOG_DEBUG); - - File file = new File(this.getFileName()); - if (file.exists() && file.isFile() && file.canRead()) - { - int retries = Integer.parseInt(HBCIUtils.getParam("client.retries.passphrase","3")); - - while (true) // loop for entering the correct passphrase - { - if (this.getPassportKey() == null) - this.setPassportKey(calculatePassportKey(FOR_LOAD)); - - PBEParameterSpec paramspec = new PBEParameterSpec(CIPHER_SALT,CIPHER_ITERATIONS); - String provider = HBCIUtils.getParam("kernel.security.provider"); - Cipher cipher = provider == null ? Cipher.getInstance("PBEWithMD5AndDES") : Cipher.getInstance("PBEWithMD5AndDES", provider); - cipher.init(Cipher.DECRYPT_MODE,getPassportKey(),paramspec); - + // Verbindung zum Kartenleser nur im Fehlerfall schliessen try { - is = new ObjectInputStream(new CipherInputStream(new FileInputStream(file),cipher)); - } - catch (StreamCorruptedException e1) - { - setPassportKey(null); // Passwort resetten - retries--; - if (retries<=0) - throw new InvalidPassphraseException(); + closeCT(); } catch (Exception e2) { - throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSPORT_READERR"),e2); + HBCIUtils.log(e2); } - // wir habens - if (is != null) - { - setBPD((Properties)(is.readObject())); - setUPD((Properties)(is.readObject())); - setHBCIVersion((String)is.readObject()); - break; - } - } - } - // - //////////////////////////////////////////////////////////////////////// - } - catch (Exception e) - { - // Im Fehlerfall wieder schliessen - try { - closeCT(); - } - catch (Exception ex) { - HBCIUtils.log(ex); - } - - if (e instanceof HBCI_Exception) - throw (HBCI_Exception) e; - - throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_CTERR"),e); - } - finally - { - // Close Passport-File - if (is != null) { - try { - is.close(); - } - catch (Exception e) { - HBCIUtils.log(e); - } + if (e instanceof HBCI_Exception) + throw (HBCI_Exception) e; + + throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_CTERR"),e); } - } } /** diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportInternal.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportInternal.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportInternal.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportInternal.java 2019-11-27 09:04:22.000000000 +0000 @@ -25,10 +25,10 @@ import org.kapott.hbci.comm.Comm; import org.kapott.hbci.comm.Filter; -import org.kapott.hbci.manager.HBCIDialog; +import org.kapott.hbci.dialog.DialogContext; +import org.kapott.hbci.dialog.DialogEvent; import org.kapott.hbci.manager.HBCIKey; import org.kapott.hbci.manager.IHandlerData; -import org.kapott.hbci.status.HBCIMsgStatus; /** Interface, welches alle Passport-Varianten implementieren müssen. Diese Schnittstelle wird nur intern verwendet. Sie beschreibt alle @@ -117,22 +117,12 @@ public void setParentHandlerData(IHandlerData handler); public IHandlerData getParentHandlerData(); - /* Diese Methode wird nach jeder Dialog-Initialisierung aufgerufen. Ein - * Passport-Objekt kann den Status der Response mit Hilfe von msgStatus - * auswerten. Durch Zurückgeben von "true" wird angezeigt, dass eine - * erneute Dialog-Initialisierung stattfinden sollte (z.B. weil sich grund- - * legende Zugangsdaten geändert haben, secMechs neu festgelegt wurden o.ä.) */ - public boolean postInitResponseHook(HBCIMsgStatus msgStatus, boolean anonDialog); - - /* Diese Methode wird aufgerufen, bevor ein "normaler" Dialog (also mit GVs) - * geführt wird. */ - public void beforeCustomDialogHook(HBCIDialog dialog); - - /* Diese Methode wird aufgerufen, nachdem bei einem normalen Dialog die - * Dialog-Initialisierung abgeschlossen ist. - * Wird im Moment nur von PinTan-Passports benutzt, um bei - * Verwendung des Zweischritt-Verfahrens die Message-Liste zu patchen */ - public void afterCustomDialogInitHook(HBCIDialog dialog); + /** + * Wird bei einem Dialog-Event ausgefuehrt. + * @param event das Event. + * @param ctx der Dialog-Kontext. + */ + public void onDialogEvent(DialogEvent event, DialogContext ctx); /* Gibt zurück, wieviele GV-Segmente in einer Nachricht enthalten sein dürfen. * Normalerweise wird das schon durch die BPD bzw. die Job-Params festgelegt, @@ -147,4 +137,5 @@ * oder nicht. */ public int getMaxGVSegsPerMsg(); + } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportPinTan.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportPinTan.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportPinTan.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportPinTan.java 2019-11-27 09:04:22.000000000 +0000 @@ -22,31 +22,23 @@ package org.kapott.hbci.passport; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.StreamCorruptedException; import java.util.List; import java.util.Properties; import java.util.StringTokenizer; -import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.CipherOutputStream; -import javax.crypto.SecretKey; -import javax.crypto.spec.PBEParameterSpec; - import org.kapott.hbci.callback.HBCICallback; import org.kapott.hbci.exceptions.HBCI_Exception; -import org.kapott.hbci.exceptions.InvalidPassphraseException; import org.kapott.hbci.manager.FlickerCode; import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.manager.HBCIUtilsInternal; import org.kapott.hbci.manager.HHDVersion; import org.kapott.hbci.manager.HHDVersion.Type; import org.kapott.hbci.manager.LogFilter; -import org.kapott.hbci.security.Sig; +import org.kapott.hbci.manager.MatrixCode; +import org.kapott.hbci.manager.QRCode; +import org.kapott.hbci.manager.TanMethod; +import org.kapott.hbci.passport.storage.PassportData; +import org.kapott.hbci.passport.storage.PassportStorage; /**

Passport-Klasse für HBCI mit PIN/TAN. Dieses Sicherheitsverfahren wird erst in FinTS 3.0 spezifiziert, von einigen Banken aber schon mit früheren HBCI-Versionen @@ -62,37 +54,42 @@ Transportschicht. Die Nachrichten werden nämlich nicht direkt via TCP/IP übertragen, sondern in das HTTP-Protokoll eingebettet. Die Verschlüsselung der übertragenen Daten erfolgt dabei auf HTTP-Ebene (via SSL = HTTPS).

- Wie auch bei {@link org.kapott.hbci.passport.HBCIPassportRDH} wird eine "Schlüsseldatei" + Wie auch bei {@link org.kapott.hbci.passport.HBCIPassportRDHNew} wird eine "Schlüsseldatei" verwendet. In dieser werden allerdings keine kryptografischen Schlüssel abgelegt, sondern lediglich die Zugangsdaten für den HBCI-Server (Hostadresse, Nutzerkennung, usw.) sowie einige zusätzliche Daten (BPD, UPD, zuletzt benutzte HBCI-Version). Diese Datei wird vor dem Abspeichern verschlüsselt. Vor dem Erzeugen bzw. erstmaligen Einlesen wird via Callback nach einem Passwort gefragt, aus welchem der Schlüssel für die Verschlüsselung der Datei berechnet wird

*/ -public class HBCIPassportPinTan - extends AbstractPinTanPassport +public class HBCIPassportPinTan extends AbstractPinTanPassport { - private String filename; - private SecretKey passportKey; - - private final static byte[] CIPHER_SALT={(byte)0x26,(byte)0x19,(byte)0x38,(byte)0xa7, - (byte)0x99,(byte)0xbc,(byte)0xf1,(byte)0x55}; - private final static int CIPHER_ITERATIONS=987; + private String filename; + /** + * ct. + * @param init + * @param dummy + */ public HBCIPassportPinTan(Object init,int dummy) { super(init); } + /** + * ct. + * @param initObject + */ public HBCIPassportPinTan(Object initObject) { this(initObject,0); String header="client.passport.PinTan."; - String fname=HBCIUtils.getParam(header+"filename"); - boolean init=HBCIUtils.getParam(header+"init","1").equals("1"); - setFileName(fname); + String filename = HBCIUtils.getParam(header+"filename"); + if (initObject instanceof File) + filename = ((File) initObject).getAbsolutePath(); + + this.setFileName(filename); setCertFile(HBCIUtils.getParam(header+"certfile")); setCheckCert(HBCIUtils.getParam(header+"checkcert","1").equals("1")); @@ -100,7 +97,9 @@ setProxyUser(HBCIUtils.getParam(header+"proxyuser","")); setProxyPass(HBCIUtils.getParam(header+"proxypass","")); - if (init) { + boolean init=HBCIUtils.getParam(header+"init","1").equals("1"); + if (init) + { this.read(); @@ -132,7 +131,6 @@ */ public void resetPassphrase() { - passportKey=null; } /** @@ -165,86 +163,23 @@ create(); String fname = this.getFileName(); - if (fname==null) { + if (fname == null) throw new NullPointerException("client.passport.PinTan.filename must not be null"); - } - - HBCIUtils.log("loading data from file " + fname,HBCIUtils.LOG_DEBUG); - ObjectInputStream o = null; - try - { - int retries = Integer.parseInt(HBCIUtils.getParam("client.retries.passphrase","3")); - - while (true) { - if (passportKey == null) - passportKey = calculatePassportKey(FOR_LOAD); - - PBEParameterSpec paramspec=new PBEParameterSpec(CIPHER_SALT,CIPHER_ITERATIONS); - String provider = HBCIUtils.getParam("kernel.security.provider"); - Cipher cipher = provider == null ? Cipher.getInstance("PBEWithMD5AndDES") : Cipher.getInstance("PBEWithMD5AndDES", provider); - cipher.init(Cipher.DECRYPT_MODE,passportKey,paramspec); - - o = null; - try - { - o=new ObjectInputStream(new CipherInputStream(new FileInputStream(fname),cipher)); - } - catch (StreamCorruptedException e) - { - passportKey=null; - - retries--; - if (retries<=0) - throw new InvalidPassphraseException(); - } - - if (o!=null) - break; - } - - setCountry((String)(o.readObject())); - setBLZ((String)(o.readObject())); - setHost((String)(o.readObject())); - setPort((Integer)(o.readObject())); - setUserId((String)(o.readObject())); - setSysId((String)(o.readObject())); - setBPD((Properties)(o.readObject())); - setUPD((Properties)(o.readObject())); - - setHBCIVersion((String)o.readObject()); - setCustomerId((String)o.readObject()); - setFilterType((String)o.readObject()); - - try { - setAllowedTwostepMechanisms((List)o.readObject()); - try - { - setCurrentTANMethod((String)o.readObject()); - } - catch (Exception e) - { - HBCIUtils.log("no current secmech found in passport file - automatically upgrading to new file format", HBCIUtils.LOG_WARN); - } - } - catch (Exception e) - { - HBCIUtils.log("no list of allowed secmechs found in passport file - automatically upgrading to new file format", HBCIUtils.LOG_WARN); - } - } - catch (Exception e) - { - throw new HBCI_Exception("*** loading of passport file failed",e); - } - - try - { - o.close(); - } - catch (Exception e) - { - HBCIUtils.log(e); - } + PassportData data = PassportStorage.load(this,new File(fname)); + this.setCountry(data.country); + this.setBLZ(data.blz); + this.setHost(data.host); + this.setPort(data.port); + this.setUserId(data.userId); + this.setSysId(data.sysId); + this.setBPD(data.bpd); + this.setUPD(data.upd); + this.setHBCIVersion(data.hbciVersion); + this.setCustomerId(data.customerId); + this.setFilterType(data.filter); + this.setAllowedTwostepMechanisms(data.twostepMechs); + this.setCurrentTANMethod(data.tanMethod); } /** @@ -253,52 +188,44 @@ @Override public void saveChanges() { - File passportfile = new File(getFileName()); - File tempfile = null; - - try - { - if (passportKey==null) - passportKey=calculatePassportKey(FOR_SAVE); + try { + + final PassportData data = new PassportData(); - PBEParameterSpec paramspec=new PBEParameterSpec(CIPHER_SALT,CIPHER_ITERATIONS); - String provider = HBCIUtils.getParam("kernel.security.provider"); - Cipher cipher = provider == null ? Cipher.getInstance("PBEWithMD5AndDES") : Cipher.getInstance("PBEWithMD5AndDES", provider); - cipher.init(Cipher.ENCRYPT_MODE,passportKey,paramspec); - - File directory = passportfile.getAbsoluteFile().getParentFile(); - String prefix = passportfile.getName()+"_"; - tempfile = File.createTempFile(prefix,"",directory); - - HBCIUtils.log("writing to passport file " + tempfile, HBCIUtils.LOG_DEBUG); - ObjectOutputStream o = new ObjectOutputStream(new CipherOutputStream(new FileOutputStream(tempfile),cipher)); - - o.writeObject(getCountry()); - o.writeObject(getBLZ()); - o.writeObject(getHost()); - o.writeObject(getPort()); - o.writeObject(getUserId()); - o.writeObject(getSysId()); - o.writeObject(getBPD()); - o.writeObject(getUPD()); - - o.writeObject(getHBCIVersion()); - o.writeObject(getCustomerId()); - o.writeObject(getFilterType()); + data.country = this.getCountry(); + data.blz = this.getBLZ(); + data.host = this.getHost(); + data.port = this.getPort(); + data.userId = this.getUserId(); + data.sysId = this.getSysId(); + data.bpd = this.getBPD(); + data.upd = this.getUPD(); + + data.hbciVersion = this.getHBCIVersion(); + data.customerId = this.getCustomerId(); + data.filter = this.getFilterType(); - // hier auch gewähltes zweischritt-verfahren abspeichern - List l = getAllowedTwostepMechanisms(); + final List l = getAllowedTwostepMechanisms(); HBCIUtils.log("saving two step mechs: " + l, HBCIUtils.LOG_DEBUG); - o.writeObject(l); + data.twostepMechs = l; - String s=getCurrentTANMethod(false); - HBCIUtils.log("saving current tan method: "+s, HBCIUtils.LOG_DEBUG); - o.writeObject(s); + try + { + final String s = this.getCurrentTANMethod(false); + HBCIUtils.log("saving current tan method: "+s, HBCIUtils.LOG_DEBUG); + data.tanMethod = s; + } + catch (Exception e) + { + // Nur zur Sicherheit. In der obigen Funktion werden u.U. eine Menge Sachen losgetreten. + // Wenn da irgendwas schief laeuft, soll deswegen nicht gleich das Speichern der Config + // scheitern. Im Zweifel speichern wir dann halt das ausgewaehlte Verfahren erstmal nicht + // und der User muss es beim naechsten Mal neu waehlen + HBCIUtils.log("could not determine current tan methode, skipping: " + e.getMessage(),HBCIUtils.LOG_DEBUG); + HBCIUtils.log(e,HBCIUtils.LOG_DEBUG2); + } - HBCIUtils.log("closing output stream", HBCIUtils.LOG_DEBUG); - o.close(); - - this.safeReplace(passportfile,tempfile); + PassportStorage.save(this,data,new File(this.getFileName())); } catch (HBCI_Exception he) { @@ -306,7 +233,7 @@ } catch (Exception e) { - throw new HBCI_Exception("*** saving of passport file failed",e); + throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSPORT_WRITEERR"),e); } } @@ -354,7 +281,7 @@ String pintanMethod=getCurrentTANMethod(false); - if (pintanMethod.equals(Sig.SECFUNC_SIG_PT_1STEP)) { + if (pintanMethod.equals(TanMethod.ONESTEP.getId())) { // nur beim normalen einschritt-verfahren muss anhand der segment- // codes ermittelt werden, ob eine tan benötigt wird HBCIUtils.log("onestep method - checking GVs to decide whether or not we need a TAN",HBCIUtils.LOG_DEBUG); @@ -409,14 +336,22 @@ } } } else { + HBCIUtils.log("twostep method - checking passport(challenge) to decide whether or not we need a TAN",HBCIUtils.LOG_DEBUG); Properties secmechInfo=getCurrentSecMechInfo(); - + + String haveSCA = (String) getPersistentData(KEY_PD_SCA); + setPersistentData(KEY_PD_SCA,null); + // gespeicherte challenge aus passport holen - String challenge=(String)getPersistentData("pintan_challenge"); - setPersistentData("pintan_challenge",null); + String challenge=(String)getPersistentData(KEY_PD_CHALLENGE); + setPersistentData(KEY_PD_CHALLENGE,null); - if (challenge==null) + if (haveSCA != null) + { + HBCIUtils.log("will not sign with a TAN, found status code 3076, no SCA required",HBCIUtils.LOG_DEBUG); + } + else if (challenge==null) // manche Banken senden auch "nochallenge" *facepalm* { // es gibt noch keine challenge HBCIUtils.log("will not sign with a TAN, because there is no challenge",HBCIUtils.LOG_DEBUG); @@ -426,39 +361,47 @@ HBCIUtils.log("found challenge in passport, so we ask for a TAN",HBCIUtils.LOG_DEBUG); // willuhn 2011-05-27 Wir versuchen, den Flickercode zu ermitteln und zu parsen - String hhduc = (String) getPersistentData("pintan_challenge_hhd_uc"); - setPersistentData("pintan_challenge_hhd_uc",null); // gleich wieder aus dem Passport loeschen + String hhduc = (String) getPersistentData(KEY_PD_HHDUC); + setPersistentData(KEY_PD_HHDUC,null); // gleich wieder aus dem Passport loeschen HHDVersion hhd = HHDVersion.find(secmechInfo); HBCIUtils.log("detected HHD version: " + hhd,HBCIUtils.LOG_DEBUG); - + final StringBuffer payload = new StringBuffer(); final String msg = secmechInfo.getProperty("name")+"\n"+secmechInfo.getProperty("inputinfo")+"\n\n"+challenge; - - if (hhd.getType() == Type.PHOTOTAN) + + int callback = HBCICallback.NEED_PT_TAN; + + // Um sicherzustellen, dass wird keinen falschen Callback ausloesen, weil wir die HHD-Version + // eventuell falsch erkannt haben, versuchen wir bei PhotoTAN und QR-Code zusaetzlich, die Daten + // zu parsen. Nur wenn sie korrekt geparst werden koennen, verwenden wir auch den spezifischen Callback + if (hhd.getType() == Type.PHOTOTAN && (MatrixCode.tryParse(hhduc) != null)) { // Bei PhotoTAN haengen wir ungeparst das HHDuc an. Das kann dann auf // Anwendungsseite per MatrixCode geparst werden payload.append(hhduc); - HBCIUtilsInternal.getCallback().callback(this,HBCICallback.NEED_PT_PHOTOTAN,msg,HBCICallback.TYPE_TEXT,payload); + callback = HBCICallback.NEED_PT_PHOTOTAN; } - else if (hhd.getType() == Type.QRCODE) + else if (hhd.getType() == Type.QRCODE && (QRCode.tryParse(hhduc,msg) != null)) { // Bei QR-Code haengen wir ungeparst das HHDuc an. Das kann dann auf // Anwendungsseite per QRCode geparst werden payload.append(hhduc); - HBCIUtilsInternal.getCallback().callback(this,HBCICallback.NEED_PT_QRTAN,msg,HBCICallback.TYPE_TEXT,payload); + callback = HBCICallback.NEED_PT_QRTAN; } else { - // willuhn 2011-05-27: Flicker-Code anhaengen, falls vorhanden - String flicker = parseFlickercode(challenge,hhduc); + FlickerCode flicker = FlickerCode.tryParse(hhd,challenge,hhduc); if (flicker != null) - payload.append(flicker); - - HBCIUtilsInternal.getCallback().callback(this,HBCICallback.NEED_PT_TAN,msg,HBCICallback.TYPE_TEXT,payload); + { + // Bei chipTAN liefern wir den bereits geparsten und gerenderten Flickercode + payload.append(flicker.render()); + } } + // Callback durchfuehren + HBCIUtilsInternal.getCallback().callback(this,callback,msg,HBCICallback.TYPE_TEXT,payload); + setPersistentData("externalid",null); // External-ID aus Passport entfernen if (payload == null || payload.length()==0) { throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_TANZERO")); @@ -477,63 +420,17 @@ } /** - * Versucht, aus Challenge und Challenge HHDuc den Flicker-Code zu extrahieren - * und ihn in einen flickerfaehigen Code umzuwandeln. - * Nur wenn tatsaechlich ein gueltiger Code enthalten ist, der als - * HHDuc-Code geparst und in einen Flicker-Code umgewandelt werden konnte, - * liefert die Funktion den Code. Sonst immer NULL. - * @param challenge der Challenge-Text. Das DE "Challenge HHDuc" gibt es - * erst seit HITAN4. Einige Banken haben aber schon vorher optisches chipTAN - * gemacht. Die haben das HHDuc dann direkt im Freitext des Challenge - * mitgeschickt (mit String-Tokens zum Extrahieren markiert). Die werden vom - * FlickerCode-Parser auch unterstuetzt. - * @param hhduc das echte Challenge HHDuc. - * @return der geparste und in Flicker-Format konvertierte Code oder NULL. - */ - private String parseFlickercode(String challenge, String hhduc) - { - // 1. Prioritaet hat hhduc. Gibts aber erst seit HITAN4 - if (hhduc != null && hhduc.trim().length() > 0) - { - try - { - FlickerCode code = new FlickerCode(hhduc); - return code.render(); - } - catch (Exception e) - { - HBCIUtils.log("unable to parse Challenge HHDuc " + hhduc + ":" + HBCIUtils.exception2String(e),HBCIUtils.LOG_DEBUG); - } - } - - // 2. Checken, ob im Freitext-Challenge was parse-faehiges steht. - // Kann seit HITAN1 auftreten - if (challenge != null && challenge.trim().length() > 0) - { - try - { - FlickerCode code = new FlickerCode(challenge); - return code.render(); - } - catch (Exception e) - { - // Das darf durchaus vorkommen, weil das Challenge auch bei manuellem - // chipTAN- und smsTAN Verfahren verwendet wird, wo gar kein Flicker-Code enthalten ist. - // Wir loggen es aber trotzdem - fuer den Fall, dass tatsaechlich ein Flicker-Code - // enthalten ist. Sonst koennen wir das nicht debuggen. - HBCIUtils.log("challenge contains no HHDuc (no problem in most cases):" + HBCIUtils.exception2String(e),HBCIUtils.LOG_DEBUG2); - } - } - // Ne, definitiv kein Flicker-Code. - return null; - } - + * @see org.kapott.hbci.passport.HBCIPassportInternal#verify(byte[], byte[]) + */ public boolean verify(byte[] data,byte[] sig) { // TODO: fuer bankensignaturen fuer HITAN muss dass hier geändert werden return true; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#encrypt(byte[]) + */ public byte[][] encrypt(byte[] plainMsg) { try { @@ -545,6 +442,9 @@ } } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#decrypt(byte[], byte[]) + */ public byte[] decrypt(byte[] cryptedKey,byte[] cryptedMsg) { try { @@ -554,10 +454,12 @@ } } + /** + * @see org.kapott.hbci.passport.AbstractHBCIPassport#close() + */ public void close() { super.close(); - passportKey=null; } } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDH.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDH.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDH.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDH.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,228 +0,0 @@ - -/* $Id: HBCIPassportRDH.java,v 1.1 2011/05/04 22:37:42 willuhn Exp $ - - This file is part of HBCI4Java - Copyright (C) 2001-2008 Stefan Palme - - HBCI4Java is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - HBCI4Java is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package org.kapott.hbci.passport; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.StreamCorruptedException; -import java.security.interfaces.RSAPrivateCrtKey; -import java.util.Properties; - -import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.CipherOutputStream; -import javax.crypto.spec.PBEParameterSpec; - -import org.kapott.hbci.exceptions.HBCI_Exception; -import org.kapott.hbci.exceptions.InvalidPassphraseException; -import org.kapott.hbci.manager.HBCIKey; -import org.kapott.hbci.manager.HBCIUtils; - -/**

Veraltete Passport-Klasse für RDH-Zugänge mit Sicherheitsmedium "Datei". - Diese Klasse sollte nicht mehr benutzt werden, sondern statt dessen die Klasse - {@link org.kapott.hbci.passport.HBCIPassportRDHNew}. - RDH-Passport-Datei können mit dem Tool - {@link org.kapott.hbci.tools.ConvertRDHPassport} oder - mit Hilfe des separat verfügbaren HBCI4Java Passport Editors - in RDHNew-Passport-Dateien umgewandelt werden. Siehe dazu auch die Daten - README.RDHNew

-

Das API dieser Klasse ist identisch zu dem der Klasse - {@link org.kapott.hbci.passport.HBCIPassportRDHNew}. Siehe - Beschreibung dort.

.*/ -public class HBCIPassportRDH - extends AbstractRDHSWFileBasedPassport -{ - public HBCIPassportRDH(Object init,int dummy) - { - super(init); - setParamHeader("client.passport.RDH"); - } - - public HBCIPassportRDH(Object initObject) - { - this(initObject,0); - - String header=getParamHeader(); - String fname=HBCIUtils.getParam(header+".filename"); - boolean init=HBCIUtils.getParam(header+".init","1").equals("1"); - - if (fname==null) { - throw new NullPointerException(header+".filename must not be null"); - } - - HBCIUtils.log("loading passport data from file "+fname,HBCIUtils.LOG_DEBUG); - setFilename(fname); - - if (init) { - HBCIUtils.log("loading data from file "+fname,HBCIUtils.LOG_DEBUG); - - setFilterType("None"); - setPort(new Integer(3000)); - - if (!new File(fname).canRead()) { - HBCIUtils.log("have to create new passport file",HBCIUtils.LOG_WARN); - askForMissingData(true,true,true,true,false,true,true); - saveChanges(); - } - - ObjectInputStream o=null; - try { - int retries=Integer.parseInt(HBCIUtils.getParam("client.retries.passphrase","3")); - - while (true) { // loop for entering the correct passphrase - if (getPassportKey()==null) - setPassportKey(calculatePassportKey(FOR_LOAD)); - - PBEParameterSpec paramspec=new PBEParameterSpec(CIPHER_SALT,CIPHER_ITERATIONS); - String provider = HBCIUtils.getParam("kernel.security.provider"); - Cipher cipher = provider == null ? Cipher.getInstance("PBEWithMD5AndDES") : Cipher.getInstance("PBEWithMD5AndDES", provider); - cipher.init(Cipher.DECRYPT_MODE,getPassportKey(),paramspec); - - o=null; - try { - o=new ObjectInputStream(new CipherInputStream(new FileInputStream(fname),cipher)); - } catch (StreamCorruptedException e) { - setPassportKey(null); - - retries--; - if (retries<=0) - throw new InvalidPassphraseException(); - } - - if (o!=null) - break; - } - - setCountry((String)(o.readObject())); - setBLZ((String)(o.readObject())); - setHost((String)(o.readObject())); - setPort((Integer)(o.readObject())); - setUserId((String)(o.readObject())); - setSysId((String)(o.readObject())); - setSigId((Long)(o.readObject())); - setBPD((Properties)(o.readObject())); - setUPD((Properties)(o.readObject())); - - for (int i=0;i<3;i++) { - for (int j=0;j<2;j++) { - setKey(i,j,(HBCIKey)(o.readObject())); - } - } - - setCID((String)(o.readObject())); - setHBCIVersion((String)o.readObject()); - setCustomerId((String)o.readObject()); - - HBCIKey k=getMyPrivateSigKey(); - if (k!=null && k.key!=null && !(k.key instanceof RSAPrivateCrtKey)) { - HBCIUtils.log("private sig key is no CRT key, please contact the author!",HBCIUtils.LOG_WARN); - } - - k=getMyPrivateEncKey(); - if (k!=null && k.key!=null && !(k.key instanceof RSAPrivateCrtKey)) { - HBCIUtils.log("private enc key is no CRT key, please contact the author!",HBCIUtils.LOG_WARN); - } - } catch (Exception e) { - throw new HBCI_Exception("*** loading of passport file failed",e); - } - - try { - o.close(); - } catch (Exception e) { - HBCIUtils.log(e); - } - - if (askForMissingData(true,true,true,true,false,true,true)) - saveChanges(); - } - } - - public void saveChanges() - { - try { - if (getPassportKey()==null) - setPassportKey(calculatePassportKey(FOR_SAVE)); - - PBEParameterSpec paramspec=new PBEParameterSpec(CIPHER_SALT,CIPHER_ITERATIONS); - String provider = HBCIUtils.getParam("kernel.security.provider"); - Cipher cipher = provider == null ? Cipher.getInstance("PBEWithMD5AndDES") : Cipher.getInstance("PBEWithMD5AndDES", provider); - cipher.init(Cipher.ENCRYPT_MODE,getPassportKey(),paramspec); - - File passportfile=new File(getFilename()); - File directory=passportfile.getAbsoluteFile().getParentFile(); - String prefix=passportfile.getName()+"_"; - File tempfile=File.createTempFile(prefix,"",directory); - - ObjectOutputStream o=new ObjectOutputStream(new CipherOutputStream(new FileOutputStream(tempfile),cipher)); - - o.writeObject(getCountry()); - o.writeObject(getBLZ()); - o.writeObject(getHost()); - o.writeObject(getPort()); - o.writeObject(getUserId()); - o.writeObject(getSysId()); - o.writeObject(getSigId()); - o.writeObject(getBPD()); - o.writeObject(getUPD()); - - for (int i=0;i<3;i++) { - for (int j=0;j<2;j++) { - HBCIKey key=getKey(i,j); - - if (key!=null) { - o.writeObject(new HBCIKey(key.country,key.blz,key.userid,key.num,key.version,key.key)); - } - else o.writeObject(null); - } - } - - o.writeObject(getCID()); - o.writeObject(getHBCIVersion()); - o.writeObject(getCustomerId()); - - o.close(); - - this.safeReplace(passportfile,tempfile); - - HBCIKey k=getMyPrivateSigKey(); - if (k!=null && k.key!=null && !(k.key instanceof RSAPrivateCrtKey)) { - HBCIUtils.log("private sig key is no CRT key, please contact the author!",HBCIUtils.LOG_WARN); - } - - k=getMyPrivateEncKey(); - if (k!=null && k.key!=null && !(k.key instanceof RSAPrivateCrtKey)) { - HBCIUtils.log("private enc key is no CRT key, please contact the author!",HBCIUtils.LOG_WARN); - } - } catch (Exception e) { - throw new HBCI_Exception("*** saving of passport file failed",e); - } - } - - public String getProfileVersion() - { - // old RDH format can only be used for profile RDH-1 - return "1"; - } -} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDH10File.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDH10File.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDH10File.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDH10File.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ - -/* $Id: HBCIPassportRDH10File.java,v 1.1 2011/05/04 22:37:43 willuhn Exp $ - - This file is part of HBCI4Java - Copyright (C) 2001-2008 Stefan Palme - - HBCI4Java is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - HBCI4Java is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package org.kapott.hbci.passport; - -import org.kapott.hbci.manager.HBCIUtils; - -/** Compatibility and convenience class for applications using the "RDH2File" - * passport variant. To avoid user confusion (using RDH-10-keys with passport - * variant "RDH2File") this class is a kind of alias for RDH2File (with exact - * the same behaviour). - * Both RDH2File and RDH10File are deprecated now - use the more general name - * RDHXFile instead. - * @Deprecated Use RDHXFile instead */ -public class HBCIPassportRDH10File - extends HBCIPassportRDHXFile -{ - protected String getCompatName() - { - HBCIUtils.log("RDH10File should not be used any longer - use RDHXFile instead!", HBCIUtils.LOG_WARN); - return "RDH10File"; - } - - public HBCIPassportRDH10File(Object initObject) - { - super(initObject); - } -} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDH2File.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDH2File.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDH2File.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDH2File.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ - -/* $Id: HBCIPassportRDH2File.java,v 1.1 2011/05/04 22:37:43 willuhn Exp $ - - This file is part of HBCI4Java - Copyright (C) 2001-2008 Stefan Palme - - HBCI4Java is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - HBCI4Java is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -package org.kapott.hbci.passport; - -import org.kapott.hbci.manager.HBCIUtils; - -/** Compatibility class for old applications using "RDH2File" passport variant - * @Deprecated Use RDHXFile instead */ -public class HBCIPassportRDH2File - extends HBCIPassportRDHXFile -{ - protected String getCompatName() - { - HBCIUtils.log("RDH2File should not be used any longer - use RDHXFile instead!", HBCIUtils.LOG_WARN); - return "RDH2File"; - } - - public HBCIPassportRDH2File(Object initObject) - { - super(initObject); - } -} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDHNew.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDHNew.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDHNew.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRDHNew.java 2019-11-27 09:04:22.000000000 +0000 @@ -21,44 +21,13 @@ package org.kapott.hbci.passport; -import java.io.CharConversionException; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.math.BigInteger; -import java.security.Key; -import java.security.KeyFactory; -import java.security.interfaces.RSAPrivateCrtKey; -import java.security.interfaces.RSAPrivateKey; -import java.security.interfaces.RSAPublicKey; -import java.security.spec.RSAPrivateCrtKeySpec; -import java.security.spec.RSAPrivateKeySpec; -import java.security.spec.RSAPublicKeySpec; -import java.util.Enumeration; -import java.util.Properties; - -import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.CipherOutputStream; -import javax.crypto.spec.PBEParameterSpec; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import org.kapott.cryptalgs.RSAPrivateCrtKey2; import org.kapott.hbci.exceptions.HBCI_Exception; -import org.kapott.hbci.exceptions.InvalidPassphraseException; -import org.kapott.hbci.manager.HBCIKey; import org.kapott.hbci.manager.HBCIUtils; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; +import org.kapott.hbci.manager.HBCIUtilsInternal; +import org.kapott.hbci.passport.storage.PassportData; +import org.kapott.hbci.passport.storage.PassportStorage; /**

Passport-Klasse für RDH-Zugänge mit Sicherheitsmedium "Datei". Bei dieser Variante werden sowohl die HBCI-Zugangsdaten wie auch die kryptografischen Schlüssel für @@ -94,16 +63,24 @@ dem Autor zukommen zu lassen, damit eine Passport-Variante implementiert werden kann, die mit Schlüsseldateien dieser "anderen" Software arbeiten kann.

@see org.kapott.hbci.tools.INILetter INILetter */ -public class HBCIPassportRDHNew - extends AbstractRDHSWFileBasedPassport +public class HBCIPassportRDHNew extends AbstractRDHSWFileBasedPassport { private String profileVersion; + /** + * ct. + * @param init + * @param dummy + */ public HBCIPassportRDHNew(Object init,int dummy) { super(init); } + /** + * ct. + * @param initObject + */ public HBCIPassportRDHNew(Object initObject) { this(initObject,0); @@ -112,9 +89,8 @@ String filename=HBCIUtils.getParam(getParamHeader()+".filename"); boolean init=HBCIUtils.getParam(getParamHeader()+".init","1").equals("1"); - if (filename==null) { + if (filename==null) throw new NullPointerException(getParamHeader()+".filename must not be null"); - } HBCIUtils.log("loading passport data from file "+filename,HBCIUtils.LOG_DEBUG); setFilename(filename); @@ -125,385 +101,89 @@ setFilterType("None"); setPort(new Integer(3000)); - if (!new File(filename).canRead()) { + if (!new File(filename).canRead()) + { HBCIUtils.log("have to create new passport file",HBCIUtils.LOG_WARN); askForMissingData(true,true,true,true,false,true,true); saveChanges(); } - try { - DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance(); - dbf.setValidating(false); - DocumentBuilder db=dbf.newDocumentBuilder(); - Element root=null; - - int retries=Integer.parseInt(HBCIUtils.getParam("client.retries.passphrase","3")); - - while (true) { // loop for entering the correct passphrase - if (getPassportKey()==null) - setPassportKey(calculatePassportKey(FOR_LOAD)); - - PBEParameterSpec paramspec=new PBEParameterSpec(CIPHER_SALT,CIPHER_ITERATIONS); - String provider = HBCIUtils.getParam("kernel.security.provider"); - Cipher cipher = provider == null ? Cipher.getInstance("PBEWithMD5AndDES") : Cipher.getInstance("PBEWithMD5AndDES", provider); - cipher.init(Cipher.DECRYPT_MODE,getPassportKey(),paramspec); - - root=null; - CipherInputStream ci=null; - - try { - ci=new CipherInputStream(new FileInputStream(getFilename()),cipher); - root=db.parse(ci).getDocumentElement(); - } catch (Exception e) { - - // willuhn 2011-05-25 Wir lassen einen erneuten Versuch nur bei einer der beiden - // folgenden Exceptions zu. - // Die "CharConversionException" ist in der Praxis eine - // " com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: Invalid byte 2 of 2-byte UTF-8 sequence." - // Sie fliegt in "db.parse()". Sprich: Der CipherInputStream kann nicht erkennen, - // ob das Passwort falsch ist. Stattdessen decodiert er einfach Muell, der - // anschliessend nicht als XML-Dokument gelesen werden kann - if (!(e instanceof SAXException) && !(e instanceof CharConversionException)) - throw e; - - resetPassphrase(); - - retries--; - if (retries<=0) - throw new InvalidPassphraseException(); - } finally { - if (ci!=null) - ci.close(); - } - - if (root!=null) - break; - } - - setBLZ(getElementValue(root,"blz")); - setCountry(getElementValue(root,"country")); - setHost(getElementValue(root,"host")); - setPort(new Integer(getElementValue(root,"port"))); - setUserId(getElementValue(root,"userid")); - setCustomerId(getElementValue(root,"customerid")); - setSysId(getElementValue(root,"sysid")); - setSigId(new Long(getElementValue(root,"sigid"))); - - String rdhprofile=getElementValue(root,"rdhprofile"); - setProfileVersion(rdhprofile!=null?rdhprofile:""); - - setHBCIVersion(getElementValue(root,"hbciversion")); + PassportData data = PassportStorage.load(this,new File(filename)); + this.setBLZ(data.blz); + this.setCountry(data.country); + this.setHost(data.host); + this.setPort(data.port); + this.setUserId(data.userId); + this.setCustomerId(data.customerId); + this.setSysId(data.sysId); + this.setSigId(data.sigId); + this.setProfileVersion(data.profileVersion != null ? data.profileVersion : ""); + this.setHBCIVersion(data.hbciVersion); + + this.setBPD(data.bpd); + this.setUPD(data.upd); + this.setInstSigKey(data.instSigKey); + this.setInstEncKey(data.instEncKey); + this.setMyPublicSigKey(data.myPublicSigKey); + this.setMyPrivateSigKey(data.myPrivateSigKey); + this.setMyPublicEncKey(data.myPublicEncKey); + this.setMyPrivateEncKey(data.myPrivateEncKey); - setBPD(getElementProps(root,"bpd")); - setUPD(getElementProps(root,"upd")); - - setInstSigKey(getElementKey(root,"inst","S","public")); - setInstEncKey(getElementKey(root,"inst","V","public")); - setMyPublicSigKey(getElementKey(root,"user","S","public")); - setMyPrivateSigKey(getElementKey(root,"user","S","private")); - setMyPublicEncKey(getElementKey(root,"user","V","public")); - setMyPrivateEncKey(getElementKey(root,"user","V","private")); - - if (askForMissingData(true,true,true,true,false,true,true)) - saveChanges(); - } - catch (HBCI_Exception e1) - { - throw e1; - } - catch (Exception e) { - throw new HBCI_Exception("*** error while reading passport file",e); - } - } - } - - protected String getElementValue(Element root,String name) - { - String ret=null; - - NodeList list=root.getElementsByTagName(name); - if (list!=null && list.getLength()!=0) { - Node content=list.item(0).getFirstChild(); - if (content!=null) - ret=content.getNodeValue(); - } - - return ret != null && ret.length() > 0 ? ret : null; - } - - protected Properties getElementProps(Element root,String name) - { - Properties ret=null; - - Node base=root.getElementsByTagName(name).item(0); - if (base!=null) { - ret=new Properties(); - NodeList entries=base.getChildNodes(); - int len=entries.getLength(); - - for (int i=0;i * Passport-Klasse für die Verwendung von RDH-2- und RDH-10-Schlüsseldateien mit @@ -87,6 +90,8 @@ saveChanges(); } + InputStream is = null; + try { if (this.passphrase==null) { StringBuffer retData=new StringBuffer(); @@ -95,26 +100,16 @@ HBCIUtilsInternal.getLocMsg("CALLB_NEED_PASS"), HBCICallback.TYPE_SECRET, retData); - // TODO: passwort-bedingungen nach spez. prüfen LogFilter.getInstance().addSecretData(retData.toString(),"X",LogFilter.FILTER_SECRETS); setPassphrase(retData.toString().getBytes()); } - // daten einlesen - FileInputStream fin=new FileInputStream(fname); - StringBuffer sb=new StringBuffer(); - byte[] buffer=new byte[1024]; - int size; - - while ((size=fin.read(buffer))>0) { - sb.append(new String(buffer,0,size,"ISO-8859-1")); - } + is = new BufferedInputStream(new FileInputStream(fname)); + byte[] data = IOUtils.read(is); - fin.close(); - byte[] data=sb.toString().getBytes("ISO-8859-1"); - // System.out.println("read "+data.length+" bytes from "+getFileName()); + // Wichtig. File-Handle schliessen. Sonst schlaegt das Speichern fehl. Siehe https://homebanking-hilfe.de/forum/topic.php?p=151578#real151578 sowie die Mail von Simon vom 28.10.2019 + is.close(); - // filecontent-content this.filecontent=new RDHXFile(data, passphrase); this.entryIdx=0; @@ -179,6 +174,11 @@ } catch (Exception e) { throw new HBCI_Exception("*** error while reading passport file",e); } + finally + { + // Nur zur Sicherheit, falls das Schliessen oben fehlschlug + IOUtils.close(is); + } } } @@ -238,7 +238,7 @@ fo.write(data); fo.close(); - this.safeReplace(passportfile,tempfile); + IOUtils.safeReplace(passportfile,tempfile); } catch (Exception e) { throw new HBCI_Exception("*** saving of passport file failed", e); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRSA.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRSA.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRSA.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportRSA.java 2019-11-27 09:04:22.000000000 +0000 @@ -4,50 +4,38 @@ package org.kapott.hbci.passport; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.StreamCorruptedException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.util.Arrays; -import java.util.Properties; import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.CipherOutputStream; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESedeKeySpec; import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.PBEParameterSpec; import org.kapott.cryptalgs.SignatureParamSpec; import org.kapott.hbci.callback.HBCICallback; import org.kapott.hbci.datatypes.SyntaxCtr; import org.kapott.hbci.exceptions.HBCI_Exception; -import org.kapott.hbci.exceptions.InvalidPassphraseException; import org.kapott.hbci.manager.HBCIKey; import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.manager.HBCIUtilsInternal; import org.kapott.hbci.manager.LogFilter; +import org.kapott.hbci.passport.storage.PassportData; +import org.kapott.hbci.passport.storage.PassportStorage; import org.kapott.hbci.smartcardio.RSABankData; import org.kapott.hbci.smartcardio.RSACardService; import org.kapott.hbci.smartcardio.RSAKeyData; import org.kapott.hbci.smartcardio.SmartCardService; +import org.kapott.hbci.tools.IOUtils; /** * HBCI-Passport fuer RDH-Chipkarten. */ public class HBCIPassportRSA extends AbstractRDHPassport implements HBCIPassportChipcard { - - protected final static byte[] CIPHER_SALT={(byte) 0x56, (byte) 0xbc, (byte) 0x1c, (byte) 0x88, - (byte) 0x1f, (byte) 0xe3, (byte) 0x73, (byte) 0xcc}; - protected final static int CIPHER_ITERATIONS=987; - private final static int KEY_INST_SIG = 0; private final static int KEY_INST_ENC = 1; private final static int KEY_MY_PUBLIC_SIG = 2; @@ -59,7 +47,6 @@ private boolean pinEntered; private int useSoftPin; private byte[] softPin; - private SecretKey passportKey; private int entryIdx; private String forcedProfileVersion; private String bankId; @@ -94,9 +81,8 @@ { this(init, 0); - ObjectInputStream is = null; - - try { + try + { //////////////////////////////////////////////////////////////////////// // set parameters for initializing card //setUseBio(Integer.parseInt(HBCIUtils.getParam(getParamHeader() + ".usebio", "-1"))); @@ -133,78 +119,51 @@ //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// - // read passport file - String path = HBCIUtils.getParam(getParamHeader() + ".path", "./"); - setFileName(HBCIUtilsInternal.withCounter(path + getCardId(), getEntryIdx() - 1)); + // Lokale Passport-Datei laden, wenn sie existiert + final String path = HBCIUtils.getParam(getParamHeader() + ".path", "./"); + this.setFileName(HBCIUtilsInternal.withCounter(path + getCardId(), getEntryIdx() - 1)); HBCIUtils.log("loading passport data from file " + getFileName(), HBCIUtils.LOG_DEBUG); File file = new File(getFileName()); - if (!file.exists() || !file.isFile() || !file.canRead()) { + if (!file.exists() || !file.isFile() || !file.canRead()) + { HBCIUtils.log("have to create new passport file", HBCIUtils.LOG_WARN); askForMissingData(true, true, true, false, false, true, true); saveChanges(); } - - int retries = Integer.parseInt(HBCIUtils.getParam("client.retries.passphrase", "3")); - - while (true) { // loop for entering the correct passphrase - if (getPassportKey() == null) - setPassportKey(calculatePassportKey(FOR_LOAD)); - - PBEParameterSpec paramspec = new PBEParameterSpec(CIPHER_SALT, CIPHER_ITERATIONS); - String provider = HBCIUtils.getParam("kernel.security.provider"); - Cipher cipher = provider == null ? Cipher.getInstance("PBEWithMD5AndDES") : Cipher.getInstance("PBEWithMD5AndDES", provider); - cipher.init(Cipher.DECRYPT_MODE, getPassportKey(), paramspec); - - try { - is = new ObjectInputStream(new CipherInputStream(new FileInputStream(file), cipher)); - } catch (StreamCorruptedException e) { - setPassportKey(null); // Passwort resetten - retries--; - if (retries<=0) - throw new InvalidPassphraseException(); - } catch (Exception e) { - throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSPORT_READERR"), e); - } - - // wir habens - if (is != null) { - setBPD((Properties) is.readObject()); - setUPD((Properties) is.readObject()); - setHBCIVersion((String) is.readObject()); - // this could be stored on the chipcard, but we use the file - setSysId((String) is.readObject()); - setCustomerId((String) is.readObject()); - break; - } - } + + PassportData data = PassportStorage.load(this,file); + this.setBPD(data.bpd); + this.setUPD(data.upd); + this.setHBCIVersion(data.hbciVersion); + this.setSysId(data.sysId); + this.setCustomerId(data.customerId); // //////////////////////////////////////////////////////////////////////// - } catch (Exception e) { - // Im Fehlerfall wieder schliessen - try { + } + catch (Exception e) + { + // Verbindung zum Kartenleser nur im Fehlerfall schliessen + try + { closeCT(); - } catch (Exception ex) { - HBCIUtils.log(ex); + } + catch (Exception e2) + { + HBCIUtils.log(e2); } if (e instanceof HBCI_Exception) throw (HBCI_Exception) e; - - throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_CTERR"), e); - } finally { - // Close Passport-File - if (is != null) { - try { - is.close(); - } catch (Exception e) { - HBCIUtils.log(e); - } - } + + throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_CTERR"),e); } } + /** + * @see org.kapott.hbci.passport.AbstractHBCIPassport#getCustomerId() + */ @Override public String getCustomerId() { if (getStoredCustomerId() == null || getStoredCustomerId().length() == 0) { @@ -218,18 +177,34 @@ } } + /** + * Liefert die Default-Kundenkennung. + * @return die Default-Kundenkennung. + */ public String getDefaultCustomerId() { return defaultCustomerId; } + /** + * Speichert die Default-Kundenkennung. + * @param defaultCustomerId die Default-Kundenkennung. + */ public void setDefaultCustomerId(String defaultCustomerId) { this.defaultCustomerId = defaultCustomerId; } + /** + * Speichert die Bank-ID. + * @param bankId die Bank-ID. + */ public void setBankId(String bankId) { this.bankId = bankId; } + /** + * Liefert die Bank-ID. + * @return die Bank-ID. + */ public String getBankId() { return bankId; } @@ -240,7 +215,7 @@ @Override public void setFileName(String filename) { - this.filename = filename; + this.filename = IOUtils.safeFilename(filename); } /** @@ -252,55 +227,91 @@ return filename; } + /** + * Speichert die Karten-ID. + * @param cardid + */ public void setCardId(String cardid) { this.cardid = cardid; } + /** + * Liefert die Karten-ID. + * @return die Karten-ID. + */ public String getCardId() { return cardid; } + /** + * Speichert, ob die PIN eingegeben wurde. + * @param pinEntered true, wenn die PIN eingegeben wurde. + */ public void setPINEntered(boolean pinEntered) { this.pinEntered = pinEntered; } + /** + * Liefert true, wenn die PIN eingegeben wurde. + * @return true, wenn die PIN eingegeben wurde. + */ public boolean isPINEntered() { return pinEntered; } + /** + * Legt fest, ob die PIN per Tastatur eingegeben werden soll. + * @param useSoftPin true, wenn die PIN per Tastatur eingegeben werden soll. + */ public void setUseSoftPin(int useSoftPin) { this.useSoftPin = useSoftPin; } - public int getUseSoftPin() { + /** + * Prueft, ob die PIN per Tastatur eingegeben werden soll. + * @return true, wenn die PIN per Tastatur eingegeben werden soll. + */ + public int getUseSoftPin() + { return useSoftPin; } + /** + * Speichert die ueber die Tastatur eingegebene PIN. + * @param softPin die ueber die Tastatur eingegebene PIN. + */ public void setSoftPin(byte[] softPin) { LogFilter.getInstance().addSecretData(new String(softPin), "X", LogFilter.FILTER_SECRETS); this.softPin = softPin; } + /** + * Liefert die ueber die Tastatur eingegebene PIN. + * @return die ueber die Tastatur eingegebene PIN. + */ public byte[] getSoftPin() { return softPin; } - protected void setPassportKey(SecretKey passportKey) { - this.passportKey = passportKey; - } - - protected SecretKey getPassportKey() { - return passportKey; - } - + /** + * Speichert den Index des Bankzugangs. + * @param entryIdx der Index des Bankzugangs. + */ public void setEntryIdx(int entryIdx) { this.entryIdx = entryIdx; } + /** + * Liefert den Index des Bankzugangs. + * @return der Index des Bankzugangs. + */ public int getEntryIdx() { return entryIdx; } + /** + * @see org.kapott.hbci.passport.AbstractHBCIPassport#setProfileVersion(java.lang.String) + */ @Override public void setProfileVersion(String version) { if (version != null) { @@ -309,6 +320,9 @@ this.forcedProfileVersion = version; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getProfileVersion() + */ @Override public String getProfileVersion() { String result = this.forcedProfileVersion; @@ -339,9 +353,7 @@ HBCIUtils.log("using inst enc key num '" + result + "' as RDH profile version", HBCIUtils.LOG_DEBUG); } else { // neither user keys nor inst keys present - using highest available profile - HBCIUtils.log( - "no keys found in passport - so we use the highest available profile", - HBCIUtils.LOG_DEBUG); + HBCIUtils.log("no keys found in passport - so we use the highest available profile",HBCIUtils.LOG_DEBUG); // es gibt noch gar keine schlüssel - also nehmen wir die // höchste unterstützte profil-nummer @@ -380,111 +392,183 @@ return result; } + /** + * Speichert den Schluesel. + * @param i der Index. + * @param key der Schluessel. + */ private void setKey(int i, HBCIKey key) { - // System.out.println("passportDDV: setting key "+i+" to "+(key==null?"null":key.country+":"+key.blz+":"+key.cid+":"+key.num+":"+key.version)); keys[i] = key; } + /** + * Liefert den Schluessel. + * @param i der Index. + * @return der Schluessel. + */ private HBCIKey getKey(int i) { return keys[i]; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#setInstSigKey(org.kapott.hbci.manager.HBCIKey) + */ @Override public void setInstSigKey(HBCIKey key) { setKey(KEY_INST_SIG, key); } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#setInstEncKey(org.kapott.hbci.manager.HBCIKey) + */ @Override public void setInstEncKey(HBCIKey key) { setKey(KEY_INST_ENC, key); } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#setMyPublicSigKey(org.kapott.hbci.manager.HBCIKey) + */ @Override public void setMyPublicSigKey(HBCIKey key) { setKey(KEY_MY_PUBLIC_SIG, key); } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#setMyPrivateSigKey(org.kapott.hbci.manager.HBCIKey) + */ @Override public void setMyPrivateSigKey(HBCIKey key) { } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#setMyPublicEncKey(org.kapott.hbci.manager.HBCIKey) + */ @Override public void setMyPublicEncKey(HBCIKey key) { setKey(KEY_MY_PUBLIC_ENC, key); } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#setMyPrivateEncKey(org.kapott.hbci.manager.HBCIKey) + */ @Override public void setMyPrivateEncKey(HBCIKey key) { } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#setMyPublicDigKey(org.kapott.hbci.manager.HBCIKey) + */ @Override public void setMyPublicDigKey(HBCIKey key) { } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#setMyPrivateDigKey(org.kapott.hbci.manager.HBCIKey) + */ @Override public void setMyPrivateDigKey(HBCIKey key) { } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getInstSigKeyName() + */ @Override public String getInstSigKeyName() { return getInstSigKey() != null ? getInstSigKey().userid : null; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getInstSigKeyNum() + */ @Override public String getInstSigKeyNum() { return getInstSigKey() != null ? getInstSigKey().num : null; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getInstSigKeyVersion() + */ @Override public String getInstSigKeyVersion() { return getInstSigKey() != null ? getInstSigKey().version : null; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getInstEncKeyName() + */ @Override public String getInstEncKeyName() { return getInstEncKey() != null ? getInstEncKey().userid : null; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getInstEncKeyNum() + */ @Override public String getInstEncKeyNum() { return getInstEncKey() != null ? getInstEncKey().num : null; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getInstEncKeyVersion() + */ @Override public String getInstEncKeyVersion() { return getInstEncKey() != null ? getInstEncKey().version : null; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getMySigKeyName() + */ @Override public String getMySigKeyName() { return getMyPublicSigKey() != null ? getMyPublicSigKey().userid : null; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getMySigKeyNum() + */ @Override public String getMySigKeyNum() { return getMyPublicSigKey() != null ? getMyPublicSigKey().num : null; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getMySigKeyVersion() + */ @Override public String getMySigKeyVersion() { return getMyPublicSigKey() != null ? getMyPublicSigKey().version : null; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getMyEncKeyName() + */ @Override public String getMyEncKeyName() { return getMyPublicEncKey() != null ? getMyPublicEncKey().userid : null; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getMyEncKeyNum() + */ @Override public String getMyEncKeyNum() { return getMyPublicEncKey() != null ? getMyPublicEncKey().num : null; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#getMyEncKeyVersion() + */ @Override public String getMyEncKeyVersion() { return getMyPublicEncKey() != null ? getMyPublicEncKey().version : null; } + /** + * @see org.kapott.hbci.passport.AbstractRDHPassport#hash(byte[]) + */ @Override public byte[] hash(byte[] data) { data = super.hash(data); @@ -501,18 +585,30 @@ return dig.digest(data); } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#sign(byte[]) + */ @Override public byte[] sign(byte[] data) { checkPIN(); return ctSign(data); } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#verify(byte[], byte[]) + */ @Override public boolean verify(byte[] data, byte[] sig) { checkPIN(); return ctVerify(data, sig); } + /** + * Verschluesselt die Nachricht. + * @param plainMsg die Nachricht. + * @param msgkey der Schluessel. + * @return die verschluesselte Nachricht. + */ private byte[] encryptMessage(byte[] plainMsg, SecretKey msgkey) { try { String provider = HBCIUtils.getParam("kernel.security.provider"); @@ -528,6 +624,11 @@ } } + /** + * Verschluesselt den Key. + * @param msgkey der Key. + * @return der verschluesselte Key. + */ private byte[] encryptKey(SecretKey msgkey) { try { // schluessel als byte-array abspeichern @@ -551,6 +652,9 @@ } } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#encrypt(byte[]) + */ @Override public byte[][] encrypt(byte[] plainMsg) { try { @@ -568,6 +672,9 @@ } } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#decrypt(byte[], byte[]) + */ @Override public byte[] decrypt(byte[] cryptedKey, byte[] cryptedMsg) { try { @@ -595,6 +702,9 @@ } } + /** + * @see org.kapott.hbci.passport.AbstractHBCIPassport#close() + */ @Override public void close() { super.close(); @@ -613,114 +723,149 @@ try { checkPIN(); ctSaveBankData(); - } catch (Exception e) { + } + catch (Exception e) + { throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSPORT_INSTSAVEERR"),e); } } + /** + * @see org.kapott.hbci.passport.HBCIPassport#hasInstSigKey() + */ @Override public void resetPassphrase() { - passportKey = null; } + /** + * @see org.kapott.hbci.passport.HBCIPassport#hasInstSigKey() + */ @Override public boolean hasInstSigKey() { return getInstSigKey() != null; } + /** + * @see org.kapott.hbci.passport.HBCIPassport#hasInstEncKey() + */ @Override public boolean hasInstEncKey() { return getInstEncKey() != null; } + /** + * @see org.kapott.hbci.passport.HBCIPassport#hasMySigKey() + */ @Override public boolean hasMySigKey() { return getMyPublicSigKey() != null; } + /** + * @see org.kapott.hbci.passport.HBCIPassport#hasMyEncKey() + */ @Override public boolean hasMyEncKey() { return getMyPublicEncKey() != null; } + /** + * @see org.kapott.hbci.passport.HBCIPassport#getMyPublicSigKey() + */ @Override public HBCIKey getMyPublicSigKey() { return getKey(KEY_MY_PUBLIC_SIG); } + /** + * @see org.kapott.hbci.passport.HBCIPassport#getMyPublicEncKey() + */ @Override public HBCIKey getMyPublicEncKey() { return getKey(KEY_MY_PUBLIC_ENC); } + /** + * @see org.kapott.hbci.passport.HBCIPassport#getMyPublicDigKey() + */ @Override public HBCIKey getMyPublicDigKey() { return null; } + /** + * @see org.kapott.hbci.passport.HBCIPassport#getMyPrivateSigKey() + */ @Override public HBCIKey getMyPrivateSigKey() { return getMyPublicSigKey(); } + /** + * @see org.kapott.hbci.passport.HBCIPassport#getMyPrivateEncKey() + */ @Override public HBCIKey getMyPrivateEncKey() { return getMyPublicEncKey(); } + /** + * @see org.kapott.hbci.passport.HBCIPassport#getMyPrivateDigKey() + */ @Override public HBCIKey getMyPrivateDigKey() { return getMyPublicDigKey(); } + /** + * @see org.kapott.hbci.passport.HBCIPassport#getInstSigKey() + */ @Override public HBCIKey getInstSigKey() { return getKey(KEY_INST_SIG); } + /** + * @see org.kapott.hbci.passport.HBCIPassport#getInstEncKey() + */ @Override public HBCIKey getInstEncKey() { return getKey(KEY_INST_ENC); } + /** + * @see org.kapott.hbci.passport.HBCIPassport#saveChanges() + */ @Override public void saveChanges() { try { - checkPIN(); - ctSaveBankData(); - ctSaveSigId(); - - if (passportKey == null) - passportKey = calculatePassportKey(FOR_SAVE); - - File passportfile = new File(getFileName()); - File directory = passportfile.getAbsoluteFile().getParentFile(); - String prefix = passportfile.getName() + "_"; - File tempfile = File.createTempFile(prefix, "", directory); - - PBEParameterSpec paramspec = new PBEParameterSpec(CIPHER_SALT, CIPHER_ITERATIONS); - String provider = HBCIUtils.getParam("kernel.security.provider"); - Cipher cipher = provider == null ? Cipher.getInstance("PBEWithMD5AndDES") : Cipher.getInstance("PBEWithMD5AndDES", provider); - cipher.init(Cipher.ENCRYPT_MODE, passportKey, paramspec); - ObjectOutputStream o = new ObjectOutputStream(new CipherOutputStream(new FileOutputStream(tempfile), cipher)); - - o.writeObject(getBPD()); - o.writeObject(getUPD()); - o.writeObject(getHBCIVersion()); - // this could be stored on the chipcard, but we use the file - o.writeObject(getSysId()); - o.writeObject(getCustomerId()); + this.checkPIN(); + this.ctSaveBankData(); + this.ctSaveSigId(); + + final PassportData data = new PassportData(); + data.bpd = this.getBPD(); + data.upd = this.getUPD(); + data.hbciVersion = this.getHBCIVersion(); + data.sysId = this.getSysId(); + data.customerId = this.getCustomerId(); - o.close(); - - this.safeReplace(passportfile,tempfile); - - } catch (Exception e) { - throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSPORT_WRITEERR"), e); + PassportStorage.save(this,data,new File(this.getFileName())); + } + catch (HBCI_Exception he) + { + throw he; + } + catch (Exception e) + { + throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSPORT_WRITEERR"),e); } } + /** + * Liest die Bank-Daten. + */ public void readBankData() { try { checkPIN(); @@ -730,6 +875,9 @@ } } + /** + * Liest die Schluesseldaten. + */ public void readKeyData() { try { checkPIN(); @@ -739,6 +887,9 @@ } } + /** + * Prueft die PIN. + */ private void checkPIN() { try { if (!isPINEntered()) { @@ -798,14 +949,22 @@ this.setCardId(this.cardService.getCardId()); } - protected void ctEnterPIN() { + /** + * Fuehrt die PIN-Eingabe durch. + */ + protected void ctEnterPIN() + { if (getUseSoftPin() == 1) cardService.verifySoftPIN(0x10, this.getSoftPin()); else cardService.verifyHardPIN(0x10); } - protected void ctReadBankData() { + /** + * Liest die Bankdaten. + */ + protected void ctReadBankData() + { int idx = getEntryIdx() - 1; RSABankData bankData = cardService.readBankData(idx); @@ -820,6 +979,9 @@ setDefaultCustomerId(bankData.getCustomerId()); } + /** + * Speichert die Bankdaten. + */ protected void ctSaveBankData() { int idx = getEntryIdx() - 1; @@ -836,6 +998,9 @@ cardService.writeBankData(idx, bankData); } + /** + * Liest die Schluesseldaten. + */ protected void ctReadKeyData() { int idx = getEntryIdx() - 1; @@ -877,36 +1042,63 @@ } } + /** + * Speichert die Signatur-ID. + */ protected void ctSaveSigId() { int idx = getEntryIdx() - 1; cardService.writeSigId(idx, getSigId().intValue()); } + /** + * Signiert die Daten. + * @param data die zu signierenden Daten. + * @return + */ protected byte[] ctSign(byte[] data) { int idx = getEntryIdx() - 1; return cardService.sign(idx, data); } + /** + * Prueft die Signatur. + * @param data die Daten. + * @param sig die Signatur. + * @return true, wenn die Signatur ok ist. + */ protected boolean ctVerify(byte[] data, byte[] sig) { int idx = getEntryIdx() - 1; return cardService.verify(idx, data, sig); } + /** + * Verschluesselt die Daten. + * @param data die Daten. + * @return die verschluesselten Daten. + */ protected byte[] ctEncipher(byte[] data) { int idx = getEntryIdx() - 1; return cardService.encipher(idx, data); } + /** + * Entschluesselt die Daten. + * @param data die verschluesselten Daten. + * @return die entschluesselten Daten. + */ protected byte[] ctDecipher(byte[] data) { int idx = getEntryIdx() - 1; return cardService.decipher(idx, data); } + /** + * Schliesst den Kartenleser. + */ protected void closeCT() { try diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportSIZRDHFile.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportSIZRDHFile.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportSIZRDHFile.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/HBCIPassportSIZRDHFile.java 2019-11-27 09:04:22.000000000 +0000 @@ -28,6 +28,7 @@ import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.manager.HBCIUtilsInternal; import org.kapott.hbci.manager.LogFilter; +import org.kapott.hbci.tools.IOUtils; /**

Passport-Klasse für die Verwendung von SIZ-RDH-Schlüsseldateien mit HBCI4Java. SIZ-RDH-Schlüsseldateien sind Schlüsseldateien für RDH-Zugänge, die von @@ -125,6 +126,15 @@ return passphrase; } + /** + * @see org.kapott.hbci.passport.HBCIPassportInternal#resetPassphrase() + */ + @Override + public void resetPassphrase() + { + this.passphrase = null; + } + public void saveChanges() { try { @@ -135,7 +145,7 @@ saveData(tempfile.getAbsolutePath()); - this.safeReplace(passportfile,tempfile); + IOUtils.safeReplace(passportfile,tempfile); } catch (Exception e) { throw new HBCI_Exception("*** saving of passport file failed",e); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/PassportData.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/PassportData.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/PassportData.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/PassportData.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,129 @@ +/********************************************************************** + * + * Copyright (c) by Olaf Willuhn + * All rights reserved + * + **********************************************************************/ + +package org.kapott.hbci.passport.storage; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.kapott.hbci.manager.HBCIKey; + +/** + * Kapselt die Daten des Passport zum Lesen und Schreiben. + */ +public class PassportData implements Serializable +{ + // Stellt sicher, dass das Objekt auch dann noch deserialisierbar ist, wenn Attribute hinzugekommen sind. + private static final long serialVersionUID = 1L; + + /** + * Die BPD. + */ + public Properties bpd = null; + + /** + * Die UPD. + */ + public Properties upd = null; + + /** + * Die HBCI-Version. + */ + public String hbciVersion = null; + + /** + * Der Laendercode. + */ + public String country = null; + + /** + * Die BLZ. + */ + public String blz = null; + + /** + * Der Host. + */ + public String host = null; + + /** + * Der TCP-Port. + */ + public Integer port = null; + + /** + * Die User-ID. + */ + public String userId = null; + + /** + * Die System-ID. + */ + public String sysId = null; + + /** + * Die Signatur-ID. + */ + public Long sigId = null; + + /** + * Die Profil-Version. + */ + public String profileVersion = null; + + /** + * Die Kunden-ID. + */ + public String customerId = null; + + /** + * Der Transport-Filter. + */ + public String filter = null; + + /** + * Die Liste der Zweischritt-Verfahren. + */ + public List twostepMechs = new ArrayList(); + + /** + * Das TAN-Verfahren. + */ + public String tanMethod = null; + + /** + * Der Signatur-Key der Bank. + */ + public HBCIKey instSigKey = null; + + /** + * Der Verschluesselungskey der Bank. + */ + public HBCIKey instEncKey = null; + + /** + * Der eigene Public-Signatur-Key. + */ + public HBCIKey myPublicSigKey = null; + + /** + * Der eigene Private-Signatur-Key. + */ + public HBCIKey myPrivateSigKey = null; + + /** + * Der eigene Public-Verschluesselungskey. + */ + public HBCIKey myPublicEncKey = null; + + /** + * Der eigene Private-Verschluesselungskey. + */ + public HBCIKey myPrivateEncKey = null; +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/PassportStorage.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/PassportStorage.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/PassportStorage.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/PassportStorage.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,311 @@ +/********************************************************************** + * + * Copyright (c) by Olaf Willuhn + * All rights reserved + * + **********************************************************************/ + +package org.kapott.hbci.passport.storage; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; + +import org.kapott.hbci.exceptions.HBCI_Exception; +import org.kapott.hbci.manager.HBCIUtils; +import org.kapott.hbci.passport.HBCIPassport; +import org.kapott.hbci.passport.storage.format.AESFormat; +import org.kapott.hbci.passport.storage.format.PassportFormat; +import org.kapott.hbci.tools.IOUtils; + +/** + * Kapselt das Lesen/Schreiben und Verschluesseln/Entschluesseln der Passport-Dateien. + */ +public class PassportStorage +{ + private final static List ORDER_DEFAULT = Arrays.asList("AESFormat","LegacyFormat"); + private static Map formats = null; + + static + { + init(); + } + + /** + * Liest die Passportdatei ein. + * @param passport der Passport, zu dem die Daten gelesen werden sollen. + * @param file die Passport-Datei. + * @return das Passport-Format. + */ + public static PassportData load(HBCIPassport passport, File file) + { + if (file == null) + throw new HBCI_Exception("no passport file given"); + + if (!file.canRead() || !file.isFile()) + throw new HBCI_Exception("passport file " + file + " not readable or no file"); + + HBCIUtils.log("loading passport data from " + file,HBCIUtils.LOG_DEBUG); + + InputStream is = null; + try + { + is = new BufferedInputStream(new FileInputStream(file)); + return load(passport, is); + } + catch (IOException e) + { + throw new HBCI_Exception("unable to read passport file " + file,e); + } + finally + { + IOUtils.close(is); + } + } + + /** + * Liest die Passportdatei ein. + * @param passport der Passport, zu dem die Daten gelesen werden sollen. + * @param is Stream mit der Datei. + * Die Funktion schliesst den Stream nicht. Das ist Aufgabe des Aufrufers. + * @return die gelesenen Passport-Daten. + */ + public static PassportData load(HBCIPassport passport, InputStream is) + { + if (passport == null) + throw new HBCI_Exception("no passport given"); + + if (is == null) + throw new HBCI_Exception("no inputstream given"); + try + { + // Wir laden die Datei erstmal komplett in den Speicher, damit wir die Formate durchprobieren koennen + byte[] data = IOUtils.read(is); + + // Wenn die Datei leer ist, brauchen wir sie nicht einlesen sondern liefern einfach ein leeres + // PassportData-Objekt. Dann werfen wir naemlich keinen Fehler beim Laden, wenn das Schreiben einer + // Passport-Datei mal fehlschlug und sie als leere Datei erzeugt wurde. Stattdessen erstellen wir + // einfach einen neuen Passport. + if (data != null && data.length == 0) + return new PassportData(); + + for (PassportFormat format:getLoadFormats()) + { + try + { + PassportData result = format.load(passport,data); + HBCIUtils.log("passport data loaded using " + format.getClass().getSimpleName(),HBCIUtils.LOG_DEBUG); + return result; + } + catch (UnsupportedOperationException ue) + { + // Passport unterstuetzt das Format nicht - ueberspringen - Fehlermeldung koennen wir ignorieren, + // sie wird vom Format bewusst geworfen, um anzuzeigen, dass es das Format nicht unterstuetzt + } + } + + // Wenn wir hier angekommen sind, unterstuetzen wir das Format nicht. + throw new HBCI_Exception("unknown passport file format"); + } + catch (IOException e) + { + throw new HBCI_Exception("unable to read passport file",e); + } + } + + /** + * Speichert die Passport-Daten. + * @param passport der Passport. + * @param data die Daten. + * @param file die Zieldatei. + */ + public static void save(HBCIPassport passport, PassportData data, File file) + { + if (file == null) + throw new HBCI_Exception("no passport file given"); + + HBCIUtils.log("saving passport data to " + file,HBCIUtils.LOG_DEBUG); + + OutputStream os = null; + + try + { + // Wir schreiben die Daten erstmal in eine Temp-Datei. + // Wenn die geschrieben ist, verschieben wir die Datei auf den vorherigen Namen. + File directory = file.getAbsoluteFile().getParentFile(); + String prefix = file.getName() + "_"; + File tempfile = File.createTempFile(prefix,"",directory); + + os = new BufferedOutputStream(new FileOutputStream(tempfile)); + save(passport,data,os); + os.close(); + + IOUtils.safeReplace(file,tempfile); + } + catch (IOException e) + { + throw new HBCI_Exception("unable to write passport file " + file,e); + } + finally + { + IOUtils.close(os); + } + } + + /** + * Speichert die Passport-Daten. + * @param passport der Passport. + * @param data die Daten. + * @param os der Stream, in den die Daten geschrieben werden. + */ + public static void save(HBCIPassport passport, PassportData data, OutputStream os) + { + if (passport == null) + throw new HBCI_Exception("no passport given"); + + if (data == null) + throw new HBCI_Exception("no passport data given"); + + // Ermitteln, welches Dateiformat verwendet werden soll. + final PassportFormat format = getSaveFormat(passport); + + try + { + byte[] bytes = format.save(passport,data); + os.write(bytes); + os.flush(); // Sicherstellen, dass die Daten geflusht sind. + HBCIUtils.log("passport data saved using " + format.getClass().getSimpleName(),HBCIUtils.LOG_DEBUG); + return; // Wenn wir fehlerfrei geschrieben haben, sind wir fertig + } + catch (IOException e) + { + throw new HBCI_Exception("unable to write passport file",e); + } + } + + /** + * Initialisiert die Liste der unterstuetzten Dateiformate. + */ + private static void init() + { + if (formats != null) + return; + + formats = new HashMap(); + + HBCIUtils.log("searching supported passport formats",HBCIUtils.LOG_DEBUG); + final ServiceLoader loader = ServiceLoader.load(PassportFormat.class); + for (PassportFormat f:loader) + { + final String name = f.getClass().getSimpleName(); + + if (!f.supported()) + { + HBCIUtils.log("passport format " + name + " not supported on this plattform",HBCIUtils.LOG_INFO); + continue; + } + formats.put(name,f); + } + + if (formats.size() == 0) + HBCIUtils.log("No supported passport formats found",HBCIUtils.LOG_ERR); + } + + /** + * Liefert die Passport-Formate in der angegebenen Reihenfolge zum Laden. + * @return die Passports in der konfigurierten Reihenfolge. + */ + private static List getLoadFormats() + { + List result = new LinkedList(); + for (String name:getFormatOrder()) + { + PassportFormat f = formats.get(name); + if (f == null) + { + HBCIUtils.log("passport format unknown or not supported: " + name,HBCIUtils.LOG_DEBUG); + continue; + } + + result.add(f); + } + + return result; + } + + + /** + * Liefert das fuer die Speicherung zu verwendende Dateiformat. + * @param passport der Passport. + * @return das zu verwendende Format. Nie NULL. + * Wenn keines per Konfiguration ermittelbar ist, wird das Default-Format {@link AESFormat} verwendet. + */ + private static PassportFormat getSaveFormat(HBCIPassport passport) + { + final String type = passport.getClass().getSimpleName(); + + // Checkt erst, ob es ein passport-spezifisches Format-Parameter gibt. Z.Bsp. "passport.format.HBCIPassportPinTan" + // Wenn icht, dann ob es einen generischen Format-Parameter "passport.format" + final String name = HBCIUtils.getParam("passport.format." + type,HBCIUtils.getParam("passport.format")); + + + // Es ist ein Format konfiguriert. Checken, ob wir es unterstuetzen + if (name != null) + { + PassportFormat format = formats.get(name); + if (format != null) + return format; + } + + // Entweder wir kennen es nicht oder wir unterstuetzen es nicht. Dann nehmen wir das + // erste Format, das wir kennen + for (String s:getFormatOrder()) + { + PassportFormat format = formats.get(s); + if (format != null) + return format; + } + + // Wenn auch hier nichts da ist, nehmen wir das erste, das ueberhaupt existiert + if (formats.size() == 0) + throw new HBCI_Exception("No supported passport formats found"); + + return formats.values().iterator().next(); + } + + /** + * Liefert die Format-Reihenfolge. + * @return die zu verwendende Format-Reihenfolge. + */ + private static List getFormatOrder() + { + String value = HBCIUtils.getParam("passport.order"); + if (value == null || value.length() == 0) + return ORDER_DEFAULT; + + // Whitespaces und Leerzeichen entfernen - auch mittendrin + value = value.trim().replace(" ",""); + + // Nur Leerzeichen? + if (value.length() == 0) + return ORDER_DEFAULT; + + + // Liste enthaelt nur einen Wert + if (!value.contains(",")) + return Arrays.asList(value); + + return Arrays.asList(value.split(",")); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/AESFormat.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/AESFormat.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/AESFormat.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/AESFormat.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,328 @@ +/********************************************************************** + * + * Copyright (c) by Olaf Willuhn + * All rights reserved + * + **********************************************************************/ + +package org.kapott.hbci.passport.storage.format; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.KeySpec; +import java.util.Arrays; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; + +import org.kapott.hbci.exceptions.HBCI_Exception; +import org.kapott.hbci.manager.HBCIUtils; +import org.kapott.hbci.passport.HBCIPassport; +import org.kapott.hbci.passport.storage.PassportData; +import org.kapott.hbci.tools.IOUtils; + +/** + * Implementierung des neuen AES-basierten Formats. + */ +public class AESFormat extends AbstractFormat +{ + private final static String FORMAT_NAME = "H4JAES"; // Ein paar Bytes am Anfang, anhand derer wir unser eigenes Dateiformat wiedererkennen + private final static int FORMAT_VERSION = 1; // Versionsnummer des Formats + private final static String ENCODING = "UTF-8"; + + private final static SecureRandom RAND = new SecureRandom(); + private final static String KEY_ALG_NAME = "PBKDF2WithHmacSHA256"; + private final static String KEY_ALG = "AES"; + private final static String CIPHER_ALG = "AES/CBC/PKCS5Padding"; + private final static int CIPHER_ITERATIONS = 64 * 1024; + private final static int KEY_SIZE = 256; + private final static int SALT_SIZE = 8; + + /** + * @see org.kapott.hbci.passport.storage.format.PassportFormat#load(org.kapott.hbci.passport.HBCIPassport, byte[]) + */ + @Override + public PassportData load(HBCIPassport passport, byte[] data) throws UnsupportedOperationException + { + final long started = System.currentTimeMillis(); + + if (data == null || data.length < 20) // Entweder keine Daten oder einfach zu wenig. Das kann nicht unser Format sein. + throw new UnsupportedOperationException("not enough data"); + + int pos = 0; + + ////////////////////////////////////////////////////////////////// + // Pre-Checks + try + { + // 1. Die ersten Bytes mit dem Formatnamen lesen und checken, ob der Name stimmt + if (!FORMAT_NAME.equals(new String(Arrays.copyOfRange(data,pos,pos+FORMAT_NAME.length()),ENCODING))) + throw new UnsupportedOperationException("wrong format identifier, expected: " + FORMAT_NAME); + + pos += FORMAT_NAME.length(); + + // 2. Versionsnummer checken + if (FORMAT_VERSION != data[FORMAT_NAME.length()]) + throw new UnsupportedOperationException("wrong format version, expected: " + FORMAT_VERSION); + + pos += 1; // Fuer die Versionsnummer haben wir 1 Byte vorgesehen + } + catch (UnsupportedEncodingException e) + { + HBCIUtils.log(e); + throw new UnsupportedOperationException(); + } + // + ////////////////////////////////////////////////////////////////// + + // Wenn wir hier angekommen sind, ist es auf jeden Fall das richtige Dateiformat. + // Wenn wir es jetzt nicht lesen koennen, dann kann es sonst auch niemand + + + ////////////////////////////////////////////////////////////////// + // Salt + int saltLen = data[pos]; + pos += 1; + + // Wir checken noch, ob ueberhaupt genuegend Daten im Byte-Array vorhanden sind + if (data.length < (pos + saltLen)) + throw new HBCI_Exception("passport file corrupted, not enough data"); + + byte[] salt = Arrays.copyOfRange(data,pos,pos+saltLen); + pos += saltLen; + // + ////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////// + // IV + int ivLen = data[pos]; + pos += 1; + + // Wir checken noch, ob ueberhaupt genuegend Daten im Byte-Array vorhanden sind + if (data.length < (pos + ivLen)) + throw new HBCI_Exception("passport file corrupted, not enough data"); + + byte[] iv = Arrays.copyOfRange(data,pos,pos+ivLen); + pos += ivLen; + // + ////////////////////////////////////////////////////////////////// + + int retries = this.getRetries(); + + for (int i=0;i<10;++i) // Mehr als 10 mal brauchen wir es nicht versuchen + { + ObjectInputStream is = null; + + try + { + final Cipher cipher = this.getCipher(); + final SecretKey key = this.getPassportKey(passport, salt, false); + cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); + + is = new ObjectInputStream(new CipherInputStream(new ByteArrayInputStream(Arrays.copyOfRange(data,pos,data.length)),cipher)); + PassportData result = (PassportData) is.readObject(); + + HBCIUtils.log("used time for decrypting " + data.length + " bytes: " + (System.currentTimeMillis() - started) + " millis",HBCIUtils.LOG_DEBUG); + return result; + } + catch (UnsupportedOperationException uoe) + { + throw uoe; + } + catch (HBCI_Exception e) + { + if (retries-- <= 0) + throw e; + } + catch (Exception ex) + { + if (retries-- <= 0) + throw new HBCI_Exception("unable to load passport data",ex); + } + finally + { + IOUtils.close(is); + } + } + + throw new HBCI_Exception("unable to load passport data"); + } + + /** + * @see org.kapott.hbci.passport.storage.format.PassportFormat#save(org.kapott.hbci.passport.HBCIPassport, org.kapott.hbci.passport.storage.PassportData) + */ + @Override + public byte[] save(HBCIPassport passport, PassportData data) throws UnsupportedOperationException + { + final long started = System.currentTimeMillis(); + + ObjectOutputStream os = null; + + try + { + final Cipher cipher = this.getCipher(); + + // Neues Salt generieren + final byte[] salt = new byte[SALT_SIZE]; + RAND.nextBytes(salt); + + // Secret Key erzeugen + final SecretKey key = this.getPassportKey(passport,salt,true); + cipher.init(Cipher.ENCRYPT_MODE, key); + final AlgorithmParameters params = cipher.getParameters(); + + // IV erzeugen + final byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV(); + + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + ////////////////////////////////////////////////////////////////// + // Datei-Header schreiben. Der ist immer Klartext, da er vor der Entschluesselung lesbar sein muss + // 1. Header mit dem Formatnamen + bos.write(FORMAT_NAME.getBytes(ENCODING)); + + // 2. Versiosnsnummer + bos.write(FORMAT_VERSION); + + // 3. Salt + bos.write(salt.length); + bos.write(salt); + + // 4. IV + bos.write(iv.length); + bos.write(iv); + // + ////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////// + // Eigentlichen Datei-Inhalt verschluesselt schreiben + os = new ObjectOutputStream(new CipherOutputStream(bos,cipher)); + os.writeObject(data); + // + ////////////////////////////////////////////////////////////////// + + os.close(); // Stellt sicher, dass die Verschluesselung abgeschlossen ist + + final byte[] result = bos.toByteArray(); + HBCIUtils.log("used time for encrypting passort into " + result.length + " bytes: " + (System.currentTimeMillis() - started) + " millis",HBCIUtils.LOG_DEBUG); + return result; + } + catch (UnsupportedOperationException uoe) + { + throw uoe; + } + catch (HBCI_Exception e) + { + throw e; + } + catch (Exception ex) + { + throw new HBCI_Exception("unable to load passport data",ex); + } + finally + { + IOUtils.close(os); + } + } + + /** + * @see org.kapott.hbci.passport.storage.format.AbstractFormat#getCipherAlg() + */ + @Override + protected String getCipherAlg() + { + return CIPHER_ALG; + } + + /** + * @see org.kapott.hbci.passport.storage.format.AbstractFormat#supported() + */ + @Override + public boolean supported() + { + try + { + final Cipher cipher = this.getCipher(); + + // Salt generieren + final byte[] salt = new byte[SALT_SIZE]; + RAND.nextBytes(salt); + + // Zufaelliges Passwort generieren - das Passwort selbst ist egal, wir wollen + // lediglich testen, ob sich der Cipher initialisieren laesst + final byte[] b = new byte[10]; + RAND.nextBytes(b); + final char[] pw = new String(b,StandardCharsets.UTF_8).toCharArray(); + + // Secret Key erzeugen + final SecretKey key = this.getPassportKey(pw,salt); + cipher.init(Cipher.ENCRYPT_MODE, key); + return super.supported(); + } + catch (Exception e) { + HBCIUtils.log("no support for passport format " + this.getClass().getSimpleName() + ": " + e.getMessage(),HBCIUtils.LOG_INFO); + } + return false; + } + + /** + * Fragt den User per Callback nach dem Passwort fuer die Passport-Datei. + * @param passport der Passport. + * @param salt das zu verwendende Salt. + * @param forSaving true, wenn das Passwort zum Speichern erfragt werden soll. + * @return der Secret-Key. + * @throws GeneralSecurityException wenn das Passwort nicht ermittelt werden konnte. + */ + private SecretKey getPassportKey(final HBCIPassport passport, final byte[] salt, final boolean forSaving) throws GeneralSecurityException + { + try + { + char[] pw = this.getPassword(passport,forSaving); + return this.getPassportKey(pw,salt); + } + catch (NoSuchAlgorithmException e) + { + HBCIUtils.log("AES-Format not supported in this Java version",HBCIUtils.LOG_DEBUG); + throw new UnsupportedOperationException("AES-Format not supported in this Java version"); + } + } + + /** + * Erzeugt den Secret-Key aus Passwort und Salt. + * @param password das zu verwendende Passwort. + * @param salt das zu verwendende Salt. + * @return der Secret-Key. + * @throws GeneralSecurityException wenn das Passwort nicht ermittelt werden konnte. + */ + private SecretKey getPassportKey(final char[] password, final byte[] salt) throws GeneralSecurityException + { + try + { + final String provider = this.getSecurityProvider(); + final SecretKeyFactory fac = provider != null ? SecretKeyFactory.getInstance(KEY_ALG_NAME,provider) : SecretKeyFactory.getInstance(KEY_ALG_NAME); + final KeySpec spec = new PBEKeySpec(password, salt, CIPHER_ITERATIONS, KEY_SIZE); + final SecretKey tmp = fac.generateSecret(spec); + final SecretKey secret = new SecretKeySpec(tmp.getEncoded(),KEY_ALG); + return secret; + } + catch (NoSuchAlgorithmException e) + { + HBCIUtils.log("AES-Format not supported in this Java version",HBCIUtils.LOG_DEBUG); + throw new UnsupportedOperationException("AES-Format not supported in this Java version"); + } + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/AbstractFormat.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/AbstractFormat.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/AbstractFormat.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/AbstractFormat.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,115 @@ +/********************************************************************** + * + * Copyright (c) by Olaf Willuhn + * All rights reserved + * + **********************************************************************/ + +package org.kapott.hbci.passport.storage.format; + +import java.security.GeneralSecurityException; + +import javax.crypto.Cipher; + +import org.kapott.hbci.callback.HBCICallback; +import org.kapott.hbci.exceptions.InvalidUserDataException; +import org.kapott.hbci.manager.HBCIUtils; +import org.kapott.hbci.manager.HBCIUtilsInternal; +import org.kapott.hbci.manager.LogFilter; +import org.kapott.hbci.passport.HBCIPassport; + +/** + * Abstrakte Basis-Klasse der Formate. + */ +public abstract class AbstractFormat implements PassportFormat +{ + private final static String CACHE_KEY = "__cached_passphrase__"; + + /** + * Liefert einen optionalen Security-Provier. + * @return der Security-Provider oder NULL, wenn keiner definiert ist. + */ + String getSecurityProvider() + { + return HBCIUtils.getParam("kernel.security.provider"); + } + + /** + * @see org.kapott.hbci.passport.storage.format.PassportFormat#supported() + */ + @Override + public boolean supported() + { + try + { + this.getCipher(); + return true; + } + catch (Exception e) { + HBCIUtils.log("no support for passport format " + this.getClass().getSimpleName() + ": " + e.getMessage(),HBCIUtils.LOG_INFO); + } + return false; + } + + /** + * Liefert den zu verwendenden Cipher. + * @return der zu verwendende Cipher. + * @throws GeneralSecurityException + */ + protected Cipher getCipher() throws GeneralSecurityException + { + final String provider = this.getSecurityProvider(); + final String alg = this.getCipherAlg(); + return provider != null ? Cipher.getInstance(alg,provider) : Cipher.getInstance(alg); + } + + /** + * Liefert den zu verwendenden Cipher-Algorithmus. + * @return der zu verwendende Cipher-Algorithmus. + */ + protected abstract String getCipherAlg(); + + /** + * Liefert die Anzahl der Versuche beim Entschluesseln. + * @return die Anzahl der Versuche beim Entschluesseln. + */ + int getRetries() + { + return Integer.parseInt(HBCIUtils.getParam("client.retries.passphrase","3")); + } + + /** + * Fragt den User per Callback nach dem Passwort fuer die Passport-Datei. + * @param passport der Passport. + * @param forSaving true, wenn das Passwort zum Speichern erfragt werden soll. + * @return das Passwort. + * @throws GeneralSecurityException wenn das Passwort nicht ermittelt werden konnte. + */ + protected char[] getPassword(final HBCIPassport passport, final boolean forSaving) throws GeneralSecurityException + { + // Wir cachen das Passwort in der Instanz des Passport. Dann haben wir das selbe Verhalten wir vor der Umstellung + // auf das neue PassportStorage - dort wurde der SecretKey als Member-Variable im Passport gehalten. + // Direkt den SecretKey koennen wir aber nicht zwischenspeichern, weil je nach Format unterschiedliche Algorithmen + // zum Einsatz kommen. Das Passwort ist aber der gemeinsame Nenner. + char[] pw = (char[]) passport.getClientData(CACHE_KEY); + if (pw != null && pw.length > 0) + return pw; + + StringBuffer passphrase = new StringBuffer(); + HBCIUtilsInternal.getCallback().callback(passport, + forSaving ? HBCICallback.NEED_PASSPHRASE_SAVE : HBCICallback.NEED_PASSPHRASE_LOAD, + forSaving ? HBCIUtilsInternal.getLocMsg("CALLB_NEED_PASS_NEW") : HBCIUtilsInternal.getLocMsg("CALLB_NEED_PASS"), + HBCICallback.TYPE_SECRET, + passphrase); + if (passphrase.length() == 0) + throw new InvalidUserDataException(HBCIUtilsInternal.getLocMsg("EXCMSG_PASSZERO")); + + String s = passphrase.toString(); + LogFilter.getInstance().addSecretData(s,"X",LogFilter.FILTER_SECRETS); + + pw = s.toCharArray(); + passport.setClientData(CACHE_KEY,pw); + return pw; + } + +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/LegacyFormat.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/LegacyFormat.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/LegacyFormat.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/LegacyFormat.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,202 @@ +/********************************************************************** + * + * Copyright (c) by Olaf Willuhn + * All rights reserved + * + **********************************************************************/ + +package org.kapott.hbci.passport.storage.format; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.util.LinkedList; +import java.util.List; +import java.util.ServiceLoader; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +import org.kapott.hbci.exceptions.HBCI_Exception; +import org.kapott.hbci.manager.HBCIUtils; +import org.kapott.hbci.passport.HBCIPassport; +import org.kapott.hbci.passport.storage.PassportData; +import org.kapott.hbci.passport.storage.format.legacy.Converter; +import org.kapott.hbci.tools.IOUtils; + +/** + * Liest Dateien im alten HBCI4Java-Format. + */ +public class LegacyFormat extends AbstractFormat +{ + private static List converters = null; + + private final static String CIPHER_ALG = "PBEWithMD5AndDES"; + private final static int CIPHER_ITERATIONS = 987; + + static + { + init(); + } + + /** + * @see org.kapott.hbci.passport.storage.format.PassportFormat#load(org.kapott.hbci.passport.HBCIPassport, byte[]) + */ + @Override + public PassportData load(final HBCIPassport passport, final byte[] data) throws UnsupportedOperationException + { + // Passenden Converter ermitteln + final Converter converter = this.getConverter(passport); + int retries = this.getRetries(); + + + for (int i=0;i<10;++i) // Mehr als 10 mal brauchen wir es nicht versuchen + { + InputStream is = null; + + try + { + final Cipher cipher = this.getCipher(); + + final SecretKey key = this.getPassportKey(passport, false); + final PBEParameterSpec paramspec = new PBEParameterSpec(converter.getSalt(), CIPHER_ITERATIONS); + + cipher.init(Cipher.DECRYPT_MODE, key, paramspec); + + is = new CipherInputStream(new ByteArrayInputStream(data),cipher); + return converter.load(is); + } + catch (UnsupportedOperationException uoe) + { + throw uoe; + } + catch (HBCI_Exception e) + { + if (retries-- <= 0) + throw e; + } + catch (Exception ex) + { + if (retries-- <= 0) + throw new HBCI_Exception("unable to load passport data",ex); + } + finally + { + IOUtils.close(is); + } + } + + throw new HBCI_Exception("unable to load passport data"); + } + + /** + * @see org.kapott.hbci.passport.storage.format.PassportFormat#save(org.kapott.hbci.passport.HBCIPassport, org.kapott.hbci.passport.storage.PassportData) + */ + @Override + public byte[] save(HBCIPassport passport, PassportData data) throws UnsupportedOperationException + { + // Passenden Converter ermitteln + final Converter converter = this.getConverter(passport); + + CipherOutputStream os = null; + + try + { + final Cipher cipher = this.getCipher(); + + final SecretKey key = this.getPassportKey(passport, false); + final PBEParameterSpec paramspec = new PBEParameterSpec(converter.getSalt(), CIPHER_ITERATIONS); + + cipher.init(Cipher.ENCRYPT_MODE, key, paramspec); + + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + os = new CipherOutputStream(bos,cipher); + converter.save(data,os); + os.close(); // Stellt sicher, dass die Verschluesselung abgeschlossen ist + + return bos.toByteArray(); + } + catch (HBCI_Exception e) + { + throw e; + } + catch (Exception ex) + { + throw new HBCI_Exception("unable to load passport data",ex); + } + finally + { + IOUtils.close(os); + } + } + + /** + * @see org.kapott.hbci.passport.storage.format.AbstractFormat#getCipherAlg() + */ + @Override + protected String getCipherAlg() + { + return CIPHER_ALG; + } + + /** + * Fragt den User per Callback nach dem Passwort fuer die Passport-Datei. + * @param passport der Passport. + * @param forSaving true, wenn das Passwort zum Speichern erfragt werden soll. + * @return der Secret-Key. + * @throws GeneralSecurityException wenn das Passwort nicht ermittelt werden konnte. + */ + private SecretKey getPassportKey(final HBCIPassport passport, final boolean forSaving) throws GeneralSecurityException + { + char[] pw = this.getPassword(passport,forSaving); + final String provider = this.getSecurityProvider(); + final SecretKeyFactory fac = provider != null ? SecretKeyFactory.getInstance(CIPHER_ALG,provider) : SecretKeyFactory.getInstance(CIPHER_ALG); + final PBEKeySpec keyspec = new PBEKeySpec(pw); + final SecretKey passportKey = fac.generateSecret(keyspec); + keyspec.clearPassword(); + + return passportKey; + } + + /** + * Liefert den passenden Converter. + * @param p der Passport. + * @return der Converter. + */ + private Converter getConverter(HBCIPassport p) + { + for (Converter c:converters) + { + if (c.supports(p)) + return c; + } + + throw new UnsupportedOperationException("found no matching converter for passport type " + p.getClass().getSimpleName()); + } + + /** + * Initialisiert die Liste der unterstuetzten Converter. + */ + private static void init() + { + if (converters != null) + return; + + converters = new LinkedList(); + HBCIUtils.log("searching supported converters",HBCIUtils.LOG_DEBUG); + final ServiceLoader loader = ServiceLoader.load(Converter.class); + for (Converter c:loader) + { + HBCIUtils.log(" " + c.getClass().getSimpleName(),HBCIUtils.LOG_DEBUG); + converters.add(c); + } + if (converters.size() == 0) + HBCIUtils.log("no supported legacy converters found",HBCIUtils.LOG_ERR); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/PassportFormat.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/PassportFormat.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/PassportFormat.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/PassportFormat.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,45 @@ +/********************************************************************** + * + * Copyright (c) by Olaf Willuhn + * All rights reserved + * + **********************************************************************/ + +package org.kapott.hbci.passport.storage.format; + +import org.kapott.hbci.passport.HBCIPassport; +import org.kapott.hbci.passport.storage.PassportData; + +/** + * Dieses Interface kapselt die verschiedenen Dateiformate von Passport-Dateien. + */ +public interface PassportFormat +{ + /** + * Liest die Passport-Datei. + * @param passport der Passport, fuer den die Daten gelesen werden sollen. + * @param data das Byte-Array mit dem Datei-Inhalt. Wir uebergeben hier keinen Stream, damit wir + * mehrere Formate mit den selben Daten durchprobieren koennen, ohne jedesmal den Stream neu oeffnen zu muessen + * (mark/reset unterstuetzen viele InputStream-Implementierungen nicht). + * Und da wir die Daten zur Deserialisierung ohnehin komplett lesen muessen, koennen wir sie auch gleich in + * ein Byte-Array lesen. + * @return die gelesenen Daten des Passport. + * @throws UnsupportedOperationException wenn die Implementierung dieses Dateiformat nicht unterstuetzt. + */ + public PassportData load(HBCIPassport passport, byte[] data) throws UnsupportedOperationException; + + /** + * Speichert die Passport-Daten- + * @param passport der Passport, fuer den die Daten gespeichert werden sollen. + * @param data die zu speichernden Daten. + * @return die gespeicherten Daten als Byte-Array. + * @throws UnsupportedOperationException + */ + public byte[] save(HBCIPassport passport, PassportData data) throws UnsupportedOperationException; + + /** + * Testet, ob das Format auf dem System unterstuetzt wird. + * @return true, wenn es unterstuetzt wird. + */ + public boolean supported(); +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/AbstractConverter.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/AbstractConverter.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/AbstractConverter.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/AbstractConverter.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,25 @@ +/********************************************************************** + * + * Copyright (c) by Olaf Willuhn + * All rights reserved + * + **********************************************************************/ + +package org.kapott.hbci.passport.storage.format.legacy; + +/** + * Basis-Klasse der Converter. + */ +public abstract class AbstractConverter implements Converter +{ + private final static byte[] CIPHER_SALT = {(byte)0x26,(byte)0x19,(byte)0x38,(byte)0xa7,(byte)0x99,(byte)0xbc,(byte)0xf1,(byte)0x55}; + + /** + * @see org.kapott.hbci.passport.storage.format.legacy.Converter#getSalt() + */ + @Override + public byte[] getSalt() + { + return CIPHER_SALT; + } +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/AbstractConverterXML.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/AbstractConverterXML.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/AbstractConverterXML.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/AbstractConverterXML.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,357 @@ +/********************************************************************** + * + * Copyright (c) by Olaf Willuhn + * All rights reserved + * + **********************************************************************/ + +package org.kapott.hbci.passport.storage.format.legacy; + +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.Key; +import java.security.KeyFactory; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.RSAPrivateCrtKeySpec; +import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.RSAPublicKeySpec; +import java.util.Enumeration; +import java.util.Properties; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.kapott.cryptalgs.RSAPrivateCrtKey2; +import org.kapott.hbci.manager.HBCIKey; +import org.kapott.hbci.manager.HBCIUtils; +import org.kapott.hbci.passport.storage.PassportData; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Abstrakte Basis-Implementierung des Converter fuer Passports, die intern XML-basiert speichern. + */ +public abstract class AbstractConverterXML extends AbstractConverter +{ + /** + * Parst die XML-Datei und liefert das Root-Element. + * @param is der InputStream. + * @return das Root-Element. + * @throws Exception + */ + protected Element read(InputStream is) throws Exception + { + final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setValidating(false); + + final DocumentBuilder db = dbf.newDocumentBuilder(); + return db.parse(is).getDocumentElement(); + } + + /** + * @see org.kapott.hbci.passport.storage.format.legacy.Converter#save(org.kapott.hbci.passport.storage.PassportData, java.io.OutputStream) + */ + @Override + public void save(PassportData data, OutputStream os) throws Exception + { + final DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance(); + fac.setValidating(false); + final DocumentBuilder db = fac.newDocumentBuilder(); + + final Document doc = db.newDocument(); + final Element root = doc.createElement("HBCIPassportRDHNew"); + + this.fill(doc,root,data); + + final TransformerFactory tfac = TransformerFactory.newInstance(); + final Transformer tform = tfac.newTransformer(); + + tform.setOutputProperty(OutputKeys.METHOD,"xml"); + tform.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,"no"); + tform.setOutputProperty(OutputKeys.ENCODING,"ISO-8859-1"); + tform.setOutputProperty(OutputKeys.INDENT,"yes"); + + tform.transform(new DOMSource(root),new StreamResult(os)); + os.flush(); + } + + /** + * Schreibt die Daten in die XML-Struktur. + * @param doc das Dokument. + * @param root das Root-Element. + * @param data die zu schreibenden Daten. + */ + protected abstract void fill(Document doc, Element root, PassportData data); + + /** + * Liefert einen einzelnen Wert. + * @param root das Element. + * @param name der Name des Elements. + * @return + */ + protected String getElementValue(Element root, String name) + { + NodeList list = root.getElementsByTagName(name); + if (list != null && list.getLength() != 0) + { + Node content = list.item(0).getFirstChild(); + if (content != null) + return content.getNodeValue(); + } + return null; + } + + /** + * Liefert die Werte aus dem XML-Teil als Properties. + * @param root Das Basis-Element. + * @param name der Name des Elements. + * @return die Properties. + */ + protected Properties getElementProps(Element root, String name) + { + Node base=root.getElementsByTagName(name).item(0); + if (base == null) + return null; + + Properties ret = new Properties(); + NodeList entries=base.getChildNodes(); + int len=entries.getLength(); + + for (int i=0;i) o.readObject(); + } + catch (Exception e) + { + HBCIUtils.log("no list of allowed secmechs found in passport file", HBCIUtils.LOG_WARN); + } + try + { + data.tanMethod = (String) o.readObject(); + } + catch (Exception e) + { + HBCIUtils.log("no current secmech found in passport file", HBCIUtils.LOG_WARN); + } + + return data; + } + + /** + * @see org.kapott.hbci.passport.storage.format.legacy.Converter#save(org.kapott.hbci.passport.storage.PassportData, java.io.OutputStream) + */ + @Override + public void save(PassportData data, OutputStream os) throws Exception + { + final ObjectOutputStream o = new ObjectOutputStream(os); + o.writeObject(data.country); + o.writeObject(data.blz); + o.writeObject(data.host); + o.writeObject(data.port); + o.writeObject(data.userId); + o.writeObject(data.sysId); + o.writeObject(data.bpd); + o.writeObject(data.upd); + o.writeObject(data.hbciVersion); + o.writeObject(data.customerId); + o.writeObject(data.filter); + o.writeObject(data.twostepMechs); + o.writeObject(data.tanMethod); + os.flush(); + } + + /** + * @see org.kapott.hbci.passport.storage.format.legacy.Converter#supports(org.kapott.hbci.passport.HBCIPassport) + */ + @Override + public boolean supports(HBCIPassport passport) + { + return passport != null && (passport instanceof HBCIPassportPinTan); + } + +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/ConverterRDHNew.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/ConverterRDHNew.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/ConverterRDHNew.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/ConverterRDHNew.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,109 @@ +/********************************************************************** + * + * Copyright (c) by Olaf Willuhn + * All rights reserved + * + **********************************************************************/ + +package org.kapott.hbci.passport.storage.format.legacy; + +import java.io.InputStream; + +import org.kapott.hbci.passport.HBCIPassport; +import org.kapott.hbci.passport.HBCIPassportRDHNew; +import org.kapott.hbci.passport.storage.PassportData; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Implementierung des Converter fuer RDHNew. + */ +public class ConverterRDHNew extends AbstractConverterXML +{ + + /** + * @see org.kapott.hbci.passport.storage.format.legacy.Converter#load(java.io.InputStream) + */ + @Override + public PassportData load(InputStream is) throws Exception + { + Element root = this.read(is); + + final PassportData data = new PassportData(); + data.blz = this.getElementValue(root,"blz"); + data.country = this.getElementValue(root,"country"); + data.host = this.getElementValue(root,"host"); + + String port = this.getElementValue(root,"port"); + if (port != null) + data.port = Integer.parseInt(port); + + data.userId = this.getElementValue(root,"userid"); + data.customerId = this.getElementValue(root,"customerid"); + data.sysId = this.getElementValue(root,"sysid"); + + String sigId = this.getElementValue(root,"sigid"); + if (sigId != null) + data.sigId = Long.parseLong(sigId); + + data.profileVersion = this.getElementValue(root,"rdhprofile"); + data.hbciVersion = this.getElementValue(root,"hbciversion"); + + data.bpd = this.getElementProps(root,"bpd"); + data.upd = this.getElementProps(root,"upd"); + + data.instSigKey = this.getElementKey(root,"inst","S","public"); + data.instEncKey = this.getElementKey(root,"inst","V","public"); + data.myPublicSigKey = this.getElementKey(root,"user","S","public"); + data.myPrivateSigKey = this.getElementKey(root,"user","S","private"); + data.myPublicEncKey = this.getElementKey(root,"user","V","public"); + data.myPrivateEncKey = this.getElementKey(root,"user","V","private"); + + return data; + } + + /** + * @see org.kapott.hbci.passport.storage.format.legacy.AbstractConverterXML#fill(org.w3c.dom.Document, org.w3c.dom.Element, org.kapott.hbci.passport.storage.PassportData) + */ + @Override + protected void fill(Document doc, Element root, PassportData data) + { + this.createElement(doc,root,"country",data.country); + this.createElement(doc,root,"blz",data.blz); + this.createElement(doc,root,"host",data.host); + + if (data.port != null) + this.createElement(doc,root,"port",Integer.toString(data.port)); + + this.createElement(doc,root,"userid",data.userId); + this.createElement(doc,root,"customerid",data.customerId); + this.createElement(doc,root,"sysid",data.sysId); + + if (data.sigId != null) + this.createElement(doc,root,"sigid",Long.toString(data.sigId)); + + this.createElement(doc,root,"rdhprofile",data.profileVersion); + this.createElement(doc,root,"hbciversion",data.hbciVersion); + + this.createPropsElement(doc,root,"bpd",data.bpd); + this.createPropsElement(doc,root,"upd",data.upd); + + this.createKeyElement(doc,root,"inst","S","public",data.instSigKey); + this.createKeyElement(doc,root,"inst","V","public",data.instEncKey); + this.createKeyElement(doc,root,"user","S","public",data.myPublicSigKey); + this.createKeyElement(doc,root,"user","S","private",data.myPrivateSigKey); + this.createKeyElement(doc,root,"user","V","public",data.myPublicEncKey); + this.createKeyElement(doc,root,"user","V","private",data.myPrivateEncKey); + } + + /** + * @see org.kapott.hbci.passport.storage.format.legacy.Converter#supports(org.kapott.hbci.passport.HBCIPassport) + */ + @Override + public boolean supports(HBCIPassport passport) + { + // Wir unterstuetzen nur den RSA-Passport. + return passport != null && (passport instanceof HBCIPassportRDHNew); + } + +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/ConverterRSA.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/ConverterRSA.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/ConverterRSA.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/passport/storage/format/legacy/ConverterRSA.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,69 @@ +/********************************************************************** + * + * Copyright (c) by Olaf Willuhn + * All rights reserved + * + **********************************************************************/ + +package org.kapott.hbci.passport.storage.format.legacy; + +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.util.Properties; + +import org.kapott.hbci.passport.HBCIPassport; +import org.kapott.hbci.passport.HBCIPassportRSA; +import org.kapott.hbci.passport.storage.PassportData; + +/** + * Implementierung des Converter fuer RSA. + * Von ConverterDDV abgeleitet, weil es den selben Salt verwendet. + */ +public class ConverterRSA extends ConverterDDV +{ + /** + * @see org.kapott.hbci.passport.storage.format.legacy.Converter#load(java.io.InputStream) + */ + @Override + public PassportData load(InputStream is) throws Exception + { + final PassportData data = new PassportData(); + + final ObjectInputStream o = new ObjectInputStream(is); + data.bpd = (Properties) o.readObject(); + data.upd = (Properties) o.readObject(); + data.hbciVersion = (String) o.readObject(); + data.sysId = (String) o.readObject(); + data.customerId = (String) o.readObject(); + + return data; + } + + /** + * @see org.kapott.hbci.passport.storage.format.legacy.Converter#save(org.kapott.hbci.passport.storage.PassportData, java.io.OutputStream) + */ + @Override + public void save(PassportData data, OutputStream os) throws Exception + { + final ObjectOutputStream o = new ObjectOutputStream(os); + o.writeObject(data.bpd); + o.writeObject(data.upd); + o.writeObject(data.hbciVersion); + o.writeObject(data.sysId); + o.writeObject(data.customerId); + os.flush(); + } + + /** + * @see org.kapott.hbci.passport.storage.format.legacy.Converter#supports(org.kapott.hbci.passport.HBCIPassport) + */ + @Override + public boolean supports(HBCIPassport passport) + { + // Wir unterstuetzen nur den RSA-Passport. + return passport != null && (passport instanceof HBCIPassportRSA); + } + +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/security/Sig.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/security/Sig.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/security/Sig.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/security/Sig.java 2019-11-27 09:04:22.000000000 +0000 @@ -52,7 +52,6 @@ public final static String SECFUNC_FINTS_SIG_DIG="1"; public final static String SECFUNC_FINTS_SIG_SIG="2"; - public final static String SECFUNC_SIG_PT_1STEP="999"; public final static String SECFUNC_SIG_PT_2STEP_MIN="900"; public final static String SECFUNC_SIG_PT_2STEP_MAX="997"; diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/sepa/PainVersion.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/sepa/PainVersion.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/sepa/PainVersion.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/sepa/PainVersion.java 2019-11-27 09:04:22.000000000 +0000 @@ -431,8 +431,8 @@ final PainVersion versionDesc = haveDesc ? PainVersion.byURN(sepadesc) : null; final PainVersion versionData = haveData ? PainVersion.autodetect(new ByteArrayInputStream(sepadata.getBytes(Comm.ENCODING))) : null; - HBCIUtils.log("pain version given in sepadescr: " + versionDesc,HBCIUtils.LOG_INFO); - HBCIUtils.log("pain version according to data: " + versionData,HBCIUtils.LOG_INFO); + HBCIUtils.log("pain version given in sepadescr: " + versionDesc,HBCIUtils.LOG_DEBUG); + HBCIUtils.log("pain version according to data: " + versionData,HBCIUtils.LOG_DEBUG); // Wir haben keine Version im Deskriptor, dann bleibt nur die aus den Daten if (versionDesc == null) diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/smartcardio/ChipTanCardService.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/smartcardio/ChipTanCardService.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/smartcardio/ChipTanCardService.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/smartcardio/ChipTanCardService.java 2019-11-27 09:04:22.000000000 +0000 @@ -1,10 +1,21 @@ /********************************************************************** * + * This file is part of HBCI4Java. * Copyright (c) 2018 Olaf Willuhn - * All rights reserved. - * - * This software is copyrighted work licensed under the terms of the - * Jameica License. Please consult the file "LICENSE" for details. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * **********************************************************************/ @@ -65,7 +76,7 @@ // Flickercode als Byte-Array rendern byte[] hhdBytes = this.toBytes(hhduc); - HBCIUtils.log("sending HHDuc to chipcard using ScardControl, length: " + hhdBytes.length,HBCIUtils.LOG_INFO); + HBCIUtils.log("sending HHDuc to chipcard using ScardControl, length: " + hhdBytes.length,HBCIUtils.LOG_DEBUG); int len = 4 + 1 + 1 + 2 + hhdBytes.length; // Laenge von Activation ID, POF, Control Byte, HHD-Laenge, Payload ByteArrayOutputStream cmd = new ByteArrayOutputStream(); @@ -94,13 +105,13 @@ if (data != null && data.length > 1) { int tanLength = data[0]; - HBCIUtils.log("received TAN",HBCIUtils.LOG_INFO); + HBCIUtils.log("received TAN",HBCIUtils.LOG_DEBUG); if (tanLength > 0) { byte[] tanBytes = new byte[tanLength]; System.arraycopy(data,1,tanBytes,0,tanLength); tan = this.parseTAN(tanBytes); - HBCIUtils.log("TAN length: " + tan.length() + " chars",HBCIUtils.LOG_INFO); + HBCIUtils.log("TAN length: " + tan.length() + " chars",HBCIUtils.LOG_DEBUG); } } } @@ -111,8 +122,9 @@ // SECODER FINALIZE TRANSACTION (SECODER 3G) // Transaktion finalisieren + try { - HBCIUtils.log("finalizing chipcard transaction",HBCIUtils.LOG_INFO); + HBCIUtils.log("finalizing chipcard transaction",HBCIUtils.LOG_DEBUG); ByteArrayOutputStream cmd = new ByteArrayOutputStream(); cmd.write(new byte[]{0x20,0x77,0x00,0x00}); // Header cmd.write(new byte[]{0x00,0x00,0x06}); // Li Lc Lc @@ -130,8 +142,14 @@ // lassen wir das durchgehen. this.check(apdu,new byte[]{(byte)0x90,(byte)0x91, (byte) 0x6D}); } - - HBCIUtils.log("returning TAN",HBCIUtils.LOG_INFO); + catch (Exception e) + { + // Den Fehler tolerieren wir + HBCIUtils.log("error while finalizing transaction, continue nevertheless since we already have a TAN - stacktrace for debugging purpose",HBCIUtils.LOG_DEBUG); + HBCIUtils.log(e,HBCIUtils.LOG_DEBUG); + } + + HBCIUtils.log("returning TAN",HBCIUtils.LOG_DEBUG); return tan; // @@ -156,7 +174,7 @@ // Flickercode als Byte-Array rendern byte[] hhdBytes = this.toBytes(hhduc); - HBCIUtils.log("sending HHDuc to chipcard using ScardTransmit, length: " + hhdBytes.length,HBCIUtils.LOG_INFO); + HBCIUtils.log("sending HHDuc to chipcard using ScardTransmit, length: " + hhdBytes.length,HBCIUtils.LOG_DEBUG); int len = 4 + 1 + 1 + 2 + hhdBytes.length; // Laenge von Activation ID, POF, Control Byte, HHD-Laenge, Payload ByteArrayOutputStream cmd = new ByteArrayOutputStream(); @@ -180,13 +198,13 @@ if (data != null && data.length > 1) { int tanLength = data[0]; - HBCIUtils.log("received TAN",HBCIUtils.LOG_INFO); + HBCIUtils.log("received TAN",HBCIUtils.LOG_DEBUG); if (tanLength > 0) { byte[] tanBytes = new byte[tanLength]; System.arraycopy(data,1,tanBytes,0,tanLength); tan = this.parseTAN(tanBytes); - HBCIUtils.log("TAN length: " + tan.length() + " chars",HBCIUtils.LOG_INFO); + HBCIUtils.log("TAN length: " + tan.length() + " chars",HBCIUtils.LOG_DEBUG); } } } @@ -197,8 +215,9 @@ // SECODER FINALIZE TRANSACTION (SECODER 3G) // Transaktion finalisieren + try { - HBCIUtils.log("finalizing chipcard transaction",HBCIUtils.LOG_INFO); + HBCIUtils.log("finalizing chipcard transaction",HBCIUtils.LOG_DEBUG); ByteArrayOutputStream cmd = new ByteArrayOutputStream(); cmd.write(new byte[]{(byte)0xFF,(byte)0x91,0x07,0x00}); // Header cmd.write(new byte[]{0x00,0x00,0x06}); // Li Lc Lc @@ -214,10 +233,16 @@ // ja bereits. Wenn das finalize danach nicht unterstuetzt wird, // lassen wir das durchgehen. CommandAPDU apdu = new CommandAPDU(request); - this.receive(apdu,new byte[]{(byte)0x90,(byte)0x91,(byte)0x6D}); + this.receive(apdu,new byte[]{(byte)0x90,(byte)0x91,(byte)0x6D,(byte)0x01}); + } + catch (Exception e) + { + // Den Fehler tolerieren wir + HBCIUtils.log("error while finalizing transaction, continue nevertheless since we already have a TAN - stacktrace for debugging purpose",HBCIUtils.LOG_DEBUG); + HBCIUtils.log(e,HBCIUtils.LOG_DEBUG); } - HBCIUtils.log("returning TAN",HBCIUtils.LOG_INFO); + HBCIUtils.log("returning TAN",HBCIUtils.LOG_DEBUG); return tan; // diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/smartcardio/HBCICardService.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/smartcardio/HBCICardService.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/smartcardio/HBCICardService.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/smartcardio/HBCICardService.java 2019-11-27 09:04:22.000000000 +0000 @@ -1,10 +1,21 @@ /********************************************************************** * + * This file is part of HBCI4Java. * Copyright (c) 2018 Olaf Willuhn - * All rights reserved. - * - * This software is copyrighted work licensed under the terms of the - * Jameica License. Please consult the file "LICENSE" for details. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * **********************************************************************/ diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/smartcardio/SmartCardService.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/smartcardio/SmartCardService.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/smartcardio/SmartCardService.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/smartcardio/SmartCardService.java 2019-11-27 09:04:22.000000000 +0000 @@ -199,7 +199,7 @@ try { - HBCIUtils.log("creating smartcard-service, using type " + type.getSimpleName(), HBCIUtils.LOG_INFO); + HBCIUtils.log("Starte Smartcard-Service (Typ " + type.getSimpleName() + ")", HBCIUtils.LOG_INFO); TerminalFactory terminalFactory = TerminalFactory.getDefault(); CardTerminals terminals = terminalFactory.terminals(); @@ -210,7 +210,7 @@ if (list == null || list.size() == 0) throw new HBCI_Exception("Kein Kartenleser gefunden"); - HBCIUtils.log("found card terminals:",HBCIUtils.LOG_INFO); + HBCIUtils.log("Gefundene Kartenlesegeräte:",HBCIUtils.LOG_INFO); for (CardTerminal t : list) { HBCIUtils.log(" " + t.getName(), HBCIUtils.LOG_INFO); @@ -221,7 +221,7 @@ // Checken, ob der User einen konkreten Kartenleser vorgegeben hat if (name != null) { - HBCIUtils.log("explicit terminal name given, trying to open terminal: " + name,HBCIUtils.LOG_DEBUG); + HBCIUtils.log("terminal name given, trying to open terminal: " + name,HBCIUtils.LOG_DEBUG); terminal = terminals.getTerminal(name); if (terminal == null) throw new HBCI_Exception("Kartenleser \"" + name + "\" nicht gefunden"); @@ -248,7 +248,7 @@ throw new HBCI_Exception("Keine Karte angegeben"); String proto = smartCard.getProtocol(); - HBCIUtils.log("card type: " + proto,HBCIUtils.LOG_INFO); + HBCIUtils.log("Kartetyp: " + proto,HBCIUtils.LOG_INFO); // Card-Service basierend auf dem Kartentyp erzeugen if (proto == null || proto.indexOf("=") == -1) @@ -269,7 +269,7 @@ } SmartCardService cardService = type.newInstance(); - HBCIUtils.log(" using: " + cardService.getClass().getName(),HBCIUtils.LOG_INFO); + HBCIUtils.log(" using: " + cardService.getClass().getName(),HBCIUtils.LOG_DEBUG); cardService.init(smartCard); return (T) cardService; } @@ -326,7 +326,7 @@ // Liste der Features abrufen try { - HBCIUtils.log("querying features",HBCIUtils.LOG_INFO); + HBCIUtils.log("querying features",HBCIUtils.LOG_DEBUG); byte[] response = this.smartCard.transmitControlCommand(IOCTL_GET_FEATURE_REQUEST, new byte[0]); for (int i = 0; i < response.length; i += 6) { @@ -338,11 +338,11 @@ Feature feature = Feature.find(response[i]); if (feature == null) { - HBCIUtils.log(" unknown feature: " + Integer.toHexString(ioctl.intValue()),HBCIUtils.LOG_INFO); + HBCIUtils.log(" unknown feature: " + Integer.toHexString(ioctl.intValue()),HBCIUtils.LOG_DEBUG); continue; } - HBCIUtils.log(" " + feature.name() + ": " + Integer.toHexString(ioctl.intValue()),HBCIUtils.LOG_INFO); + HBCIUtils.log(" " + feature.name() + ": " + Integer.toHexString(ioctl.intValue()),HBCIUtils.LOG_DEBUG); features.put(feature, ioctl); } } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/status/HBCIMsgStatus.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/status/HBCIMsgStatus.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/status/HBCIMsgStatus.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/status/HBCIMsgStatus.java 2019-11-27 09:04:22.000000000 +0000 @@ -27,6 +27,7 @@ import java.util.List; import java.util.Properties; +import org.kapott.hbci.dialog.KnownReturncode; import org.kapott.hbci.manager.HBCIUtilsInternal; /**

Enthält alle Status-Informationen zu genau einem Nachrichtenaustausch. @@ -207,22 +208,25 @@ * @return true oder false */ public boolean isInvalidPIN() { - boolean result=false; - + return this.getInvalidPINCode() != null; + } + + /** + * Liefert den Status-Code fuer "PIN falsch", insofern er im Response enthalten ist. + * @return der Status-Code fuer "PIN falsch", insofern er im Response enthalten ist. + */ + public HBCIRetVal getInvalidPINCode() + { List retvals=new ArrayList(Arrays.asList(globStatus.getErrors())); retvals.addAll(new ArrayList(Arrays.asList(segStatus.getErrors()))); for (Iterator i=retvals.iterator(); i.hasNext();) { HBCIRetVal ret=i.next(); - if (ret.code.equals("9942") || // PIN falsch (konkret) - ret.code.equals("9340")) // Signatur falsch (generisch) - { - result=true; - break; - } + if (KnownReturncode.contains(ret.code,KnownReturncode.LIST_AUTH_FAIL)) + return ret; } - return result; + return null; } } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/status/HBCIStatus.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/status/HBCIStatus.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/status/HBCIStatus.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/status/HBCIStatus.java 2019-11-27 09:04:22.000000000 +0000 @@ -69,7 +69,7 @@ { retVals.add(ret); if (ret.isError()) { - HBCIUtils.log("HBCI error code: "+ret.toString(), HBCIUtils.LOG_ERR); + HBCIUtils.log("Meldung der Bank: " + ret.toString(), HBCIUtils.LOG_ERR); } } diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/swift/Swift.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/swift/Swift.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/swift/Swift.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/swift/Swift.java 2019-11-27 09:04:22.000000000 +0000 @@ -28,6 +28,8 @@ public class Swift { + private final static Pattern PATTERN_NL_TAG = Pattern.compile("\\r\\n(-|-\\r\\n)?:\\d{2}[A-Z]?:"); // Zu dem "(-)?" siehe TestBrokenMT940.java + /* With this, a block always ends with \r\n- */ public static String getOneBlock(StringBuffer stream) { @@ -47,7 +49,6 @@ public static String getTagValue(String st,String tag,int counter) { String ret=null; - Pattern patternNLTag=Pattern.compile("\\r\\n(-|-\\r\\n)?:\\d{2}[A-Z]?:"); // Zu dem "(-)?" siehe TestBrokenMT940.java int endpos=0; while (true) { @@ -55,12 +56,19 @@ // find start-posi of the requested tag int startpos=st.indexOf("\r\n:"+tag+":",endpos); + int skipLength = 3; + + if (startpos == -1) { + startpos = st.indexOf("\r\n-:" + tag + ":", endpos); + skipLength = 4; + } + if (startpos!=-1) { // skip \r\n:XY: of start tag - startpos+=3+tag.length()+1; + startpos += skipLength + tag.length() + 1; // tag found - find start of next tag - Matcher matcher=patternNLTag.matcher(st); + Matcher matcher=PATTERN_NL_TAG.matcher(st); if (matcher.find(startpos)) { endpos=matcher.start(); diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/tools/DigestUtils.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/tools/DigestUtils.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/tools/DigestUtils.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/tools/DigestUtils.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,106 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.tools; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.util.Arrays; +import java.util.List; + +import org.kapott.cryptalgs.CryptAlgs4JavaProvider; +import org.kapott.hbci.comm.Comm; +import org.kapott.hbci.exceptions.HBCI_Exception; +import org.kapott.hbci.manager.HBCIUtils; + +/** + * Hilfsklasse zum Erzeugen von Hashes. + */ +public class DigestUtils +{ + /** + * Hash-Algorithmus RIPEMD160. + */ + public final static String ALG_RIPE_MD160 = "RIPEMD160"; + + /** + * Hash-Algorithmus SHA1. + */ + public final static String ALG_SHA1 = "SHA-1"; + + /** + * Die Liste der Algorithmen, bei denen der eigene Provider verwendet werden soll. + */ + private final static List OWN_PROVIDER = Arrays.asList(ALG_RIPE_MD160); + + /** + * Hasht die Daten. + * @param data die zu hashenden Daten. + * @param alg der zu verwendende Algorithmus. + * @return der Hash. + * @throws HBCI_Exception + */ + public final static String hash(String data, String alg) throws HBCI_Exception + { + try + { + byte[] hash = hash(data.getBytes(Comm.ENCODING),alg); + return new String(hash,Comm.ENCODING); + } + catch (HBCI_Exception e) + { + throw e; + } + catch (UnsupportedEncodingException e2) + { + throw new HBCI_Exception(e2); + } + } + + /** + * Hasht die Daten. + * @param data die zu hashenden Daten. + * @param alg der zu verwendende Algorithmus. + * @return der Hash. + * @throws HBCI_Exception + */ + public final static byte[] hash(byte[] data, String alg) throws HBCI_Exception + { + try + { + final String provider = OWN_PROVIDER.contains(alg) ? CryptAlgs4JavaProvider.NAME : null; + HBCIUtils.log("using " + alg + "/" + provider + " for generating hash of " + data.length + " bytes", HBCIUtils.LOG_DEBUG); + MessageDigest digest = MessageDigest.getInstance(alg,provider); + digest.update(data); + return digest.digest(); + } + catch (HBCI_Exception he) + { + throw he; + } + catch (Exception e) + { + throw new HBCI_Exception(e); + } + } +} + + diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/tools/HBCIBatch.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/tools/HBCIBatch.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/tools/HBCIBatch.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/tools/HBCIBatch.java 2019-11-27 09:04:22.000000000 +0000 @@ -175,6 +175,10 @@ retData.replace(0,retData.length(),answers.getProperty("secmech")); break; + case NEED_PT_TANMEDIA: + retData.replace(0,retData.length(),answers.getProperty("tanmedia")); + break; + case NEED_PT_PIN: retData.replace(0,retData.length(),answers.getProperty("pin")); break; diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/tools/IOUtils.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/tools/IOUtils.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/tools/IOUtils.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/tools/IOUtils.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,207 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.tools; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.kapott.hbci.exceptions.HBCI_Exception; +import org.kapott.hbci.manager.HBCIUtils; + +/** + * Hilfsfunktionen fuer IO-Operationen. + */ +public class IOUtils +{ + /** + * Ersetzt die Datei origFile gegen tmpFile. + * Nach dem Loeschen der Datei origFile wartet die Methode jedoch maximal 20 Sekunden, + * um sicherzustellen, dass z.Bsp. Virenscanner die Datei wieder losgelassen haben und + * sie wirklich verschwunden ist, bevor tmpFile auf den Namen von origFile umbenannt wird. + * Wichtig ist, dass zum Zeitpunkt des Aufrufes dieser Methode alle Streams auf die + * Dateien bereits geschlossen wurden. Die Schreibvorgaenge auf die Dateien muessen also + * abgeschlossen sein. Heisst: "os.close()" nicht erst im finally-Block machen sondern + * VOR dem Aufruf dieser Methode. + * @param origFile die originale zu ersetzende Datei. + * @param tmpFile die neue Datei, welche die originale ersetzen soll. + */ + public static void safeReplace(File origFile, File tmpFile) + { + HBCIUtils.log("saving file " + origFile, HBCIUtils.LOG_DEBUG); + + if (origFile.exists()) // Nur loeschen, wenn es ueberhaupt existiert + { + HBCIUtils.log("deleting old file " + origFile, HBCIUtils.LOG_DEBUG); + if (!origFile.delete()) + HBCIUtils.log("file " + origFile + " not yet deleted, waiting...", HBCIUtils.LOG_WARN); + } + + // Wenn die Datei noch existiert, warten wir noch etwas + int retry = 0; + while (origFile.exists() && retry++ < 20) + { + try + { + HBCIUtils.log("wait a little bit, maybe another thread (antivirus scanner) holds a lock, file still exists", HBCIUtils.LOG_WARN); + Thread.sleep(1000L); + } + catch (InterruptedException e) + { + HBCIUtils.log("interrupted", HBCIUtils.LOG_WARN); + break; + } + if (!origFile.exists()) + { + HBCIUtils.log("file now gone: " + origFile, HBCIUtils.LOG_DEBUG); + break; + } + } + + // Datei existiert immer noch, dann brauchen wir das Rename gar nicht erst versuchen + if (origFile.exists()) + throw new HBCI_Exception("could not delete " + origFile); + + // Das Rename versuchen wir jetzt auch wiederholt mehrfach + retry = 0; + HBCIUtils.log("renaming " + tmpFile.getName() + " to " + origFile.getName(), HBCIUtils.LOG_DEBUG); + while (!origFile.exists() && retry++ < 20) + { + if (!tmpFile.renameTo(origFile)) + HBCIUtils.log("file " + tmpFile + " not yet renamed to " + origFile + ", waiting ", HBCIUtils.LOG_WARN); + + if (origFile.exists()) + { + HBCIUtils.log("new file now exists: " + origFile, HBCIUtils.LOG_DEBUG); + break; + } + + try + { + HBCIUtils.log("wait a little bit, maybe another thread (antivirus scanner) holds a lock, file still not renamed", HBCIUtils.LOG_WARN); + Thread.sleep(1000L); + } + catch (InterruptedException e) + { + HBCIUtils.log("interrupted", HBCIUtils.LOG_WARN); + break; + } + } + + if (!origFile.exists()) + throw new HBCI_Exception("could not rename " + tmpFile.getName() + " to " + origFile.getName()); + } + + /** + * Kopiert die Daten aus dem InputStream in den OutputStream. + * @param is der InputStream. + * Der InputStream wird nicht geschlossen. Das ist Aufgabe des Aufrufers. + * @param os der OutputStream. + * @return die Anzahl geschriebener Bytes. + * @throws IOException + */ + public static long copy(InputStream is, OutputStream os) throws IOException + { + byte[] buf = new byte[4096]; + long size = 0; + int read; + while ((read = is.read(buf)) != -1) + { + os.write(buf, 0, read); + size += read; + } + return size; + } + + /** + * Liest die Datei komplett in das Byte-Array. + * @param is der InputStream. + * Der InputStream wird nicht geschlossen. Das ist Aufgabe des Aufrufers. + * @return das Byte-Array. + * @throws IOException + */ + public static byte[] read(InputStream is) throws IOException + { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + copy(is,bos); + return bos.toByteArray(); + } + + /** + * Schliesst das Closeable, ohne eine Exception zu werfen. + * Auch dann nicht, wenn c NULL ist. + * @param c das Closeable. + */ + public static void close(Closeable c) + { + if (c == null) + return; + + try + { + c.close(); + } + catch (Exception e) + { + HBCIUtils.log(e, HBCIUtils.LOG_DEBUG); + } + } + + /** + * Prueft, ob ein Dateipfad ungueltige Zeichen enthaelt oder zu lang ist und kuerzt ihn automatisch. + * Siehe https://homebanking-hilfe.de/forum/topic.php?p=138325#real138325 + * Die Funktion laesst in Dateinamen ausschliesslich Buchstaben, Zahlen, Unterstrich, Bindestrich und Punkt zu. + * Wenn der Dateiname laenger als 25 Zeichen ist, wuerde er auf 25 Zeichen abgeschnitten. + * @param filename der zu pruefende Dateiname inclusive Pfad. + * @return der ggf korrigierte Dateiname. + */ + public static String safeFilename(String filename) + { + if (filename == null || filename.length() == 0) + return filename; + + File f = new File(filename).getAbsoluteFile(); + String name = f.getName(); + + // Zeichen, die nicht enthalten sein sollten, entfernen wir. + name = name.replaceAll("[^a-zA-Z0-9_\\.-]",""); + + // Wenn die Datei laenger als 25 Zeichen ist, schneiden wir die ueberzaehligen ab + if (name.length() > 25) + name = name.substring(0,25); + + f = new File(f.getParentFile(),name); + String newName = f.getAbsolutePath(); + + // Dateiname ist unveraendert. Dann wurden keine Korrekturen vorgenommen. + if (newName.equals(filename)) + return filename; + + HBCIUtils.log("auto-fixed filename from " + f + " to " + newName,HBCIUtils.LOG_DEBUG); + return newName; + } +} + + diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/tools/NumberUtil.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/tools/NumberUtil.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/tools/NumberUtil.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/tools/NumberUtil.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,57 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.tools; + +/** + * Hilfsmethoden fuer das Handling mit Zahlen. + */ +public class NumberUtil +{ + /** + * Parst den Text als Zahl. + * Die Funktion wirft keine Exception. Weder bei NULL noch bei einer nicht zu parsenden Zahl. In dem Fall wird der Default-Wert geliefert. + * @param s der zu parsende String. + * @param defaultValue der Default-Wert. + * @return der geparste Wert oder der Default-Wert. + */ + public static int parseInt(String s, int defaultValue) + { + if (s == null) + return defaultValue; + + s = s.trim(); + if (s.length() == 0) + return defaultValue; + + try + { + return Integer.parseInt(s); + } + catch (Exception e) + { + } + return defaultValue; + } + +} + + diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/tools/ParameterFinder.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/tools/ParameterFinder.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/tools/ParameterFinder.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/tools/ParameterFinder.java 2019-11-27 09:04:22.000000000 +0000 @@ -1,111 +1,260 @@ /********************************************************************** - * $Source: /cvsroot/hibiscus/hbci4java/src/org/kapott/hbci/tools/ParameterFinder.java,v $ - * $Revision: 1.1 $ - * $Date: 2011/05/13 15:22:08 $ - * $Author: willuhn $ * - * Copyright (c) by willuhn - software & services - * All rights reserved + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * **********************************************************************/ package org.kapott.hbci.tools; +import java.text.MessageFormat; import java.util.Enumeration; import java.util.Properties; +import org.kapott.hbci.exceptions.HBCI_Exception; +import org.kapott.hbci.manager.HBCIUtils; + /** * Parser zum bequemen Zugriff auf BPD/UPD-Parameter. */ public class ParameterFinder { - /** - * Sucht in props nach allen Schluesseln im genannten Pfad und liefert sie zurueck. - * @param props die Properties, in denen gesucht werden soll. - * @param path der Pfad. - * Es koennen Wildcards verwendet werden. Etwa so: - * Params_*.TAN2StepPar*.ParTAN2Step*.TAN2StepParams*.*secfunc") - * @return Liefert die gefundenen Properties. Als Schluessel - * wird jeweils nicht der gesamte Pfad verwendet sondern nur der Teil hinter - * dem letzten Punkt. - */ - public static Properties find(Properties props, String path) - { - // Kein Pfad angegeben. Also treffen alle. - if (path == null || path.length() == 0) - return props; + /** + * Liste bekannter Queries. + */ + public static class Query + { + /** + * Informationen zur Verfuegbarkeit von Einschritt-Verfahren in den BPD. + */ + public final static Query BPD_PINTAN_CAN1STEP = new Query("Params_*.TAN2StepPar*.ParTAN2Step*.can1step",false); - // Die neue Map fuer die naechste Runde - Properties next = new Properties(); + /** + * Information ueber den Order-Hashmode. Ist ein Query, welches einen Parameter benoetigt. + */ + public final static Query BPD_PINTAN_ORDERHASHMODE = new Query("Params_*.TAN2StepPar{0}.ParTAN2Step*.orderhashmode",true); - String[] keys = path.split("\\."); - String key = keys[0]; - - boolean endsWith = key.startsWith("*"); - boolean startsWith = key.endsWith("*"); - key = key.replace("*",""); - - Enumeration e = props.keys(); - while (e.hasMoreElements()) + private String query = null; + private boolean paramsSet = false; + + /** + * ct. + * @param q das Query. + * @param needParams true, wenn das Query Parameter benoetigt. + */ + private Query(String q, boolean needParams) + { + this.query = q; + this.paramsSet = !needParams; + } + + /** + * Liefert eine neue Instanz des Querys mit gesetzten Parametern. + * @param parameters die Parameter. + * @return das neue Query. + */ + public final Query withParameters(Object... parameters) + { + return new Query(MessageFormat.format(this.query,parameters),false); + } + + /** + * Liefert das Query. + * @return das Query. + */ + public String getQuery() + { + if (!this.paramsSet) + throw new HBCI_Exception("Parameters not set in query: " + this.query); + + return this.query; + } + } + + /** + * Sucht in props nach allen Schluesseln im genannten Pfad und liefert sie zurueck. + * @param props die Properties, in denen gesucht werden soll. + * @param query das Query. + * @return Liefert die gefundenen Properties. Niemals NULL sondern hoechstens leere Properties. + * Als Schluessel wird jeweils nicht der gesamte Pfad verwendet sondern nur der Teil hinter dem letzten Punkt. + */ + public static Properties find(Properties props, Query query) + { + return find(props,query != null ? query.getQuery() : null); + } + + /** + * Sucht in props nach allen Schluesseln im genannten Pfad und liefert sie zurueck. + * @param props die Properties, in denen gesucht werden soll. + * @param path der Pfad. Es koennen Wildcards verwendet werden. + * Etwa so: Params_*.TAN2StepPar*.ParTAN2Step*.TAN2StepParams*.*secfunc") + * @return Liefert die gefundenen Properties. Niemals NULL sondern hoechstens leere Properties. + * Als Schluessel wird jeweils nicht der gesamte Pfad verwendet sondern nur der Teil hinter dem letzten Punkt. + */ + public static Properties find(Properties props, String path) { - String name = (String) e.nextElement(); - - String[] names = name.split("\\."); - - if (startsWith && !endsWith && !names[0].startsWith(key)) // Beginnt mit? - continue; - else if (!startsWith && endsWith && !names[0].endsWith(key)) // Endet mit? - continue; - else if (startsWith && endsWith && !names[0].contains(key)) // Enthaelt? - continue; - else if (!startsWith && !endsWith && !names[0].equals(key)) // Ist gleich? - continue; - - // Wenn wir einen Wert haben, uebernehmen wir ihn in die naechste Runde. - // Wir schneiden den geprueften Teil ab - String newName = name.substring(name.indexOf(".")+1); - next.put(newName,props.getProperty(name)); - } - - // Wir sind hinten angekommen - if (!path.contains(".")) - return next; + // Kein Pfad angegeben. Also treffen alle. + if (path == null || path.length() == 0) + return props != null ? props : new Properties(); + + // Die neue Map fuer die naechste Runde + Properties next = new Properties(); + + String[] keys = path.split("\\."); + String key = keys[0]; + + boolean endsWith = key.startsWith("*"); + boolean startsWith = key.endsWith("*"); + key = key.replace("*", ""); + + Enumeration e = props.keys(); + while (e.hasMoreElements()) + { + String name = (String) e.nextElement(); + + String[] names = name.split("\\."); + + if (startsWith && !endsWith && !names[0].startsWith(key)) // Beginnt mit? + continue; + else if (!startsWith && endsWith && !names[0].endsWith(key)) // Endet mit? + continue; + else if (startsWith && endsWith && !names[0].contains(key)) // Enthaelt? + continue; + else if (!startsWith && !endsWith && !names[0].equals(key)) // Ist gleich? + continue; + + // Wenn wir einen Wert haben, uebernehmen wir ihn in die naechste Runde. + // Wir schneiden den geprueften Teil ab + String newName = name.substring(name.indexOf(".") + 1); + next.put(newName, props.getProperty(name)); + } + + // Wir sind hinten angekommen + if (!path.contains(".")) + return next; + + // naechste Runde + return find(next, path.substring(path.indexOf(".") + 1)); + } + + /** + * Sucht in props nach allen Schluesseln im genannten Pfad und liefert sie zurueck. + * Als Schluessel bleibt hierbei jedoch der gesamte Pfad erhalten. Das ist sinnvoll, + * wenn man ueber grosse Bereiche sucht und die Namen des letzen Elements im Baum gleich lauten koennen. + * @param props die Properties, in denen gesucht werden soll. + * @param query das Query. + * @return Liefert die gefundenen Properties. Niemals NULL sondern hoechstens leere Properties. + */ + public static Properties findAll(Properties props, Query query) + { + return findAll(props,query != null ? query.getQuery() : null); + } - // naechste Runde - return find(next,path.substring(path.indexOf(".")+1)); - } - - /** - * Test. - * @param args - */ - public static void main(String[] args) - { - Properties props = new Properties(); - props.put("Params_1.TAN2StepParams3.ParTAN2Step4.TAN2StepParams2.secfunc","Test 1"); - props.put("Params_2.TAN2StepParams3.ParTAN2Step4.TAN2StepParams2.1secfunc","Test 2"); + /** + * Sucht in props nach allen Schluesseln im genannten Pfad und liefert sie zurueck. + * Als Schluessel bleibt hierbei jedoch der gesamte Pfad erhalten. Das ist sinnvoll, + * wenn man ueber grosse Bereiche sucht und die Namen des letzen Elements im Baum gleich lauten koennen. + * @param props die Properties, in denen gesucht werden soll. + * @param path der Pfad. Es koennen Wildcards verwendet werden. + * Etwa so: Params_*.TAN2StepPar*.ParTAN2Step*.TAN2StepParams*.*secfunc") + * @return Liefert die gefundenen Properties. Niemals NULL sondern hoechstens leere Properties. + */ + public static Properties findAll(Properties props, String path) + { + // Kein Pfad angegeben. Also treffen alle. + if (path == null || path.length() == 0) + return props != null ? props : new Properties(); + + // Die Restmenge, nachdem wir durch sind + Properties rest = new Properties(); + rest.putAll(props); + + for (int i = 0; i < 100; ++i) + { + String[] keys = path.split("\\."); + if (keys.length < i + 1) + break; // Ende + + String key = keys[i]; + + boolean endsWith = key.startsWith("*"); + boolean startsWith = key.endsWith("*"); + key = key.replace("*", ""); + + Enumeration e = props.keys(); + while (e.hasMoreElements()) + { + String name = (String) e.nextElement(); + + String[] names = name.split("\\."); + if (names.length < i + 1) + { + rest.remove(name); + continue; + } + + boolean b1 = (startsWith && !endsWith && names[i].startsWith(key)); // Beginnt mit? + boolean b2 = (!startsWith && endsWith && names[i].endsWith(key)); // Endet mit? + boolean b3 = (startsWith && endsWith && names[i].contains(key)); // Enthaelt? + boolean b4 = (!startsWith && !endsWith && names[i].equals(key)); // Ist gleich? + + if (!b1 && !b2 && !b3 && !b4) + { + rest.remove(name); + } + } + } + + return rest; + } - props.put("Params_1.PIN2StepParams3.ParTAN2Step4.TAN2StepParams2.2secfunc","Test 3"); - props.put("Params_1.TANStepParams3.ParTAN2Step4.TAN2StepParams2.3secfunc","Test 4"); - props.put("Params_1.TAN2StepParams3.ParTAN2Step4.TAN2StepParams2.Foo","Test 5"); - props.put("Params_2.TAN2StepPar.ParTAN2Step.TAN2StepParams.5secfunc","Test 5"); + /** + * Liefert einen einzelnen Wert. + * Die Funktion loggt eine Warnung, wenn der gefundene Wert nicht eindeutig ist. + * @param props die Properties. + * @param query das Query. + * @param defaultValue der Default-Wert, falls kein Wert gefunden wurde. + * @return der gefundene Wert oder der Default-Wert. + */ + public static String getValue(Properties props, Query query, String defaultValue) + { + return getValue(props,query != null ? query.getQuery() : null,defaultValue); + } - Properties result = find(props,"Params_*.TAN2StepPar*.ParTAN2Step*.TAN2StepParams*.*secfunc"); - Enumeration e = result.keys(); - while (e.hasMoreElements()) - { - String name = (String) e.nextElement(); - String value = (String) result.get(name); - System.out.println(name + ": " + value); + /** + * Liefert einen einzelnen Wert. + * Die Funktion loggt einen Hinweis, wenn der gefundene Wert nicht eindeutig ist. + * @param props die Properties. + * @param path der Pfad. + * @param defaultValue der Default-Wert, falls kein Wert gefunden wurde. + * @return der gefundene Wert oder der Default-Wert. + */ + public static String getValue(Properties props, String path, String defaultValue) + { + Properties result = findAll(props,path); + if (result == null || result.size() == 0) + return defaultValue; + + if (result.size() > 1) + HBCIUtils.log("query " + path + " mode ambiguous, found multiple values: " + result,HBCIUtils.LOG_INFO); + + // Liefert den ersten Treffer + String s = (String) result.values().iterator().next(); + return s != null ? s : defaultValue; } - } -} - - -/********************************************************************** - * $Log: ParameterFinder.java,v $ - * Revision 1.1 2011/05/13 15:22:08 willuhn - * @N Hilfsklasse zum Suchen von Parametern in BPD/UPD - * - **********************************************************************/ \ No newline at end of file +} diff -Nru hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/tools/StringUtil.java hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/tools/StringUtil.java --- hbci4java-3.0.22+dfsg/src/main/java/org/kapott/hbci/tools/StringUtil.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/java/org/kapott/hbci/tools/StringUtil.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,70 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci.tools; + +/** + * Verschiedene String-Hilfsklassen. + */ +public class StringUtil +{ + /** + * Liefert zu einem HBCI-Code vom Client den zugehoerigen HBCI-Code des Instituts. + * @param hbciCode der HBCI-Code des Clients. + * @return der HBCI-Code des Instituts. + */ + public static String toInsCode(String hbciCode) + { + if (hbciCode == null || hbciCode.length() < 3) + return hbciCode; + return new StringBuffer(hbciCode).replace(1,2,"I").toString(); + } + + /** + * Liefert zu einem HBCI-Code vom Client den zugehoerigen HBCI-Code der BPD/UPD. + * @param hbciCode der HBCI-Code des Clients. + * @return der HBCI-Code der BPD/UPD. + */ + public static String toParameterCode(String hbciCode) + { + if (hbciCode == null) + return hbciCode; + + return toInsCode(hbciCode) + "S"; + } + + /** + * Prueft, ob im String Text enthalten ist, der kein Whitespace ist. + * @param s der zu pruefende Text. + * @return true, wenn Text enthalten ist. + * Wenn nur Leerzeichen oder andere Whitespaces enthalten sind, liefert die Funktion false. + * Ebenso wenn der String NULL ist. + */ + public static boolean hasText(String s) + { + if (s == null || s.length() == 0) + return false; + + return s.trim().length() > 0; + } +} + + diff -Nru hbci4java-3.0.22+dfsg/src/main/resources/META-INF/services/org.kapott.hbci.passport.storage.format.PassportFormat hbci4java-3.1.29+dfsg/src/main/resources/META-INF/services/org.kapott.hbci.passport.storage.format.PassportFormat --- hbci4java-3.0.22+dfsg/src/main/resources/META-INF/services/org.kapott.hbci.passport.storage.format.PassportFormat 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/resources/META-INF/services/org.kapott.hbci.passport.storage.format.PassportFormat 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,7 @@ +# Die unterstuetzten Passport-Formate. + +# Das neue AES-basierte Format +org.kapott.hbci.passport.storage.format.AESFormat + +# Das alte Format von HBCI4Java +org.kapott.hbci.passport.storage.format.LegacyFormat diff -Nru hbci4java-3.0.22+dfsg/src/main/resources/META-INF/services/org.kapott.hbci.passport.storage.format.legacy.Converter hbci4java-3.1.29+dfsg/src/main/resources/META-INF/services/org.kapott.hbci.passport.storage.format.legacy.Converter --- hbci4java-3.0.22+dfsg/src/main/resources/META-INF/services/org.kapott.hbci.passport.storage.format.legacy.Converter 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/resources/META-INF/services/org.kapott.hbci.passport.storage.format.legacy.Converter 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,7 @@ +# Die unterstuetzten Legacy-Formate. + +org.kapott.hbci.passport.storage.format.legacy.ConverterDDV +org.kapott.hbci.passport.storage.format.legacy.ConverterPinTan +org.kapott.hbci.passport.storage.format.legacy.ConverterAnonymous +org.kapott.hbci.passport.storage.format.legacy.ConverterRSA +org.kapott.hbci.passport.storage.format.legacy.ConverterRDHNew diff -Nru hbci4java-3.0.22+dfsg/src/main/resources/blz.properties hbci4java-3.1.29+dfsg/src/main/resources/blz.properties --- hbci4java-3.0.22+dfsg/src/main/resources/blz.properties 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/resources/blz.properties 2019-11-27 09:04:22.000000000 +0000 @@ -53,7 +53,7 @@ 51420300=Bank Julius Bär Europe|Frankfurt am Main|BAERDEF1XXX|17||https://www.bv-activebanking.de/hbciTunnel/hbciTransfer.jsp||300| 74066749=Raiffeisenbank im Südl Bayerischen Wald|Hauzenberg, Niederbay|GENODEF1HZN|88|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| 20730002=UniCredit Bank - HVB Settlement EAC02|Hamburg|HYVEDEMME02|09||||| -30010700=The Bank of Tokyo-Mitsubishi UFJ, Ltd.|Düsseldorf|BOTKDEDXXXX|09||||| +30010700=The Bank of Tokyo-Mitsubishi UFJ, Ltd.|Düsseldorf|BOTKDEDXXXX|09||https://www.bv-activebanking.de/hbciTunnel/hbciTransfer.jsp||300| 60069336=Raiffeisenbank Maitis|Göppingen|GENODES1RMA|10|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| 27070043=Deutsche Bank|Helmstedt|DEUTDE2H273|63|193.150.167.7|https://fints.deutsche-bank.de/|300|300| 25970024=Deutsche Bank Privat und Geschäftskunden|Hildesheim|DEUTDEDB259|63|193.150.167.8|https://fints.deutsche-bank.de/|300|300| @@ -820,7 +820,7 @@ 73320073=UniCredit Bank - HypoVereinsbank|Kempten (Allgäu)|HYVEDEMM428|99|hbci.hypovereinsbank.de|https://hbci-01.hypovereinsbank.de/bank/hbci|300|300| 78050000=Sparkasse Hochfranken|Hof, Saale|BYLADEM1HOF|00|i035.by.s-hbci.de|https://banking-by4.s-fints-pt-by.de/fints30|220|300| 79364069=Raiffeisenbank Frankenwinheim und Umgebung|Frankenwinheim|GENODEF1FWH|88|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| -51230502=ETC Standard Chartered Bank Germany Branch|Frankfurt am Main|SCBLDEF1ETC|09||||| +51230502=ETC Standard Chartered Bank Germany Branch|Frankfurt am Main|SCBLDEFXXXX|09||||| 60050101=Landesbank Baden-Württemberg/Baden-Württembergische Bank|Stuttgart|SOLADEST600|01|160.44.72.91|https://banking-li4.s-fints-pt-li.de/fints30|220|300| 60069595=Raiffeisenbank Schrozberg-Rot am See|Schrozberg|GENODES1SBB|10|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| 76069410=Raiffeisenbank Dietersheim und Umgebung|Dietersheim, Mittelfr|GENODEF1DIM|88|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| @@ -1166,7 +1166,7 @@ 72090500=Sparda-Bank Augsburg|Augsburg, Bay|GENODEF1S03|84||https://fints.bankingonline.de/fints/FinTs30PinTanHttpGate||300| 10090900=PSD Bank Berlin-Brandenburg|Berlin|GENODEF1P01|91|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet||300| 72060300=Handels- und Gewerbebank Augsburg -alt-|Augsburg, Bay|GENODEF1A03|88|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| -10030500=Bankhaus Löbbecke|Berlin|LOEBDEBBXXX|09|loebbecke-bank.de|https://loebbecke-bank.de/fints/|210|300| +10030500=Bankhaus Löbbecke|Berlin|LOEBDEBBXXX|09|loebbecke-bank.de|https://www.warburg-bank.de/fints|210|300| 27090618=apoBank|Braunschweig|DAAEDED1018|A4||||| 45060009=Märkische Bank|Hagen, Westf|GENODEM1HGN|34|hbci.gad.de|https://hbci-pintan.gad.de/cgi-bin/hbciservlet|300|300| 50210170=SEB TZN MB Frankfurt|Frankfurt am Main|ESSEDE5FXXX|09||||| @@ -1239,7 +1239,7 @@ 26600000=Bundesbank eh Lingen (Ems)|Osnabrück|MARKDEF1266|09||||| 24040000=Commerzbank|Lüneburg|COBADEFF240|13||||| 73100000=Bundesbank eh Memmingen|Augsburg, Bay|MARKDEF1731|09||||| -29030400=Bankhaus Carl F. Plump & CO|Bremen|PLUMDE29XXX|C4|banking.bankhaus-plump.de |https://banking.bankhaus-plump.de/fints/|300|300| +29030400=Bankhaus Carl F. Plump & CO|Bremen|PLUMDE29XXX|C4|banking.bankhaus-plump.de |https://www.warburg-bank.de/fints|300|300| 62062215=Volksbank Beilstein-Ilsfeld-Abstatt|Beilstein, Württ|GENODES1BIA|10|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| 45251480=Stadtsparkasse Wetter|Wetter (Ruhr)|WELADED1WET|00|w040.s-hbci.de|https://banking-wl6.s-fints-pt-wl.de/fints30|220|300| 50120900=VakifBank International Wien Zndl Frankfurt|Frankfurt am Main|TVBADEFFXXX|06|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| @@ -1802,7 +1802,7 @@ 21570011=Deutsche Bank|Flensburg|DEUTDEHH215|63||https://fints.deutsche-bank.de/||300| 75360011=Raiffeisenbank Weiden|Weiden i.d.OPf.|GENODEF1WEO|88|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| 62020100=FGA Bank Germany|Heilbronn, Neckar|FBHNDE61XXX|09||||| -50110636=DTC Standard Chartered Bank Germany Branch|Frankfurt am Main|SCBLDEF1DTC|09||||| +50110636=DTC Standard Chartered Bank Germany Branch|Frankfurt am Main|SCBLDEFXXXX|09||||| 30030900=Merck Finck & Co|Düsseldorf|MEFIDEMM300|00|hbciserver.bankverlag.de|https://www.bv-activebanking.de/hbciTunnel/hbciTransfer.jsp|300|300| 40361627=Volksbank Westerkappeln-Wersen|Westerkappeln|GENODEM1WKP|34|hbci.gad.de|https://hbci-pintan.gad.de/cgi-bin/hbciservlet|300|300| 60450050=Kreissparkasse Ludwigsburg|Ludwigsburg, Württ|SOLADES1LBG|01|i021.bw.s-hbci.de|https://banking-bw4.s-fints-pt-bw.de/fints30|220|300| @@ -1850,7 +1850,7 @@ 22140028=Commerzbank|Elmshorn|COBADEFF221|13||||| 50520190=UniCredit Bank - HypoVereinsbank|Offenbach am Main|HYVEDEMM467|99|hbci.hypovereinsbank.de|https://hbci-01.hypovereinsbank.de/bank/hbci|300|300| 37040060=Commerzbank CC|Köln|COBADEFF086|09||||| -25060180=Bankhaus Hallbaum|Hannover|HALLDE2HXXX|C3|ebanking.bankhaus-hallbaum.de |https://ebanking.bankhaus-hallbaum.de/fints|300|300| +25060180=Bankhaus Hallbaum|Hannover|HALLDE2HXXX|C3|ebanking.bankhaus-hallbaum.de |https://www.warburg-bank.de/fints|300|300| 79650000=Sparkasse Miltenberg-Obernburg|Miltenberg|BYLADEM1MIL|00|i040.by.s-hbci.de|https://banking-by7.s-fints-pt-by.de/fints30|220|300| 53250000=Sparkasse Bad Hersfeld-Rotenburg|Bad Hersfeld|HELADEF1HER|A6|h011.s-hbci.de|https://banking-hs3.s-fints-pt-hs.de/fints30|220|300| 64040033=Commerzbank|Reutlingen|COBADEFF640|13|hbci.commerzbank.de||300|| @@ -1890,7 +1890,7 @@ 68092000=Volksbank Breisgau Nord|Emmendingen|GENODE61EMM|06|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| 59393000=levoBank|Lebach|GENODE51LEB|06|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| 51080085=Commerzbank vormals Dresdner Bank, PCC DCC-ITGK 1|Wiesbaden|DRESDEFFJ16|09||||| -60020100=Schwäbische Bank|Stuttgart|SCHWDESSXXX|09|217.110.68.70|https://ebanking.schwaebische-bank.de/fints|220|300| +60020100=Schwäbische Bank|Stuttgart|SCHWDESSXXX|09|217.110.68.70|https://www.warburg-bank.de/fints|220|300| 38070059=Deutsche Bank|Bonn|DEUTDEDK380|63|193.150.167.7|https://fints.deutsche-bank.de/|300|300| 36040060=Commerzbank CC|Essen, Ruhr|COBADEFF082|09||||| 76069576=Raiffeisenbank Plankstetten|Berching|GENODEF1BPL|88|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| @@ -1935,7 +1935,7 @@ 68310111=SEB|Lörrach|ESSEDE5F683|13|193.243.184.115||201|| 25971071=Deutsche Bank|Alfeld (Leine)|DEUTDE2H253|63|193.150.167.7|https://fints.deutsche-bank.de/|300|300| 73460046=VR Bank Kaufbeuren-Ostallgäu|Kaufbeuren|GENODEF1KFB|88|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| -50010517=ING-DiBa|Frankfurt am Main|INGDDEFFXXX|C1||https://fints.ing-diba.de/fints/||300| +50010517=ING-DiBa|Frankfurt am Main|INGDDEFFXXX|C1||https://fints.ing.de/fints/||300| 24061392=Volksbank Bleckede-Dahlenburg -alt-|Dahlenburg|GENODEF1DAB|28||||| 51640043=Commerzbank|Dillenburg|COBADEFF516|13|hbci.commerzbank.de||300|| 10220500=Bank of Scotland Ndl Berlin|Berlin|BOFSDEB1XXX|00||||| @@ -2457,7 +2457,7 @@ 80053722=Kreissparkasse Anhalt-Bitterfeld|Bitterfeld-Wolfen|NOLADE21BTF|C0|i002.s-fints-st.de|https://banking-st5.s-fints-pt-st.de/fints30|220|300| 26522319=Oldenburgische Landesbank AG|Quakenbrück|OLBODEH2219|61||||| 20080094=Commerzbank vormals Dresdner Bank, PCC DCC-ITGK 10|Hamburg|DRESDEFFJ36|09||||| -20050550=Hamburger Sparkasse|Hamburg|HASPDEHHXXX|00|online-banking.haspa.de|https://banking.haspa.de/OnlineBankingFinTS/pintan|220|300| +20050550=Hamburger Sparkasse|Hamburg|HASPDEHHXXX|00|online-banking.haspa.de|https://banking-hh7.s-fints-pt-hh.de/fints30|220|300| 70040070=Commerzbank, CC SP|München|COBADEFFS07|09||||| 50210148=SEB TZN MB Ffm.|Frankfurt am Main|ESSEDE5FXXX|09||||| 21762550=Husumer Volksbank|Husum, Nordsee|GENODEF1HUM|32|hbci.gad.de|https://hbci-pintan.gad.de/cgi-bin/hbciservlet|300|300| @@ -3049,7 +3049,7 @@ 38462135=Volksbank Oberberg|Wiehl|GENODED1WIL|06|hbci.gad.de|https://hbci-pintan.gad.de/cgi-bin/hbciservlet|300|300| 21270020=Deutsche Bank|Neumünster, Holst|DEUTDEHH212|63||https://fints.deutsche-bank.de/||300| 40069546=Volksbank Senden|Senden, Westf|GENODEM1SDN|34|hbci.gad.de|https://hbci-pintan.gad.de/cgi-bin/hbciservlet|300|300| -20310300=Europäisch-Iranische Handelsbank|Hamburg| |06||||| +20310300=Europäisch-Iranische Handelsbank|Hamburg| |06||https://www.bv-activebanking.de/hbciTunnel/hbciTransfer.jsp||300| 59291300=Volksbank Spiesen-Elversberg -alt-|Spiesen-Elversberg|GENODE51SPI|09|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| 55020500=Bank für Sozialwirtschaft|Mainz a Rhein|BFSWDE33MNZ|09||||| 50030100=HKB Bank Frankfurt|Frankfurt am Main|HKBBDEF1FRA|00||https://hbci-pintan.gad.de/cgi-bin/hbciservlet||300| @@ -3894,7 +3894,7 @@ 41670024=Deutsche Bank Privat und Geschäftskunden|Lippstadt|DEUTDEDB416|63|193.150.167.8|https://fints.deutsche-bank.de/|300|300| 21340010=Commerzbank|Neustadt in Holstein|COBADEFF226|13||||| 61361722=Raiffeisenbank Rosenstein|Heubach, Württ|GENODES1HEU|10|hbci01.fiducia.de|https://hbci11.fiducia.de/cgi-bin/hbciservlet|300|300| -70013000=European Bank for Financial Services|Aschheim|COBADEMXXXX|67||||| +70013000=European Bank for Financial Services|Aschheim|EBSGDEMXXXX|67||||| 64050000=Kreissparkasse Reutlingen|Reutlingen|SOLADES1REU|01|i007.bw.s-hbci.de|https://banking-bw3.s-fints-pt-bw.de/fints30|220|300| 37069342=Volksbank Heimbach|Heimbach, Eifel|GENODED1HMB|06|hbci.gad.de|https://hbci-pintan.gad.de/cgi-bin/hbciservlet|300|300| 37080088=Commerzbank vormals Dresdner Bank, PCC DCC-ITGK 6|Köln|DRESDEFFI97|09||||| diff -Nru hbci4java-3.0.22+dfsg/src/main/resources/challengedata.xml hbci4java-3.1.29+dfsg/src/main/resources/challengedata.xml --- hbci4java-3.0.22+dfsg/src/main/resources/challengedata.xml 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/resources/challengedata.xml 2019-11-27 09:04:22.000000000 +0000 @@ -1,845 +1,975 @@ + - - - - - - - - - + + + + + + + + + -]> + ]> - - - - - 90 - - - 64 - - - - - - - 20 - Other.number - BTG.value - BTG.curr - - - 20 - SegHead.code - Other.number - BTG.value - BTG.curr - - - 10 - BTG.value - Other.KIK.blz - otheriban - Other.number - - - - - - 90 - - - 39 - - - - - - - 90 - - - 39 - - KTV.KIK.blz - - KTV.number - - - - - - - 22 - sepa.dst.iban - sepa.btg.value - sepa.btg.curr - - - 22 - SegHead.code - sepa.dst.iban - sepa.btg.value - sepa.btg.curr - - - 09 - sepa.btg.value - sepa.dst.iban - - - - - - - 29 - sepa.btg.value - sepa.dst.iban - sepa.targetdate - - - - - - - 29 - sepa.btg.value - sepa.dst.iban - sepa.targetdate - - - - - - - 29 - sepa.btg.value - sepa.dst.iban - sepa.targetdate - - - - - - - - 90 - - - 39 - - KTV.KIK.blz - - KTV.number - - - - - - 10 - Other.number - BTG.value - BTG.curr - - - 10 - SegHead.code - Other.number - BTG.value - BTG.curr - - - 34 - BTG.value - Other.KIK.blz - Other.number - - - - - - 10 - Other.number - BTG.value - BTG.curr - - - 10 - SegHead.code - Other.number - BTG.value - BTG.curr - - - 40 - - orderid - BTG.value - My.number - - Other.number - - - - - - - - 10 - Other.number - BTG.value - BTG.curr - - - 10 - SegHead.code - Other.number - BTG.value - BTG.curr - - - 43 - - BTG.value - Other.KIK.blz - - Other.number - - - - - - 50 - sumOthers - sumValue - sumCurr - - - 50 - SegHead.code - sumOthers - sumValue - sumCurr - - - 12 - - - - - - - 10 - Other.number - BTG.value - BTG.curr - - - 10 - SegHead.code - Other.number - BTG.value - BTG.curr - - - 04 - BTG.value - Other.KIK.blz - Other.number - - - - - - 90 - - - 51 - My.KIK.blz - - My.number - - - - - - 90 - - - 39 - - KTV.KIK.blz - - KTV.number - - - - - - 90 - - - 39 - - - - - - - - - - 10 - Belastungskto.number - Anlagebetrag.value - Anlagebetrag.curr - - - 10 - SegHead.code - Belastungskto.number - Anlagebetrag.value - Anlagebetrag.curr - - - 59 - - - - - - - 10 - Other.number - BTG.value - BTG.curr - - - 10 - SegHead.code - Other.number - BTG.value - BTG.curr - - - 04 - BTG.value - Other.KIK.blz - Other.number - - - - - - 90 - - - 63 - Address.plz - - - - - - 10 - Other.number - BTG.value - BTG.curr - - - 10 - SegHead.code - Other.number - BTG.value - BTG.curr - - - 15 - BTG.value - Other.KIK.blz - Other.number - - - - - - 10 - Other.number - BTG.value - BTG.curr - - - 10 - SegHead.code - Other.number - BTG.value - BTG.curr - - - 16 - - - - - - - 90 - - - 39 - - KTV.KIK.blz - - KTV.number - - - - - - 90 - - - 39 - - KTV.KIK.blz - - KTV.number - - - - - - 90 - - - 63 - - - - - - - 90 - - - 39 - - - - - - - - - - 90 - - - 39 - - KTV.KIK.blz - - KTV.number - - - - - - 90 - - - 39 - - - - - - - 90 - - - 63 - - - - - - - 90 - - - 39 - - - - - - - - - - 90 - - - 39 - - KTV.KIK.blz - - KTV.number - - - - - - 50 - sumOthers - sumValue - sumCurr - - - 50 - SegHead.code - sumOthers - sumValue - sumCurr - - - 19 - sumCount - sumValue - KTV.number - sumOthers - - - - - - 90 - - - 39 - - - - - - - 50 - sumOthers - sumValue - sumCurr - - - 50 - SegHead.code - sumOthers - sumValue - sumCurr - - - 31 - - - - - - - 90 - - - 40 - - - - - - - 50 - sumOthers - sumValue - sumCurr - - - 50 - SegHead.code - sumOthers - sumValue - sumCurr - - - 12 - sumCount - sumValue - KTV.number - sumOthers - - - - - - 90 - - - 64 - - - - - - - - - - 90 - - - 39 - - - - - - - 50 - sumOthers - sumValue - sumCurr - - - 50 - SegHead.code - sumOthers - sumValue - sumCurr - - - 25 - - - - - - - 90 - - - 40 - - - - - - - 10 - Other.number - BTG.value - BTG.curr - - - 10 - SegHead.code - Other.number - BTG.value - BTG.curr - - - 43 - - BTG.value - Other.KIK.blz - - Other.number - - - - - - 90 - - - 39 - - KTV.KIK.blz - - KTV.number - - - - - - 10 - Other.number - BTG.value - BTG.curr - - - 10 - SegHead.code - Other.number - BTG.value - BTG.curr - - - 22 - BTG.value - Other.KIK.blz - Other.number - date - - - - - - 10 - Other.number - BTG.value - BTG.curr - - - 10 - SegHead.code - Other.number - BTG.value - BTG.curr - - - 40 - - id - - - - - - - - - - - - 10 - Other.number - BTG.value - BTG.curr - - - 10 - SegHead.code - Other.number - BTG.value - BTG.curr - - - 04 - BTG.value - Other.KIK.blz - Other.number - - - - - - 10 - Other.number - BTG.value - BTG.curr - - - 10 - SegHead.code - Other.number - BTG.value - BTG.curr - - - 05 - BTG.value - Other.number - - - - - - 90 - - - 39 - - - - - - - 90 - - - 39 - - - - - - - 90 - - - 39 - - - - - - - 90 - - - 39 - - - - - - - 90 - - - 39 - - - - - - - 90 - - - 39 - - - + + + + + 90 + + + 64 + + + + + + + 20 + Other.number + BTG.value + BTG.curr + + + 20 + SegHead.code + Other.number + BTG.value + BTG.curr + + + 10 + BTG.value + Other.KIK.blz + otheriban + Other.number + + + + + + 90 + + + 39 + + + + + + + 90 + + + 39 + + KTV.KIK.blz + + KTV.number + + + + + + + 22 + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 22 + SegHead.code + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 09 + sepa.btg.value + sepa.dst.iban + + + + + + + 22 + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 22 + SegHead.code + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 09 + sepa.btg.value + sepa.dst.iban + + + + + + + 22 + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 22 + SegHead.code + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 09 + sepa.btg.value + sepa.dst.iban + + + + + + + 22 + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 22 + SegHead.code + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 09 + sepa.btg.value + sepa.dst.iban + + + + + + + 22 + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 22 + SegHead.code + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 09 + sepa.btg.value + sepa.dst.iban + + + + + + + 29 + sepa.btg.value + sepa.dst.iban + sepa.targetdate + + + + + + + 29 + sepa.btg.value + sepa.dst.iban + sepa.targetdate + + + + + + + 29 + sepa.btg.value + sepa.dst.iban + sepa.targetdate + + + + + + + 34 + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 34 + SegHead.code + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 34 + sepa.btg.value + sepa.dst.iban + + + + + + + 34 + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 34 + SegHead.code + sepa.dst.iban + sepa.btg.value + sepa.btg.curr + + + 34 + sepa.btg.value + sepa.dst.iban + + + + + + 90 + + + 39 + + KTV.KIK.blz + + KTV.number + + + + + + 10 + Other.number + BTG.value + BTG.curr + + + 10 + SegHead.code + Other.number + BTG.value + BTG.curr + + + 34 + BTG.value + Other.KIK.blz + Other.number + + + + + + 10 + Other.number + BTG.value + BTG.curr + + + 10 + SegHead.code + Other.number + BTG.value + BTG.curr + + + 40 + + orderid + BTG.value + My.number + + Other.number + + + + + + + + 10 + Other.number + BTG.value + BTG.curr + + + 10 + SegHead.code + Other.number + BTG.value + BTG.curr + + + 43 + + BTG.value + Other.KIK.blz + + Other.number + + + + + + 50 + sumOthers + sumValue + sumCurr + + + 50 + SegHead.code + sumOthers + sumValue + sumCurr + + + 12 + + + + + + + 10 + Other.number + BTG.value + BTG.curr + + + 10 + SegHead.code + Other.number + BTG.value + BTG.curr + + + 04 + BTG.value + Other.KIK.blz + Other.number + + + + + + 90 + + + 51 + My.KIK.blz + + My.number + + + + + + 90 + + + 39 + + KTV.KIK.blz + + KTV.number + + + + + + 90 + + + 39 + + + + + + + + + + 10 + Belastungskto.number + Anlagebetrag.value + Anlagebetrag.curr + + + 10 + SegHead.code + Belastungskto.number + Anlagebetrag.value + Anlagebetrag.curr + + + 59 + + + + + + + 10 + Other.number + BTG.value + BTG.curr + + + 10 + SegHead.code + Other.number + BTG.value + BTG.curr + + + 04 + BTG.value + Other.KIK.blz + Other.number + + + + + + 90 + + + 63 + Address.plz + + + + + + 10 + Other.number + BTG.value + BTG.curr + + + 10 + SegHead.code + Other.number + BTG.value + BTG.curr + + + 15 + BTG.value + Other.KIK.blz + Other.number + + + + + + 10 + Other.number + BTG.value + BTG.curr + + + 10 + SegHead.code + Other.number + BTG.value + BTG.curr + + + 16 + + + + + + + 90 + + + 39 + + KTV.KIK.blz + + KTV.number + + + + + + 90 + + + 39 + + KTV.KIK.blz + + KTV.number + + + + + + 90 + + + 63 + + + + + + + 90 + + + 39 + + + + + + + + + + 90 + + + 39 + + KTV.KIK.blz + + KTV.number + + + + + + 90 + + + 39 + + + + + + + 90 + + + 63 + + + + + + + 90 + + + 39 + + + + + + + + + + 90 + + + 39 + + KTV.KIK.blz + + KTV.number + + + + + + 50 + sumOthers + sumValue + sumCurr + + + 50 + SegHead.code + sumOthers + sumValue + sumCurr + + + 19 + sumCount + sumValue + KTV.number + sumOthers + + + + + + 90 + + + 39 + + + + + + + 50 + sumOthers + sumValue + sumCurr + + + 50 + SegHead.code + sumOthers + sumValue + sumCurr + + + 31 + + + + + + + 90 + + + 40 + + + + + + + 50 + sumOthers + sumValue + sumCurr + + + 50 + SegHead.code + sumOthers + sumValue + sumCurr + + + 12 + sumCount + sumValue + KTV.number + sumOthers + + + + + + 90 + + + 64 + + + + + + + + + + 90 + + + 39 + + + + + + + 50 + sumOthers + sumValue + sumCurr + + + 50 + SegHead.code + sumOthers + sumValue + sumCurr + + + 25 + + + + + + + 90 + + + 40 + + + + + + + 10 + Other.number + BTG.value + BTG.curr + + + 10 + SegHead.code + Other.number + BTG.value + BTG.curr + + + 43 + + BTG.value + Other.KIK.blz + + Other.number + + + + + + 90 + + + 39 + + KTV.KIK.blz + + KTV.number + + + + + + 10 + Other.number + BTG.value + BTG.curr + + + 10 + SegHead.code + Other.number + BTG.value + BTG.curr + + + 22 + BTG.value + Other.KIK.blz + Other.number + date + + + + + + 10 + Other.number + BTG.value + BTG.curr + + + 10 + SegHead.code + Other.number + BTG.value + BTG.curr + + + 40 + + id + + + + + + + + + + + + 10 + Other.number + BTG.value + BTG.curr + + + 10 + SegHead.code + Other.number + BTG.value + BTG.curr + + + 04 + BTG.value + Other.KIK.blz + Other.number + + + + + + 10 + Other.number + BTG.value + BTG.curr + + + 10 + SegHead.code + Other.number + BTG.value + BTG.curr + + + 05 + BTG.value + Other.number + + + + + + 90 + + + 39 + + + + + + + 90 + + + 39 + + + + + + + 90 + + + 39 + + + + + + + 90 + + + 39 + + + + + + + 90 + + + 39 + + + + + + + 90 + + + 39 + + + diff -Nru hbci4java-3.0.22+dfsg/src/main/resources/hbci-210.xml hbci4java-3.1.29+dfsg/src/main/resources/hbci-210.xml --- hbci4java-3.0.22+dfsg/src/main/resources/hbci-210.xml 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/resources/hbci-210.xml 2019-11-27 09:04:22.000000000 +0000 @@ -6552,6 +6552,8 @@ &MsgSigTailInst; + @@ -6559,8 +6561,6 @@ - 0 - 1 9999999999 0 0 @@ -6570,7 +6570,6 @@ 999 999 999 - 1 &MsgSigHeadInst; @@ -6588,13 +6587,6 @@ - - 0 - 1 - 9999999999 - 0 - 0 - 1 &MsgSigHeadInst; @@ -6612,10 +6604,6 @@ &MsgSigTailUser; - - 0 - 1 - 1 &MsgSigHeadInst; @@ -6628,6 +6616,21 @@ &MsgSigTailInst; + + + &MsgSigHeadUser; + + &MsgSigTailUser; + + + &MsgSigHeadInst; + + + + + &MsgSigTailInst; + + @@ -6652,66 +6655,66 @@ &MsgSigTailInst; - + &MsgSigHeadUser; - + &MsgSigTailUser; - + &MsgSigHeadInst; - &MsgSigTailInst; - + &MsgSigHeadUser; - - - - + &MsgSigTailUser; - + &MsgSigHeadInst; - - - - - + &MsgSigTailInst; - + &MsgSigHeadUser; + &MsgSigTailUser; + + 0 + 1 + 1 + 1 - + &MsgSigHeadInst; &MsgSigTailInst; - + &MsgSigHeadUser; - + + + &MsgSigTailUser; - - 0 - 1 - 1 - 1 - + &MsgSigHeadInst; + + + + + &MsgSigTailInst; diff -Nru hbci4java-3.0.22+dfsg/src/main/resources/hbci-300.xml hbci4java-3.1.29+dfsg/src/main/resources/hbci-300.xml --- hbci4java-3.0.22+dfsg/src/main/resources/hbci-300.xml 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/resources/hbci-300.xml 2019-11-27 09:04:22.000000000 +0000 @@ -988,6 +988,14 @@ + + + + + + + + @@ -1515,6 +1523,19 @@ + + + + + + + + 0 + 1 + 2 + + + @@ -1773,8 +1794,8 @@ - - + + @@ -1785,10 +1806,10 @@ - + - + @@ -1837,6 +1858,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + + + 1 + 2 + + + @@ -1960,6 +2014,40 @@ 4 + + + + + + + + + + + + + + + + + + + + + + G + L + M + S + A + + + 1 + 2 + 3 + 4 + + @@ -5285,6 +5373,64 @@ + + + + + + + + + + + + + + + + + + 1 + 2 + 3 + 4 + + + HKTAN + 6 + + + + + + + + + + + + + 1 + 2 + 3 + 4 + + + HITAN + 6 + + + &GVP2; + + + &SecClassValids; + + HITANS + 6 + + + + @@ -5442,6 +5588,55 @@ 4 + + + + + + + + + 0 + 1 + 2 + + + A + L + G + M + S + B + + + HKTAB + 5 + + + + + + + + + 0 + 1 + 2 + + HITAB + 5 + + + + &GVP2; + &SecClassValids; + + HITABS + 5 + + + + @@ -6064,6 +6259,58 @@ 1 + + + + + + + + + HKIPZ + 1 + + + + + + + + + + + 1 + 2 + 3 + 4 + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + + + HIIPZ + 1 + + + + + &GVP2; + + &SecClassValids; + + HIIPZS + 1 + + + @@ -7374,6 +7621,7 @@ + @@ -7400,7 +7648,7 @@ - + @@ -7428,11 +7676,13 @@ + + @@ -7532,6 +7782,7 @@ + @@ -7564,16 +7815,12 @@ - - - - - + @@ -7614,6 +7861,12 @@ + + + + + + @@ -7621,8 +7874,8 @@ - - + + @@ -7668,6 +7921,7 @@ + @@ -7724,11 +7978,13 @@ + + @@ -7807,6 +8063,8 @@ &MsgSigTailInst; + @@ -7814,8 +8072,6 @@ - 0 - 1 9999999999 0 0 @@ -7825,7 +8081,6 @@ 999 999 999 - 1 &MsgSigHeadInst; @@ -7839,59 +8094,143 @@ - - - - - - 0 - 1 - 9999999999 - 0 - 0 - 1 + + + + + + &MsgSigHeadInst; + + + + + &MsgSigTailInst; + &MsgSigHeadUser; + + + + + &MsgSigTailUser; + + + &MsgSigHeadInst; + + + + + + + + + + + + + &MsgSigTailInst; + + + &MsgSigHeadUser; - - - + + + + + &MsgSigTailUser; + + + &MsgSigHeadInst; + + + + + + + + + + + &MsgSigTailInst; + - 0 - 1 - 1 + + &MsgSigHeadUser; + + + &MsgSigTailUser; - + &MsgSigHeadInst; + + + + + &MsgSigTailInst; + + + + &MsgSigHeadUser; + + + + + + &MsgSigTailUser; + + + &MsgSigHeadInst; + + + + + + + + + + &MsgSigTailInst; - - - - + + &MsgSigHeadUser; + + &MsgSigTailUser; - + &MsgSigHeadInst; + + + + + + + + + + + + &MsgSigTailInst; @@ -7907,6 +8246,18 @@ &MsgSigTailInst; + + + + + + + &MsgSigHeadInst; + + + &MsgSigTailInst; + + &MsgSigHeadUser; @@ -7950,27 +8301,6 @@ &MsgSigTailInst; - - &MsgSigHeadUser; - - - - - &MsgSigTailUser; - - - &MsgSigHeadInst; - - - - - - - - - &MsgSigTailInst; - - diff -Nru hbci4java-3.0.22+dfsg/src/main/resources/hbci-plus.xml hbci4java-3.1.29+dfsg/src/main/resources/hbci-plus.xml --- hbci4java-3.0.22+dfsg/src/main/resources/hbci-plus.xml 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/resources/hbci-plus.xml 2019-11-27 09:04:22.000000000 +0000 @@ -7610,6 +7610,8 @@ &MsgSigTailInst; + @@ -7617,8 +7619,6 @@ - 0 - 1 9999999999 0 0 @@ -7628,7 +7628,6 @@ 999 999 999 - 1 &MsgSigHeadInst; @@ -7646,13 +7645,6 @@ - - 0 - 1 - 9999999999 - 0 - 0 - 1 &MsgSigHeadInst; @@ -7670,10 +7662,6 @@ &MsgSigTailUser; - - 0 - 1 - 1 &MsgSigHeadInst; @@ -7685,6 +7673,38 @@ &MsgSigTailInst; + + + &MsgSigHeadUser; + + + + &MsgSigTailUser; + + + &MsgSigHeadInst; + + + + + + + &MsgSigTailInst; + + + + &MsgSigHeadUser; + + &MsgSigTailUser; + + + &MsgSigHeadInst; + + + + + &MsgSigTailInst; + diff -Nru hbci4java-3.0.22+dfsg/src/main/resources/hbci4java-messages_de.properties hbci4java-3.1.29+dfsg/src/main/resources/hbci4java-messages_de.properties --- hbci4java-3.0.22+dfsg/src/main/resources/hbci4java-messages_de.properties 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/main/resources/hbci4java-messages_de.properties 2019-11-27 09:04:22.000000000 +0000 @@ -203,23 +203,23 @@ STATUS_SEND_MY_KEYS_DONE=öffentliche Schlüssel des Nutzers übermittelt STATUS_USR_LOCK=sperre Nutzerschlüssel STATUS_USR_LOCK_DONE=Nutzerschlüssel gesperrt -STATUS_DIALOG_INIT=führe Dialog-Initialisierung aus +STATUS_DIALOG_INIT=Führe Dialog-Initialisierung aus STATUS_DIALOG_INIT_DONE=Dialog initialisiert - Dialog-ID ist {0} -STATUS_DIALOG_NEW_JOB=erstelle Auftragsdaten für Geschäftsvorfall {0} +STATUS_DIALOG_NEW_JOB=Erstelle Auftragsdaten für Geschäftsvorfall {0} STATUS_DIALOG_JOB_DONE=Ergebnisdaten für Geschäftsvorfall {0} empfangen -STATUS_DIALOG_END=beende Dialog +STATUS_DIALOG_END=Beende Dialog STATUS_DIALOG_END_DONE=Dialog beendet -STATUS_MSG_CREATE=erzeuge HBCI-Nachricht {0} +STATUS_MSG_CREATE=Erzeuge HBCI-Nachricht {0} STATUS_MSG_SIGN=signiere HBCI-Nachricht -STATUS_MSG_CRYPT=verschlüssele HBCI-Nachricht -STATUS_MSG_SEND=versende HBCI-Nachricht -STATUS_MSG_RECV=warte auf Antwortdaten -STATUS_MSG_PARSE=parse empfangene Antwortnachricht ({0}) -STATUS_MSG_DECRYPT=entschlüssele Antwortnachricht -STATUS_MSG_VERIFY=überprüfe Signatur der Antwortnachricht +STATUS_MSG_CRYPT=Verschlüssele HBCI-Nachricht +STATUS_MSG_SEND=Versende HBCI-Nachricht +STATUS_MSG_RECV=Warte auf Antwortdaten +STATUS_MSG_PARSE=Lese empfangene Antwortnachricht ({0}) +STATUS_MSG_DECRYPT=Entschlüssele Antwortnachricht +STATUS_MSG_VERIFY=Überprüfe Signatur der Antwortnachricht STATUS_SEND_INFOPOINT_DATA=sende Daten an InfoPoint-Server STATUS_INVALID=ungültiges Status-Tag: {0} -STATUS_MSG_RAW_SEND=sende HBCI-Nachricht: {0} +STATUS_MSG_RAW_SEND=Sende HBCI-Nachricht: {0} STATUS_MSG_RAW_RECV=HBCI-Nachricht empfangen und entschlüsselt: {0} STATUS_MSG_RAW_RECV_ENCRYPTED=HBCI-Nachricht empfangen: {0} diff -Nru hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/AbstractTestGV.java hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/AbstractTestGV.java --- hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/AbstractTestGV.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/AbstractTestGV.java 2019-11-27 09:04:22.000000000 +0000 @@ -28,7 +28,7 @@ import org.kapott.hbci.manager.HBCIVersion; import org.kapott.hbci.passport.AbstractHBCIPassport; import org.kapott.hbci.passport.HBCIPassport; -import org.kapott.hbci.passport.HBCIPassportPinTanMemory; +import org.kapott.hbci.passport.HBCIPassportPinTan; import org.kapott.hbci.status.HBCIExecStatus; /** @@ -37,10 +37,10 @@ public abstract class AbstractTestGV { private final Map callbackValues = new HashMap(); - private Properties params = null; - private HBCIPassportPinTanMemory passport = null; - private HBCIHandler handler = null; - private PrintStream out = null; + private Properties params = null; + private HBCIPassportPinTan passport = null; + private HBCIHandler handler = null; + private PrintStream out = null; /** * Deaktiviert den Test, wenn das System-Property nicht auf "true" steht. @@ -90,11 +90,15 @@ // Presets fuer den Callback this.callbackValues.put(HBCICallback.NEED_COUNTRY, params.getProperty("country",System.getProperty("country","DE"))); - this.callbackValues.put(HBCICallback.NEED_CUSTOMERID, params.getProperty("customerid",System.getProperty("customerid"))); - this.callbackValues.put(HBCICallback.NEED_USERID, params.getProperty("userid",System.getProperty("useris"))); - this.callbackValues.put(HBCICallback.NEED_PT_PIN, params.getProperty("pin",System.getProperty("pin"))); - this.callbackValues.put(HBCICallback.NEED_PT_SECMECH, params.getProperty("secmech",System.getProperty("secmech"))); - + this.callbackValues.put(HBCICallback.NEED_CUSTOMERID, params.getProperty("customerid",System.getProperty("customerid",""))); + this.callbackValues.put(HBCICallback.NEED_USERID, params.getProperty("userid",System.getProperty("userid",""))); + this.callbackValues.put(HBCICallback.NEED_PT_PIN, params.getProperty("pin",System.getProperty("pin",""))); + this.callbackValues.put(HBCICallback.NEED_PT_SECMECH, params.getProperty("secmech",System.getProperty("secmech",""))); + + // Das Passport-Passwort + this.callbackValues.put(HBCICallback.NEED_PASSPHRASE_LOAD, params.getProperty("password",System.getProperty("password",""))); + this.callbackValues.put(HBCICallback.NEED_PASSPHRASE_SAVE, params.getProperty("password",System.getProperty("password",""))); + final String blz = params.getProperty("blz",System.getProperty("blz")); this.callbackValues.put(HBCICallback.NEED_BLZ, blz); this.callbackValues.put(HBCICallback.NEED_PORT, params.getProperty("port",System.getProperty("port","443"))); @@ -106,11 +110,17 @@ // Initialisierungsparameter fuer HBCI4Java selbst Properties props = new Properties(); props.put("log.loglevel.default",this.params.getProperty("log.loglevel.default",System.getProperty("log.loglevel.default",Integer.toString(HBCIUtils.LOG_DEBUG)))); + + props.put("passportformat.order.load",this.params.getProperty("passportformat.order.load",System.getProperty("passportformat.order.load",""))); + props.put("passportformat.order.save",this.params.getProperty("passportformat.order.save",System.getProperty("passportformat.order.save",""))); + props.put("client.passport.PinTan.init","1"); - props.put("client.passport.PinTan.checkcert",this.params.getProperty("client.passport.PinTan.checkcert","1")); + props.put("client.passport.PinTan.checkcert",this.params.getProperty("client.passport.PinTan.checkcert",System.getProperty("checkcert","1"))); props.put("client.passport.PinTan.proxy", this.params.getProperty("client.passport.PinTan.proxy","")); props.put("client.passport.PinTan.proxyuser",this.params.getProperty("client.passport.PinTan.proxyuser","")); props.put("client.passport.PinTan.proxypass",this.params.getProperty("client.passport.PinTan.proxypass","")); + props.put("client.passport.PinTan.filename", this.params.getProperty("client.passport.PinTan.filename",System.getProperty("file",""))); + // Callback initialisieren. // Wenn wir passende Antworten in den Presets haben, koennen wir sie direkt @@ -156,7 +166,7 @@ //////////////////////////////////////////////////////////////////////// // Nach dem Initialisieren von HBCI4Java noch checken, ob wir den // Host selbst ermitteln koennen - String host = params.getProperty("host"); + String host = params.getProperty("host",System.getProperty("host")); if (blz != null && host == null) { BankInfo bank = HBCIUtils.getBankInfo(blz); @@ -167,10 +177,11 @@ // //////////////////////////////////////////////////////////////////////// - this.passport = (HBCIPassportPinTanMemory) AbstractHBCIPassport.getInstance("PinTanMemory"); + + this.passport = (HBCIPassportPinTan) AbstractHBCIPassport.getInstance(System.getProperty("passport","PinTanMemory")); // init handler - this.handler = new HBCIHandler(this.params.getProperty("hbciversion",HBCIVersion.HBCI_300.getId()),this.passport); + this.handler = new HBCIHandler(this.params.getProperty("hbciversion",System.getProperty("hbciversion",HBCIVersion.HBCI_300.getId())),this.passport); } /** diff -Nru hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/bpd/TestParseSync.java hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/bpd/TestParseSync.java --- hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/bpd/TestParseSync.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/bpd/TestParseSync.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,48 @@ +/********************************************************************** + * + * Copyright (c) by Olaf Willuhn + * All rights reserved + * LGPLv2 + * + **********************************************************************/ + +package org.kapott.hbci4java.bpd; + +import java.util.Hashtable; + +import org.junit.Test; +import org.kapott.hbci.manager.HBCIKernelImpl; +import org.kapott.hbci.manager.MsgGen; +import org.kapott.hbci.protocol.MSG; +import org.kapott.hbci.protocol.factory.MSGFactory; +import org.kapott.hbci4java.AbstractTest; + +/** + */ +public class TestParseSync extends AbstractTest +{ + /** + * @throws Exception + */ + @Test + public void test001() throws Exception + { + try + { + String data = getFile("bpd-psd2-consors.txt"); + HBCIKernelImpl kernel = new HBCIKernelImpl(null,"300"); + kernel.rawNewMsg("Synch"); + + MsgGen gen = kernel.getMsgGen(); + MSG msg = MSGFactory.getInstance().createMSG("SynchRes",data,data.length(),gen); + Hashtable ht = new Hashtable(); + msg.extractValues(ht); + } + catch (Exception e) + { + e.printStackTrace(); + throw e; + } + } +} + diff -Nru hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/secmech/FlickerTest.java hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/secmech/FlickerTest.java --- hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/secmech/FlickerTest.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/secmech/FlickerTest.java 2019-11-27 09:04:22.000000000 +0000 @@ -7,12 +7,14 @@ package org.kapott.hbci4java.secmech; +import java.util.Properties; + import org.junit.Assert; import org.junit.Test; import org.kapott.hbci.manager.FlickerCode; import org.kapott.hbci.manager.FlickerCode.HHDVersion; -import org.kapott.hbci4java.AbstractTest; import org.kapott.hbci.manager.FlickerRenderer; +import org.kapott.hbci4java.AbstractTest; /** * Testet die Flicker-Codes. @@ -333,6 +335,25 @@ Assert.assertEquals(rendered,"23840120932160564445313235303031303531373036343834383938393044312C303005"); } + /** + * Unit-Test fuer den Fall https://homebanking-hilfe.de/forum/topic.php?p=150121#real150121 + * @throws Exception + */ + @Test + public void test10() throws Exception + { + Properties props = new Properties(); + props.setProperty("id","HHD1.3.2OPT"); + props.setProperty("name","chipTAN optisch"); + props.setProperty("secfunc","911"); + props.setProperty("zkamethod_name","HHDOPT1"); + props.setProperty("zkamethod_version","1.3.2"); + org.kapott.hbci.manager.HHDVersion hhd = org.kapott.hbci.manager.HHDVersion.find(props); + FlickerCode code = FlickerCode.tryParse(hhd,null,"2908881904551000000039990515,00"); + String rendered = code.render(); + Assert.assertEquals(rendered,"1204881904550500000039991531352C30308A"); + } + /** diff -Nru hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/secmech/TestHHDVersion.java hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/secmech/TestHHDVersion.java --- hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/secmech/TestHHDVersion.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/secmech/TestHHDVersion.java 2019-11-27 09:04:22.000000000 +0000 @@ -1,10 +1,21 @@ /********************************************************************** * + * This file is part of HBCI4Java. * Copyright (c) 2019 Olaf Willuhn - * All rights reserved. - * - * This software is copyrighted work licensed under the terms of the - * Jameica License. Please consult the file "LICENSE" for details. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * **********************************************************************/ @@ -93,6 +104,27 @@ t.props.put("segversion","5"); testdata.add(t); } + + // smsTAN bei der HASPA - wurde versehentlich als QR-TAN erkannt. + // https://homebanking-hilfe.de/forum/topic.php?t=22515&page=2 + // {zkamethod_name=mobileTAN, segversion=4, zkamethod_version=1.3.2, id=mTAN, name=smsTAN} + { + TestData t = new TestData(HHDVersion.HHD_1_3); + t.props.put("id","mTAN"); + t.props.put("name","smsTAN"); + t.props.put("zkamethod_version","1.3.2"); + t.props.put("segversion","4"); + testdata.add(t); + } + + // Konstruiertes Beispiel fuer smsTAN + { + TestData t = new TestData(HHDVersion.HHD_1_3); + t.props.put("id","mTAN"); + t.props.put("zkamethod_version","1.3"); + testdata.add(t); + } + } diff -Nru hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/sepa/TestGVKUmsAll.java hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/sepa/TestGVKUmsAll.java --- hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/sepa/TestGVKUmsAll.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/sepa/TestGVKUmsAll.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,47 @@ +package org.kapott.hbci4java.sepa; + +import java.util.Properties; + +import org.junit.Test; +import org.kapott.hbci.GV.HBCIJob; +import org.kapott.hbci.passport.HBCIPassport; +import org.kapott.hbci4java.AbstractTestGV; + +/** + * Testet die Abrufen der Umsaetze. + */ +public class TestGVKUmsAll extends AbstractTestGV +{ + /** + * Testet das Abrufen der Umsaetze. + */ + @Test + public void test() + { + this.execute(new Execution() { + + @Override + public String getJobname() { + return "KUmsAll"; + } + + /** + * @see org.kapott.hbci4java.AbstractTestGV.Execution#configure(org.kapott.hbci.GV.HBCIJob, org.kapott.hbci.passport.HBCIPassport, java.util.Properties) + */ + @Override + public void configure(HBCIJob job, HBCIPassport passport, Properties params) { + super.configure(job, passport, params); + job.setParam("my.blz",params.getProperty("blz",System.getProperty("blz"))); + job.setParam("my.number",params.getProperty("number",System.getProperty("number"))); + + String start = params.getProperty("startdate",System.getProperty("startdate")); + if (start != null) + job.setParam("startdate",start); + + String end = params.getProperty("enddate",System.getProperty("enddate")); + if (end != null) + job.setParam("enddate",end); + } + }); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/sepa/TestGVKUmsAllCamt.java hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/sepa/TestGVKUmsAllCamt.java --- hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/sepa/TestGVKUmsAllCamt.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/sepa/TestGVKUmsAllCamt.java 2019-11-27 09:04:22.000000000 +0000 @@ -13,7 +13,7 @@ public class TestGVKUmsAllCamt extends AbstractTestGV { /** - * Testet das Ausfuehren einer SEPA-Lastschrift. + * Testet das Abrufen der Umsaetze per CAMT. */ @Test public void test() diff -Nru hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/swift/TestBrokenMT940.java hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/swift/TestBrokenMT940.java --- hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/swift/TestBrokenMT940.java 2019-01-30 08:46:05.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/swift/TestBrokenMT940.java 2019-11-27 09:04:22.000000000 +0000 @@ -120,4 +120,16 @@ String value = Swift.getTagValue(st,"62F",0); Assert.assertEquals("C150626EUR91,32",value); } + + /** + * Testet ein falsches "-" nach dem Header. + * @throws Exception + */ + @Test + public void test008() throws Exception + { + String st = "\r\n:20:STARTUMSE\r\n-:25:12030000/1019815776\r\n:28C:00000/002\r\n:60M:C181031EUR2776,22\r\n"; + String value = Swift.getTagValue(st, "25", 0); + Assert.assertEquals("12030000/1019815776", value); + } } diff -Nru hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/tools/TestIOUtils.java hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/tools/TestIOUtils.java --- hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/tools/TestIOUtils.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/tools/TestIOUtils.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,59 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci4java.tools; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.junit.Assert; +import org.junit.Test; +import org.kapott.hbci.tools.IOUtils; +import org.kapott.hbci4java.AbstractTest; + +/** + * Testet Funktionen in IOUtils. + */ +public class TestIOUtils extends AbstractTest +{ + /** + * Testet die automatische Konvertierung von unsicheren Dateinamen. + * @throws Exception + */ + @Test + public void testSafeFilename() throws Exception + { + Map tests = new HashMap(); + tests.put("foobar.txt","foobar.txt"); + tests.put("123456789012345678901234567890","1234567890123456789012345"); + tests.put("abc&(%$-.txt","abc-.txt"); + + for (Entry e:tests.entrySet()) + { + File f = new File(IOUtils.safeFilename(e.getKey())); + Assert.assertEquals("Dateiname falsch",e.getValue(),f.getName()); + } + } +} + + diff -Nru hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/tools/TestParameterFinder.java hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/tools/TestParameterFinder.java --- hbci4java-3.0.22+dfsg/src/test/java/org/kapott/hbci4java/tools/TestParameterFinder.java 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/test/java/org/kapott/hbci4java/tools/TestParameterFinder.java 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,133 @@ +/********************************************************************** + * + * This file is part of HBCI4Java. + * Copyright (c) 2019 Olaf Willuhn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + **********************************************************************/ + +package org.kapott.hbci4java.tools; + +import java.util.Properties; + +import org.junit.Assert; +import org.junit.Test; +import org.kapott.hbci.exceptions.HBCI_Exception; +import org.kapott.hbci.tools.ParameterFinder; +import org.kapott.hbci.tools.ParameterFinder.Query; +import org.kapott.hbci4java.AbstractTest; + +/** + * Tests fuer den Parameter-Finder. + */ +public class TestParameterFinder extends AbstractTest +{ + /** + * @throws Exception + */ + @Test + public void test001() throws Exception + { + Properties props = new Properties(); + props.put("Params_1.TAN2StepParams1.ParTAN2Step4.TAN2StepParams2.secfunc", "a"); + props.put("Params_2.TAN2StepParamsFoo.ParTAN2Step1.TAN2StepParams1.secfunc", "b"); + props.put("Params_2.TAN2StepParamsFoo.ParTAN2Step.TAN2StepParams.2secfunc", "c"); + + props.put("Params.TAN2StepParams1.ParTAN2Step.TAN2StepParams.2secfunc", "d1"); + props.put("Params.TAN2StepParams1.ParTAN2Step.TAN2StepParams.3secfunc", "d2"); + props.put("Params_1.TAN2StepParams1.ParTAN2Step.TAN2StepParams.foo", "e"); + props.put("Params_1.TAN2StepParams1.ParTAN2Step.secfunc", "f"); + + Properties result = ParameterFinder.find(props, "Params_*.TAN2StepPar*.ParTAN2Step*.TAN2StepParams*.*secfunc"); + Assert.assertTrue(result.containsKey("secfunc")); + Assert.assertTrue(result.containsKey("2secfunc")); + Assert.assertFalse(result.containsKey("3secfunc")); + Assert.assertFalse(result.containsKey("foo")); + + String v = result.getProperty("secfunc"); + Assert.assertTrue(v != null && (v.equals("a") || v.equals("b"))); + + v = result.getProperty("2secfunc"); + Assert.assertTrue(v != null && v.equals("c")); + } + + /** + * @throws Exception + */ + @Test + public void test002() throws Exception + { + Properties props = new Properties(); + props.put("Params_1.TAN2StepParams1.ParTAN2Step4.TAN2StepParams2.secfunc", "a"); + props.put("Params_2.TAN2StepParamsFoo.ParTAN2Step1.TAN2StepParams1.secfunc", "b"); + props.put("Params_2.TAN2StepParamsFoo.ParTAN2Step.TAN2StepParams.2secfunc", "c"); + + props.put("Params.TAN2StepParams1.ParTAN2Step.TAN2StepParams.2secfunc", "d"); + props.put("Params_1.TAN2StepParams1.ParTAN2Step.TAN2StepParams.foo", "e"); + props.put("Params_1.TAN2StepParams1.ParTAN2Step.secfunc", "f"); + + Properties result = ParameterFinder.findAll(props, "Params_*.TAN2StepPar*.ParTAN2Step*.TAN2StepParams*.*secfunc"); + Assert.assertTrue(result.containsValue("a")); + Assert.assertTrue(result.containsValue("b")); + Assert.assertTrue(result.containsValue("c")); + Assert.assertFalse(result.containsValue("d")); + Assert.assertFalse(result.containsValue("e")); + Assert.assertFalse(result.containsValue("f")); + } + + /** + * @throws Exception + */ + @Test + public void test003() throws Exception + { + Properties props = new Properties(); + props.put("Params_160.TAN2StepPar1.ParTAN2Step.can1step", "N"); + props.put("Params_2.TAN2StepParamsFoo.ParTAN2Step.TAN2StepParams.can1step", "X"); + props.put("Params_161.TAN2StepPar3.ParTAN2Step.can1step", "J"); + + Properties result = ParameterFinder.findAll(props, Query.BPD_PINTAN_CAN1STEP); + Assert.assertTrue(result.containsValue("J")); + Assert.assertTrue(result.containsValue("N")); + Assert.assertFalse(result.containsValue("X")); + } + + /** + * @throws Exception + */ + @Test + public void test004() throws Exception + { + Properties props = new Properties(); + props.put("Params_160.TAN2StepPar1.ParTAN2Step.orderhashmode", "0"); + props.put("Params_161.TAN2StepPar3.ParTAN2Step.orderhashmode", "1"); + props.put("Params_162.TAN2StepPar6.ParTAN2Step.orderhashmode", "2"); + + String s = ParameterFinder.getValue(props,Query.BPD_PINTAN_ORDERHASHMODE.withParameters("6"),null); + Assert.assertEquals("2",s); + } + + + /** + * @throws Exception + */ + @Test(expected = HBCI_Exception.class) + public void test005() throws Exception + { + Properties props = new Properties(); + ParameterFinder.getValue(props,Query.BPD_PINTAN_ORDERHASHMODE,null); + } +} diff -Nru hbci4java-3.0.22+dfsg/src/test/resources/org/kapott/hbci4java/bpd/bpd-psd2-consors.txt hbci4java-3.1.29+dfsg/src/test/resources/org/kapott/hbci4java/bpd/bpd-psd2-consors.txt --- hbci4java-3.0.22+dfsg/src/test/resources/org/kapott/hbci4java/bpd/bpd-psd2-consors.txt 1970-01-01 00:00:00.000000000 +0000 +++ hbci4java-3.1.29+dfsg/src/test/resources/org/kapott/hbci4java/bpd/bpd-psd2-consors.txt 2019-11-27 09:04:22.000000000 +0000 @@ -0,0 +1,27 @@ +HNHBK:1:3+000000002500+300+000003OVVOUIGFJ995A7NH0KRH7QRH+2+000003OVVOUIGFJ995A7NH0KRH7QRH:2 +'HIRMG:2:2:+3060::Teilweise liegen Warnungen/Hinweise vor. +'HIRMS:3:2:3+0020::Angemeldet.+3050::BPD nicht mehr aktuell. Aktuelle Version folgt.+3050::UPD nicht mehr aktuell. Aktuelle Version folgt.+3920::Zugelassene Ein- und Zwei-Schritt-Verfahren für den Benutzer:900:999 +'HITAN:4:6:3+2++000003OVVOUIGFJ995A7NH0KRH7QRHvb +'HIBPA:5:3:3+1+280:76030080+Consors+0+1:2+201:210:220:300:400+100 +'HIKOM:6:4:3+280:76030080+1+3:https?://brokerage-hbci.consorsbank.de/hbci::MIM:1 +'HISPAS:7:1:3+1+1+0+J:J:J:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.002.03:urn?:swift?:xsd?:$pain.001.002.02:sepade.pain.001.001.02.xsd:urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.008.002.02:urn?:swift?:xsd?:$pain.008.002.01:sepade.pain.008.001.01.xsd +'HIKAZS:8:7:3+1+1+0+30:J:N +'HISALS:9:3:3+1+1 +'HIKAZS:10:6:3+1+1+1+90:J:N +'HICCSS:11:1:3+1+1+0 +'HIKAZS:12:5:3+1+1+90:J:N +'HIKAZS:13:4:3+1+1+90:J'XIADAS:14:1:3+1+1 +'HIKAZS:15:3:3+1+1+90:J +'HITANS:16:6:3+1+1+0+J:N:1:900:2:MS1.0.0:photoTAN::SecurePlus:8:1:Secure Plus TAN:999:J:1:N:0:0:N:J:00:0:J:1 +'HIWPDS:17:2:3+1+1+J +'HIWPDS:18:6:3+1+1+0+J:J:J +'HIWPDS:19:5:3+1+1+J:J:J +'HIPROS:20:4:3+1+1+0 +'HIWPDS:21:4:3+1+1+J:J:J +'HIPROS:22:3:3+1+1 +'HIWPDS:23:3:3+1+1+J'XIADSS:24:1:3+1+1 +'HISALS:25:4:3+1+1 +'HISALS:26:5:3+1+1 +'HISALS:27:6:3+1+1+0 +'HIPINS:28:1:3+1+1+0+:::::HKSPA:J:HKKAZ:J:HKSAL:J:HKCCS:J:XKADA:N:HKTAN:N:HKWPD:N:HKPRO:N:XKADS:N +'HNHBS:29:1+2