diff --git a/.gitignore b/.gitignore index a1c2a23..15de9bf 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # Log file *.log +**/log/* # BlueJ files *.ctxt @@ -21,3 +22,8 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +# IntelliJ files +.idea/ +*.iml +**/target/ diff --git a/LICENSE b/LICENSE index 261eeb9..1939f9a 100644 --- a/LICENSE +++ b/LICENSE @@ -175,18 +175,7 @@ END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] + Copyright 2021 petaFuel GmbH Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/aeskey.properties.example b/aeskey.properties.example new file mode 100644 index 0000000..4021b16 --- /dev/null +++ b/aeskey.properties.example @@ -0,0 +1 @@ +aes_key=youraeskey \ No newline at end of file diff --git a/config/fuelifints.properties b/config/fuelifints.properties new file mode 100644 index 0000000..439742e --- /dev/null +++ b/config/fuelifints.properties @@ -0,0 +1,57 @@ +# Server Settings: +rdh_port = 3000 +ssl_port = 8443 +server_ip = 127.0.0.1 + +# SSL Keystore: +keystore_location = src/main/resources/keystore/your_keystore +keysotre_password = yourkeystorepassword + +# Default Bank Settings: +bankcode = 12345678 +bankname = FueliFinTS Testbank + +# RDH Keystore: +rdh_keystore_location = rdh.ks +rdh_keystore_cipher_alias = rdh_cipher +rdh_keystore_signer_alias = rdh_signer +rdh_keystore_ds_alias = rdh_ds +rdh_keystore_password = yourrdhpassword +rdh_keystore_cipher_password = yourrdhpassword +rdh_keystore_signer_password = yourrdhpassword +rdh_keystore_ds_password = yourrdhpassword +# AES key for database encryption +aes_key_location=aeskey.properties + +# Cipher Suites +use_cipher_suites = TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256;TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256;TLS_RSA_WITH_AES_128_CBC_SHA256;TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256;TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256;TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA;TLS_RSA_WITH_AES_128_CBC_SHA;TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA;TLS_ECDH_RSA_WITH_AES_128_CBC_SHA;TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA;TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA;SSL_RSA_WITH_3DES_EDE_CBC_SHA;TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA;TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA;TLS_EMPTY_RENEGOTIATION_INFO_SCSV;SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA;SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA;SSL_RSA_WITH_RC4_128_MD5 + +#don't use following cipher suites: +#TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 +#TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 +#TLS_DHE_RSA_WITH_AES_128_CBC_SHA +#TLS_DHE_DSS_WITH_AES_128_CBC_SHA +#TLS_ECDHE_ECDSA_WITH_RC4_128_SHA +#TLS_ECDHE_RSA_WITH_RC4_128_SHA +#SSL_RSA_WITH_RC4_128_SHA +#TLS_ECDH_ECDSA_WITH_RC4_128_SHA +#TLS_ECDH_RSA_WITH_RC4_128_SHA +#SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA +#SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA +#SSL_RSA_WITH_RC4_128_MD5 + +#protocols +use_protocols = TLSv1;TLSv1.1;TLSv1.2;SSLv3;SSLv2Hello + +#don't use following protocols +#SSLv2Hello +#SSLv3 + +#enabled HBCI Security Protocols, changes here needs to update the BPD version or clients won't recognize +hbci.security.protocols=PIN_2;RDH_9;RDH_10;RAH_9;RAH_10 +#dialog request timeout in milliseconds +dialog.timeout.millis=60000 + +productinfo.csv.filepath=FinTS_Produktregistrierungen_Lizenzdatei.csv +productinfo.csv.check=true +productinfo.csv.startRow=10 diff --git a/connectionpool.properties.example b/connectionpool.properties.example new file mode 100644 index 0000000..bd6291a --- /dev/null +++ b/connectionpool.properties.example @@ -0,0 +1,15 @@ +db.jdbcurl.12345678=jdbc:mysql://yourdbserver:3306/yourbankdb?characterEncoding=ISO-8859-1 +db.user.12345678=dbuser +db.password.12345678=dbpassword + +db.jdbcurl.23456789=jdbc:mysql://anotherdbserver:3306/anotherbankdb?characterEncoding=ISO-8859-1 +db.user.23456789=otherdbuser +db.password.23456789=otherdbpassword + +db.driverclass=com.mysql.jdbc.Driver + +db.pool.initialsize=1 +db.pool.maxactive=30 +db.pool.testonborrow=true +db.pool.validationquery=SELECT 1 +db.pool.maxwait=30000 diff --git a/src/main/java/net/petafuel/fuelifints/ExecutorManager.java b/src/main/java/net/petafuel/fuelifints/ExecutorManager.java new file mode 100644 index 0000000..1e64c9e --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/ExecutorManager.java @@ -0,0 +1,100 @@ +package net.petafuel.fuelifints; + +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.protocol.FinTSPayload; +import net.petafuel.fuelifints.protocol.IFinTSDecryptor; +import net.petafuel.fuelifints.protocol.IFinTSEncryptor; +import net.petafuel.fuelifints.protocol.IFinTSExecutor; +import net.petafuel.fuelifints.protocol.IFinTSInjector; +import net.petafuel.fuelifints.protocol.IFinTSParser; +import net.petafuel.fuelifints.protocol.IFinTSReplyThread; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class ExecutorManager { + private static final Logger LOG = LogManager.getLogger(ExecutorManager.class); + + private ThreadPoolExecutor decryptQueue = new ThreadPoolExecutor(2, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue(15)); + private ThreadPoolExecutor parseQueue = new ThreadPoolExecutor(2, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue(15)); + private ThreadPoolExecutor injectionQueue = new ThreadPoolExecutor(2, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue(15)); + private ThreadPoolExecutor executeQueue = new ThreadPoolExecutor(2, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue(15)); + private ThreadPoolExecutor encryptionQueue = new ThreadPoolExecutor(2, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue(15)); + private ThreadPoolExecutor replyQueue = new ThreadPoolExecutor(2, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue(15)); + + public boolean addToDecryptionQueue(Dialog dialog, FinTSPayload payload, IFinTSDecryptor decryptor) { + LOG.trace("addToDecryptionQueue called by {}", new Throwable().getStackTrace()[1].getMethodName()); + try { + decryptor.setPayload(payload); + decryptor.setDialog(dialog); + decryptQueue.execute(decryptor); + return true; + } catch (Exception e) { + LOG.error("addToDecryptionQueue", e); + return false; + } + } + + /** + * @param payload message from client + taskId + * @param parser FinTS3 or FinTS4 Parser + * @return true, if no problems with adding request to parseQueue occured + */ + public boolean addToParseQueue(Dialog dialog, FinTSPayload payload, IFinTSParser parser) { + LOG.trace("addToParseQueue called by {}", new Throwable().getStackTrace()[1].getMethodName()); + try { + parser.setPayload(payload); + parser.setDialog(dialog); + parseQueue.execute(parser); + return true; + } catch (Exception e) { + LOG.error("addToParseQueue", e); + return false; + } + } + + public boolean addToInjectionQueue(Dialog dialog, IFinTSInjector injector) { + try { + injector.setDialog(dialog); + injectionQueue.execute(injector); + return true; + } catch (Exception e) { + LOG.error("addToInjectionQueue", e); + return false; + } + } + + public boolean addToExecuteQueue(Dialog dialog, IFinTSExecutor executor) { + LOG.trace("addToExecuteQueue"); + try { + executor.setDialog(dialog); + executeQueue.execute(executor); + return true; + } catch (Exception e) { + LOG.error("addToExecuteQueue", e); + return false; + } + } + + public boolean addToReplyQueue(final Dialog dialog, final FinTSServer finTSServer, IFinTSReplyThread finTSReplyThread) { + LOG.trace("addToReplyQueue"); + finTSReplyThread.setDialog(dialog); + finTSReplyThread.setFinTSServer(finTSServer); + try { + replyQueue.execute(finTSReplyThread); + return true; + } catch (Exception e) { + LOG.error("addToReplyQueue", e); + return false; + } + } + + public void addToEncryptionQueue(Dialog dialog, IFinTSEncryptor finTSEncryptor) { + LOG.trace("addToEncryptionQueue"); + finTSEncryptor.setDialog(dialog); + encryptionQueue.execute(finTSEncryptor); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/FinTSServer.java b/src/main/java/net/petafuel/fuelifints/FinTSServer.java new file mode 100644 index 0000000..a499d96 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/FinTSServer.java @@ -0,0 +1,266 @@ +package net.petafuel.fuelifints; + +import net.petafuel.fuelifints.communication.*; +import net.petafuel.fuelifints.management.CommunicationManagement; +import net.petafuel.fuelifints.protocol.fints3.FinTS3Controller; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.management.MBeanServer; +import javax.management.Notification; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.lang.management.ManagementFactory; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Properties; + +public class FinTSServer { + private static final Logger LOG = LogManager.getLogger(FinTSServer.class); + + private final long timeoutMilliseconds; + + private final ArrayList runningRequests = new ArrayList(); + private MBeanServer mBeanServer; + private CommunicationManagement comManagement; + + private NIOSSLRequestListenerThread nioSSL; + private NIOSocketListenerThread nioSocket; + private BlockingSSLRequestListenerThread blockingSSL; + private BlockingSocketListenerThread blockingSocket; + + private InetAddress serverAdress; + + public static void main(String[] args) { + try { + LOG.info("* * * Starting FueliFinTS server * * *"); + + Thread.setDefaultUncaughtExceptionHandler((t, e) -> LOG.error("UncaughtException in {}", t, e)); + + FinTSServer server = new FinTSServer(); + + Runtime.getRuntime().addShutdownHook(new Thread(server::stop)); + + } catch (Exception e) { + LOG.error("Exception in FueliFinTS", e); + } + } + + private void stop() { + + try { + LOG.info("Stopping FueliFinTS server..."); + + //<< TODO: shutdown all open resources ... sockets etc >> + + this.stopListeners(); + + } catch (Exception ex) { + LOG.error("Exception stopping!", ex); + } + + } + + public FinTSServer() { + + Properties properties = new Properties(); + try { + BufferedInputStream stream = new BufferedInputStream(new FileInputStream("config/fuelifints.properties")); + properties.load(stream); + stream.close(); + } catch (Exception e) { + LOG.error("Die Datei fuelifints.properties konnte nicht geladen werden."); + LOG.error(e.getMessage()); + throw new RuntimeException(e); + } + + @SuppressWarnings("unchecked") + Enumeration enums = (Enumeration) properties.propertyNames(); + while (enums.hasMoreElements()) { + String key = enums.nextElement(); + String value = properties.getProperty(key); + System.setProperty(key, value); + } + + //Diverse Serverparameter können über die Java Console geändert werden, dafür wird die CommunicationManagement Klasse genutzt: + comManagement = new CommunicationManagement(); + timeoutMilliseconds = Long.parseLong(System.getProperty(CommunicationManagement.DIALOG_TIMEOUT_MILLIS, "10000")); + + IFinTSController controller = FinTS3Controller.getInstance(); + controller.setFinTSServer(this); + registerMBeans(); + + //Es erfolgt der Start der Listener für Socket und HTTPS Verbindungen: + startListeners(comManagement.isNioMode()); + } + + private void startListeners(boolean nioMode) { + if (comManagement.getServer_ip() != null + && !comManagement.getServer_ip().isEmpty() + && !comManagement.getServer_ip().equals("127.0.0.1")) + try { + this.serverAdress = InetAddress.getByName(comManagement.getServer_ip()); + } catch (UnknownHostException e) { + LOG.error("Unknown Host: {}", comManagement.getServer_ip(), e); + } + + try { + this.stopListeners(); + if (nioMode) { + if (comManagement.getSslPort() != 0) { + nioSSL = new NIOSSLRequestListenerThread(comManagement.getSslPort(), this); + nioSSL.start(); + } + if (comManagement.getSocketPort() != 0) { + nioSocket = new NIOSocketListenerThread(comManagement.getSocketPort(), this); + nioSocket.start(); + } + } else { + if (comManagement.getSocketPort() != 0) { + blockingSocket = new BlockingSocketListenerThread(comManagement.getSocketPort(), this); + blockingSocket.start(); + } + if (comManagement.getSslPort() != 0) { + blockingSSL = new BlockingSSLRequestListenerThread(comManagement.getSslPort(), this); + blockingSSL.start(); + } + } + } catch (Exception e) { + LOG.error("Error starting ServerListernerThreads: ", e); + } + } + + private void registerMBeans() { + try { + mBeanServer = ManagementFactory.getPlatformMBeanServer(); + + ObjectName cmName = new ObjectName("net.petafuel.fuelifints.management:type=CommunicationManagement"); + mBeanServer.registerMBean(comManagement, cmName); + mBeanServer.addNotificationListener(cmName, new CommunicationListener(), new CommunicationFilter(), cmName); + } catch (Exception ex) { + LOG.error("Error registering MBeans", ex); + } + } + + public InetAddress getServerAdress() { + return serverAdress; + } + + /** + * Beantwortet einen Request + * + * @param taskId Die ID, die diesem Request zugeordnet ist + * @param responseData Ein Byte Array, das als Antwort geschickt werden soll + * @param isLastMessage gibt an, ob es sich bei dieser Nachricht um die letzte Nachricht an den Nutzer handelt (ist entwedet bei HKEND (FinTS 3.0) oder eine Fehlernachricht der Fall) + */ + public boolean respond(int taskId, byte[] responseData, boolean isLastMessage) { + + LOG.debug("isLastMessage? {}", isLastMessage); + + /* + try { + LOG.info("received Response " + new String(responseData, "ISO-8859-1")); + } catch (UnsupportedEncodingException e) { + //ISO-8859-1 + } + */ + FinTSCommunicationHandler currentRequest = null; + synchronized (runningRequests) { + for (FinTSCommunicationHandler request : runningRequests) { + if (request.getTaskId() == taskId) { + currentRequest = request; + currentRequest.updateTimeStamp(); + break; + } + } + } + if (currentRequest != null) { + currentRequest.sendResponse(responseData, isLastMessage); + if (isLastMessage) { + currentRequest.setTimestamp(0); + } + cleanRunningRequests(); + } else { + LOG.error("could not find running request"); + } + return true; + } + + class CommunicationFilter implements NotificationFilter { + public boolean isNotificationEnabled(Notification n) { + return (n.getType().equals("jmx.attribute.change")); + } + } + + public boolean addRequest(FinTSCommunicationHandler req) { + synchronized (runningRequests) { + if (!runningRequests.contains(req)) { + runningRequests.add(req); + return true; + } + } + return false; + } + + private void cleanRunningRequests() { + long currentTimeStamp = new Date().getTime(); + synchronized (runningRequests) { + Iterator iterator = runningRequests.iterator(); + while (iterator.hasNext()) { + FinTSCommunicationHandler handler = iterator.next(); + if ((currentTimeStamp - handler.getTimestamp()) > timeoutMilliseconds) { + LOG.debug("Removing ... {}", handler); + iterator.remove(); + } + } + } + } + + private void stopListeners() { + try { + if (blockingSSL != null && blockingSSL.isAlive()) + blockingSSL.stopListening(); + + if (blockingSocket != null && blockingSocket.isAlive()) + blockingSocket.stopListening(); + + if (nioSSL != null && nioSSL.isAlive()) + nioSSL.stopListening(); + + if (nioSocket != null && nioSocket.isAlive()) + nioSocket.stopListening(); + + while (true) { + //wait for Threads to finish executing + if ((blockingSocket != null && blockingSocket.isAlive()) || + blockingSSL != null && blockingSSL.isAlive()) { + Thread.sleep(200); + } else if ((nioSSL != null && nioSSL.isAlive()) || + nioSocket != null && nioSocket.isAlive()) { + Thread.sleep(200); + } else break; + } + + } catch (Exception e) { + LOG.error("Error starting ServerListernerThreads!", e); + } + } + + class CommunicationListener implements NotificationListener { + @Override + public void handleNotification(Notification notification, Object o) { + String type = notification.getType(); + if (type.equals("jmx.attribute.change")) { + LOG.trace(notification.getMessage()); + startListeners(comManagement.isNioMode()); + } + } + } +} diff --git a/src/main/java/net/petafuel/fuelifints/FinTSVersionSwitch.java b/src/main/java/net/petafuel/fuelifints/FinTSVersionSwitch.java new file mode 100644 index 0000000..b0ba9e8 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/FinTSVersionSwitch.java @@ -0,0 +1,73 @@ +package net.petafuel.fuelifints; + +import net.petafuel.fuelifints.communication.FinTSCommunicationHandler; +import net.petafuel.fuelifints.protocol.FinTSPayload; +import net.petafuel.fuelifints.protocol.fints3.FinTS3Controller; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +public class FinTSVersionSwitch { + + public static enum FinTSVersion { + FINTS_VERSION_3_0, + FINTS_VERSION_4_0, + HBCI_VERSION_2_2, + FINTS_ERROR + } + + public static IFinTSController controller; //for testing purposes + + public static boolean selectController(int taskId, byte[] request, FinTSCommunicationHandler.CommunicationChannel channel) throws UnsupportedEncodingException { + FinTSPayload payload = new FinTSPayload(request, taskId); + switch (checkFinTSVersion(request)) { + case FINTS_VERSION_3_0: + if (controller == null) + FinTS3Controller.getInstance().newRequest(payload, channel); + else + controller.newRequest(payload, channel); + break; + case FINTS_VERSION_4_0: + //FinTS 4 wird noch nicht unterstützt! + //break; + case HBCI_VERSION_2_2: + FinTS3Controller.getInstance().replyWithError(taskId, generateVersionNotSupportedError()); + return false; + } + return true; + } + + private static FinTSVersion checkFinTSVersion(byte[] request) { + String version = new String(request, 23, 3, StandardCharsets.ISO_8859_1); + switch (version) { + case "300": + return FinTSVersion.FINTS_VERSION_3_0; + case "220": + return FinTSVersion.HBCI_VERSION_2_2; + default: + return FinTSVersion.FINTS_ERROR; + } + } + + private static String generateVersionNotSupportedError() { + String defaultBankcode = "12345678"; + String defaultBankname = "FueliFinTS Testbank"; + Properties properties = new Properties(); + try { + BufferedInputStream bis = new BufferedInputStream(new FileInputStream("config/fuelifints.properties")); + properties.load(bis); + bis.close(); + defaultBankcode = properties.getProperty("bankcode"); + defaultBankname = properties.getProperty("bankname"); + } catch (FileNotFoundException e) { + } catch (IOException e) { + } + + + return "HNHBK:1:3+000000000194+220+0+1'" + + "HIRMG:2:2+9010::HBCI Version wird nicht unterstützt. Bitte aktualisieren Sie Ihr Kundenprodukt'" + + "HIBPA:3:2:4+1+280:" + defaultBankcode + "+" + defaultBankname + "+0+1+300'" + + "HNHBS:4:1+1'"; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/HBCIParseException.java b/src/main/java/net/petafuel/fuelifints/HBCIParseException.java new file mode 100644 index 0000000..c3d590b --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/HBCIParseException.java @@ -0,0 +1,13 @@ +package net.petafuel.fuelifints; + +import net.petafuel.fuelifints.exceptions.HBCISyntaxException; + +public class HBCIParseException extends Exception { + public HBCIParseException(String reason) { + super(reason); + } + + public HBCIParseException(Exception e) { + super(e); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/IFinTSController.java b/src/main/java/net/petafuel/fuelifints/IFinTSController.java new file mode 100644 index 0000000..cbbe80a --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/IFinTSController.java @@ -0,0 +1,94 @@ +package net.petafuel.fuelifints; + +import net.petafuel.fuelifints.communication.FinTSCommunicationHandler; +import net.petafuel.fuelifints.exceptions.DependencyResolveException; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.Message; +import net.petafuel.fuelifints.protocol.FinTSPayload; + +/** + * Interface für den FinTSController, der den Dialogablauf organisiert + */ +public interface IFinTSController { + /** + * + * + * @param payload + * @return + */ + boolean newRequest(FinTSPayload payload, FinTSCommunicationHandler.CommunicationChannel channel); + + /** + * Nur für Unit Tests - manuelles Setzen des ExecutorManagers ist normalerweise nicht erforderlich + * + * @param manager Executormanager, der genutzt werden soll + */ + void setExecutor(ExecutorManager manager); + + /** + * Funktion wird vom {@link net.petafuel.fuelifints.protocol.IFinTSDecryptor} aufgerufen, wenn + * die Entschlüsselung der Kundennachricht fertiggestellt ist + * + * @param payload + * @param dialog + */ + void finishedDecryption(FinTSPayload payload, Dialog dialog); + + /** + * Funktion wird vom {@link net.petafuel.fuelifints.protocol.IFinTSParser} aufgerufen, wenn + * das Parsing der Kundennachricht abgeschlossen ist + * + * @param taskId + * @param dialog Dialog mit enthaltener Kundennachricht + */ + void finishedParsing(int taskId, Dialog dialog); + + /** + * Wird im {@link net.petafuel.fuelifints.protocol.IFinTSExecutor} vor dem eigentlichen execute aufgerufen, + * um die Vorraussetzungen für das Ausführen der Auftragssegmente des Kunden zu setzen + * + * @param elem + * @param dialog + * @throws DependencyResolveException + */ + void injectDependencies(IMessageElement elem, Dialog dialog) throws DependencyResolveException; + + /** + * Funktion wird vom {@link net.petafuel.fuelifints.protocol.IFinTSExecutor} aufgerufen, wenn + * das Ausführen der Kundenaufträge abgeschlossen und die Antwortsegmente generiert worden sind + * + * @param msg + */ + void finishedExecuting(Message msg); + + /** + * Funktion wird vom {@link net.petafuel.fuelifints.protocol.IFinTSEncryptor} aufgerufen, wenn + * die Antwortnachricht des Servers erfolgreich verschlüsselt wurde + * + * @param msg + */ + void finishedEncryption(Message msg); + + /** + * Gibt die Anzahl der Dialoge zurück, die gerade laufen und vom FinTSController verwaltet werden + * + * @return + */ + int getRunningDialogCount(); + + + /** + * Nur für Unit Tests, um manuell einen FinTSServer zu setzen (bzw zu mocken) + * + * @param finTSServer FinTSServer, der vom FinTSController genutzt werden soll + */ + void setFinTSServer(FinTSServer finTSServer); + + /** + * Getter für den aktuell benutzten FinTSServer + * + * @return FinTSServer, der gerade vom FinTSController genutzt wird + */ + FinTSServer getFinTSServer(); +} diff --git a/src/main/java/net/petafuel/fuelifints/communication/BlockingSSLRequestListenerThread.java b/src/main/java/net/petafuel/fuelifints/communication/BlockingSSLRequestListenerThread.java new file mode 100644 index 0000000..035092c --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/communication/BlockingSSLRequestListenerThread.java @@ -0,0 +1,240 @@ +package net.petafuel.fuelifints.communication; + +import net.petafuel.fuelifints.FinTSServer; +import net.petafuel.fuelifints.FinTSVersionSwitch; +import org.apache.http.ConnectionClosedException; +import org.apache.http.HttpEntity; +import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.HttpException; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.HttpResponseInterceptor; +import org.apache.http.HttpStatus; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.impl.DefaultConnectionReuseStrategy; +import org.apache.http.impl.DefaultHttpResponseFactory; +import org.apache.http.impl.DefaultHttpServerConnection; +import org.apache.http.message.BasicHeader; +import org.apache.http.params.CoreConnectionPNames; +import org.apache.http.params.CoreProtocolPNames; +import org.apache.http.params.HttpParams; +import org.apache.http.params.SyncBasicHttpParams; +import org.apache.http.protocol.BasicHttpContext; +import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpProcessor; +import org.apache.http.protocol.HttpRequestHandler; +import org.apache.http.protocol.HttpRequestHandlerRegistry; +import org.apache.http.protocol.HttpService; +import org.apache.http.protocol.ImmutableHttpProcessor; +import org.apache.http.protocol.ResponseConnControl; +import org.apache.http.protocol.ResponseContent; +import org.apache.http.protocol.ResponseDate; +import org.apache.http.protocol.ResponseServer; +import org.apache.http.util.EntityUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.util.encoders.Base64; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.Socket; +import java.util.Properties; + +public class BlockingSSLRequestListenerThread extends Thread { + + private static final Logger LOG = LogManager.getLogger(BlockingSSLRequestListenerThread.class); + private boolean shouldStop = false; + + + private final SSLServerSocket serversocket; + private final HttpParams params; + private final HttpService httpService; + + private FinTSServer finTSServer; + + public BlockingSSLRequestListenerThread(int port, FinTSServer server) throws IOException { + this.finTSServer = server; + Properties systemProps = System.getProperties(); + Properties properties = new Properties(); + String[] useCipherSuites = null; + String[] useProtocols = null; + try { + BufferedInputStream bis = new BufferedInputStream(new FileInputStream("config/fuelifints.properties")); + properties.load(bis); + bis.close(); + systemProps.put("javax.net.ssl.keyStore", properties.getProperty("keystore_location")); + systemProps.put("javax.net.ssl.keyStorePassword", properties.getProperty("keysotre_password")); + useCipherSuites = properties.getProperty("use_cipher_suites").split(";"); + useProtocols = properties.getProperty("use_protocols").split(";"); + } catch (Exception e) { + LOG.error("I/O error in constructor", e.getMessage()); + } + System.setProperties(systemProps); + + SSLServerSocketFactory sslsocketfactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + + serversocket = (SSLServerSocket) sslsocketfactory.createServerSocket(port, 0, finTSServer.getServerAdress()); + if (useCipherSuites != null) { + serversocket.setEnabledCipherSuites(useCipherSuites); + } + if (useProtocols != null) { + serversocket.setEnabledProtocols(useProtocols); + } + this.params = new SyncBasicHttpParams(); + this.params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000) + .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024) + .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false) + .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true) + .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1"); + + // Set up the HTTP protocol processor + HttpProcessor httpproc = new ImmutableHttpProcessor(new HttpResponseInterceptor[]{ + new ResponseDate(), + new ResponseServer(), + new ResponseContent(), + new ResponseConnControl() + }); + + // Set up request handlers + HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry(); + reqistry.register("*", new SslRequestHandler()); + + // Set up the HTTP service + this.httpService = new HttpService( + httpproc, + new DefaultConnectionReuseStrategy(), + new DefaultHttpResponseFactory(), + reqistry, + this.params); + } + + public void stopListening() { + try { + shouldStop = true; + serversocket.close(); + } catch (IOException e) { + LOG.error("I/O error in stopListening: ", e.getMessage()); + } + } + + @Override + public void run() { + LOG.info("SSLServer listening on port {}", this.serversocket.getLocalPort()); + shouldStop = false; + while (!shouldStop) { + try { + // Set up HTTP connection + LOG.info("Accepting new incoming connection"); + final Socket socket = this.serversocket.accept(); + + new Thread() { + public void run() { + DefaultHttpServerConnection conn = new DefaultHttpServerConnection(); + LOG.info("Incoming connection from {}", socket.getInetAddress()); + try { + conn.bind(socket, BlockingSSLRequestListenerThread.this.params); + } catch (IOException e) { + LOG.error("io exception ", e); + } + + HttpContext context = new BasicHttpContext(null); + while (conn.isOpen()) { + try { + BlockingSSLRequestListenerThread.this.httpService.handleRequest(conn, context); + } catch (ConnectionClosedException ex) { + LOG.debug("Client closed connection"); + try { + conn.shutdown(); + } catch (IOException e) { + + } + } catch (IOException ex) { + LOG.error("I/O error", ex.getMessage()); + LOG.debug("I/O error", ex.getStackTrace()); + try { + conn.shutdown(); + } catch (IOException e) { + + } + } catch (HttpException ex) { + LOG.debug("Unrecoverable HTTP protocol violation", ex); + try { + conn.shutdown(); + } catch (IOException e) { + + } + } /*finally { + try { + conn.shutdown(); + } catch (IOException ignore) { + } + } */ + } + } + }.start(); + } catch (InterruptedIOException e) { + LOG.error("Blocking SSL Listener interupted", e.getMessage()); + break; + } catch (IOException e) { + LOG.error("I/O error initializing connection thread", e.getMessage()); + LOG.debug("I/O error initializing connection thread", e.getStackTrace()); + break; + } + } + } + + class SslRequestHandler implements HttpRequestHandler { + + + @Override + public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { + + String method = request.getRequestLine().getMethod().toUpperCase(); + if (!method.equals("POST")) { + response.setStatusCode(HttpStatus.SC_METHOD_NOT_ALLOWED); + response.removeHeaders("Server"); + response.setHeader(new BasicHeader("Server", "")); + return; + + //throw new MethodNotSupportedException(method + " method not supported"); + } + + final HttpResponse httpResponse = response; + + FinTSCommunicationHandler handler = new FinTSCommunicationHandler(FinTSCommunicationHandler.CommunicationChannel.SSL) { + @Override + public void sendResponse(byte[] responseData, boolean isLastMessage) { + updateTimeStamp(); + httpResponse.setStatusCode(HttpStatus.SC_OK); + ByteArrayEntity entity = new ByteArrayEntity(Base64.encode(responseData)); + httpResponse.setEntity(entity); + synchronized (httpResponse) { + httpResponse.notifyAll(); + } + } + }; + + finTSServer.addRequest(handler); + + if (request instanceof HttpEntityEnclosingRequest) { + HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity(); + byte[] entityContent = EntityUtils.toByteArray(entity); + byte[] decoded = Base64.decode(new String(entityContent)); + if (FinTSVersionSwitch.selectController(handler.getTaskId(), decoded, FinTSCommunicationHandler.CommunicationChannel.SSL)) { + synchronized (httpResponse) { + try { + httpResponse.wait(); + } catch (InterruptedException ex) { + LOG.error(ex.getMessage()); + } + } + } + } + + } + } + +} diff --git a/src/main/java/net/petafuel/fuelifints/communication/BlockingSocketListenerThread.java b/src/main/java/net/petafuel/fuelifints/communication/BlockingSocketListenerThread.java new file mode 100644 index 0000000..bd2f622 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/communication/BlockingSocketListenerThread.java @@ -0,0 +1,160 @@ +package net.petafuel.fuelifints.communication; + +import net.petafuel.fuelifints.FinTSServer; +import net.petafuel.fuelifints.FinTSVersionSwitch; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.charset.StandardCharsets; + +public class BlockingSocketListenerThread extends Thread { + + private static final Logger LOG = LogManager.getLogger(BlockingSocketListenerThread.class); + private FinTSServer finTSServer; + private boolean shouldStop = false; + private ServerSocket socket; + + private int port; + + public BlockingSocketListenerThread(int port, FinTSServer server) { + this.finTSServer = server; + this.port = port; + + } + + public void stopListening() { + try { + shouldStop = true; + socket.close(); + } catch (IOException e) { + LOG.error("I/O error while stopListening", e); + } + } + + public void run() { + try { + shouldStop = false; + socket = new ServerSocket(port, 0, finTSServer.getServerAdress()); + LOG.info("SocketServer listening on port {}", socket.getLocalPort()); + while (!shouldStop) { + Socket clientSocket = socket.accept(); + new SocketWorkerThread(clientSocket).start(); + } + } catch (IOException e) { + LOG.error("I/O error in run()", e); + } + } + + class SocketWorkerThread extends Thread { + + private final Object monitor = new Object(); + private Socket clientSocket; + private boolean isLastMessage = false; + + public SocketWorkerThread(Socket clientSocket) { + super(); + this.clientSocket = clientSocket; + } + + @Override + public void run() { + LOG.info("New socket connection thread"); + SocketRequestHandler handler = new SocketRequestHandler(this); + finTSServer.addRequest(handler); + while (!shouldStop && !isLastMessage) { + handler.handle(this.clientSocket); + try { + if (!isLastMessage) { + synchronized (this) { + this.wait(); + } + } + } catch (InterruptedException ex) { + LOG.warn("Interrupted Exception", ex); + } + } + try { + clientSocket.close(); + } catch (IOException e) { + LOG.error("Exception closing clientSocket", e); + } + } + } + + class SocketRequestHandler extends FinTSCommunicationHandler { + private final Object monitor; + private Socket clientSocket; + private BufferedInputStream bis; + private BufferedOutputStream bos; + + public SocketRequestHandler(Object monitor) { + super(CommunicationChannel.SOCKET); + this.monitor = monitor; + } + + private void handle(Socket clientSocket) { + LOG.trace("begin handling socket..."); + try { + this.clientSocket = clientSocket; + if (bis == null) { + bis = new BufferedInputStream(this.clientSocket.getInputStream()); + } + int c; + ByteArrayOutputStream inputPuffer = new ByteArrayOutputStream(); + int i = 0; + + int messageSize = 0; + + while ((c = bis.read()) >= 0) { + //receivedMessage += (char) c; + inputPuffer.write(c); + i++; + if (messageSize != 0 && i == messageSize) + break; + + // Diese Abbruchbedingung ist HBCI 2 / 3 spezifisch und wird für FinTS 4 nicht mehr funktionieren: + if (inputPuffer.size() == 22) { //in den ersten 22 Zeichen einer HBCI Nachricht steht die Nachrichtenlänge + try { + String responseSize = new String(inputPuffer.toByteArray(), 10, 12, StandardCharsets.ISO_8859_1); + messageSize = Integer.parseInt(responseSize); + } catch (NumberFormatException nfe) { + LOG.error("Exception parsing responseSize as integer", nfe); + break; + } + } + } + if (inputPuffer.size() > 0) { + FinTSVersionSwitch.selectController(this.taskId, inputPuffer.toByteArray(), CommunicationChannel.SOCKET); + } else { + LOG.info("could not read data"); + } + } catch (IOException e) { + LOG.error("I/O error in handle(clientSocket)", e); + } + } + + @Override + public void sendResponse(byte[] response, boolean isLastMessage) { + updateTimeStamp(); + try { + if (bos == null) { + bos = new BufferedOutputStream(clientSocket.getOutputStream()); + } + bos.write(response); + bos.flush(); + } catch (IOException e) { + LOG.error("I/O error in sendResponse", e); + } + synchronized (monitor) { + ((SocketWorkerThread) monitor).isLastMessage = isLastMessage; + monitor.notifyAll(); + } + } + } +} diff --git a/src/main/java/net/petafuel/fuelifints/communication/FinTSCommunicationHandler.java b/src/main/java/net/petafuel/fuelifints/communication/FinTSCommunicationHandler.java new file mode 100644 index 0000000..769dced --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/communication/FinTSCommunicationHandler.java @@ -0,0 +1,40 @@ +package net.petafuel.fuelifints.communication; + +import java.util.Date; + +public abstract class FinTSCommunicationHandler { + + + public enum CommunicationChannel { + SSL, + SOCKET + } + + protected int taskId; + private long timestamp; + private CommunicationChannel communicationChannel; + + public FinTSCommunicationHandler(CommunicationChannel channel) { + this.communicationChannel = channel; + this.updateTimeStamp(); + this.taskId = (int) (Math.random() * Integer.MAX_VALUE); + } + + public int getTaskId() { + return taskId; + } + + public abstract void sendResponse(byte[] response, boolean isLastMessage); + + public void updateTimeStamp() { + this.timestamp = new Date().getTime(); + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/communication/NIOSSLRequestListenerThread.java b/src/main/java/net/petafuel/fuelifints/communication/NIOSSLRequestListenerThread.java new file mode 100644 index 0000000..17422b4 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/communication/NIOSSLRequestListenerThread.java @@ -0,0 +1,180 @@ +package net.petafuel.fuelifints.communication; + +import net.petafuel.fuelifints.FinTSServer; +import net.petafuel.fuelifints.FinTSVersionSwitch; +import org.apache.http.*; +import org.apache.http.impl.DefaultConnectionReuseStrategy; +import org.apache.http.impl.nio.DefaultHttpServerIODispatch; +import org.apache.http.impl.nio.DefaultNHttpServerConnection; +import org.apache.http.impl.nio.SSLNHttpServerConnectionFactory; +import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor; +import org.apache.http.nio.NHttpConnectionFactory; +import org.apache.http.nio.NHttpServerConnection; +import org.apache.http.nio.entity.NByteArrayEntity; +import org.apache.http.nio.protocol.*; +import org.apache.http.nio.reactor.IOEventDispatch; +import org.apache.http.nio.reactor.ListeningIOReactor; +import org.apache.http.params.CoreConnectionPNames; +import org.apache.http.params.CoreProtocolPNames; +import org.apache.http.params.HttpParams; +import org.apache.http.params.SyncBasicHttpParams; +import org.apache.http.protocol.*; +import org.apache.http.util.EntityUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetSocketAddress; +import java.security.KeyStore; + +public class NIOSSLRequestListenerThread extends Thread { + + private static final Logger LOG = LogManager.getLogger(NIOSSLRequestListenerThread.class); + + private int port; + private FinTSServer finTSServer; + private ListeningIOReactor ioReactor; + + public NIOSSLRequestListenerThread(int port, FinTSServer server) { + this.port = port; + this.finTSServer = server; + } + + public void stopListening() { + if (ioReactor != null) { + try { + long gracePeriod = 3000; + ioReactor.shutdown(gracePeriod); + } catch (IOException e) { + LOG.error("I/O error while shutting down ioReactor (stopListening)", e); + } + } + } + + /** + * @see Apache HttpCore NIO Example + */ + @Override + public void run() { + try { + /*** HTTP Core SSL Server ***/ + + // HTTP parameters for the server + HttpParams params = new SyncBasicHttpParams(); + params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000) + .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024) + .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1"); + + // Create HTTP protocol processing chain + HttpProcessor httpproc = new ImmutableHttpProcessor(new HttpResponseInterceptor[]{ + // Use standard server-side protocol interceptors + new ResponseDate(), + new ResponseServer(), + new ResponseContent(), + new ResponseConnControl() + }); + + // Create request handler registry + HttpAsyncRequestHandlerRegistry reqistry = new HttpAsyncRequestHandlerRegistry(); + // Register the default handler for all URIs + reqistry.register("*", new NioSSLHandler()); + + // Create server-side HTTP protocol handler + HttpAsyncService protocolHandler = new HttpAsyncService( + httpproc, new DefaultConnectionReuseStrategy(), reqistry, params) { + + @Override + public void connected(final NHttpServerConnection conn) { + LOG.info("{}: connection open", conn); + super.connected(conn); + } + + @Override + public void closed(final NHttpServerConnection conn) { + LOG.info("{}: connection closed", conn); + super.closed(conn); + } + + }; + + // Create HTTP connection factory + NHttpConnectionFactory connFactory; + // Initialize SSL context + final KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(new FileInputStream("src/test/resources/keystore/keystore"), "123456".toCharArray()); + KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmfactory.init(ks, "123456".toCharArray()); + KeyManager[] keymanagers = kmfactory.getKeyManagers(); + SSLContext sslcontext = SSLContext.getInstance("TLS"); + sslcontext.init(keymanagers, null, null); + connFactory = new SSLNHttpServerConnectionFactory(sslcontext, null, params); + + // Create server-side I/O event dispatch + IOEventDispatch ioEventDispatch = new DefaultHttpServerIODispatch(protocolHandler, connFactory); + // Create server-side I/O reactor + ioReactor = new DefaultListeningIOReactor(); + try { + LOG.info("NIO SSLServer listening on port {}", port); + // Listen of the given port + ioReactor.listen(new InetSocketAddress(finTSServer.getServerAdress(), port)); + // Ready to go! + ioReactor.execute(ioEventDispatch); + } catch (InterruptedIOException ex) { + LOG.error("Interrupted", ex); + } catch (IOException e) { + LOG.error("I/O error", e); + } + LOG.info("Shutdown"); + } catch (Exception e) { + LOG.error("Exception", e); + } + } + + private class NioSSLHandler extends FinTSCommunicationHandler implements HttpAsyncRequestHandler { + + HttpResponse response; + HttpAsyncExchange exchange; + + public NioSSLHandler() { + super(CommunicationChannel.SSL); + } + + @Override + public HttpAsyncRequestConsumer processRequest(HttpRequest request, HttpContext httpContext) throws HttpException, IOException { + return new BasicAsyncRequestConsumer(); + } + + @Override + public void handle(HttpRequest request, HttpAsyncExchange httpAsyncExchange, HttpContext httpContext) throws HttpException, IOException { + finTSServer.addRequest(this); + + this.response = httpAsyncExchange.getResponse(); + this.exchange = httpAsyncExchange; + + String method = request.getRequestLine().getMethod().toUpperCase(); + if (!method.equals("POST")) { + throw new MethodNotSupportedException(method + " method not supported"); + } + + if (request instanceof HttpEntityEnclosingRequest) { + HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity(); + byte[] entityContent = EntityUtils.toByteArray(entity); + FinTSVersionSwitch.selectController(this.taskId, entityContent, CommunicationChannel.SSL); + } + } + + @Override + public void sendResponse(byte[] responseData, boolean isLastMessage) { + updateTimeStamp(); + response.setStatusCode(HttpStatus.SC_OK); + NByteArrayEntity entity = new NByteArrayEntity(responseData); + response.setEntity(entity); + exchange.submitResponse(new BasicAsyncResponseProducer(response)); + } + } +} diff --git a/src/main/java/net/petafuel/fuelifints/communication/NIOSocketListenerThread.java b/src/main/java/net/petafuel/fuelifints/communication/NIOSocketListenerThread.java new file mode 100644 index 0000000..9d0f39c --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/communication/NIOSocketListenerThread.java @@ -0,0 +1,184 @@ +package net.petafuel.fuelifints.communication; + +import net.petafuel.fuelifints.FinTSServer; +import net.petafuel.fuelifints.FinTSVersionSwitch; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; + +public class NIOSocketListenerThread extends Thread { + private static final Logger LOG = LogManager.getLogger(NIOSocketListenerThread.class); + private int port; + private FinTSServer finTSServer; + private Selector socketSelector; + ByteBuffer buffer = ByteBuffer.allocate(512); + CharsetEncoder encoder; + CharsetDecoder decoder; + ServerSocketChannel serverSocketChannel; + private boolean shouldStop = false; + + private ArrayList runningWorkers = new ArrayList(); + + public NIOSocketListenerThread(int port, FinTSServer server) { + this.finTSServer = server; + this.port = port; + Charset charset = Charset.forName("ISO-8859-1"); //Latin-1 + encoder = charset.newEncoder(); + decoder = charset.newDecoder(); + } + + public void stopListening() { + if (socketSelector != null) { + try { + shouldStop = true; + serverSocketChannel.close(); + socketSelector.wakeup(); + } catch (IOException e) { + LOG.error("I/O error while shutting down ioReactor (stopListening)", e); + } + } + } + + public void run() { + try { + shouldStop = false; + + socketSelector = Selector.open(); + + serverSocketChannel = ServerSocketChannel.open(); + serverSocketChannel.socket().bind(new InetSocketAddress(finTSServer.getServerAdress(), this.port)); + serverSocketChannel.configureBlocking(false); + + serverSocketChannel.register(socketSelector, SelectionKey.OP_ACCEPT); + + LOG.info("NIO SocketServer listening on port {}", port); + + while (!shouldStop) { + //Check if Worker is ready: + for (NioSocketWorker worker : runningWorkers) { + if (worker.isReady) { + worker.nioSocketClientChannel.keyFor(socketSelector).interestOps(SelectionKey.OP_WRITE); + } + } + + //waiting for events from the selector + socketSelector.select(); + Set keys = socketSelector.selectedKeys(); + + for (Iterator i = keys.iterator(); i.hasNext(); ) { + SelectionKey key = (SelectionKey) i.next(); + i.remove(); + + if (!key.isValid()) + continue; + + //isAcceptable? + if (key.isAcceptable()) { + //get Client socket Channel: + SocketChannel client = serverSocketChannel.accept(); + client.configureBlocking(false); + //read to the selector + client.register(socketSelector, SelectionKey.OP_READ); + continue; + } + + //isReadable? + if (key.isReadable()) { + SocketChannel client = (SocketChannel) key.channel(); + new NioSocketWorker(client, key); + continue; + } + + //isWriteable? + if (key.isWritable()) { + for (NioSocketWorker worker : runningWorkers) { + if (worker.isReady && worker.nioSocketClientChannel.equals(key.channel())) { + worker.writeResponse(); + key.interestOps(SelectionKey.OP_READ); + } + } + } + } + } + } catch (IOException e) { + LOG.error("I/O error: ", e); + } catch (ClosedSelectorException e) { + LOG.error("selector closed: ", e); + } + } + + private class NioSocketWorker extends FinTSCommunicationHandler { + private boolean isReady = false; + private byte[] response; + private SocketChannel nioSocketClientChannel; + + + private NioSocketWorker(SocketChannel client, SelectionKey key) { + super(CommunicationChannel.SOCKET); + + try { + //Read Data From ClientSocket: + buffer.clear(); + + // Read byte coming from the client + int numRead = -1; + try { + numRead = client.read(buffer); + } catch (Exception e) { + // client is no longer active + key.cancel(); + client.close(); + LOG.error("Exception", e); + } + + if (numRead == -1) { + // client shut down channel cleanly + key.channel().close(); + key.cancel(); + } + + buffer.flip(); + buffer.clear(); + + this.nioSocketClientChannel = client; + finTSServer.addRequest(this); + if (!runningWorkers.contains(this)) + runningWorkers.add(this); + FinTSVersionSwitch.selectController(this.taskId, buffer.array(), CommunicationChannel.SOCKET); + } catch (CharacterCodingException e) { + LOG.error("Encoding error", e); + } catch (IOException e) { + LOG.error("I/O error", e); + } + } + + @Override + public void sendResponse(byte[] response, boolean isLastMessage) { + updateTimeStamp(); + LOG.info("Received Response: {}", this.nioSocketClientChannel); + this.response = response; + this.isReady = true; + socketSelector.wakeup(); + } + + private void writeResponse() { + try { + nioSocketClientChannel.write(ByteBuffer.wrap(response)); + this.isReady = false; + } catch (IOException e) { + LOG.error("I/O error in writeResponse", e); + } + } + } +} diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/Cryptography.java b/src/main/java/net/petafuel/fuelifints/cryptography/Cryptography.java new file mode 100644 index 0000000..a8b8d1e --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/Cryptography.java @@ -0,0 +1,8 @@ +package net.petafuel.fuelifints.cryptography; + +public interface Cryptography { + public byte[] decrypt(byte[] encrypted, byte[] key, String bankId); + public byte[] encryptMessage(byte[] message, byte[] key, String bankId); + public byte[] encryptEncryptionKey(byte[] encryptionKey, String bankId, String userId); + public byte[] generateKey(); +} diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/CryptographyFactory.java b/src/main/java/net/petafuel/fuelifints/cryptography/CryptographyFactory.java new file mode 100644 index 0000000..9e39bca --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/CryptographyFactory.java @@ -0,0 +1,22 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; + +public class CryptographyFactory { + public static Cryptography getCryptography(SecurityMethod securityMethod) { + switch (securityMethod) { + case PIN_1: + case PIN_2: + return new PinTanCryptography(); + case RDH_10: + return new RDH10Cryptography(); + case RAH_10: + return new RAH10Cryptography(); + case RDH_9: + return new RDH9Cryptography(); + case RAH_9: + return new RAH9Cryptography(); + } + return null; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/KeyManager.java b/src/main/java/net/petafuel/fuelifints/cryptography/KeyManager.java new file mode 100644 index 0000000..b4d954b --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/KeyManager.java @@ -0,0 +1,245 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.util.encoders.Base64; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.UnrecoverableEntryException; +import java.security.cert.CertificateException; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Properties; + +public class KeyManager { + + private static final Logger LOG = LogManager.getLogger(KeyManager.class); + + { + Properties properties = new Properties(); + try { + BufferedInputStream bis = new BufferedInputStream(new FileInputStream("config/fuelifints.properties")); + properties.load(bis); + bis.close(); + //RDH zeugs ... + KEY_STORE_PATH = properties.getProperty("rdh_keystore_location"); + PRIVATE_KEY_CIPHER_ALIAS = properties.getProperty("rdh_keystore_cipher_alias"); + PRIVATE_KEY_SIGNER_ALIAS = properties.getProperty("rdh_keystore_signer_alias"); + PRIVATE_KEY_DS_ALIAS = properties.getProperty("rdh_keystore_ds_alias"); + KEY_STORE_PASSWORD = properties.getProperty("rdh_keystore_password"); + CIPHER_ALIAS_PASSWORD = properties.getProperty("rdh_keystore_cipher_password"); + SIGNER_ALIAS_PASSWORD = properties.getProperty("rdh_keystore_signer_password"); + DS_ALIAS_PASSWORD = properties.getProperty("rdh_keystore_ds_password"); + } catch (IOException ex) { + LOG.error("Could not read from properties", ex); + } + } + + + private static String KEY_STORE_PASSWORD; + private static String CIPHER_ALIAS_PASSWORD; + private static String SIGNER_ALIAS_PASSWORD; + private static String DS_ALIAS_PASSWORD; + private static String KEY_STORE_PATH = "rdh.ks"; + private static String PRIVATE_KEY_CIPHER_ALIAS = "rdh_cipher"; + private static String PRIVATE_KEY_SIGNER_ALIAS = "rdh_signer"; + private static String PRIVATE_KEY_DS_ALIAS = "rdh_ds"; + private static HashMap instances = new HashMap<>(); + private String bankId; + private DataAccessFacade dataAccessFacade; + private PrivateKey signingPrivateKey; + private PublicKey signingPublicKey; + private PrivateKey cipherPrivateKey; + private PublicKey cipherPublicKey; + private PrivateKey dsPrivateKey; + private PublicKey dsPublicKey; + + private KeyManager() { + } + + /** + * @return + */ + public static final KeyManager getInstance(String bankId) { + KeyManager instance = null; + synchronized (instances) { + instance = instances.get(bankId); + if (instance == null) { + instance = new KeyManager(); + instance.bankId = bankId; + instance.dataAccessFacade = DataAccessFacadeManager.getAccessFacade(bankId); + instances.put(bankId, instance); + } + } + return instance; + } + + public PrivateKey getPrivateKey(SecurityMethod securityMethod, String keyType) { + if (signingPrivateKey == null) { + readKeysFromKeyStore(keyType); + } + switch (keyType) { + case "S": + return signingPrivateKey; + case "V": + return cipherPrivateKey; + case "D": + return dsPrivateKey; + default: + return null; + } + } + + private void readKeysFromKeyStore(String keyType) { + try { + KeyStore keyStore = KeyStore.getInstance("JKS"); + File keyStoreFile = new File(KEY_STORE_PATH); + keyStore.load(new FileInputStream(keyStoreFile), getKeyStorePassword()); + + KeyStore.ProtectionParameter protectionParameter = new KeyStore.PasswordProtection(getPrivateKeyAliasPassword(keyType)); + KeyStore.PrivateKeyEntry privateKeyEntry; + privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(PRIVATE_KEY_SIGNER_ALIAS, protectionParameter); + signingPrivateKey = privateKeyEntry.getPrivateKey(); + signingPublicKey = privateKeyEntry.getCertificate().getPublicKey(); + privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(PRIVATE_KEY_CIPHER_ALIAS, protectionParameter); + cipherPrivateKey = privateKeyEntry.getPrivateKey(); + cipherPublicKey = privateKeyEntry.getCertificate().getPublicKey(); + privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(PRIVATE_KEY_DS_ALIAS, protectionParameter); + dsPrivateKey = privateKeyEntry.getPrivateKey(); + dsPublicKey = privateKeyEntry.getCertificate().getPublicKey(); + } catch (KeyStoreException e) { + LOG.error("Error while accessing keystore ", e); + } catch (CertificateException e) { + LOG.error("Error while accessing keystore ", e); + } catch (NoSuchAlgorithmException e) { + LOG.error("Error while accessing keystore ", e); + } catch (UnrecoverableEntryException e) { + LOG.error("Error while accessing keystore ", e); + } catch (IOException e) { + LOG.error("Error while accessing keystore ", e); + } + + } + + private char[] getPrivateKeyAliasPassword(String keyType) { + if (keyType.equals("S")) { + return SIGNER_ALIAS_PASSWORD.toCharArray(); + } else if (keyType.equals("C")) { + return CIPHER_ALIAS_PASSWORD.toCharArray(); + } else { + return DS_ALIAS_PASSWORD.toCharArray(); + } + } + + private char[] getKeyStorePassword() { + return KEY_STORE_PASSWORD.toCharArray(); + } + + public PublicKey getPublicKey(SecurityMethod securityMethod, String keyType) { + if (!dataAccessFacade.hasKeyPair(securityMethod, keyType)) { + readKeysFromKeyStore(keyType); + switch (keyType) { + case "S": + dataAccessFacade.updateKeyPair(securityMethod, "", new String(Base64.encode(signingPublicKey.getEncoded())), keyType, ((RSAPublicKey) signingPublicKey).getModulus(), ((RSAPublicKey) signingPublicKey).getPublicExponent()); + break; + case "V": + dataAccessFacade.updateKeyPair(securityMethod, "", new String(Base64.encode(cipherPublicKey.getEncoded())), keyType, ((RSAPublicKey) cipherPublicKey).getModulus(), ((RSAPublicKey) cipherPublicKey).getPublicExponent()); + break; + case "D": + dataAccessFacade.updateKeyPair(securityMethod, "", new String(Base64.encode(dsPublicKey.getEncoded())), keyType, ((RSAPublicKey) dsPublicKey).getModulus(), ((RSAPublicKey) dsPublicKey).getPublicExponent()); + break; + } + } + String encodedPublicKey = dataAccessFacade.getPublicKey(securityMethod, keyType); + try { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(encodedPublicKey)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(keySpec); + return publicKey; + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } catch (InvalidKeySpecException e) { + throw new RuntimeException(e); + } + } + + /* + private void generateAndStoreKeyPair(SecurityMethod securityMethod, String keyType) { + try { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); + int bits = 0; + switch (securityMethod) { + case RDH_9: + case RDH_10: + case RAH_9: + case RAH_10: + bits = 2048; + break; + } + keyPairGenerator.initialize(bits, secureRandom); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + PublicKey signingPublicKey = keyPair.getPublic(); + PrivateKey signingPrivateKey = keyPair.getPrivate(); + String base64PublicKey = new String(Base64.encode(signingPublicKey.getEncoded())); + String base64PrivateKey = new String(Base64.encode(signingPrivateKey.getEncoded())); + + try { + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(null,getKeyStorePassword()); + + KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry(signingPrivateKey); + } catch (KeyStoreException e) { + + } + + dataAccessFacade.updateKeyPair(securityMethod, base64PrivateKey, base64PublicKey, keyType); + } catch (NoSuchAlgorithmException e) { + } + } + */ + + public boolean addUserPublicKey(String userId, SecurityMethod securityMethod, String keyType, int keyVersion, int keyNumber, PublicKey publicKey) { + String base64PublicKey = new String(Base64.encode(publicKey.getEncoded())); + return dataAccessFacade.addUserPublicKey(userId, null, securityMethod, base64PublicKey, keyType, keyVersion, keyNumber, ((RSAPublicKey) publicKey).getModulus(), ((RSAPublicKey) publicKey).getPublicExponent()); + } + + public PublicKey getUserPublicKey(String userId, String customerId, SecurityMethod securityMethod, String keyType, int keyVersion, int keyNumber) { + String encodedPublicKey = dataAccessFacade.getUserPublicKey(userId, customerId, securityMethod, keyType, keyVersion, keyNumber); + if (encodedPublicKey == null) { + return null; + } + try { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(encodedPublicKey)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(keySpec); + return publicKey; + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } catch (InvalidKeySpecException e) { + throw new RuntimeException(e); + } + } + + public boolean existsPublicKey(String userId, String customerId, SecurityMethod securityMethod, String keyType, Integer keyVersion, Integer keyNumber) { + return dataAccessFacade.existsUserKey(userId, customerId, securityMethod, keyType, keyVersion, keyNumber); + } + + public PublicKey getUserPublicKey(String userId, String customerId, SecurityMethod securityMethod, String keyType) { + return getUserPublicKey(userId, customerId, securityMethod, keyType, -1, securityMethod.getVersionNumber()); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/PinTanCryptography.java b/src/main/java/net/petafuel/fuelifints/cryptography/PinTanCryptography.java new file mode 100644 index 0000000..36604a1 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/PinTanCryptography.java @@ -0,0 +1,31 @@ +package net.petafuel.fuelifints.cryptography; + +public class PinTanCryptography implements Cryptography { + + /** + * Beim PIN/TAN Verfahren sind die Daten in HNVSD nicht verschlüsselt. + * Gilt für HBCI 2.2+ , FinTS 3 - FinTS 4 muss geprüft werden. + * @param encrypted + * @param key + * @return + */ + @Override + public byte[] decrypt(byte[] encrypted, byte[] key, String bankId) { + return encrypted; + } + + @Override + public byte[] encryptMessage(byte[] message, byte[] key, String bankId) { + return message; + } + + @Override + public byte[] encryptEncryptionKey(byte[] encryptionKey, String bankId, String userId) { + return encryptionKey; + } + + @Override + public byte[] generateKey() { + return new byte[]{0,0,0,0,0,0,0,0}; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/PinTanSignatureHelper.java b/src/main/java/net/petafuel/fuelifints/cryptography/PinTanSignatureHelper.java new file mode 100644 index 0000000..6fde2da --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/PinTanSignatureHelper.java @@ -0,0 +1,69 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.client.LegitimationInfo; +import net.petafuel.fuelifints.protocol.fints3.FinTS3Controller; +import net.petafuel.fuelifints.protocol.fints3.segments.HNSHA; +import net.petafuel.fuelifints.protocol.fints3.segments.HNSHK; + +public class PinTanSignatureHelper implements SignatureHelper { + @Override + public ReturnDataObject validateSignature(byte[] bytes, HNSHK hnshk, HNSHA hnsha, Dialog dialog) { + /* + * In PIN/TAN wird keine Signatur der Nachricht erstellt. + * Es werden nur das Passwort und die Tan eingestellt. + */ + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(hnshk.getSchluesselname().getKreditsinstitutkennung().getKreditinstitutscode()); + String userId = hnshk.getSchluesselname().getBenutzerkennung(); + String pin = hnsha.getBenutzerdefinierteSignatur().getPin(); + ReturnDataObject checkSignature = dataAccessFacade.checkUserPin(userId, pin, dialog); + if (!checkSignature.isSuccess()) { + dialog.getLegitimationsInfo().setUserId(userId); + dialog.setUserId(userId); + dialog.getLegitimationsInfo().setCustomerId(userId); + } + String tan = hnsha.getBenutzerdefinierteSignatur().getTan(); + + //TAN zwischenspeichern für Prepaid Backend + if (dialog.getLegitimationsInfo() != null) { + LegitimationInfo legitimationInfo = dialog.getLegitimationsInfo(); + legitimationInfo.setTanResponse(tan); + } + + if (tan == null && dialog.getTransactionInfo() != null && dialog.getLegitimationsInfo().isStrongAuthenticated()) { + //falls keine TAN mit geschickt wurde, aber der Kunde stark authentifiziert ist + //dann wird auch keine benötigt + checkSignature.setSuccess(true); + } else if(tan == null && dialog.getTransactionInfo() != null) { + //falls eine TAN erwartet, aber keine mitgeliefert wird schlägt die TAN Prüfung fehl + //und es sollen keine Aufträge ausgeführt werden, unabhängig ob die PIN korrekt ist + checkSignature.setSuccess(false); + } else if (dialog.getTransactionInfo() != null && checkSignature.isSuccess()) { + //es wurde eine TAN übermittelt und es gibt das Transaktions-Objekt + ReturnDataObject checkTan = dataAccessFacade.checkTan(dialog.getDialogId(), dialog.getLegitimationsInfo(), dialog.getClientProductInfo(),tan, dialog.getTransactionInfo().getAuftragsHashwert()); + checkSignature.setSuccess(checkTan.isSuccess()); + if(!"".equals(checkTan.getMessage())) + { + checkSignature.setMessage(checkTan.getMessage()); + } + if(!"".equals(checkTan.getReturnCode())) + { + checkSignature.setReturnCode(checkTan.getReturnCode()); + } + + if (checkTan.isSuccess()) { + dialog.getLegitimationsInfo().setStrongAuthenticated(true); + FinTS3Controller.getInstance().updateLegitimationInfo(dialog.getDialogId(), dialog.getLegitimationsInfo()); + } + } + return checkSignature; + } + + @Override + public byte[] sign(byte[] toSign, HNSHK hnshk) { + return null; // Kein Validierungsresultat bei PIN / TAN + } +} diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/RAH10Cryptography.java b/src/main/java/net/petafuel/fuelifints/cryptography/RAH10Cryptography.java new file mode 100644 index 0000000..4a6b8d8 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/RAH10Cryptography.java @@ -0,0 +1,151 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.*; + +public class RAH10Cryptography implements Cryptography { + + private static final Logger LOG = LogManager.getLogger(RAH10Cryptography.class); + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + private byte[] iv = new byte[16]; + + @Override + public byte[] decrypt(byte[] encrypted, byte[] key, String bankId) { + try { + KeyManager keyManager = KeyManager.getInstance(bankId); + PrivateKey privateKey = keyManager.getPrivateKey(SecurityMethod.RAH_10, "V"); + Cipher cipher = Cipher.getInstance("RSA/NONE/NOPADDING"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] symmetricKey = cipher.doFinal(key); + LOG.debug("decryptedKeyLength: {}", symmetricKey.length); + + // Always read the symmetric key as bytes to avoid 0 removal from the beginning of the key + // number datatypes will cut leading zeros + byte[] temp = new byte[32]; + System.arraycopy(symmetricKey, 0, temp, 32 - symmetricKey.length, symmetricKey.length); + + symmetricKey = temp; + + SecretKeySpec algorithmParameterSpec = new SecretKeySpec(symmetricKey, "AES"); + + Cipher aesCipher = Cipher.getInstance("AES/CBC/NOPADDING"); + aesCipher.init(Cipher.DECRYPT_MODE, algorithmParameterSpec, new IvParameterSpec(iv)); + return removeZkaPadding(aesCipher.doFinal(encrypted)); + + } catch (NoSuchAlgorithmException e) { + LOG.error("An Error occurred during decryption.", e.getMessage()); + } catch (NoSuchPaddingException e) { + LOG.error("An Error occurred during decryption.", e.getMessage()); + } catch (InvalidKeyException e) { + LOG.error("An Error occurred during decryption.", e.getMessage()); + } catch (BadPaddingException e) { + LOG.error("An Error occurred during decryption.", e.getMessage()); + } catch (IllegalBlockSizeException e) { + LOG.error("An Error occurred during decryption.", e.getMessage()); + } catch (InvalidAlgorithmParameterException e) { + LOG.error("An Error occurred during decryption.", e.getMessage()); + } + return new byte[0]; + } + + private byte[] removeZkaPadding(byte[] bytes) { + int index = bytes.length - 1; + for (; index >= 0; index--) { + if (bytes[index] == (byte) 0x80) { + break; + } + } + byte[] message = new byte[index]; + System.arraycopy(bytes, 0, message, 0, message.length); + return message; + } + + @Override + public byte[] encryptMessage(byte[] message, byte[] key, String bankId) { + byte[] paddedMessage = zkaPadding(message); + + try { + SecretKeySpec algorithmParameterSpec = new SecretKeySpec(key, "AES"); + Cipher aesCipher = Cipher.getInstance("AES/CBC/NOPADDING"); + aesCipher.init(Cipher.ENCRYPT_MODE, algorithmParameterSpec, new IvParameterSpec(iv)); + return aesCipher.doFinal(paddedMessage); + } catch (NoSuchAlgorithmException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (NoSuchPaddingException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (InvalidKeyException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (BadPaddingException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (IllegalBlockSizeException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (InvalidAlgorithmParameterException e) { + LOG.error("An Error occurred during decryption.", e); + } + return new byte[0]; + } + + private byte[] zkaPadding(byte[] message) { + byte[] paddedMessage; + int length = (message.length + 1) % 16; + if (length == 0) { + paddedMessage = new byte[message.length + 1]; + System.arraycopy(message, 0, paddedMessage, 0, message.length); + paddedMessage[paddedMessage.length - 1] = (byte) 0x80; + } else { + paddedMessage = new byte[message.length + 1 + (16 - length)]; + System.arraycopy(message, 0, paddedMessage, 0, message.length); + paddedMessage[message.length] = (byte) 0x80; + } + return paddedMessage; + } + + @Override + public byte[] encryptEncryptionKey(byte[] encryptionKey, String bankId, String userId) { + KeyManager keyManager = KeyManager.getInstance(bankId); + PublicKey publicKey = keyManager.getUserPublicKey(userId, null, SecurityMethod.RAH_10, "V"); + try { + Cipher cipher = Cipher.getInstance("RSA/NONE/NOPADDING"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return cipher.doFinal(encryptionKey); + } catch (IllegalBlockSizeException e) { + LOG.error("An Error occurred during encryption.", e); + } catch (BadPaddingException e) { + LOG.error("An Error occurred during encryption.", e); + } catch (InvalidKeyException e) { + LOG.error("An Error occurred during encryption.", e); + } catch (NoSuchAlgorithmException e) { + LOG.error("An Error occurred during encryption.", e); + } catch (NoSuchPaddingException e) { + LOG.error("An Error occurred during encryption.", e); + } + return new byte[0]; + } + + @Override + public byte[] generateKey() { + SecureRandom secureRandom = new SecureRandom(); + byte[] secretKey = new byte[32]; + secureRandom.nextBytes(secretKey); + //starmoney key fix + if (secretKey[0] == 0) { + secretKey[0] = 1; + } + LOG.info("SecretKeyLength: {}", secretKey.length); + return secretKey; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/RAH9Cryptography.java b/src/main/java/net/petafuel/fuelifints/cryptography/RAH9Cryptography.java new file mode 100644 index 0000000..0a8c5ae --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/RAH9Cryptography.java @@ -0,0 +1,124 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.*; + +public class RAH9Cryptography implements Cryptography { + + private static final Logger LOG = LogManager.getLogger(RAH9Cryptography.class); + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + private byte[] iv = new byte[16]; + + @Override + public byte[] decrypt(byte[] encrypted, byte[] key, String bankId) { + try { + KeyManager keyManager = KeyManager.getInstance(bankId); + PrivateKey privateKey = keyManager.getPrivateKey(SecurityMethod.RAH_9, "V"); + Cipher cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] symmetricKey = cipher.doFinal(key); + LOG.debug("decryptedKeyLength: {}", symmetricKey.length); + + SecretKeySpec algorithmParameterSpec = new SecretKeySpec(symmetricKey, "AES"); + + Cipher aesCipher = Cipher.getInstance("AES/CBC/NOPADDING"); + aesCipher.init(Cipher.DECRYPT_MODE, algorithmParameterSpec, new IvParameterSpec(iv)); + return removeZkaPadding(aesCipher.doFinal(encrypted)); + + } catch (NoSuchAlgorithmException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (NoSuchPaddingException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (InvalidKeyException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (BadPaddingException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (IllegalBlockSizeException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (InvalidAlgorithmParameterException e) { + LOG.error("An Error occurred during decryption.", e); + } + return new byte[0]; + } + + private byte[] removeZkaPadding(byte[] bytes) { + int index = bytes.length - 1; + for (; index >= 0; index--) { + if (bytes[index] == (byte) 0x80) { + break; + } + } + byte[] message = new byte[index]; + System.arraycopy(bytes, 0, message, 0, message.length); + return message; + } + + @Override + public byte[] encryptMessage(byte[] message, byte[] key, String bankId) { + return new RAH10Cryptography().encryptMessage(message, key, bankId); + } + + private byte[] zkaPadding(byte[] message) { + byte[] paddedMessage; + int length = (message.length + 1) % 16; + if (length == 0) { + paddedMessage = new byte[message.length + 1]; + System.arraycopy(message, 0, paddedMessage, 0, message.length); + paddedMessage[paddedMessage.length - 1] = (byte) 0x80; + } else { + paddedMessage = new byte[message.length + 1 + (16 - length)]; + System.arraycopy(message, 0, paddedMessage, 0, message.length); + paddedMessage[message.length] = (byte) 0x80; + } + return paddedMessage; + } + + @Override + public byte[] encryptEncryptionKey(byte[] encryptionKey, String bankId, String userId) { + KeyManager keyManager = KeyManager.getInstance(bankId); + PublicKey publicKey = keyManager.getUserPublicKey(userId, null, SecurityMethod.RAH_9, "V"); + try { + Cipher cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return cipher.doFinal(encryptionKey); + } catch (IllegalBlockSizeException e) { + LOG.error("An Error occurred during encryption.", e); + } catch (BadPaddingException e) { + LOG.error("An Error occurred during encryption.", e); + } catch (InvalidKeyException e) { + LOG.error("An Error occurred during encryption.", e); + } catch (NoSuchAlgorithmException e) { + LOG.error("An Error occurred during encryption.", e); + } catch (NoSuchPaddingException e) { + LOG.error("An Error occurred during encryption.", e); + } + return new byte[0]; + } + + @Override + public byte[] generateKey() { + SecureRandom secureRandom = new SecureRandom(); + byte[] secretKey = new byte[32]; + secureRandom.nextBytes(secretKey); + //starmoney key fix + if (secretKey[0] == 0) { + secretKey[0] = 1; + } + LOG.info("SecretKeyLength: {}", secretKey.length); + return secretKey; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/RDH10Cryptography.java b/src/main/java/net/petafuel/fuelifints/cryptography/RDH10Cryptography.java new file mode 100644 index 0000000..c7a0527 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/RDH10Cryptography.java @@ -0,0 +1,88 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Security; +import java.util.Arrays; + +public class RDH10Cryptography implements Cryptography { + + private static final Logger LOG = LogManager.getLogger(RDH10Cryptography.class); + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + @Override + public byte[] decrypt(byte[] encrypted, byte[] key, String bankId) { + try { + KeyManager keyManager = KeyManager.getInstance(bankId); + PrivateKey privateKey = keyManager.getPrivateKey(SecurityMethod.RDH_10, "V"); + Cipher cipher = Cipher.getInstance("RSA/NONE/NOPADDING"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] symmetricKey = cipher.doFinal(key); + LOG.debug("Successfully decrypted the symmetric key. SymmetricKeyLength: {}", symmetricKey.length); + LOG.debug("Symmetric Key: {}", Arrays.toString(symmetricKey)); + + Cipher cipherTwoKeyTripleDES = new TwoKeyTripleDESCryptography().createCipher(symmetricKey, Cipher.DECRYPT_MODE); + + return cipherTwoKeyTripleDES.doFinal(encrypted); + } catch (BadPaddingException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (NoSuchAlgorithmException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (IllegalBlockSizeException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (NoSuchPaddingException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (InvalidKeyException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (GeneralSecurityException e) { + LOG.error("An Error occurred during decryption.", e); + } + return new byte[0]; + } + + @Override + public byte[] encryptMessage(byte[] message, byte[] key, String bankId) { + TwoKeyTripleDESCryptography twoKeyTripleDESCryptography = new TwoKeyTripleDESCryptography(); + try { + Cipher twoKeyTripleDesCipher = twoKeyTripleDESCryptography.createCipher(key, Cipher.ENCRYPT_MODE); + return twoKeyTripleDesCipher.doFinal(message); + } catch (GeneralSecurityException e) { + LOG.error("An Error occurred during encryption.", e); + } + return new byte[0]; + } + + @Override + public byte[] encryptEncryptionKey(byte[] encryptionKey, String bankId, String userId) { + KeyManager keyManager = KeyManager.getInstance(bankId); + PublicKey publicKey = keyManager.getUserPublicKey(userId, null, SecurityMethod.RDH_10, "V"); + try { + Cipher cipher = Cipher.getInstance("RSA/NONE/NOPADDING"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return cipher.doFinal(encryptionKey); + } catch (Exception e) { + LOG.error("An Error occurred during encryption.", e.getMessage()); + } + return new byte[0]; + } + + @Override + public byte[] generateKey() { + return new TwoKeyTripleDESCryptography().generatePassword(); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/RDH10SignatureHelper.java b/src/main/java/net/petafuel/fuelifints/cryptography/RDH10SignatureHelper.java new file mode 100644 index 0000000..75d0a6e --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/RDH10SignatureHelper.java @@ -0,0 +1,194 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.exceptions.ElementParseException; +import net.petafuel.fuelifints.exceptions.HBCISyntaxException; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.HKSAK; +import net.petafuel.fuelifints.protocol.fints3.segments.HNSHA; +import net.petafuel.fuelifints.protocol.fints3.segments.HNSHK; +import net.petafuel.fuelifints.protocol.fints3.segments.SegmentUtil; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Kik; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.OeffentlicherSchluessel; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Schluesselname; +import net.petafuel.fuelifints.support.ByteSplit; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.engines.RSAEngine; +import org.bouncycastle.crypto.params.RSAKeyParameters; +import org.bouncycastle.crypto.signers.PSSSigner; + +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.RSAPublicKeySpec; +import java.util.List; + +public class RDH10SignatureHelper implements SignatureHelper { + + + private static final Logger LOG = LogManager.getLogger(RDH10SignatureHelper.class); + + @Override + public ReturnDataObject validateSignature(byte[] bytes, HNSHK hnshk, HNSHA hnsha, Dialog dialog) { + Schluesselname schluesselname = hnshk.getSchluesselname(); + Kik kreditinstitutkennung = schluesselname.getKreditsinstitutkennung(); + String benutzerkennung = schluesselname.getBenutzerkennung(); + + KeyManager keyManager = KeyManager.getInstance(kreditinstitutkennung.getKreditinstitutscode()); + SecurityMethod securityMethod = SecurityMethod.valueOf(hnshk.getSicherheitsprofil().getSicherheitsverfahren() + "_" + hnshk.getSicherheitsprofil().getSicherheitsverfahrensversion()); + PublicKey userPublicKey = null; + /** + * Falls die Schlüssel gerade eingereicht werden existiert noch kein Eintrag in der Datenbank. + * Es wird nach dem Segment gesucht welches den Signierschlüssel beinhaltet und falls vorhanden die Signatur geprüft. + */ + + if (benutzerkennung != null) { + dialog.setUserId(benutzerkennung); + } + + if (!keyManager.existsPublicKey(benutzerkennung, null, securityMethod, schluesselname.getSchluesselart(), schluesselname.getSchluesselversion(), schluesselname.getSchluesselnummer())) { + List segments = null; + try { + segments = ByteSplit.split(bytes, ByteSplit.MODE_SEGMENT); + } catch (HBCISyntaxException e) { + return new ReturnDataObject(false, HBCISyntaxException.class.getSimpleName() + ": " + e.getCause()); + } + if (segments == null || segments.size() == 0) { + return new ReturnDataObject(false, ""); + } + HKSAK hksak = null; + for (byte[] segment : segments) { + if ("HKSAK".equals(SegmentUtil.getSegmentName(segment))) { + hksak = new HKSAK(segment); + try { + hksak.parseElement(); + } catch (ElementParseException e) { + return new ReturnDataObject(false, ""); + } + if ("S".equals(hksak.getSchluesselname().getSchluesselart())) { + break; + } else { + hksak = null; + } + } + } + if (hksak == null) { + LOG.error("HKSAK for signingkey not found."); + //Das benötigte Segment wurde nicht gefunden. + return new ReturnDataObject(false, ""); + } + OeffentlicherSchluessel oeffentlicherSchluessel = hksak.getOeffentlicherSchluessel(); + if (oeffentlicherSchluessel != null) { + BigInteger modulus = new BigInteger(1, SegmentUtil.getBytes(oeffentlicherSchluessel.getModulus())); + BigInteger exponent = new BigInteger(1, SegmentUtil.getBytes(oeffentlicherSchluessel.getExponent())); + RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulus, exponent); + try { + LOG.debug("generating publickey from data..."); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + userPublicKey = keyFactory.generatePublic(publicKeySpec); + } catch (NoSuchAlgorithmException e) { + return new ReturnDataObject(false, ""); + } catch (InvalidKeySpecException e) { + return new ReturnDataObject(false, ""); + } + } else { + LOG.error("öffentlicher schlüssel nicht vorhanden.."); + } + } else { + LOG.debug("PublicKey should be in database..."); + if (new String(bytes).contains("HKSAK")) { + //Schlüsseleinreichung obwohl schon Schlüssel in der Datenbank vorhanden ist: + dialog.getLegitimationsInfo().setUserKeysSubmitted(true); + return new ReturnDataObject(false, ""); + } + userPublicKey = keyManager.getUserPublicKey(benutzerkennung, null, securityMethod, schluesselname.getSchluesselart(), schluesselname.getSchluesselversion(), schluesselname.getSchluesselnummer()); + + } + if (userPublicKey == null) { + dialog.getLegitimationsInfo().setUserKeysSubmitted(true); + LOG.error("public key not found."); + return new ReturnDataObject(false, ""); + } + RSAPublicKey rsaPublicKey = (RSAPublicKey) userPublicKey; + + /* + byte[] signatureBytes = hnsha.getValidierungsresultat(); + int offset = 1; + for (; offset < signatureBytes.length; offset++) { + if (signatureBytes[offset] == 0x40) { + offset++; + break; + } + } + */ + byte[] signature = SegmentUtil.getBytes(hnsha.getValidierungsresultat());//new byte[signatureBytes.length - offset]; + //System.arraycopy(signatureBytes, offset, signature, 0, signature.length); + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(bytes); + + PSSSigner signer = new PSSSigner(new RSAEngine(), new SHA256Digest(), hash.length); + signer.init(false, new RSAKeyParameters(false, rsaPublicKey.getModulus(), rsaPublicKey.getPublicExponent())); + signer.update(hash, 0, hash.length); + + boolean isSignatureVerified = signer.verifySignature(signature); + + if (isSignatureVerified) { + dialog.getLegitimationsInfo().setStrongAuthenticated(true); + } + + return new ReturnDataObject(isSignatureVerified, ""); + + /* + Signature signature = Signature.getInstance("SHA256withRSAandMGF1"); + signature.initVerify(userPublicKey); + signature.update(bytes); + return signature.verify(signatureBytes, offset, signatureBytes.length - offset); + */ + } catch (NoSuchAlgorithmException e) { + //SHA256withRSA is supported + } + + return new ReturnDataObject(false, ""); + } + + @Override + public byte[] sign(byte[] toSign, HNSHK hnshk) { + Schluesselname schluesselname = hnshk.getSchluesselname(); + Kik kreditinstitutkennung = schluesselname.getKreditsinstitutkennung(); + + KeyManager keyManager = KeyManager.getInstance(kreditinstitutkennung.getKreditinstitutscode()); + SecurityMethod securityMethod = SecurityMethod.valueOf(hnshk.getSicherheitsprofil().getSicherheitsverfahren() + "_" + hnshk.getSicherheitsprofil().getSicherheitsverfahrensversion()); + LOG.debug("verwendete Schlüsselart: {}", schluesselname.getSchluesselart()); + PrivateKey privateKey = keyManager.getPrivateKey(securityMethod, schluesselname.getSchluesselart()); + RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey; + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(toSign); + + PSSSigner signer = new PSSSigner(new RSAEngine(), new SHA256Digest(), hash.length); + signer.init(true, new RSAKeyParameters(true, rsaPrivateKey.getModulus(), rsaPrivateKey.getPrivateExponent())); + + signer.update(hash, 0, hash.length); + + byte[] sig = signer.generateSignature(); + + return sig; + } catch (NoSuchAlgorithmException e) { + //SHA256withRSA is supported + } catch (CryptoException e) { + LOG.error("Exception", e); + } + return new byte[0]; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/RDH9Cryptography.java b/src/main/java/net/petafuel/fuelifints/cryptography/RDH9Cryptography.java new file mode 100644 index 0000000..0168a60 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/RDH9Cryptography.java @@ -0,0 +1,87 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Security; + +public class RDH9Cryptography implements Cryptography { + + private static final Logger LOG = LogManager.getLogger(RDH9Cryptography.class); + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + @Override + public byte[] decrypt(byte[] encrypted, byte[] key, String bankId) { + try { + KeyManager keyManager = KeyManager.getInstance(bankId); + PrivateKey privateKey = keyManager.getPrivateKey(SecurityMethod.RDH_9, "V"); + Cipher cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] symmetricKey = cipher.doFinal(key); + LOG.debug("Successfully decrypted the symmetric key. SymmetricKeyLength: {}", symmetricKey.length); + + Cipher cipherTwoKeyTripleDES = new TwoKeyTripleDESCryptography().createCipher(symmetricKey, Cipher.DECRYPT_MODE); + + return cipherTwoKeyTripleDES.doFinal(encrypted); + } catch (BadPaddingException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (NoSuchAlgorithmException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (IllegalBlockSizeException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (NoSuchPaddingException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (InvalidKeyException e) { + LOG.error("An Error occurred during decryption.", e); + } catch (GeneralSecurityException e) { + LOG.error("An Error occurred during decryption.", e); + } + return new byte[0]; + } + + @Override + public byte[] encryptMessage(byte[] message, byte[] key, String bankId) { + return new RDH10Cryptography().encryptMessage(message, key, bankId); + } + + @Override + public byte[] encryptEncryptionKey(byte[] encryptionKey, String bankId, String userId) { + KeyManager keyManager = KeyManager.getInstance(bankId); + PublicKey publicKey = keyManager.getUserPublicKey(userId, null, SecurityMethod.RDH_9, "V"); + try { + Cipher cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return cipher.doFinal(encryptionKey); + } catch (IllegalBlockSizeException e) { + LOG.error("An Error occurred during encryption.", e); + } catch (BadPaddingException e) { + LOG.error("An Error occurred during encryption.", e); + } catch (InvalidKeyException e) { + LOG.error("An Error occurred during encryption.", e); + } catch (NoSuchAlgorithmException e) { + LOG.error("An Error occurred during encryption.", e); + } catch (NoSuchPaddingException e) { + LOG.error("An Error occurred during encryption.", e); + } + return new byte[0]; + } + + @Override + public byte[] generateKey() { + return new RDH10Cryptography().generateKey(); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/SignatureHelper.java b/src/main/java/net/petafuel/fuelifints/cryptography/SignatureHelper.java new file mode 100644 index 0000000..d01cdce --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/SignatureHelper.java @@ -0,0 +1,12 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.protocol.fints3.segments.HNSHA; +import net.petafuel.fuelifints.protocol.fints3.segments.HNSHK; + +public interface SignatureHelper { + public ReturnDataObject validateSignature(byte[] bytes, HNSHK hnshk, HNSHA hnsha, Dialog dialog); + + public byte[] sign(byte[] toSign, HNSHK hnshk); +} diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/SignatureHelperFactory.java b/src/main/java/net/petafuel/fuelifints/cryptography/SignatureHelperFactory.java new file mode 100644 index 0000000..546b5d1 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/SignatureHelperFactory.java @@ -0,0 +1,26 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; + +public class SignatureHelperFactory { + + public static SignatureHelper getSignatureHelper(SecurityMethod method) { + switch (method) { + case PIN_1: + case PIN_2: + return new PinTanSignatureHelper(); + case RDH_9: + case RAH_9: + case RAH_10: + case RDH_10: + return new RDH10SignatureHelper(); + } + + if (method.equals("PIN-1") || method.equals("PIN-2")) { + return new PinTanSignatureHelper(); + } else if (method.equals("RDH-10")) { + return new RDH10SignatureHelper(); + } + return null; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/TwoKeyTripleDESCryptography.java b/src/main/java/net/petafuel/fuelifints/cryptography/TwoKeyTripleDESCryptography.java new file mode 100644 index 0000000..0e789cc --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/TwoKeyTripleDESCryptography.java @@ -0,0 +1,103 @@ +package net.petafuel.fuelifints.cryptography; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.GeneralSecurityException; +import java.security.SecureRandom; + +public class TwoKeyTripleDESCryptography { + + /** + * Mit dieser Methode legt jede erbende Unterklasse fest, + * mit welchen Verfahren der geheime Schluessel geniert werden soll + * + * @param password Passwort mit dem der geheime Schluessel erzeugt wird + * @return Geheimer Schluessel + * @throws java.security.GeneralSecurityException + * + */ + public SecretKey createSecretKey(byte[] password) throws GeneralSecurityException { + return new SecretKeySpec(generate2Key(password), "DESede"); + } + + private byte[] generate2Key(byte[] password) throws GeneralSecurityException { + byte[] startKey = password; + if (startKey.length != 16) { + throw new GeneralSecurityException("Ungültige Länge für das Passwort. Passwort muss 16 Zeichen haben!"); + } + + byte[] finalKey = new byte[24]; + for (int i = 0; i>> 1) << 1); + return (byte) (b | (checkOddParity(b) ? 0:1)); + } + + /** + * Prüft, ob die Parität des übergebenen bytes ungerade ist + * (== ist die Anzahl der gesetzten bits gerade) + * (== ergibt ein xor unter jedem bit 1) + * + * @param b byte, das zu prüfen ist + * @return true bei ungerader Parität, ansonsten false + */ + private boolean checkOddParity(byte b) { + return (((b ^ + (b>>>1) ^ + (b>>>2) ^ + (b>>>3) ^ + (b>>>4) ^ + (b>>>5) ^ + (b>>>6) ^ + (b>>>7)) & 1)==1); + } + + public byte[] generatePassword() { + //siehe B3.1.1, FinTS 3.0 Security HBCI + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[16]; + random.nextBytes(keyBytes); + //starmoney key fix + if (keyBytes[0] == 0) { + keyBytes[0] = 1; + } + return keyBytes; + } + + public Cipher createCipher(byte[] password, int mode) throws GeneralSecurityException { + if (password == null) { + throw new IllegalArgumentException("Parameter password must not be null."); + } + SecretKey secretKey = createSecretKey(password); + + //Initialisierungsvektor: + //X’00 00 00 00 00 00 00 00’ + //siehe B2.2.2, FinTS 3.0 Security HBCI + byte[] iv = new byte[] { + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 + }; + IvParameterSpec ivSpec = new IvParameterSpec(iv); + + Cipher cipher = Cipher.getInstance("DESede/CBC/ISO10126Padding"); + cipher.init(mode, secretKey, ivSpec); + return cipher; + } + +} + diff --git a/src/main/java/net/petafuel/fuelifints/cryptography/aesencryption/AESUtil.java b/src/main/java/net/petafuel/fuelifints/cryptography/aesencryption/AESUtil.java new file mode 100644 index 0000000..2c4ad39 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/cryptography/aesencryption/AESUtil.java @@ -0,0 +1,78 @@ +package net.petafuel.fuelifints.cryptography.aesencryption; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.FileInputStream; +import java.util.Base64; +import java.util.Properties; + +public class AESUtil { + private static final Logger LOG = LogManager.getLogger(AESUtil.class); + + /** + * Expects a payload to encrypt and returns the encryption result as Base64 + * @param toEncrypt + * @return + */ + public static String aesEncrypt(byte[] toEncrypt) { + try { + Cipher aesEncrypt = Cipher.getInstance("AES/CBC/PKCS5PADDING"); + SecretKey aesKey=initAESKey(); + aesEncrypt.init(Cipher.ENCRYPT_MODE, aesKey,new IvParameterSpec(new byte[16])); + byte[] byteToEncrypt= toEncrypt; + byte[] encryptedData = aesEncrypt.doFinal(byteToEncrypt); + return Base64.getEncoder().encodeToString(encryptedData); + } catch (Exception ex) { + LOG.error("Exception encrypt the payload", ex); + } + return null; + } + + /** + * Expects a BASE64 encoded encrypted payload and returns the decrypted result + * as byte[] + * @param toDecrypt + * @return + */ + public static byte[] aesDecrypt(String toDecrypt) { + try { + Cipher aesDecrypt = Cipher.getInstance("AES/CBC/PKCS5PADDING"); + SecretKey aesKey=initAESKey(); + byte[] byteToDecrypt=Base64.getDecoder().decode(toDecrypt); + IvParameterSpec ivParameterSpec= new IvParameterSpec(new byte[16]); + aesDecrypt.init(Cipher.DECRYPT_MODE, aesKey,ivParameterSpec); + byte[] decryptedData = aesDecrypt.doFinal(byteToDecrypt); + return decryptedData; + } catch (Exception ex) { + LOG.error("Exception decrypting the payload", ex); + } + + return null; + } + + private static SecretKeySpec initAESKey() { + try { + /** + * Read BIC and blz from fuelibus.properties + */ + Properties properties = new Properties(); + properties.load(new FileInputStream("config/fuelifints.properties")); + String keyFileConfig= properties.getProperty("aes_key_location"); + Properties keyFile= new Properties(); + keyFile.load(new FileInputStream(keyFileConfig)); + String aesKeyEncoded = keyFile.getProperty("aes_key"); + byte[] aesKey = Base64.getDecoder().decode(aesKeyEncoded); + SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + return keySpec; + } catch (Exception ex) { + LOG.error("Exception", ex); + } + return null; + + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/Banking2Access.java b/src/main/java/net/petafuel/fuelifints/dataaccess/Banking2Access.java new file mode 100644 index 0000000..4659568 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/Banking2Access.java @@ -0,0 +1,1516 @@ +package net.petafuel.fuelifints.dataaccess; + +import net.petafuel.fuelifints.FinTSVersionSwitch; +import net.petafuel.fuelifints.cryptography.aesencryption.AESUtil; +import net.petafuel.fuelifints.dataaccess.dataobjects.*; +import net.petafuel.fuelifints.dataaccess.dataobjects.database.banking2; +import net.petafuel.fuelifints.dataaccess.dataobjects.gateway.email; +import net.petafuel.fuelifints.dataaccess.dataobjects.gateway.sms; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.client.ClientProductInfo; +import net.petafuel.fuelifints.model.client.LegitimationInfo; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Betrag; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungNational; +import net.petafuel.fuelifints.support.Payments; +import net.petafuel.jsepa.SEPAParser; +import net.petafuel.jsepa.exception.SEPAParsingException; +import net.petafuel.jsepa.model.*; +import net.petafuel.jsepa.util.BankDateCalculator; +import net.petafuel.jsepa.util.SepaUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.mail.MessagingException; +import java.io.FileInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; + +import static java.lang.Double.parseDouble; +import static java.lang.Integer.parseInt; +import static java.lang.String.format; +import static net.petafuel.fuelifints.dataaccess.dataobjects.PermissionDataObject.FINTS30; +import static net.petafuel.fuelifints.dataaccess.dataobjects.PermissionDataObject.getGeschaeftsvorfall; +import static net.petafuel.fuelifints.dataaccess.dataobjects.database.banking2.addTagUser; +import static net.petafuel.fuelifints.support.Payments.derfNullSein; + + +@SuppressWarnings("unused") +public class Banking2Access implements DataAccessFacade { + private static final Logger LOG = LogManager.getLogger(Banking2Access.class); + private String bankCode; + private String configColumnSct = "ueberweisung"; + private String configColumnSdd = "sdd"; + private banking2 database; + + /** + * @param configFile Pfad zum Konfigurationsfile + * @throws IOException wenn Config nicht gefunden wird + */ + public Banking2Access(String configFile) throws IOException { + Properties configuration = new Properties(); + configuration.load(new FileInputStream(configFile)); + database = new banking2(configuration); + bankCode = config(banking2.properties.bankCode, "12345678"); + } + + /** + * Prüft ob ein Schlüsselpar für die Bank existiert + * + * @param securityMethod Sicherheitsmethode + * @param keyType SchlüsselTyp + * @return true wenn exisitert + */ + @Override + public boolean hasKeyPair(SecurityMethod securityMethod, String keyType) { + return database.isKey("0", getSchluesselArtByKeyType(keyType), 1, 1); + } + + /** + * Holt eine Schlüsselart anhand des Schlüsseltyp + * + * @param keyType Schlüsseltyp + * @return Schlüsselart + */ + private int getSchluesselArtByKeyType(String keyType) { + switch (keyType) { + case "V": + return 1; + case "S": + return 2; + default: + return 3; + } + } + + /** + * Speichert einen neuen Bankschlüssel + * + * @param securityMethod Sicherheitsmethode + * @param privateKey privater Schlüssel? + * @param publicKey öffentlicher Schlüssel + * @param keyType SchlüsselTyp + * @param modulus Modulos + * @param publicExponent Exponent + */ + @Override + public void updateKeyPair(SecurityMethod securityMethod, String privateKey, String publicKey, String keyType, BigInteger modulus, BigInteger publicExponent) { + database.saveKey("0", publicKey, getSchluesselArtByKeyType(keyType), 1, 1, modulus.toString(16), publicExponent.toString(16), true); + } + + /** + * ?? keystore! + * + * @param securityMethod Sicherheitsmethode + * @param keyType SchlüsselTyp + * @return ?? + */ + @Override + public String getPrivateKey(SecurityMethod securityMethod, String keyType) { + return null; + } + + @Override + public String getPublicKey(SecurityMethod securityMethod, String keyType) { + return database.getKey("0", getSchluesselArtByKeyType(keyType), 1, 1, true); + } + + /** + * Speichert einen Benutzer-Schlüssel in die Datenbank + * + * @param userId Kennung + * @param customerId Kunden-ID + * @param securityMethod Sicherheitsmethode + * @param pubKey öffentlicher Schlüssel + * @param keyType SchlüsselNummer -> Schlüsselart + * @param keyVersion SchlüsselVersion + * @param keyNumber Schlüsselnummer + * @param modulus Modulos + * @param publicExponent Exponent + */ + @Override + public boolean addUserPublicKey(String userId, String customerId, SecurityMethod securityMethod, String pubKey, String keyType, int keyVersion, int keyNumber, BigInteger modulus, BigInteger publicExponent) { + return database.saveKey(userId, pubKey, getSchluesselArtByKeyType(keyType), keyVersion, keyNumber, modulus.toString(16), publicExponent.toString(16), getSchluesselArtByKeyType(keyType) == 1); + } + + /** + * Holt den Benutzer-Schlüssel aus der Datenbank + * + * @param userId Kennung + * @param customerId Kunden-ID + * @param securityMethod Sicherheitsmethode + * @param keyType SchlüsselNummer -> Schlüsselart + * @param keyVersion SchlüsselVersion + * @param keyNumber Schlüsselnummer + * @return null, wenn der Schlüssel nicht existiert + */ + @Override + public String getUserPublicKey(String userId, String customerId, SecurityMethod securityMethod, String keyType, int keyVersion, int keyNumber) { + return database.getKey(userId, getSchluesselArtByKeyType(keyType), keyNumber, keyVersion, true); + } + + /** + * Prüft ob ein Benutzer-Schlüssel exisitert + * + * @param userId Kennung + * @param customerId Kunden-ID + * @param securityMethod Sicherheitsmethode + * @param keyType SchlüsselNummer -> Schlüsselart + * @param keyVersion SchlüsselVersion + * @param keyNumber Schlüsselnummer + * @return true wenn existiert + */ + @Override + public boolean existsUserKey(String userId, String customerId, SecurityMethod securityMethod, String keyType, int keyVersion, int keyNumber) { + return database.isKey(userId, getSchluesselArtByKeyType(keyType), keyNumber, keyVersion); + } + + /** + * gibt die aktuelle Kunden-System-ID zurück + * + * @param dialogid Dialog-ID + * @param legitimationInfo LegitimationInfo + * @param clientProductInfo ClientProductInfo + * @return Kunden-System-ID + */ + @Override + public String getUserSystemId(String dialogid, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo) { + return getOrGenerateUserSystemId(dialogid, legitimationInfo, clientProductInfo); + } + + /** + * Generiert eine Kunden-System-ID + * + * @param dialogId Dialog-ID + * @param legitimationInfo LegitimationInfo + * @param clientProductInfo ClientProductInfo + * @return Kunden-System-ID + */ + @Override + public String getOrGenerateUserSystemId(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo) { + return database.getOrGenerateUserSystemId(dialogId, legitimationInfo, clientProductInfo); + } + + /** + * Ermitteln der TAN-Verfahren für den Benutzer + * + * @param legitimationInfo die LegitimationInfo + * @return Liste der verfahren + */ + @Override + public List getZugelasseneTanVerfahren(LegitimationInfo legitimationInfo) { + LinkedList result = new LinkedList<>(); + if (database.mTanIsAvaible(legitimationInfo)) { + result.add(PermissionDataObject.verfahren_mTan); + } + return result; + } + + /** + * Funktion zum TAN-Entwerten + * + * @param dialogId die DialogID + * @param legitimationInfo die Legitimationsinfo + * @param clientProductInfo die Client + * @param TAN die zu Entwertende TAN + */ + @Override + public void devalueTan(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo, String TAN) { + database.devalueTan(dialogId, legitimationInfo); + } + + /** + * Keine Ahnung wofür die ist + * + * @param bankId Bankleizahl + * @return KommunikationsParameterData, null wenn nicht in Config gesetzt + */ + @Override + public KommunikationsParameterData getKommunikationsParameterData(String bankId) { + String communikationUrlListString = config(banking2.properties.communikationUrls, ""); + if (!communikationUrlListString.equals("")) { + KommunikationsParameterData kommunikationsParameterData = new KommunikationsParameterData(); + List communikationUrlList = com.mysql.jdbc.StringUtils.split(communikationUrlListString, ",", true); + communikationUrlList.forEach(kommunikationsParameterData::addKommunikationszugang); + kommunikationsParameterData.setBankId(bankId); + return kommunikationsParameterData; + } + return null; + } + + /** + * Gibt alle Banknachrichten zurück + * + * @param legitimationInfo die Legitimationsinfo + * @param clientProductInfo die ClientProductInfo + * @return Liste alle Banknachrichten + */ + @Override + public List getBankMessages(LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo) { + List messageObjectList = database.getLocalMessageList(legitimationInfo.getUserId()); + List messagesFound = new ArrayList<>(); + List> bankMessages = database.getBankMessages(legitimationInfo); + for (Map bankMessage : bankMessages) { + messageObjectList.add(new BankMessageObject(bankMessage.get("subject"), bankMessage.get("message"))); + messagesFound.add(bankMessage.get("messageid")); + } + database.setBankMessagesRead(messagesFound, legitimationInfo); + database.resetLocalMessageList(legitimationInfo.getUserId()); + return messageObjectList; + } + + /** + * Liefert den Bestand an an Sepa Einzellastschriften zurück. + * In diesem Fall muss im DirectDebitDataObject die SepaPainMessage und der SepaDescriptor gesetzt werden + * + * @param legitimationInfo die Legitmationsinfo + * @return für jede Einzellastschrift ein DirectDebitDataObject in einer Liste + */ + @Override + public List getEinzelLastschriftBestand(LegitimationInfo legitimationInfo) { + /* + * todo + */ + return null; + } + + /** + * Liefert den Bestand an an Sepa Sammellastschriften zurück. + * + * @param legitimationInfo die Legitmationsinfo + * @return für jede Einzellastschrift ein DirectDebitDataObject in einer Liste + */ + @Override + public List getSammelLastschriftBestand(LegitimationInfo legitimationInfo) { + /* + * todo + */ + return null; + } + + @Override + public boolean userMessageSubmitted(LegitimationInfo legitimationsInfo, KontoverbindungNational kontoverbindungAuftraggeber, String freitextmeldung, String betreff, String empfaengerangaben) { + /* + * todo + * Sachbearbeiter, Stammnummer & Kunden-Name heraussuchen + */ + if (kontoverbindungAuftraggeber != null) { + LOG.debug(addTagUser(format("Kontonummer: %s", kontoverbindungAuftraggeber.getKontonummer()), legitimationsInfo)); + LOG.debug(addTagUser(format("Kreditinstitutkennung: %s", String.valueOf(kontoverbindungAuftraggeber.getKreditinstitutskennung())), legitimationsInfo)); + LOG.debug(addTagUser(format("Unterkontomerkmal: %s", kontoverbindungAuftraggeber.getUnterkontomerkmal()), legitimationsInfo)); + } + LOG.debug(addTagUser(format("Betreff: %s", betreff), legitimationsInfo)); + LOG.debug(addTagUser(format("Empfaengerangaben: %s", empfaengerangaben), legitimationsInfo)); + String absender = config(banking2.properties.mailSender, "banking@yourbank.url"); + String empfaenger = config(banking2.properties.mailEmpfaenger, ""); + String subject = config(banking2.properties.mailSubject, ""); + email mail = new email(config(banking2.properties.mailSmtpHost, ""), config(banking2.properties.mailSmtpUserName, ""), config(banking2.properties.mailSmtpPassword, "")); + try { + mail.sendMail(absender, empfaenger, subject, freitextmeldung.replaceAll("<.*?>", "")); + return true; + } catch (MessagingException e) { + LOG.warn(addTagUser(format("Fehler beim Senden der Mail: %s", e.getMessage()), legitimationsInfo), e); + } + return false; + } + + @Override + public String getBic() { + return config(banking2.properties.bankBic, "NOOODE00BIC"); + } + + /** + * Holt die aktuelle BPD-Version + * + * @return BPD-Version + */ + @Override + public int getCurrentBpdVersion() { + return database.getCurrentBpdVersion(); + } + + /** + * Prüft ob die bpdVersion übereinstimmt + * + * @param bpdVersion die BPD-Version des Kunden + * @return übereinstimmung + */ + @Override + public boolean isBpdVersionCurrent(int bpdVersion) { + return (getCurrentBpdVersion() == bpdVersion); + } + + /** + * @param legitimationInfo LegitimationInfo, für die die Version geholt werden soll + * @return aktuelle UPD-Version + */ + @Override + public int getCurrentUpdVersion(LegitimationInfo legitimationInfo) { + if (legitimationInfo.getCustomerId().equals("9999999999")) { + return 1; + } + return database.getLatestUpd(legitimationInfo); + } + + /** + * @param legitimationInfo Benutzerkennung + * @param updVersion updVersion + * @return übereinstimmung + */ + @Override + public boolean isUpdVersionCurrent(LegitimationInfo legitimationInfo, int updVersion) { + return database.isUpdVersionCurrent(legitimationInfo, updVersion); + } + + /** + * Gibt alle Parameter-Daten für die FINTS-Version zurück + * + * @param fintsVersion Gibt die FinTS Version an, für die Parameterdaten gefordert wurden + * @return Liste mit ParameterDataObject + */ + @Override + public ArrayList getParameterData(FinTSVersionSwitch.FinTSVersion fintsVersion) { + String hbciversion = PermissionDataObject.getHBCIversion(fintsVersion); + String bpdVersion = String.valueOf(getCurrentBpdVersion()); + ArrayList retObjects = new ArrayList<>(); + List> geschaeftsvorfaelle = database.getGeschaeftsvorfaelle(bpdVersion, hbciversion); + List bankParameter = PermissionDataObject.getBankParameters(); + if (geschaeftsvorfaelle.size() < bankParameter.size()) { + database.resetGeschaeftsvorfaelle(bpdVersion, hbciversion); + for (PermissionDataObject.GlobalParameter globalParameter : bankParameter) { + database.insertGeschaeftsvorfall(globalParameter, bpdVersion, hbciversion); + } + geschaeftsvorfaelle = database.getGeschaeftsvorfaelle(bpdVersion, hbciversion); + } + for (Map geschaeftsvorfall : geschaeftsvorfaelle) { + retObjects.add( + new ParameterDataObject(geschaeftsvorfall.get("segmentname"), + database.getInt(geschaeftsvorfall.get("segmentversion")), + database.getInt(geschaeftsvorfall.get("maximaleanzahlauftraege")), + database.getInt(geschaeftsvorfall.get("anzahlsignaturenmindestens")), + geschaeftsvorfall.get("sicherheitsklasse"), + geschaeftsvorfall.get("fints_3_parameter")) + ); + } + return retObjects; + } + + /** + * Holt alle Komunikationsparameter ab + * + * @param fintsVersion Gibt die FinTS Version an, für die Parameterdaten gefordert wurden + * @return CommonBankParameterDataObject + */ + @Override + public CommonBankParameterDataObject getCommonBankParameters(FinTSVersionSwitch.FinTSVersion fintsVersion) { + String bankName = "Unbekannte Bank"; + String bankLang = "1"; + String bankHbciVersion = PermissionDataObject.getHBCIversion(fintsVersion); + Map bpd = database.getBpd(); + if (bpd != null) { + // bankCode = bpd.get("blz"); + bankName = bpd.get("bankname"); + bankLang = bpd.get("lang"); +// bankHbciVersion = bpd.get("hbciversion"); + } else { + LOG.warn(format("Fehler beim holen der Bankparameter: tabelle bpd leer? (HBCI: %s)", fintsVersion)); + } + return new CommonBankParameterDataObject(getCurrentBpdVersion(), bankCode, bankName, bankLang, bankHbciVersion); + } + + /** + * Gibt eine Liste aller Konten zurück + * + * @param legitimationInfo Legitimationsinfo zum Benutzer + * @return Liste mit AccountDataObjects + */ + @Override + public ArrayList getAccountData(LegitimationInfo legitimationInfo) { + String userId = legitimationInfo.getUserId(); + LOG.info(addTagUser("Konten holen", legitimationInfo)); + ArrayList retObjects = new ArrayList<>(); + List> konten = database.getKonten(legitimationInfo); + String kontonummer; + PermissionDataObject permissionDataObject; + boolean pinUsable = database.isPinUsable(legitimationInfo); + boolean mailAvaible = database.isMailAvaible(); + for (Map konto : konten) { + kontonummer = konto.get("kontonummer"); + LOG.info(addTagUser(format("Konto gefunden: %s", kontonummer), legitimationInfo)); + AccountDataObject currentData = + new AccountDataObject(kontonummer, + bankCode, + konto.get("kontoinhaber"), + konto.get("kontoart")); + // An der stelle muss immer die Angefragte Kunden-ID zurück gegeben werden, nicht die tatsächliche! + currentData.setKunden_id(legitimationInfo.getCustomerId()); + currentData.setKontoart("1"); + + /* + * @todo setKontolimit mit KontolimitObject + */ + currentData.setKontoart(konto.get("kontoart")); + /* + * saldo inland kontoverbindung sepa-single sepa-sammel umsätze tan + * HKSAL:1+HKUEB:1+HKSPA:1+HKCCS:1+HKCCM:1+HKKAZ:1+HKTAN:1 + */ + permissionDataObject = new PermissionDataObject(); + Map permittedOperations = database.getPermittedOperations(legitimationInfo, kontonummer); + configColumnSct = config(banking2.properties.columnSct, configColumnSct); + configColumnSdd = config(banking2.properties.columnSdd, configColumnSdd); + for (FINTS30 type : PermissionDataObject.getAllowedFINTS30()) { + boolean allowed = false; + switch (type) { + case SaldoAbfrage: // Saldo + case UmsatzAbfrage: // Umsätze & Vorabumsätze + case Vormerkumsaetze: // Vorabumsätze extra + case PinTanVerfahren: // TAN-Eingabe + case PinTanZweiSchrittParameter: // PIN-/TAN-Zweischritt-Parameter + case SepaKontoVerbindungen: // SEPA-Parameter + case SepaUeberweisungEinzelTerminBestand: // SEPA-Einzel-Überweisung-Termin Bestand + case SepaUeberweisungSammelTerminBestand: // SEPA-Sammel-Überweisung-Termin Bestand + allowed = true; + break; + case PinAendern: // PIN-Eingabe + allowed = pinUsable; + break; + case SepaUeberweisungEinzelEinreichen: // SEPA-Einzel-Überweisung Einreichen + case SepaUeberweisungEinzelTerminEinreichen: // SEPA-Einzel-Überweisung-Termin Einreichen + case SepaUeberweisungEinzelTerminLoeschen: // SEPA-Einzel-Überweisung-Termin Löschen + case SepaUeberweisungSammelEinreichen: // SEPA-Sammel-Überweisung Einreichen + case SepaUeberweisungSammelTerminEinreichen: // SEPA-Sammel-Überweisung-Termin Einreichen + case SepaUeberweisungSammelTerminLoeschen: // SEPA-Sammel-Überweisung-Termin Löschen + allowed = permittedOperations.get(configColumnSct) && permittedOperations.get("ade"); + break; + case SepaLastschriftEinzelEinreichenB2B: + case SepaLastschriftEinzelBestandB2B: + case SepaLastschriftEinzelLoeschenB2B: + case SepaLastschriftSammelEinreichenB2B: + case SepaLastschriftSammelBestandB2B: + case SepaLastschriftSammelLoeschenB2B: + if (!config(banking2.properties.allowedB2B, "0").equals("1")) { + allowed = false; + break; + } + case SepaLastschriftEinzelBestand: // SEPA-Einzel-Lastschriften Bestand + case SepaLastschriftSammelBestand: // SEPA-Sammel-Lastschriften Bestand + case SepaLastschriftEinzelEinreichen: // SEPA-Einzel-Lastschrift Einreichen + case SepaLastschriftEinzelEinreichenCore1: // SEPA-Einzel-Lastschrift-COR1 Einreichen + case SepaLastschriftEinzelAendern: // SEPA-Einzel-Lastschriften Ändern + case SepaLastschriftEinzelLoeschen: // SEPA-Einzel-Lastschrift Löschen + case SepaLastschriftSammelEinreichen: // SEPA-Sammel-Lastschrift Einreichen + case SepaLastschriftSammelEinreichenCore1: // SEPA-Sammel-Lastschrift-COR1 Einreichen + case SepaLastschriftSammelLoeschen: // SEPA-Sammel-Lastschrift Löschen + allowed = permittedOperations.get(configColumnSdd) && permittedOperations.get("ade"); + break; + case KundenBankNachricht: + allowed = mailAvaible; + break; + default: + allowed = false; + break; + } + if (allowed) { + permissionDataObject.addPermission(type); + } + } + currentData.setErlaubteGeschaeftsvorfaelle(permissionDataObject.toString()); + retObjects.add(currentData); + } + return retObjects; + } + + /** + * Generiert einen PinParameterObject, gibt an für welchen Vorgang eine TAN benötigt wird + * + * @return PinParameterObject + */ + @Override + public PinParameterObject getPinParameter() { + PinParameterObject pinParameterObject = new PinParameterObject(); + pinParameterObject.setAnzahlSignaturenMindestens(parseInt(config(banking2.properties.pinSignatureCount, "1"))); + pinParameterObject.setMaximaleAnzahlAuftraege(parseInt(config(banking2.properties.pinMaxEntry, "1"))); + pinParameterObject.setSicherheitsklasse(parseInt(config(banking2.properties.pinClass, "0"))); + pinParameterObject.setMinPinLaenge(parseInt(config(banking2.properties.pinMin, "5"))); + pinParameterObject.setMaxPinLaenge(parseInt(config(banking2.properties.pinMax, "20"))); + pinParameterObject.setMaxTanLaenge(parseInt(config(banking2.properties.tanLen, "6"))); + pinParameterObject.setTextBelegungBenutzerkennung(config("textKennung", "Benutzerkennung")); + pinParameterObject.setTextBelegungKundenId(config("textKundenId", "Kunden-ID")); + HashMap pinRequired = PermissionDataObject.getPinRequired(); + List allowed = PermissionDataObject.getAllowedFINTS30(); + pinRequired + .entrySet() + .stream() + .filter(entry -> allowed.contains(entry.getKey())) + .forEach(entry -> pinParameterObject.addSegment(getGeschaeftsvorfall(entry.getKey()), entry.getValue())); + return pinParameterObject; + } + + /** + * Fragt ob der Vorgang erlaubt ist + * + * @param legitimationInfo LegitimationInfo + * @param accountId die Kontonummer + * @param aClass @return + * @return true wenn erlaubt, false wenn nicht erlaubt oder nicht definiert + */ + @Override + public boolean operationAllowedForAccount(LegitimationInfo legitimationInfo, String accountId, Class aClass) { + return operationAllowedForAccount(legitimationInfo, accountId, aClass.getSimpleName()); + } + + /** + * Liefert ob ein Auftrag (aClass repräsentiert das Segment als Klasse) für den + * angegebenen Account gültig ist. + * + * @param legitimationInfo LegitimationInfo + * @param aClass @return + */ + @Override + public boolean operationAllowedForAccount(LegitimationInfo legitimationInfo, Class aClass) { + return operationAllowedForAccount(legitimationInfo, null, aClass.getSimpleName()); + } + + /** + * Interne Abfrage ob die Operation erlaubt ist + * + * @param legitimationInfo LegitimationInfo + * @param kontonummer Kontonummer + * @param geschaeftsvorfall Geschäftsvorfall + * @return true, wenn erlaubt, false wenn nicht erlaubt oder nicht definiert + */ + private boolean operationAllowedForAccount(LegitimationInfo legitimationInfo, String kontonummer, String geschaeftsvorfall) { + boolean returnBoolean; + configColumnSct = config(banking2.properties.columnSct, configColumnSct); + configColumnSdd = config(banking2.properties.columnSdd, configColumnSdd); + LOG.info(addTagUser(format("Abfrage für Geschäftsvorfall: %s", geschaeftsvorfall), legitimationInfo)); + FINTS30 geschaeftsvorfallEnum = getGeschaeftsvorfall(geschaeftsvorfall); + if (geschaeftsvorfallEnum == null) { + returnBoolean = false; + } else { + switch (geschaeftsvorfallEnum) { + case Identifikation: // Identifikation via HKIDN + case SaldoAbfrage: // Saldo + case UmsatzAbfrage: // Umsätze & Vorabumsätze + case Vormerkumsaetze: // Vorabumsätze extra + case PinTanVerfahren: // TAN-Eingabe + case PinTanZweiSchrittParameter: // PIN-/TAN-Zweischritt-Parameter + case SepaKontoVerbindungen: // SEPA-Parameter + case SepaUeberweisungEinzelTerminBestand: // SEPA-Einzel-Überweisung-Termin Bestand + case SepaUeberweisungSammelTerminBestand: // SEPA-Sammel-Überweisung-Termin Bestand + returnBoolean = true; + break; + case PinAendern: // PIN-Eingabe + returnBoolean = database.isPinUsable(legitimationInfo); + break; + case SepaUeberweisungEinzelEinreichen: // SEPA-Einzel-Überweisung Einreichen + case SepaUeberweisungEinzelTerminEinreichen: // SEPA-Einzel-Überweisung-Termin Einreichen + case SepaUeberweisungEinzelTerminLoeschen: // SEPA-Einzel-Überweisung-Termin Löschen + case SepaUeberweisungSammelEinreichen: // SEPA-Sammel-Überweisung Einreichen + case SepaUeberweisungSammelTerminEinreichen: // SEPA-Sammel-Überweisung-Termin Einreichen + case SepaUeberweisungSammelTerminLoeschen: // SEPA-Sammel-Überweisung-Termin Löschen + returnBoolean = kontonummer == null || database.isOperationPermit(legitimationInfo, kontonummer, configColumnSct); + break; + case SepaLastschriftEinzelEinreichenB2B: + case SepaLastschriftEinzelBestandB2B: + case SepaLastschriftEinzelLoeschenB2B: + case SepaLastschriftSammelEinreichenB2B: + case SepaLastschriftSammelBestandB2B: + case SepaLastschriftSammelLoeschenB2B: + if (!config(banking2.properties.allowedB2B, "0").equals("1")) { + returnBoolean = false; + break; + } + case SepaLastschriftEinzelBestand: // SEPA-Einzel-Lastschriften Bestand + case SepaLastschriftSammelBestand: // SEPA-Sammel-Lastschriften Bestand + case SepaLastschriftEinzelEinreichen: // SEPA-Einzel-Lastschrift Einreichen + case SepaLastschriftEinzelEinreichenCore1: // SEPA-Einzel-Lastschrift-COR1 Einreichen + case SepaLastschriftEinzelAendern: // SEPA-Einzel-Lastschriften Ändern + case SepaLastschriftEinzelLoeschen: // SEPA-Einzel-Lastschrift Löschen + case SepaLastschriftSammelEinreichen: // SEPA-Sammel-Lastschrift Einreichen + case SepaLastschriftSammelEinreichenCore1: // SEPA-Sammel-Lastschrift-COR1 Einreichen + case SepaLastschriftSammelLoeschen: // SEPA-Sammel-Lastschrift Löschen + returnBoolean = kontonummer == null || database.isOperationPermit(legitimationInfo, kontonummer, configColumnSdd); + break; + case KundenBankNachricht: + returnBoolean = database.isMailAvaible(); + break; + default: + returnBoolean = false; + break; + } + } + LOG.info(addTagUser(format("Abfrage für Geschäftsvorfall: %s > %s", geschaeftsvorfall, (returnBoolean ? "true" : "false")), legitimationInfo)); + return returnBoolean; + } + + /** + * Generiert ein SaldoDataObject anhand der Kontonummer und Benutzerdaten + * + * @param kontonummer Kontonummer + * @param legitimationsInfo LegitimationInfo + * @return SaldoDataObject + */ + @Override + public SaldoDataObject getSaldo(String kontonummer, LegitimationInfo legitimationsInfo) { + + /** + * vars used: vorgemerkter saldo = prebooked revenues (sum of it)[sum all entries in vorabumsaetze for the konto in hand] + * var used: gebuchterSaldo = booked saldo kontoStand from last entry of konten_bewegungen + * var used: verfuegbarerBetrag = real available amount of account from konten column virtkontoStand + */ + SaldoDataObject sDO = new SaldoDataObject(); + String customerIdByConfig = database.getCustomerIdByConfig(legitimationsInfo); + Map kontoRow = database.getKontoRow(kontonummer, legitimationsInfo); + if (kontoRow != null) { + String kontoid = kontoRow.get("kontoid"); + Double userLimit = database.getLpTageslimit(legitimationsInfo); + double verfuegbar_tageslimit = (userLimit != null) ? userLimit : parseDouble(kontoRow.get("tageslimit")) - parseDouble(kontoRow.get("tagesumsatz")); + double gebuchterSaldo = parseDouble(kontoRow.get("kontostand")); + Double gebuchterSaldoBewegung = database.getLastKontostandByBewegungen(kontoid, customerIdByConfig, legitimationsInfo); + if (gebuchterSaldoBewegung != null) { + gebuchterSaldo = gebuchterSaldoBewegung; + } + double vorgemerkterSaldo = parseDouble(kontoRow.get("virtkontostand")); + Double vorgemerkterBetrag = (vorgemerkterSaldo - gebuchterSaldo); + sDO.setGebuchterSaldo(gebuchterSaldo); + boolean kl_aktiv = kontoRow.get("kl_aktiv").equals("1"); + boolean ueberziehen = kontoRow.get("ueberziehen").equals("1"); + sDO.setOverdrawAllowed(ueberziehen); + sDO.setVorgemerkterSaldo(vorgemerkterSaldo); + if (config(banking2.properties.avActive, "false").equals("true")) { + double preBookedSum = database.getPreBookedRevenuesSum(kontonummer);//in case of av active fetch the sum of the prebooked revenues from the database. + sDO.setVorgemerkterSaldo(preBookedSum); + sDO.setVerfuegbarerBetrag(vorgemerkterSaldo); + } else { + if (!kl_aktiv) { + if (!ueberziehen) { + Double verfuegbarerBetrag = (vorgemerkterSaldo > 0d) ? vorgemerkterSaldo : 0d; + sDO.setKreditlinie(0d); + sDO.setVerfuegbarerBetrag(verfuegbarerBetrag); + } + } else // kreditlinie aktiv + { + Double verfuegbarerBetrag = parseDouble(kontoRow.get("vb")) + vorgemerkterBetrag; + verfuegbarerBetrag = (verfuegbarerBetrag > 0d) ? verfuegbarerBetrag : 0d; + Double kreditlinie = gebuchterSaldo + verfuegbarerBetrag; + kreditlinie = (ueberziehen && kreditlinie > 0d) ? kreditlinie : 0d; + sDO.setKreditlinie(kreditlinie); + if (ueberziehen) { + sDO.setVerfuegbarerBetrag(verfuegbarerBetrag); + } else if (vorgemerkterSaldo <= 0d) { + sDO.setVerfuegbarerBetrag(0d); + } else if (vorgemerkterSaldo > verfuegbarerBetrag) { + sDO.setVerfuegbarerBetrag(verfuegbarerBetrag); + } else { + sDO.setVerfuegbarerBetrag(vorgemerkterSaldo); + } + } + if (sDO.getVerfuegbarerBetrag() > verfuegbar_tageslimit) { + sDO.setVerfuegbarerBetrag(verfuegbar_tageslimit); + } + } + } else { + LOG.info(addTagUser(format("Kann Konto %s nicht finden", kontonummer), legitimationsInfo)); + } + return sDO; + } + + /** + * Überprüft einen Benutzer-PIN + * + * @param userId die ID des Nutzers. + * @param pin eingegebene PIN + * @param dialog Dialog + * @return true, wenn PIN korrekt + */ + @Override + public ReturnDataObject checkUserPin(String userId, String pin, Dialog dialog) { + return new ReturnDataObject(database.checkUserPin(userId, pin), ""); + } + + /** + * 1. + * + * @param dialogId die Dialog-ID + * @param legitimationInfo die Benutzerdaten + * @param clientProductInfo die Produktinformationen + * @param auftragsHashwert der Auftrags-Hashwert @return Auftragsreferenz + */ + @Override + public String generateAuftragsreferenz(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo, byte[] auftragsHashwert) { + return database.generateAuftragsreferenz(dialogId, auftragsHashwert, legitimationInfo); + } + + /** + * @param dialogId DialogId + * @param legitimationInfo BenutzerDaten + * @param clientProductInfo KundenProdukt + * @param auftragsHashwert der Auftrags-Hashwert + * @param methode TAN-Methode + * @return ReturnDataObject + */ + @Override + public ReturnDataObject getChallenge(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo, byte[] auftragsHashwert, String methode, List parameterChallengeKlasse) { + LOG.info(addTagUser(format("TAN-Methode: %s, Dialog-ID: %s", methode, dialogId), legitimationInfo)); + if (parameterChallengeKlasse != null) { + for (String parameter : parameterChallengeKlasse) { + LOG.info(addTagUser(format("challange-parameter: %s", parameter), legitimationInfo)); + } + } + switch (methode) { + case PermissionDataObject.verfahren_iTan: + LOG.info(addTagUser("iTAN Abfrage", legitimationInfo)); + return getChallenge_iTAN(dialogId, legitimationInfo, clientProductInfo, auftragsHashwert, parameterChallengeKlasse, false); + case PermissionDataObject.verfahren_iTanSms: + LOG.info(addTagUser("iTAN-SMS Abfrage", legitimationInfo)); + return getChallenge_iTAN(dialogId, legitimationInfo, clientProductInfo, auftragsHashwert, parameterChallengeKlasse, true); + case PermissionDataObject.verfahren_mTan: + LOG.info(addTagUser("mTAN Abfrage", legitimationInfo)); + return getChallenge_mTAN(dialogId, legitimationInfo, clientProductInfo, auftragsHashwert, parameterChallengeKlasse); + default: + LOG.info(addTagUser(format("Unbekannte TAN-Methode: %s", methode), legitimationInfo)); + return new ReturnDataObject(false, "unbekanntes TAN-Verfahren", "9999"); + } + + } + + /** + * Holt die Challange für mTAN + * + * @param dialogId Dialog-ID + * @param legitimationInfo LegitimationInfo + * @param clientProductInfo ClientProductInfo + * @param auftragsHashwert Auftragshashwert + * @param parameterChallengeKlasse ChallangeParameter + * @return ReturnDataObject + */ + @SuppressWarnings("UnusedParameters") + private ReturnDataObject getChallenge_mTAN(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo, byte[] auftragsHashwert, List parameterChallengeKlasse) { + Long tanid = database.mTanGenerate(legitimationInfo); + if (tanid != null) { + tanid = tanid * (-1); + database.setTanId(dialogId, String.valueOf(tanid), legitimationInfo, clientProductInfo); + return new ReturnDataObject(true, "Bitte geben Sie die so eben versandte mTAN ein"); + } + return new ReturnDataObject(false, "Fehler bei der Generierung Ihrer mTAN", "9999"); //To change body of created methods use File | Settings | File Templates. + } + + /** + * Holt die Challange für iTAN + * + * @param dialogId Dialog-ID + * @param legitimationInfo LegitimationInfo + * @param clientProductInfo ClientProductInfo + * @param auftragsHashwert Auftragshashwert + * @param parameterChallengeKlasse ChallangeParameter + * @param tanPerSMS wird die TAN per SMS verschickt? + * @return ReturnDataObject + */ + @SuppressWarnings("UnusedParameters") + private ReturnDataObject getChallenge_iTAN(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo, byte[] auftragsHashwert, List parameterChallengeKlasse, boolean tanPerSMS) { + Map tanliste = database.iTanGenerate(legitimationInfo, tanPerSMS); + if (tanliste != null) { + String reihenfolge = tanliste.get("reihenfolge"); + String tanlistennr = tanliste.get("tanlistenid"); + LOG.info(addTagUser(format("tanliste/tannr: %s/%s", tanlistennr, reihenfolge), legitimationInfo)); + String tanId = tanliste.get("tanid"); + String dbTan = new String((AESUtil.aesDecrypt(tanliste.get("tan")))); + if (database.setTanId(dialogId, tanId, legitimationInfo, clientProductInfo)) { + LOG.info(addTagUser(format("TAN-ID: %s", tanId), legitimationInfo)); + if (tanPerSMS) { + sms.status smsStatus = database.sendTan(legitimationInfo, format("Ihre iTAN per SMS für Ihren HBCI-Auftrag lautet wie folgt: %s", dbTan)); + switch (smsStatus) { + case okay: + return new ReturnDataObject(true, "Bitte geben Sie die so eben versandte iTAN ein"); + case queued: + return new ReturnDataObject(true, "TAN-Versand wurde von Ihrem Mobilfunk-Anbieter verzögert. Bitte geben Sie die so eben versandte iTAN ein"); + case fail: + return new ReturnDataObject(false, "Es ist ein Fehler beim versenden der SMS aufgetreten, bitte probieren Sie es später erneut", "9999"); + } + } else { + return new ReturnDataObject(true, format("Bitte geben Sie aus der TAN-Liste %s die TAN-Nr %s ein", tanlistennr, reihenfolge)); + } + } else { + LOG.warn(addTagUser("Fehler beim auswählen der TAN", legitimationInfo)); + return new ReturnDataObject(false, "Fehler auswählen der TAN", "9998"); + } + return new ReturnDataObject(false, "Fehler auswählen der TAN", "9997"); + } else { + return new ReturnDataObject(false, "Es wurde keine aktive TAN-Liste gefunden", "9999"); + } + } + + /** + * 3. + * + * @param dialogId DialogId + * @param legitimationInfo die Kunden-ID + * @param clientProductInfo ClientProductInfo + * @param tan die eingegebenen TAN + * @param auftragsHashWert der Auftrags-Hashwert @return ob TAN-Eingabe stimmt + */ + @Override + public ReturnDataObject checkTan(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo, String tan, byte[] auftragsHashWert) { + boolean check = database.checkTan(dialogId, tan, legitimationInfo); + if (check) { + devalueTan(dialogId, legitimationInfo, clientProductInfo, tan); + return new ReturnDataObject(true, "TAN wurde verbraucht.", "3913"); + } + return new ReturnDataObject(false, ""); + } + + /** + * IZV? + * + * @param legitimationInfo die LegitimationInfo + * @param kontoverbindungAuftraggeber die Kontoverbindung des Auftragsgebers + * @param kontoverbindungEmpfaenger die Kontoverbindung des Empfänger + * @param nameEmpfaenger1 der Name des Empfängers 1 + * @param nameEmpfaenger2 der Name des Empfängers 2 + * @param textschluessel der Textschlüssel der Überweisung + * @param textschluesselErgaenzung die Textschlüsselergänzung + * @param verwendungszweckzeilen die Verwendungszweckzeilen + * @param betrag der Betrag + * @param endToEnd die endToEnd-ID + * @param purposeCode der Purposecode + * @return ReturnDataObject + */ + @Override + public ReturnDataObject submitNewTransaction(LegitimationInfo legitimationInfo, KontoverbindungNational kontoverbindungAuftraggeber, KontoverbindungNational kontoverbindungEmpfaenger, String nameEmpfaenger1, String nameEmpfaenger2, String textschluessel, String textschluesselErgaenzung, List verwendungszweckzeilen, Betrag betrag, String endToEnd, String purposeCode) { + int tk = parseInt(textschluessel); + String kontonummer = kontoverbindungAuftraggeber.getKontonummer(); + double betragBuchungen = parseDouble(betrag.getWert()); + if ((tk == 4 || tk == 5)) { + if (!database.isOperationPermit(legitimationInfo, kontonummer, configColumnSdd)) { + return new ReturnDataObject(false, "Konto darf Lastschriften nicht ausführen", "9935"); + } + } else { + if (!database.isOperationPermit(legitimationInfo, kontonummer, configColumnSct)) { + return new ReturnDataObject(false, "Konto darf Buchung nicht ausführen", "9935"); + } else { + SaldoDataObject saldoDataObject = getSaldo(kontonummer, legitimationInfo); + if (saldoDataObject.getVerfuegbarerBetrag() < betragBuchungen) { + return checkBuchungenBetrag(betragBuchungen, saldoDataObject, legitimationInfo); + } + } + } + Payments.Result r = database.inDtaus( + legitimationInfo, kontonummer, + kontoverbindungEmpfaenger.getKontonummer(), + kontoverbindungEmpfaenger.getKreditinstitutskennung().getKreditinstitutscode(), + nameEmpfaenger1 + nameEmpfaenger2, parseDouble(betrag.getWert()), + textschluessel, StringUtils.join(verwendungszweckzeilen, ""), "", "", + parseInt(textschluesselErgaenzung), "", database.getNextCounterDTAUS(legitimationInfo, true), "", "", null, + "", "", null, null, endToEnd, purposeCode); + if (r.isSuccess()) { + return new ReturnDataObject(true, "Auftrag Entgegengenommen", "0010"); + } + return new ReturnDataObject(false, "Fehler bei der Einreichung", "9030"); + } + + private ReturnDataObject checkBuchungenBetrag(double betragBuchungen, SaldoDataObject saldoDataObject, LegitimationInfo legitimationInfo) { + if (saldoDataObject.getVerfuegbarTag() != null && saldoDataObject.getVerfuegbarTag() < betragBuchungen) { + LOG.info(addTagUser("Vorgang wuerde Tageslimit für Konto ueberschreiten", legitimationInfo)); + return new ReturnDataObject(false, "Vorgang wuerde Tageslimit für Konto ueberschreiten", "9935"); + } else { + LOG.info(addTagUser("Vorgang wuerde Verfügbaren Betrag ueberschreiten", legitimationInfo)); + return new ReturnDataObject(false, "Vorgang wuerde Verfügbaren Betrag ueberschreiten", "9935"); + } + } + + /** + * Fügt eine neue Buchung hinzu (bisher nur SEPA-SCT/-SDD) + * + * @param legitimationInfo LegitimationInfo + * @param sepaDescriptor Sepa-Bezeichner + * @param sepaPainMessage die SEPA-Pain-Message @return true falls Auftrag angenommen, ansonsten false + * @param isSammler gibt an, ob es ein Sammel-Auftrag ist + * @return ReturnDataObject + */ + @Override + public ReturnDataObject submitNewTransaction(LegitimationInfo legitimationInfo, String sepaDescriptor, byte[] sepaPainMessage, boolean isSammler) { + LOG.info(addTagUser(sepaDescriptor, legitimationInfo)); + if (sepaDescriptor.contains("pain.001.00")) { + return submitSCT(legitimationInfo, sepaPainMessage, isSammler, false); + } else if (sepaDescriptor.contains("pain.008.00")) { + return submitSDD(legitimationInfo, sepaPainMessage, isSammler); + } else { + LOG.warn(addTagUser(format("folgender Sepa-Descriptor wurde nicht erkannt: %s", sepaDescriptor), legitimationInfo)); + LOG.info(addTagUser(new String(sepaPainMessage), legitimationInfo)); + } + return new ReturnDataObject(false, "SEPA-Auftrag kann nicht verarbeitet werden", "9999"); + } + + /** + * Reicht die Pinänderung ein + * + * @param legitimationInfo LegitimationInfo + * @param newPin die Neue PIN, die für den Nutzer hinterlegt werden soll + * @return gibt zurück, ob die PIN erfolgreich geändert wurde + */ + @Override + public ReturnDataObject submitChangePin(LegitimationInfo legitimationInfo, String newPin) { + if (database.saveNewPin(legitimationInfo, newPin)) { + return new ReturnDataObject(true, "PIN erfolgreich geändert", "0020"); + } else { + return new ReturnDataObject(false, "Fehler beim Ändern der PIN", "9942"); + } + } + + /** + * Verarbeitet eine SEPA-SDD + * + * @param legitimationInfo LegitimationInfo + * @param sepaPainMessage XML-Inhalt + * @param isSammler ist es ein Sammler? + * @return ReturnDataObject + */ + private ReturnDataObject submitSDD(LegitimationInfo legitimationInfo, byte[] sepaPainMessage, boolean isSammler) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + SEPAParser sddParser; + DDInitiation ddInitiation; + PaymentInstructionInformation paymentInfo; + DirectDebitTransactionInformation buchung; + String sepaFile = new String(sepaPainMessage); + sepaFile = sepaFile.replaceAll("pain.008.001.01", "CstmrDrctDbtInitn").replaceAll("OthrId", "Othr"); + sddParser = new SEPAParser(sepaFile); + try { + sddParser.parseSEPA(); + ddInitiation = sddParser.getSepaDocument().getDdInitiation(); + if (ddInitiation.getPmtInfos() == null || ddInitiation.getPmtInfos().size() != 1) { + LOG.warn(addTagUser(format("Nur ein PmtInfo-Segment unterstuetzt: %s", ((ddInitiation.getPmtInfos() != null) ? ddInitiation.getPmtInfos().size() : "null")), legitimationInfo)); + return new ReturnDataObject(false, "Ungültige Anzahl an PmtInfo-Segmente gefunden", "9930"); + } + List vectorBuchungen = ddInitiation.getPmtInfos().get(0).getDirectDebitTransactionInformationVector(); + if (vectorBuchungen == null || (isSammler && vectorBuchungen.size() < 1) || (!isSammler && vectorBuchungen.size() > 1)) { + LOG.warn(addTagUser("Ungültige Menge an DDInfosegmente gefunden", legitimationInfo)); + return new ReturnDataObject(false, "Ungültige Menge an DDInfosegmente gefunden", "9930"); + } + int anzahlBuchungen = vectorBuchungen.size(); + // Zahlungsinfos: + paymentInfo = ddInitiation.getPmtInfos().get(0); + // Gesamtbetrag! +// double betragBuchungen = paymentInfo.getCtrlSum(); + String kontonummer = new SepaUtils().getAccountNoFromDeIban(paymentInfo.getCreditorAccountIBAN()); + String kontoid = database.getFieldByKontonummer(kontonummer, "kontoid", legitimationInfo); + if (kontoid == null) { + LOG.info(addTagUser(format("Konto nicht existent: %s", kontonummer), legitimationInfo)); + return new ReturnDataObject(false, "Konto nicht existent", "9935"); + } else if (!database.isOperationPermit(legitimationInfo, kontonummer, configColumnSdd)) { + LOG.info(addTagUser(format("Konto darf Lastschriften nicht ausführen: %s", kontonummer), legitimationInfo)); + return new ReturnDataObject(false, "Konto darf Lastschriften nicht ausführen", "9935"); + } + HashMap ibanBicListe = new HashMap<>(); + Boolean purposeCodeFound = false; + for (int bId = 0; bId < anzahlBuchungen; bId++) { + buchung = paymentInfo.getDirectDebitTransactionInformationVector().get(bId); + ibanBicListe.put(buchung.getDebitorIBAN(), buchung.getDebitorAgent()); + if (config(banking2.properties.checkSonderzeichen, "false").equals("true")) { + if (!database.replaceNonSepaChars(buchung.getDebitorName()).equals(buchung.getDebitorName())) { + return new ReturnDataObject(false, format("Name enthält ungültige Zeichen: %s", buchung.getDebitorName()), "9935"); + } + if (!database.replaceNonSepaChars(buchung.getVwz()).equals(buchung.getVwz())) { + return new ReturnDataObject(false, format("Verwendungszweck enthält ungültige Zeichen: %s", buchung.getVwz()), "9935"); + } + } + if (!getPurposeCode(buchung).equals("")) { + purposeCodeFound = true; + } + } + if (config(banking2.properties.checkPurposeCodeSDD, "true").equals("true") && purposeCodeFound) { + database.addLocalMessage(legitimationInfo.getUserId(), "Zahlungsarten (PurposeCodes) werden bei Lastschriften nicht übernommen.", + "Sie haben SEPA-Lastschriften mit Zahlungsarten (Purpose Code) eingereicht. " + + "Diese Zahlungsarten (PurposeCodes) werden von uns verworfen, damit wir die Lastschriften problemlos ausführen können." + ); + } + ReturnDataObject kontenCheck = database.checkEmpfaengerKonten(kontonummer, ibanBicListe, getTransactionTypeSDD(paymentInfo), legitimationInfo); + if (!kontenCheck.isSuccess()) { + return kontenCheck; + } + try { + Date execute = sdf.parse(paymentInfo.getRequestedCollectionDate()); + Payments.Result inDtausSuccess; + ReturnDataObject typeCheck = isValidDDType(paymentInfo); + if (!typeCheck.isSuccess()) { + LOG.warn(addTagUser(typeCheck.getMessage(), legitimationInfo)); + return typeCheck; + } + ReturnDataObject executeCheck = isValidTarget2ExecutionDate(paymentInfo); + if (!executeCheck.isSuccess()) { + LOG.info(addTagUser(executeCheck.getMessage(), legitimationInfo)); + return executeCheck; + } + int counter = database.getNextCounterDTAUS(legitimationInfo, true); + String glaubigerid = (paymentInfo.getCreditorSchemeId() != null) ? paymentInfo.getCreditorSchemeId().getCdtrSchmeId() : null; + for (int bId = 0; bId < anzahlBuchungen; bId++) { + buchung = paymentInfo.getDirectDebitTransactionInformationVector().get(bId); + if (glaubigerid == null && buchung.getCreditorSchemeId() != null) { + glaubigerid = buchung.getCreditorSchemeId().getCdtrSchmeId(); + } else if (glaubigerid == null) { + return new ReturnDataObject(false, "SEPA-Lastschrift entspricht nicht dem Standard", "9037"); + } + String currentTime = new SimpleDateFormat("HH:mm:ss").format(new java.util.Date()); + inDtausSuccess = database.inDtaus( + legitimationInfo, kontonummer, "", "", + buchung.getDebitorName(), buchung.getAmount(), "7", + buchung.getVwz(), "", + buchung.getDebitorIBAN(), 0, + buchung.getDebitorAgent(), counter, + paymentInfo.getDirectDebitType(), paymentInfo.getSequenceType(), + execute, buchung.getMandateID(), glaubigerid, + sdf.parse(buchung.getDtOfSgntr()), currentTime, + derfNullSein(buchung.getEndToEndID()), "" + ); + if (inDtausSuccess.isSuccess()) { + database.updateSalden(buchung.getAmount(), false, kontonummer, legitimationInfo); + database.inVorabumsaetze(kontonummer, legitimationInfo, buchung.getAmount(), false, buchung.getVwz(), buchung.getDebitorAgent(), buchung.getDebitorIBAN(), buchung.getDebitorName()); + LOG.info(addTagUser(format("Buchung ausgeführt: %s / %s / %s", (bId + 1), anzahlBuchungen, buchung.getAmount()), legitimationInfo)); + } else if (anzahlBuchungen == 1) { + LOG.info(addTagUser("Es trat ein Fehler bei der Übergabe auf", legitimationInfo)); + return new ReturnDataObject(false, "Buchung konnte nicht ausgeführt werden", "9035"); + } else { + LOG.info(addTagUser("Es trat ein Fehler bei der Übergabe auf", legitimationInfo)); + return new ReturnDataObject(false, "Eine oder mehrere Lastschriften konnten nicht ausgeführt werden", "9035"); + } + } + + return new ReturnDataObject(true, "Auftrag Entgegengenommen", "0010"); + + } catch (ParseException e) { + LOG.error(addTagUser(format("Fehler beim auslesen des Fälligkeit-Datums: %s", e.getMessage()), legitimationInfo), e); + return new ReturnDataObject(false, "Fehler beim auslesen des Fälligkeit-Datums (YYYY-MM-DD)", "9935"); + } + } catch (SEPAParsingException e) { + return new ReturnDataObject(false, "SEPA Datei kann nicht gelesen werden", "9930"); + } + } + + /** + * Verarbeitet eine SEPA-SCT + * + * @param legitimationInfo LegitimationInfo + * @param sepaPainMessage XML-Inhalt + * @param isSammler handelt es sich um einen Sammler + * @return ReturnDataObject + */ + private ReturnDataObject submitSCT(LegitimationInfo legitimationInfo, byte[] sepaPainMessage, boolean isSammler, boolean isTermin) { + Payments.AuftragsId auftragsidentifikation; + LOG.info(addTagUser(format("isSammler: %s", isSammler), legitimationInfo)); + LOG.info(addTagUser(format("isTermin: %s", isTermin), legitimationInfo)); + SEPAParser sctParser; + CCTInitiation cctInitiation; + PaymentInstructionInformation paymentInfo; + CreditTransferTransactionInformation buchung; + String sepaFile = new String(sepaPainMessage); + sctParser = new SEPAParser(sepaFile); + try { + sctParser.parseSEPA(); + cctInitiation = sctParser.getSepaDocument().getCctInitiation(); + if (cctInitiation.getPmtInfos() == null || cctInitiation.getPmtInfos().size() != 1) { + LOG.warn(addTagUser(format("Nur ein PmtInfo-Segment unterstuetzt: %s", ((cctInitiation.getPmtInfos() != null) ? cctInitiation.getPmtInfos().size() : "null")), legitimationInfo)); + return new ReturnDataObject(false, "Ungültige Anzahl an PmtInfo-Segmente gefunden", "9930"); + } + List vectorBuchungen = cctInitiation.getPmtInfos().get(0).getCreditTransferTransactionInformationVector(); + if (vectorBuchungen == null || (isSammler && vectorBuchungen.size() < 1) || (!isSammler && vectorBuchungen.size() > 1)) { + int count = (vectorBuchungen == null) ? 0 : vectorBuchungen.size(); + String singleSammel = (isSammler) ? "s" : "m"; + String message = format("Ungültige Menge an CTTInfosegmente gefunden: %s (%s)", String.valueOf(count), singleSammel); + LOG.warn(addTagUser(message, legitimationInfo)); + return new ReturnDataObject(false, message, "9930"); + } + int anzahlBuchungen = vectorBuchungen.size(); + // Zahlungsinfos: + paymentInfo = cctInitiation.getPmtInfos().get(0); + // Gesamtbetrag! + //double betragBuchungen = paymentInfo.getCtrlSum();//control sum is not available in the body + double betragBuchungen = cctInitiation.getGrpHeader().getControlSum();//Getting the control sum from the header + // Kontonummer + String kontonummer = new SepaUtils().getAccountNoFromDeIban(paymentInfo.getDebtorAccountIBAN()); + String kontoid = database.getFieldByKontonummer(kontonummer, "kontoid", legitimationInfo); + if (kontoid == null) { + LOG.info(addTagUser(format("Konto nicht existent: %s", kontonummer), legitimationInfo)); + return new ReturnDataObject(false, "Konto nicht existent", "9935"); + } else if (!database.isOperationPermit(legitimationInfo, kontonummer, configColumnSct)) { + LOG.info(addTagUser(format("Konto darf Buchung nicht ausführen: %s", kontonummer), legitimationInfo)); + return new ReturnDataObject(false, "Konto darf Buchung nicht ausführen", "9935"); + } + SaldoDataObject saldoDataObject = getSaldo(kontonummer, legitimationInfo); + HashMap ibanBicListe = new HashMap<>(); + for (int bId = 0; bId < anzahlBuchungen; bId++) { + buchung = paymentInfo.getCreditTransferTransactionInformationVector().get(bId); + ibanBicListe.put(buchung.getCreditorIBAN(), buchung.getCreditorAgent()); + if (config(banking2.properties.checkSonderzeichen, "false").equals("true")) { + if (!database.replaceNonSepaChars(buchung.getCreditorName()).equals(buchung.getCreditorName())) { + return new ReturnDataObject(false, format("Name enthält ungültige Zeichen: %s", buchung.getCreditorName()), "9935"); + } + if (!database.replaceNonSepaChars(buchung.getVwz()).equals(buchung.getVwz())) { + return new ReturnDataObject(false, format("Verwendungszweck enthält ungültige Zeichen: %s", buchung.getVwz()), "9935"); + } + } + if (config(banking2.properties.checkPurposeCodeSCT, "true").equals("true")) { + ReturnDataObject purposeCheck = database.checkPurposeCode(getPurposeCode(buchung), legitimationInfo); + if (!purposeCheck.isSuccess()) { + return purposeCheck; + } + } + } + ReturnDataObject kontenCheck = database.checkEmpfaengerKonten(kontonummer, ibanBicListe, banking2.tranactionTypes.sct, legitimationInfo); + if (!kontenCheck.isSuccess()) { + return kontenCheck; + } + Date executionTime = null; + if (isTermin) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + String regExDate = paymentInfo.getRequestedExecutionDate(); + LOG.info(addTagUser(regExDate, legitimationInfo)); + if (regExDate != null && !regExDate.equals("1999-01-01")) { + try { + executionTime = sdf.parse(regExDate); + long dd = daysDiff(new Date(), executionTime); + if (dd < 1) { + LOG.warn(addTagUser(format("Datum liegt nicht in der Zukunft, Tage: %s", String.valueOf(dd)), legitimationInfo)); + return new ReturnDataObject(false, "Datum für die Ausführung muss mindestens ein Tag in der Zukunft liegen", "9935"); + } + LOG.info(addTagUser(format("Tage-Diff: %s", String.valueOf(dd)), legitimationInfo)); + } catch (ParseException e) { + LOG.error(addTagUser(format("Datum für die Ausführung fehlt oder ist Fehlerhaft: %s", e.getMessage()), legitimationInfo), e); + return new ReturnDataObject(false, "Datum für die Ausführung fehlt oder ist Fehlerhaft", "9935"); + } + } + } + LOG.info(format("Prüfe Überziehung: Terminüberweisung: %b verfügbarer Betrag: %f CtrlSum %f", isTermin, saldoDataObject.getVerfuegbarerBetrag(), betragBuchungen)); + if (!isTermin && !saldoDataObject.isOverdrawAllowed() && saldoDataObject.getVerfuegbarerBetrag() < betragBuchungen) { + return checkBuchungenBetrag(betragBuchungen, saldoDataObject, legitimationInfo); + } else { + Payments.Result inDXXX; + String wording_1 = (isTermin) ? "Dauerauftrag" : "Buchung"; + String wording_X = (isTermin) ? "Daueraufträge" : "Buchungen"; + String wording_do = (isTermin) ? " angelegt" : " ausgeführt"; + Integer counter; + if (isTermin) { + counter = anzahlBuchungen == 1 ? null : database.getNextCounterDauerauftraege(legitimationInfo, true); + } else { + counter = database.getNextCounterDTAUS(legitimationInfo, true); + } + String lastInsertId = ""; + String currentTime = new SimpleDateFormat("HH:mm:ss").format(new java.util.Date()); + String tan = ""; + if(legitimationInfo.getSecurityMethod().equals(SecurityMethod.PIN_1) || legitimationInfo.getSecurityMethod().equals(SecurityMethod.PIN_2)) + { + tan = legitimationInfo.getTanResponse(); + } + else + { + tan = "0"; + } + + for (int bId = 0; bId < anzahlBuchungen; bId++) { + buchung = paymentInfo.getCreditTransferTransactionInformationVector().get(bId); + if (isTermin) { + inDXXX = database.inDauerauftraege( + legitimationInfo, kontonummer, "", "", buchung.getCreditorName(), buchung.getAmount(), "51", + buchung.getVwz(), tan, + buchung.getCreditorIBAN(), + buchung.getCreditorAgent(), counter, 6, + executionTime, currentTime, + derfNullSein(buchung.getEndToEndID()), derfNullSein(getPurposeCode(buchung)) + ); + } else { + inDXXX = database.inDtaus( + legitimationInfo, kontonummer, "", "", + buchung.getCreditorName(), buchung.getAmount(), "51", + derfNullSein(buchung.getVwz()), legitimationInfo.getTanResponse(), + buchung.getCreditorIBAN(), 0, + buchung.getCreditorAgent(), counter, "", "", null, + "", "", null, currentTime, + derfNullSein(buchung.getEndToEndID()), derfNullSein(getPurposeCode(buchung)) + ); + } + if (inDXXX.isSuccess()) { + lastInsertId = inDXXX.getIds().getFirst(); + LOG.info(addTagUser(wording_1 + wording_do + ": " + (bId + 1) + "/" + anzahlBuchungen + "/" + (buchung.getAmount() * (-1)), legitimationInfo)); + if (!isTermin) { + database.updateSalden(buchung.getAmount(), true, kontonummer, legitimationInfo); + database.inVorabumsaetze(kontonummer, legitimationInfo, buchung.getAmount(), true, derfNullSein(buchung.getVwz()), buchung.getCreditorAgent(), buchung.getCreditorIBAN(), buchung.getCreditorName()); + } + } else { + LOG.warn(addTagUser("Es trat ein Fehler bei der Übergabe auf", legitimationInfo), inDXXX.getError()); + if (anzahlBuchungen == 1) { + return new ReturnDataObject(false, format("%s konnte nicht %s werden", wording_1, wording_do), "9035"); + } else { + return new ReturnDataObject(false, format("Eine oder mehrere %s konnten nicht %s werden", wording_X, wording_do), "9035"); + } + } + } + auftragsidentifikation = Payments.AuftragsId.generate(isSammler, legitimationInfo.getUserId(), lastInsertId, String.valueOf(counter)); + return new ReturnDataObject(true, "Auftrag Entgegengenommen", "0010").setAdditionalData(auftragsidentifikation); + } + } catch (SEPAParsingException e) { + LOG.warn(addTagUser(new String(sepaPainMessage), legitimationInfo), e); + return new ReturnDataObject(false, "SEPA Datei kann nicht gelesen werden", "9930"); + } + } + + private static String getPurposeCode(Object buchung) { + if (buchung instanceof CreditTransferTransactionInformation) { + if (Objects.equals(buchung, null) || Objects.equals(((CreditTransferTransactionInformation) buchung).getPurpose(), null)) { + return ""; + } else { + return ((CreditTransferTransactionInformation) buchung).getPurpose().getCd(); + } + } else if (buchung instanceof DirectDebitTransactionInformation) { + if (Objects.equals(buchung, null) || Objects.equals(((DirectDebitTransactionInformation) buchung).getPurpose(), null)) { + return ""; + } else { + return ((DirectDebitTransactionInformation) buchung).getPurpose().getCd(); + } + } + return ""; + } + + /** + * Berechnet den Tagesunterschied von zwei Date's + * + * @param from start-Datum (i.d.R. kleiner) + * @param to end-Datum (i.d.R. größer + * @return Tage dazwischen + */ + private static long daysDiff(Date from, Date to) { + return daysDiff(from.getTime() - (from.getTime() % 86400000L), to.getTime()); + } + + /** + * Berechnet den Tages-Unterschied von 2 Timestamps + * + * @param from start-Datum (i.d.R. kleiner) + * @param to end-Datum (i.d.R. größer + * @return Tage dazwischen + */ + private static long daysDiff(long from, long to) { + return Math.round((to - from) / 86400000D); // 1000 * 60 * 60 * 24 + } + + /** + * Holt die gebuchten Umsätze + * + * @param kontonummer die Kontonummer + * @param vonDatum ab Datum + * @param bisDatum bis Datum + * @param legitimationsInfo LegitimationInfo + * @return Byte-Array + */ + @Override + public byte[] getGebuchteUmsaetze(String kontonummer, Date vonDatum, Date bisDatum, LegitimationInfo legitimationsInfo) { + return database.getGebuchteUmsaetze(kontonummer, vonDatum, bisDatum, legitimationsInfo); + } + + /** + * Holt die Vorgemerkten Umsätze + * soll null zurückgeben, wenn keine Vorabumsätze vorhanden sind + * + * @param kontonummer die Kontonummer + * @param vonDatum ab Datum + * @param bisDatum bis Datum + * @param legitimationInfo Legitimationsinfo + * @return Byte-Array + */ + @Override + public byte[] getNichtGebuchteUmsaetze(String kontonummer, java.util.Date vonDatum, java.util.Date bisDatum, LegitimationInfo legitimationInfo) { + return database.getNichtGebuchteUmsaetze(bankCode, kontonummer, legitimationInfo, getKontowaehrung(kontonummer), vonDatum, bisDatum); + } + + /** + * Holt die Kontoproduktbezeichnung aus der Datenbank + * + * @param kontonummer Kontonummer + * @param legitimationsInfo LegitimationInfo + * @return Kontoart + */ + @Override + public String getKontoproduktbezeichnung(String kontonummer, LegitimationInfo legitimationsInfo) { + return database.getFieldByKontonummer(kontonummer, "kontoart", legitimationsInfo); + } + + /** + * Gibt die Kontowährung zurück + * + * @param kontonummer Kontonummer + * @return eigentlich immer EUR + */ + @Override + public String getKontowaehrung(String kontonummer) { + return "EUR"; + } + + /** + * Prüft ob Kennung und Kunden-ID zusammen gehören + * Wird nicht geprüft wenn Kennung = 9999999999 = Anonyme-Anfrage + * + * @param userId die Benutzerkennung + * @param customerId die Kunden-ID + * @return true, wenn Kennung und Kunden-ID zusammen gehören + */ + @Override + public boolean isCustomerIdValid(String userId, String customerId) { + // Anonyme Anfrage + if (userId != null && userId.equals("9999999999")) return true; + String customerIdFromDb = database.getCustomerId(userId); + // Kundenid wird gar nicht benötigt (Konfiguration) + if (database.isKundenIdNotRequired()) { + // Prüfen ob Kunde überhaupt verknüpft + + boolean userExistAndConnected = (userId != null && customerIdFromDb != null); + // Kundenid nicht gesetzt oder leer übergeben + if (customerId == null || customerId.equals("") || (customerIdFromDb != null && customerIdFromDb.equals(customerId))) return userExistAndConnected; + // Kundenid sollte zumindestens mit der Benutzerkennung übereinstimmen + return userExistAndConnected && userId.equals(customerId); + } + // Kennung im System, mit KundenID verbunden, KundenID stimmt mit übergebener ID überein + return customerIdFromDb != null && customerIdFromDb.equals(customerId); + } + + + private ReturnDataObject isValidDDType(PaymentInstructionInformation paymentInfo) { + ReturnDataObject r = new ReturnDataObject(true, ""); + String type = paymentInfo.getDirectDebitType(); + if (type.equals("B2B")) { + boolean b2ballowed = config(banking2.properties.allowedB2B, "0").equals("1"); + if (!b2ballowed) r = new ReturnDataObject(false, "B2B ist nicht erlaubt"); + } else if (!type.equals("COR1") && !type.equals("CORE")) { + r = new ReturnDataObject(false, format("%s ist nicht erlaubt", type)); + } + return r; + } + + private banking2.tranactionTypes getTransactionTypeSDD(PaymentInstructionInformation paymentInfo) { + return (paymentInfo.getDirectDebitType().equals("B2B")) ? banking2.tranactionTypes.b2b : banking2.tranactionTypes.sdd; + } + + /** + * Prüft ob das Ausführungsdatum Target2-Valide ist + * + * @param paymentInfo PaymentInstructionInformation (CORE/COR1) + * @return falls alles passt, true + */ + private ReturnDataObject isValidTarget2ExecutionDate(PaymentInstructionInformation paymentInfo) { + Date executionTime; + try { + executionTime = HolidaysObject.checkDueDate( + paymentInfo.getDirectDebitType(), + paymentInfo.getSequenceType(), + banking2.sdf_sql.parse(paymentInfo.getRequestedCollectionDate()), + parseInt(config(banking2.properties.cutOffHour, "23")), + parseInt(config(banking2.properties.cutOffMinute, "59")), + parseInt(config(banking2.properties.daysToAdd, "0")), + parseInt(config(banking2.properties.sddDueDateAdd, "0")), + config(banking2.properties.checkCore1, "true").equals("true") + ); + } catch (Exception e) { + return new ReturnDataObject(false, e.getMessage(), "9930"); + } + Calendar executionCalendar = new GregorianCalendar(); + executionCalendar.setTime(executionTime); + if (BankDateCalculator.isDateTarget2BankHoliday(executionCalendar)) { + return new ReturnDataObject(false, "Ausfuehrungsdatum ist kein Banken Geschaeftstag", "9930"); + } + return new ReturnDataObject(true, "all fine"); + } + + @SuppressWarnings("unused") + private String replaceNonSepaChars(String string) { + string = derfNullSein(string); + return string.replace("/[^a-zA-Z0-9\\. \\/\\+\\-\\?:\\(\\)$,]/", " "); + } + + /** + * Holt Configurationseinstellungen + * + * @param property Einstellungs-Name + * @param defaultValue defaultValue + * @return Einstellungswert, falls nicht gesetzt die defaultValue + */ + private String config(String property, String defaultValue) { + return database.config(property, defaultValue); + } + + /** + * Liste aller Terminzahlungen + * + * @param legitimationsInfo legitimationsInfo + * @param clientProductInfo clientProductInfo + * @param isSammler ob Sammler oder Einzel-Terminzahlungen zurückgegeben werden sollen + * @param sepaVersion in welcher Sepa-Version die Daten zurück gegeben werden sollen + * @return Liste mit SEPA-Dokumente + */ + @Override + public LinkedList getTerminzahlungen(LegitimationInfo legitimationsInfo, ClientProductInfo clientProductInfo, boolean isSammler, Payments.versions sepaVersion) { + return database.getTerminzahlungen(legitimationsInfo, clientProductInfo, isSammler, sepaVersion); + } + + /** + * Legt eine neue Termin Einzel/Sammel-Überweisung an + * + * @param legitimationsInfo legitimationsInfo + * @param sepaDescriptor sepaDescriptor + * @param sepaPainMessage sepaPainMessage + * @param isSammler ob es sich um ein Sammler oder Einzel-Terminzahlung handelt + * @return Erfolgsobjekt + */ + @Override + public ReturnDataObject submitNewTransactionSchedule(LegitimationInfo legitimationsInfo, String sepaDescriptor, byte[] sepaPainMessage, boolean isSammler) { + return submitSCT(legitimationsInfo, sepaPainMessage, isSammler, true); + } + + /** + * Löscht eine Terminzahlung anhand der Auftragsid + * + * @param legitimationsInfo legitimationsInfo + * @param clientProductInfo clientProductInfo + * @param auftragsidentifikation auftragsidentifikation + * @param isSammler gibt an, ob es sich um einen Sammler oder Einzelbuchung handelt + * @return Erfolgsobjekt + */ + @Override + public ReturnDataObject deleteTerminzahlung(LegitimationInfo legitimationsInfo, ClientProductInfo clientProductInfo, String auftragsidentifikation, boolean isSammler) { + Payments.Result r = database.deleteTerminzahlung(legitimationsInfo, clientProductInfo, auftragsidentifikation, isSammler); + if (r.isSuccess()) { + return new ReturnDataObject(true, "Auftrag gelöscht", "0010"); + } else { + if (r.getError() == null) { + return new ReturnDataObject(false, "Auftrag existiert nicht bzw. wurde bereits ausgeführt", "9210"); + } else { + LOG.warn(addTagUser(format("Fehler beim Löschen eines Terminauftrags: %s %s",auftragsidentifikation,r.getError().getMessage()), legitimationsInfo), r.getError()); + return new ReturnDataObject(false, "Es ist ein Fehler beim Löschen aufgetreten", "9930"); + } + } + } + + @Override + public List getGebuchteCamtUmsaetze(String kontonummer, Date vonDatum, Date bisDatum, LegitimationInfo legitimationsInfo, String camtDescriptor) { + //TODO implementieren + return null; + } + + @Override + public byte[] getNichtGebuchteCamtUmsaetze(String kontonummer, Date vonDatum, Date bisDatum, LegitimationInfo legitimationsInfo) { + //TODO implementieren + return new byte[0]; + } + + @Override + public List getCreditCardRevenueData(String kreditkartennummer, Date von, Date bis, LegitimationInfo legitimationsInfo) { + return null; + } + + @Override + public List getEmpfaengerkontenbestand(LegitimationInfo legitimationsInfo, KontoverbindungInternational kontoverbindungInternational) { + return null; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/DataAccessFacade.java b/src/main/java/net/petafuel/fuelifints/dataaccess/DataAccessFacade.java new file mode 100644 index 0000000..f1aa16f --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/DataAccessFacade.java @@ -0,0 +1,398 @@ +package net.petafuel.fuelifints.dataaccess; + +import net.petafuel.fuelifints.FinTSVersionSwitch; +import net.petafuel.fuelifints.dataaccess.dataobjects.*; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.client.ClientProductInfo; +import net.petafuel.fuelifints.model.client.LegitimationInfo; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Betrag; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungNational; +import net.petafuel.fuelifints.support.Payments; +import net.petafuel.jsepa.model.Document; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +/** + * Facade für den Zugriff auf die Daten, die vom FueliFinTS an den Nutzer übermittelt werden können + *

+ * Die Implementierung erfolgt abhängig davon, welches Bankingsystem hinter dem HBCI Server steht + */ + +public interface DataAccessFacade { + + /** + * Prüft ob ein Schlüsselpar für die Bank existiert + * + * @param securityMethod Sicherheitsmethode + * @param keyType SchlüsselTyp + * @return true wenn exisitert + */ + boolean hasKeyPair(SecurityMethod securityMethod, String keyType); + + void updateKeyPair(SecurityMethod securityMethod, String privateKey, String publicKey, String keyType, BigInteger modulus, BigInteger publicExponent); + + String getPrivateKey(SecurityMethod securityMethod, String keyType); + + String getPublicKey(SecurityMethod securityMethod, String keyType); + + boolean addUserPublicKey(String userId, String customerId, SecurityMethod securityMethod, String pubKey, String keyType, int keyVersion, int keyNumber, BigInteger modulus, BigInteger publicExponent); + + String getUserPublicKey(String userId, String customerId, SecurityMethod securityMethod, String keyType, int keyVersion, int keyNumber); + + boolean existsUserKey(String userId, String customerId, SecurityMethod securityMethod, String keyType, int keyVersion, int keyNumber); + + String getUserSystemId(String dialogid, LegitimationInfo legitimationInfo, ClientProductInfo clientPoductInfo); + + String getOrGenerateUserSystemId(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo); + + /** + * Gibt die aktuelle Version der Bankparameter Daten zurück + * + * @return aktuelle Version der Bankparameter Daten + */ + int getCurrentBpdVersion(); + + /** + * Zeigt ob die übergebenen BDP Version auch die aktuelle Version ist. + * + * @param bpdVersion + * @return true falls bpdVersion die aktuelle Version ist, ansonsten false. + */ + boolean isBpdVersionCurrent(int bpdVersion); + + /** + * Gibt die aktuelle Version der Userparameter Daten für die übergebene Benutzerkennung zurück + * + * @param legitimationInfo Benutzerkennung, für die die Version geholt werden soll + * @return aktuelle Version der Userparameter Daten + */ + int getCurrentUpdVersion(LegitimationInfo legitimationInfo); + + /** + * Liefert ob übergebene UPD Version auch die aktuelle Version ist. + * + * @param legitimationInfo Benutzerkennung + * @param updVersion updVersion + * @return true falls aktuell, ansonsten false. + */ + boolean isUpdVersionCurrent(LegitimationInfo legitimationInfo, int updVersion); + + /** + * Liefert Parameterdaten für die Segmentfolge Bankparameterdaten als Rückmledung auf die Dialoginitilisierung + * des Kunden zurück. + *

+ * FinTS 3.0: Formals, D.1 + * + * @param fintsVersion Gibt die FinTS Version an, für die Parameterdaten gefordert wurden + * @return Liste mit Parameterdaten für alle erlaubten Geschäftsvorfälle + */ + ArrayList getParameterData(FinTSVersionSwitch.FinTSVersion fintsVersion); + + /** + * Liefert Bankparameterdaten als Rückmeldung auf die Dialoginitilisierung + * des Kunden zurück. + *

+ * FinTS 3.0: Formals, D.2 (HIBPA) + * + * @param fintsVersion Gibt die FinTS Version an, für die Parameterdaten gefordert wurden + * @return BankParameterDaten für die gewählte HBCI Version + */ + CommonBankParameterDataObject getCommonBankParameters(FinTSVersionSwitch.FinTSVersion fintsVersion); + + /** + * Liefert allgemeine Informationen zu den Konten der übergebenen Benutzerkennung zurück + *

+ * FinTS 3.0: Formals, E.1 (HIUPD) + * + * @param legitimationInfo Legitimationsinfo zum Benutzer + * @return AccountDataObject-Liste für alle passenden Konten + */ + ArrayList getAccountData(LegitimationInfo legitimationInfo); + + /** + * Liefert die Parameter die für Pin/Tan benötigt werden. + * + * @return + */ + PinParameterObject getPinParameter(); + + /** + * Liefert ob ein Auftrag (aClass repräsentiert das Segment als Klasse) für den + * angegebenen Account gültig ist. + * + * @param legitimationInfo LegitimationInfo + * @param accountId die Kontonummer + * @param aClass @return + */ + boolean operationAllowedForAccount(LegitimationInfo legitimationInfo, String accountId, Class aClass); + + + /** + * Liefert ob ein Auftrag (aClass repräsentiert das Segment als Klasse) für den + * angegebenen Account gültig ist. + * + * @param legitimationInfo LegitimationInfo + * @param aClass @return + */ + boolean operationAllowedForAccount(LegitimationInfo legitimationInfo, Class aClass); + + /** + * Liefert den Saldo zu einem angegebenen Account. + * + * @param accountId + * @param legitimationsInfo + * @return + */ + SaldoDataObject getSaldo(String accountId, LegitimationInfo legitimationsInfo); + + /** + * Liefert die PIN eines Nutzers für das PIN/TAN verfahren. + * + * @param userId die ID des Nutzers. + * @param pin + * @param dialog + * @return die PIN des Nutzers oder null. + */ + ReturnDataObject checkUserPin(String userId, String pin, Dialog dialog); + + /** + * Prüft ob eine TAN korrekt ist. + * + * + * @param dialogId + * @param legitimationInfo die Kunden-ID + * @param clientProductInfo + *@param tan die eingegebenen TAN + * @param auftragsHashWert der Auftrags-Hashwert @return true falls TAN korrekt, ansonsten false + */ + ReturnDataObject checkTan(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo, String tan, byte[] auftragsHashWert); + + /** + * Generiert eine Auftragsreferenz zu einem Auftrag. + * + * @param dialogId die Dialog-ID + * @param clientProductInfo + * @param auftragsHashwert der Auftrags-Hashwert @return die generierte Auftragsreferenz + */ + String generateAuftragsreferenz(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo, byte[] auftragsHashwert); + + /** + * Liefert die Challenge zu einem Auftrag. + * + * @param dialogId + * @param legitimationInfo BenutzerDaten + * @param clientProductInfo KundenProdukt + * @param auftragsHashwert der Auftrags-Hashwert + * @param tanVerfahren das gewählte TAN verfahren das zur Durchführung genutzt werden soll + * @param parameterChallengeKlasse Parameter zur Challenge wie Beispielsweise Beträge oder Kontonummern + * @return die Challenge + */ + ReturnDataObject getChallenge(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo, byte[] auftragsHashwert, String tanVerfahren, List parameterChallengeKlasse); + + /** + * Reicht eine Inlandsüberweisung ein. + * + * @param kontoverbindungAuftraggeber die Kontoverbindung des Auftragsgebers + * @param kontoverbindungEmpfaenger die Kontoverbindung des Empfänger + * @param nameEmpfaenger1 der Name des Empfängers 1 + * @param nameEmpfaenger2 der Name des Empfängers 2 + * @param textschluessel der Textschlüssel der Überweisung + * @param textschluesselErgaenzung die Textschlüsselergänzung + * @param verwendungszweckzeilen die Verwendungszweckzeilen + * @param betrag + * @param endToEnd + * @param purposeCode + * @return true falls Auftrag angenommen, ansonsten false + */ + ReturnDataObject submitNewTransaction(LegitimationInfo legitimationInfo, KontoverbindungNational kontoverbindungAuftraggeber, KontoverbindungNational kontoverbindungEmpfaenger, String nameEmpfaenger1, String nameEmpfaenger2, String textschluessel, String textschluesselErgaenzung, List verwendungszweckzeilen, Betrag betrag, String endToEnd, String purposeCode); + + /** + * Reicht eine SEPA-Überweisung ein. + * + * @param legitimationInfo LegitimationInfo + * @param sepaDescriptor Sepa-Bezeichner + * @param sepaPainMessage die SEPA-Pain-Message @return true falls Auftrag angenommen, ansonsten false + * @param isSammler + */ + ReturnDataObject submitNewTransaction(LegitimationInfo legitimationInfo, String sepaDescriptor, byte[] sepaPainMessage, boolean isSammler); + + + /** + * Reicht die Pinänderung ein + * + * @param legitimationInfo LegitimationInfo + * @param newPin die Neue PIN, die für den Nutzer hinterlegt werden soll + * @return + */ + ReturnDataObject submitChangePin(LegitimationInfo legitimationInfo, String newPin); + + /** + * Liefert die gebuchten Umsätze. + * + * @param kontonummer die Kontonummer + * @param vonDatum ab Datum + * @param bisDatum bis Datum + * @param legitimationsInfo LegitimationInfo + * @return die gebuchten Umsätze als MT940-byte[] + */ + byte[] getGebuchteUmsaetze(String kontonummer, Date vonDatum, Date bisDatum, LegitimationInfo legitimationsInfo); + + /** + * Liefert die nicht-gebuchten Umsätze. + * + * @param kontonummer die Kontonummer + * @param vonDatum ab Datum + * @param bisDatum bis Datum + * @return die nicht gebuchten Umsätze als MT942-byte[] + */ + byte[] getNichtGebuchteUmsaetze(String kontonummer, Date vonDatum, Date bisDatum, LegitimationInfo legitimationInfo); + + /** + * Liefert die Kontoproduktbezeichnung zu einer Kontonummer. + * + * @param accountId die Kontonummer + * @param legitimationsInfo + * @return die Kontoproduktbezeichnung als String. + */ + String getKontoproduktbezeichnung(String accountId, LegitimationInfo legitimationsInfo); + + /** + * Liefert die genutzte Kontowährung zur Kontonummer. + * + * @param accountId die Kontonummer zu der die Währung abgefragt wird. + * @return die verwendete Währung zum Konto. + */ + String getKontowaehrung(String accountId); + + /** + * Prüft ob eine Kunden-ID (customerId) korrekt ist und zur Benutzerkennung (userId) passt. + * + * @param userId die Benutzerkennung + * @param customerId die Kunden-ID + * @return true falls die Kunden-ID verifiziert wurde, ansonsten false. + */ + boolean isCustomerIdValid(String userId, String customerId); + + /** + * Liefert alle zugelassen TAN Verfahren für den Konto. + * + * @param legitimationInfo die LegitimationInfo + * @return die zugelassen TAN Verfahren für den Benutzer. + */ + List getZugelasseneTanVerfahren(LegitimationInfo legitimationInfo); + + /** + * @param dialogId die DialogID + * @param legitimationInfo die Legitimationsinfo + * @param clientProductInfo die Client + * @param TAN die zu Entwertende TAN + */ + void devalueTan(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo, String TAN); + + /** + * Liefert die Kommunikationsadressen unter der die Banken über HBCI erreichbar sind. + * + * @param bankId + * @return + */ + KommunikationsParameterData getKommunikationsParameterData(String bankId); + + /** + * Funktion für Rückgabe der Bank-Mitteilungen + * + * @param legitimationInfo die Legitimationsinfo + * @param clientProductInfo die ClientProductInfo + * @return Liste an Nachrichten + */ + List getBankMessages(LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo); + + /** + * Liefert den Bestand an an Sepa Einzellastschriften zurück. + * In diesem Fall muss im DirectDebitDataObject die SepaPainMessage und der SepaDescriptor gesetzt werden + * + * @param legitimationInfo die Legitmationsinfo + * @return für jede Einzellastschrift ein DirectDebitDataObject in einer Liste + */ + List getEinzelLastschriftBestand(LegitimationInfo legitimationInfo); + + /** + * Liefert den Bestand an an Sepa Sammellastschriften zurück. + * + * @param legitimationInfo die Legitmationsinfo + * @return für jede Einzellastschrift ein DirectDebitDataObject in einer Liste + */ + List getSammelLastschriftBestand(LegitimationInfo legitimationInfo); + + /** + * Kunden haben die Möglichkeit Nachrichten an das Kreditinstitut zu senden. + * + * @param legitimationsInfo + * @param kontoverbindungAuftraggeber kann null sein + * @param freitextmeldung + * @param betreff kann null sein + * @param empfaengerangaben kann null sein + * @return true falls die Nachricht angenommen wurden, false andernfalls. + */ + boolean userMessageSubmitted(LegitimationInfo legitimationsInfo, KontoverbindungNational kontoverbindungAuftraggeber, String freitextmeldung, String betreff, String empfaengerangaben); + + /** + * Liefert die BIC des aktuellen Bankinstituts. + * + * @return die BIC als String. + */ + String getBic(); + + LinkedList getTerminzahlungen(LegitimationInfo legitimationsInfo, ClientProductInfo clientProductInfo, boolean isSammler, Payments.versions version); + + ReturnDataObject submitNewTransactionSchedule(LegitimationInfo legitimationsInfo, String sepaDescriptor, byte[] sepaFile, boolean isSammler); + + ReturnDataObject deleteTerminzahlung(LegitimationInfo legitimationsInfo, ClientProductInfo clientProductInfo, String auftragsidentifikation, boolean isSammler); + + /** + * Liefert die gebuchten SEPA Umsätze. + * + * + * @param kontonummer die Kontonummer + * @param vonDatum ab Datum + * @param bisDatum bis Datum + * @param legitimationsInfo LegitimationInfo + * @param camtDescriptor unterstütztes camt Format des Kundenprodukts + * @return die gebuchten Umsätze als List + */ + List getGebuchteCamtUmsaetze(String kontonummer, Date vonDatum, Date bisDatum, LegitimationInfo legitimationsInfo, String camtDescriptor); + + /** + * Liefert die nicht-gebuchten SEPA Umsätze. + * + * @param kontonummer die Kontonummer + * @param vonDatum ab Datum + * @param bisDatum bis Datum + * @return die nicht gebuchten Umsätze als camt.052 + */ + byte[] getNichtGebuchteCamtUmsaetze(String kontonummer, Date vonDatum, Date bisDatum, LegitimationInfo legitimationsInfo); + + /** + * Liefert kreditkartenumsätze + * @param kreditkartennummer die Nummer der Kreditkarte + * @param von Datum ab wann Umsätze geliefert werden sollen + * @param bis Datum bis wann Umsätze geliefert werden sollen + * @param legitimationsInfo zur Überprüfung + * @return Liste von CreditCardRevenueDataObject oder null falls keine Vorhanden sind. + */ + List getCreditCardRevenueData(String kreditkartennummer, Date von, Date bis, LegitimationInfo legitimationsInfo); + + /** + * Liefert den Bestand an Empfängerkonten (für SEPA-Übertrag) + * @param legitimationsInfo zur Überprüfung + * @param kontoverbindungInternational Konto für das der Bestand zurückgeliefert werden soll + * @return Liste mit den Empfängerkonten-Daten oder null falls keine Vorhanden sind. + */ + List getEmpfaengerkontenbestand(LegitimationInfo legitimationsInfo, KontoverbindungInternational kontoverbindungInternational); +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/DataAccessFacadeManager.java b/src/main/java/net/petafuel/fuelifints/dataaccess/DataAccessFacadeManager.java new file mode 100644 index 0000000..588d141 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/DataAccessFacadeManager.java @@ -0,0 +1,81 @@ +package net.petafuel.fuelifints.dataaccess; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.util.concurrent.ConcurrentHashMap; + +public class DataAccessFacadeManager { + + private static final Logger LOG = LogManager.getLogger(DataAccessFacadeManager.class); + private static final ConcurrentHashMap accessFacadeHashMap = new ConcurrentHashMap<>(); + + private static final String CLASS_DEBUG = "net.petafuel.fuelifints.dataaccess.DummyAccessFacade"; + private static final String CLASS_PREPAID = "net.petafuel.fuelifints.dataaccess.PrepaidAccess"; + private static final String CLASS_BANKING2 = "net.petafuel.fuelifints.dataaccess.Banking2Access"; + + /** + * Diese Funktion liefert zur übergebenen Bankleitzahl die passende Dataaccessfacade zurück. + * Dafür wird die entsprechende Properties Datei aus dem Dateisystem ausgelesen. + * Die Properties Datei befindet sich im Verzeichnis ./config/ und hat das Format + * ..properties + * Sollte keine passende Configdatei gefunden werden, liefert diese Funktion null zurück + *

+ * Die DataAccessFacade wird dabei gespeichert, es wird also pro Bankleitzahl immer nur eine DataAccessFacade instanziert + * + * @param bankId Bankleitzahl, für die eine DataAccessFacade benötigt wird + * @return DataAccessFacade oder null + */ + public static DataAccessFacade getAccessFacade(String bankId) { + try { + + final String banking2properties = "config/" + bankId + ".banking2.properties"; + final String prepaidProperties = "config/" + bankId + ".prepaid.properties"; + final String dummyProperties = "config/" + bankId + ".dummy.properties"; + + if (new File(banking2properties).exists()) { + LOG.info("Banking2-config gefunden: {}", banking2properties); + if (!accessFacadeHashMap.containsKey(bankId)) { + accessFacadeHashMap.put(bankId, instantiateNewFacade(CLASS_BANKING2, banking2properties)); + } + } else { + if (new File(prepaidProperties).exists()) { + LOG.info("Prepaid-config gefunden: {}", prepaidProperties); + if (!accessFacadeHashMap.containsKey(bankId)) { + accessFacadeHashMap.put(bankId, instantiateNewFacade(CLASS_PREPAID, prepaidProperties)); + } + } else { + if (new File(dummyProperties).exists() || bankId.equals("DEBUG")) { + LOG.info("Dummy-config gefunden: {} ...(oder DEBUG BankID)", dummyProperties); + if (!accessFacadeHashMap.containsKey(bankId)) { + accessFacadeHashMap.put(bankId, instantiateNewFacade(CLASS_DEBUG, dummyProperties)); + } + } + } + } + } catch (Exception e) { + LOG.error("Cannot DataAccessFacade#getAccessFacade for bankId={}", bankId, e); + } + + DataAccessFacade dataAccessFacade = accessFacadeHashMap.get(bankId); + LOG.info("BankCode={}, DataAccessFacade={} ", bankId, dataAccessFacade); + + return dataAccessFacade; + } + + @SuppressWarnings("unchecked") + private static DataAccessFacade instantiateNewFacade(String classPath, String configPath) { + try { + Class facadeClass = Class.forName(classPath); + Class[] constructorParamClasses = new Class[]{String.class}; + Object[] constructorParamValues = new Object[]{configPath}; + Constructor constructor = facadeClass.getConstructor(constructorParamClasses); + return (DataAccessFacade) constructor.newInstance(constructorParamValues); + } catch (Exception e) { + LOG.error("Cannot instantiateNewFacade! classPath={}, configPath={}", classPath, configPath, e); + } + return null; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/SqlConnectionPool.java b/src/main/java/net/petafuel/fuelifints/dataaccess/SqlConnectionPool.java new file mode 100644 index 0000000..d8007c6 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/SqlConnectionPool.java @@ -0,0 +1,60 @@ +package net.petafuel.fuelifints.dataaccess; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Enumeration; +import java.util.Hashtable; + +public class SqlConnectionPool { + private static final Logger LOG = LogManager.getLogger(SqlConnectionPool.class); + private final Hashtable connections = new Hashtable<>(); + + + public synchronized Connection getConnection() throws SQLException { + Connection con; + Enumeration cons = connections.keys(); + + synchronized (connections) { + while (cons.hasMoreElements()) { + con = cons.nextElement(); + Boolean b = connections.get(con); + if (b == Boolean.FALSE) { + // So we found an unused connection. + // Test its integrity with a quick setAutoCommit(true) call. + // For production use, more testing should be performed, + // such as executing a simple query. + try { + con.setAutoCommit(true); + } catch (SQLException e) { + // Problem with the connection, replace it. + connections.remove(con); + con = createNew(); + } + // Update the Hashtable to show this one's taken + connections.put(con, Boolean.TRUE); + // Return the connection + return con; + } + } + // If we get here, there were no free connections. Make one more. + // A more robust connection pool would have a maximum size limit, + // and would reclaim connections after some timeout period + con = createNew(); + connections.put(con, Boolean.TRUE); + return con; + } + } + + public synchronized void putBack(Connection returned) { + if (returned != null && connections.containsKey(returned)) { + connections.put(returned, Boolean.FALSE); + } + } + + protected Connection createNew() throws SQLException { + return null; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/AccountDataObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/AccountDataObject.java new file mode 100644 index 0000000..56e00cc --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/AccountDataObject.java @@ -0,0 +1,120 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +/** + * Enthält Daten zu den Konten eines Kunden + *

+ * FinTS 3.0: Diese Daten werden im Segment HIUPD verwendet + */ +public class AccountDataObject implements IDataObject { + + private String kontonummer; + + private String bankleitzahl; + + private String kunden_id; + + private String kontoart; + + private String kontowaehrung; + + private String name; + + private String produktbzeichnung; + + private String kontolimit; + + private String erlaubteGeschaeftsvorfaelle; + + public AccountDataObject(String kontonummer, String bankleitzahl, String name, String produktbzeichnung) { + this.kontonummer = kontonummer; + this.bankleitzahl = bankleitzahl; + this.name = name; + this.produktbzeichnung = produktbzeichnung; + } + + public String getKontonummer() { + return kontonummer; + } + + public void setKontonummer(String kontonummer) { + this.kontonummer = kontonummer; + } + + public String getBankleitzahl() { + return bankleitzahl; + } + + public void setBankleitzahl(String bankleitzahl) { + this.bankleitzahl = bankleitzahl; + } + + public String getKunden_id() { + return kunden_id; + } + + public void setKunden_id(String kunden_id) { + this.kunden_id = kunden_id; + } + + public String getKontoart() { + return kontoart; + } + + public void setKontoart(String kontoart) { + this.kontoart = kontoart; + } + + public String getKontowaehrung() { + return kontowaehrung; + } + + public void setKontowaehrung(String kontowaehrung) { + this.kontowaehrung = kontowaehrung; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getProduktbzeichnung() { + return produktbzeichnung; + } + + public void setProduktbzeichnung(String produktbzeichnung) { + this.produktbzeichnung = produktbzeichnung; + } + + public String getKontolimit() { + return kontolimit; + } + + public void setKontolimit(String kontolimit) { + this.kontolimit = kontolimit; + } + + public String getErlaubteGeschaeftsvorfaelle() { + return erlaubteGeschaeftsvorfaelle; + } + + public void setErlaubteGeschaeftsvorfaelle(String erlaubteGeschaeftsvorfaelle) { + this.erlaubteGeschaeftsvorfaelle = erlaubteGeschaeftsvorfaelle; + } + + @Override + public String toString() { + return "AccountDataObject{" + + "kontonummer='" + kontonummer + '\'' + + ", bankleitzahl='" + bankleitzahl + '\'' + + ", kunden_id='" + kunden_id + '\'' + + ", kontoart='" + kontoart + '\'' + + ", kontowaehrung='" + kontowaehrung + '\'' + + ", name='" + name + '\'' + + ", produktbzeichnung='" + produktbzeichnung + '\'' + + ", kontolimit='" + kontolimit + '\'' + + '}'; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/BankMessageObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/BankMessageObject.java new file mode 100644 index 0000000..eb8c797 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/BankMessageObject.java @@ -0,0 +1,22 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +public class BankMessageObject +{ + private String subject; + private String message; + public BankMessageObject(String subject,String message) + { + this.subject = (subject != null) ? subject : ""; + this.message = (message != null) ? message : ""; + } + + public String getMessage() + { + return message; + } + + public String getSubject() + { + return subject; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/CommonBankParameterDataObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/CommonBankParameterDataObject.java new file mode 100644 index 0000000..9abb928 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/CommonBankParameterDataObject.java @@ -0,0 +1,98 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +/** + * Allgemeine Bankparameter + * + * FinTS 3.0: Informationen aus Segment HIBPA + */ +public class CommonBankParameterDataObject implements IDataObject { + + private int bpd_version; + + private String bankleitzahl; + + private String kreditinstitutsbezeichnung; + + private String unterstuetzte_Sprachen; + + private String unterstuetzte_hbci_versionen; + + private Integer maximale_nachrichten_groesse; + + private Integer minimaler_timeout_wert; + + private Integer maximaler_timeout_wert; + + public CommonBankParameterDataObject(int bpd_version, String bankleitzahl, String kreditinstitutsbezeichnung, String unterstuetzte_Sprachen, String unterstuetzte_hbci_versionen) { + this.bpd_version = bpd_version; + this.bankleitzahl = bankleitzahl; + this.kreditinstitutsbezeichnung = kreditinstitutsbezeichnung; + this.unterstuetzte_Sprachen = unterstuetzte_Sprachen; + this.unterstuetzte_hbci_versionen = unterstuetzte_hbci_versionen; + } + + public int getBpd_version() { + return bpd_version; + } + + public void setBpd_version(int bpd_version) { + this.bpd_version = bpd_version; + } + + public String getKreditinstitutsbezeichnung() { + return kreditinstitutsbezeichnung; + } + + + public void setKreditinstitutsbezeichnung(String kreditinstitutsbezeichnung) { + this.kreditinstitutsbezeichnung = kreditinstitutsbezeichnung; + } + + public String getUnterstuetzte_Sprachen() { + return unterstuetzte_Sprachen; + } + + public void setUnterstuetzte_Sprachen(String unterstuetzte_Sprachen) { + this.unterstuetzte_Sprachen = unterstuetzte_Sprachen; + } + + public String getUnterstuetzte_hbci_versionen() { + return unterstuetzte_hbci_versionen; + } + + public void setUnterstuetzte_hbci_versionen(String unterstuetzte_hbci_versionen) { + this.unterstuetzte_hbci_versionen = unterstuetzte_hbci_versionen; + } + + public Integer getMaximale_nachrichten_groesse() { + return maximale_nachrichten_groesse; + } + + public void setMaximale_nachrichten_groesse(Integer maximale_nachrichten_groesse) { + this.maximale_nachrichten_groesse = maximale_nachrichten_groesse; + } + + public Integer getMinimaler_timeout_wert() { + return minimaler_timeout_wert; + } + + public void setMinimaler_timeout_wert(Integer minimaler_timeout_wert) { + this.minimaler_timeout_wert = minimaler_timeout_wert; + } + + public Integer getMaximaler_timeout_wert() { + return maximaler_timeout_wert; + } + + public void setMaximaler_timeout_wert(Integer maximaler_timeout_wert) { + this.maximaler_timeout_wert = maximaler_timeout_wert; + } + + public String getBankleitzahl() { + return bankleitzahl; + } + + public void setBankleitzahl(String bankleitzahl) { + this.bankleitzahl = bankleitzahl; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/CreditCardRevenueDataObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/CreditCardRevenueDataObject.java new file mode 100644 index 0000000..d05f568 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/CreditCardRevenueDataObject.java @@ -0,0 +1,62 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +import java.util.Date; + +public class CreditCardRevenueDataObject { + + private String creditCardNumber; + private Date voucherDate; + private Date bookingDate; + private double amount; + private String purpose; + private boolean billed; + + public String getCreditCardNumber() { + return creditCardNumber; + } + + public void setCreditCardNumber(String creditCardNumber) { + this.creditCardNumber = creditCardNumber; + } + + public Date getVoucherDate() { + return voucherDate; + } + + public void setVoucherDate(Date voucherDate) { + this.voucherDate = voucherDate; + } + + public void setBookingDate(Date bookingDate) { + this.bookingDate = bookingDate; + } + + public Date getBookingDate() { + return bookingDate; + } + + public void setPurpose(String purpose) { + this.purpose = purpose; + } + + public double getAmount() { + return amount; + } + + public void setAmount(double amount) { + this.amount = amount; + } + + public String getPurpose() { + return purpose; + } + + + public boolean isBilled() { + return billed; + } + + public void setBilled(boolean billed) { + this.billed = billed; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/DirectDebitDataObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/DirectDebitDataObject.java new file mode 100644 index 0000000..0a2b1a4 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/DirectDebitDataObject.java @@ -0,0 +1,79 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +import java.util.Date; + +/** + * Objekt für die Rückmeldung von Lastschrift Beständen + */ +public class DirectDebitDataObject implements IDataObject { + + private String auftragsIdentifikation; //Genutzt im Einzel- und Sammellastschrift-Bestand + + private Date einreichungsdatum; //Genutzt im Sammellastschrift-Bestand + + private Date ausfuehrungsDatum; //Genutzt im Sammellastschrift-Bestand + + private Integer anzahlDerAuftraege; //Genutzt im Sammellastschrift-Bestand + + private Double summeDerBetraege; //Genutzt im Sammellastschrift-Bestand + + private String sepaPainMessage; //Genutzt im Einzellastschrift-Bestand + + private String sepaDescriptor; //Genutzt im Einzellastschrift-Bestand + + public String getAuftragsIdentifikation() { + return auftragsIdentifikation; + } + + public void setAuftragsIdentifikation(String auftragsIdentifikation) { + this.auftragsIdentifikation = auftragsIdentifikation; + } + + public Date getEinreichungsdatum() { + return einreichungsdatum; + } + + public void setEinreichungsdatum(Date einreichungsdatum) { + this.einreichungsdatum = einreichungsdatum; + } + + public Date getAusfuehrungsDatum() { + return ausfuehrungsDatum; + } + + public void setAusfuehrungsDatum(Date ausfuehrungsDatum) { + this.ausfuehrungsDatum = ausfuehrungsDatum; + } + + public Integer getAnzahlDerAuftraege() { + return anzahlDerAuftraege; + } + + public void setAnzahlDerAuftraege(Integer anzahlDerAuftraege) { + this.anzahlDerAuftraege = anzahlDerAuftraege; + } + + public Double getSummeDerBetraege() { + return summeDerBetraege; + } + + public void setSummeDerBetraege(Double summeDerBetraege) { + this.summeDerBetraege = summeDerBetraege; + } + + public String getSepaPainMessage() { + return sepaPainMessage; + } + + public void setSepaPainMessage(String sepaPainMessage) { + this.sepaPainMessage = sepaPainMessage; + } + + public String getSepaDescriptor() { + return sepaDescriptor; + } + + public void setSepaDescriptor(String sepaDescriptor) { + this.sepaDescriptor = sepaDescriptor; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/HolidaysObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/HolidaysObject.java new file mode 100644 index 0000000..49eaa3c --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/HolidaysObject.java @@ -0,0 +1,79 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +import net.petafuel.jsepa.util.BankDateCalculator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +public class HolidaysObject { + private static final Logger LOG = LogManager.getLogger(HolidaysObject.class); + private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + /** + * + * @param ls_art core1 specific + * @param ls_sequenz core1 specific + * @param sepaDueDate DueDate of the SDD + * @param cutOffHour Time of day when the cutOff is reached + * @param cutOffMinutes Time of day when the cutOff is reached + * @param cutOffAdditionalDays Days to add after the cutOff Time is reached for the specific Bank + * @param sddDueDateAdditionalDays Days to always add to the Due Date of SEPA Direct Debit Transactions + * @param checkCore1 is the direct debit of type core1 + * @return Date + * @throws UnsupportedOperationException + */ + public static Date checkDueDate(String ls_art, String ls_sequenz, Date sepaDueDate, int cutOffHour, int cutOffMinutes, int cutOffAdditionalDays, int sddDueDateAdditionalDays, boolean checkCore1) throws UnsupportedOperationException + { + // Calculate target executiondate and compare with xml executiondate: + Calendar cutOffTime = new GregorianCalendar(); + cutOffTime.set(Calendar.HOUR_OF_DAY, cutOffHour); + cutOffTime.set(Calendar.MINUTE, cutOffMinutes); + + Calendar todayTime = new GregorianCalendar(); + if (todayTime.after(cutOffTime)) { + //if the cutoff time is reached, add the additional cutoff days + sddDueDateAdditionalDays += cutOffAdditionalDays; + } + if (checkCore1) { + try { + sddDueDateAdditionalDays += BankDateCalculator.getAdditionalDayCount(ls_art, ls_sequenz); + } catch (IllegalArgumentException e) { + LOG.warn("Ungültige SEPA-Lastschrift-Art oder -Sequenz"); + throw new UnsupportedOperationException("Ungültige SEPA-Lastschrift-Art oder -Sequenz"); + } + } else { + // Always add one day as SDD is to be inquired to the bank at least one day in advance + sddDueDateAdditionalDays++; + } + Calendar targetExecutionTime = BankDateCalculator.getTarget2DiffDate(sddDueDateAdditionalDays); + //Change Calender.HOUR_OF_DAY to ensure sepaDueDate is after targetExecutionTime if both have the same date, but differ in time + targetExecutionTime.set(Calendar.HOUR_OF_DAY, 0); + targetExecutionTime.set(Calendar.MINUTE, 0); + targetExecutionTime.set(Calendar.SECOND, 0); + Calendar executionTime = new GregorianCalendar(); + executionTime.setTime(sepaDueDate); + executionTime.set(Calendar.HOUR_OF_DAY, 0); + executionTime.set(Calendar.MINUTE, 0); + executionTime.set(Calendar.SECOND, 1); + GregorianCalendar maxExecutionTime = new GregorianCalendar(); + maxExecutionTime.setTime(new Date()); + maxExecutionTime.set(Calendar.HOUR_OF_DAY, 0); + maxExecutionTime.set(Calendar.MINUTE, 0); + maxExecutionTime.set(Calendar.SECOND, 1); + maxExecutionTime.add(Calendar.DAY_OF_YEAR, 14); + LOG.info("Days to add: {}, TargetExecutionTime: {}, ExecutionTime: {}, MaxExecutionTime: {}", sddDueDateAdditionalDays, sdf.format(targetExecutionTime.getTime()), sdf.format(executionTime.getTime()), sdf.format(maxExecutionTime.getTime())); + if (!(executionTime.getTime().after(targetExecutionTime.getTime()))) { + LOG.warn("Zeitraum für Mindestvorlaufzeit stimmt nicht"); + throw new UnsupportedOperationException("Zeitraum fuer Mindestvorlaufzeit stimmt nicht"); + } + if (!(executionTime.getTime().before(maxExecutionTime.getTime()))) { + LOG.warn("Zeitraum von maximal 2 Wochen wurde nicht eingehalten"); + throw new UnsupportedOperationException("Zeitraum von maximal 2 Wochen wurde nicht eingehalten"); + } + return sepaDueDate; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/IDataObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/IDataObject.java new file mode 100644 index 0000000..d1eba08 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/IDataObject.java @@ -0,0 +1,4 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +public interface IDataObject { +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/KommunikationsParameterData.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/KommunikationsParameterData.java new file mode 100644 index 0000000..8ee8c9b --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/KommunikationsParameterData.java @@ -0,0 +1,32 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +import java.util.LinkedList; +import java.util.List; + +public class KommunikationsParameterData { + private String bankId; + private List kommunikationszugaenge; + + public String getBankId() { + return bankId; + } + + public void setBankId(String bankId) { + this.bankId = bankId; + } + + public List getKommunikationszugangsAdressen() { + return kommunikationszugaenge; + } + + public void setKommunikationszugaenge(List kommunikationszugaenge) { + this.kommunikationszugaenge = kommunikationszugaenge; + } + + public void addKommunikationszugang(String zugangsAdresse) { + if (kommunikationszugaenge == null) { + kommunikationszugaenge = new LinkedList<>(); + } + kommunikationszugaenge.add(zugangsAdresse); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/ParameterDataObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/ParameterDataObject.java new file mode 100644 index 0000000..2f57089 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/ParameterDataObject.java @@ -0,0 +1,96 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Klasse, um Parameterdaten aus den entsprechenden Facade Implementierungen zurückzugeben + *

+ * FinTS 3.0: Siehe Formals, D.1 + */ +public class ParameterDataObject implements IDataObject { + private static final Logger LOG = LogManager.getLogger(ParameterDataObject.class); + + private String segmentName; + + private int segmentVersion; + + private int maximaleAnzahlAuftraege; + + private Integer anzahlSignaturenMindestens; + + private String sicherheitsKlasse; + + private String parameter; + + public ParameterDataObject(String segmentName, int segmentVersion, int maximaleAnzahlAuftraege, int anzahlSignaturenMindestens, String sicherheitsKlasse, String parameter) { + this.segmentName = segmentName; + this.segmentVersion = segmentVersion; + this.maximaleAnzahlAuftraege = maximaleAnzahlAuftraege; + LOG.debug("Anzahl signaturen mindestens: {} {}", anzahlSignaturenMindestens, (anzahlSignaturenMindestens < 0)); + this.anzahlSignaturenMindestens = anzahlSignaturenMindestens < 0 ? null : anzahlSignaturenMindestens; + this.sicherheitsKlasse = sicherheitsKlasse; + this.parameter = parameter; + } + + public Integer getAnzahlSignaturenMindestens() { + return anzahlSignaturenMindestens; + } + + public void setAnzahlSignaturenMindestens(int anzahlSignaturenMindestens) { + this.anzahlSignaturenMindestens = anzahlSignaturenMindestens; + } + + public int getMaximaleAnzahlAuftraege() { + return maximaleAnzahlAuftraege; + } + + public void setMaximaleAnzahlAuftraege(int maximaleAnzahlAuftraege) { + this.maximaleAnzahlAuftraege = maximaleAnzahlAuftraege; + } + + + public int getSegmentVersion() { + return segmentVersion; + } + + public void setSegmentVersion(int segmentVersion) { + this.segmentVersion = segmentVersion; + } + + public String getSegmentName() { + return segmentName; + } + + public void setSegmentName(String segmentName) { + this.segmentName = segmentName; + } + + public String getSicherheitsKlasse() { + return sicherheitsKlasse; + } + + public void setSicherheitsKlasse(String sicherheitsKlasse) { + this.sicherheitsKlasse = sicherheitsKlasse; + } + + public String getParameter() { + return parameter; + } + + public void setParameter(String parameter) { + this.parameter = parameter; + } + + @Override + public String toString() { + return "ParameterDataObject{" + + "segmentName='" + segmentName + '\'' + + ", segmentVersion=" + segmentVersion + + ", maximaleAnzahlAuftraege=" + maximaleAnzahlAuftraege + + ", anzahlSignaturenMindestens=" + anzahlSignaturenMindestens + + ", sicherheitsKlasse='" + sicherheitsKlasse + '\'' + + ", parameter='" + parameter + '\'' + + '}'; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/PermissionDataObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/PermissionDataObject.java new file mode 100644 index 0000000..fbf0732 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/PermissionDataObject.java @@ -0,0 +1,409 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +import net.petafuel.fuelifints.FinTSVersionSwitch; + +import java.util.*; + +public class PermissionDataObject +{ + public static final String verfahren_iTan = "912"; + public static final String verfahren_mTan = "913"; + public static final String verfahren_iTanSms = "914"; + private static List bankParameters = null; + /** + * Liste aller Geschäftsvorfälle (auch nicht aktive) + */ + private static HashMap geschaeftsvorfaelle = new HashMap() + {{ + put(FINTS30.SaldoAbfrage, "HKSAL"); + put(FINTS30.UmsatzAbfrage, "HKKAZ"); + put(FINTS30.PinTanVerfahren, "HKTAN"); + put(FINTS30.PinAendern, "HKPAE"); + put(FINTS30.SepaKontoVerbindungen, "HKSPA"); + put(FINTS30.SepaUeberweisungEinzelEinreichen, "HKCCS"); + put(FINTS30.SepaUeberweisungSammelEinreichen, "HKCCM"); + put(FINTS30.PinTanZweiSchrittParameter, "HKTAB"); + put(FINTS30.SepaLastschriftEinzelEinreichenCore1, "HKDSC"); + put(FINTS30.SepaLastschriftEinzelEinreichenB2B,"HKBSE"); + put(FINTS30.SepaLastschriftEinzelEinreichen, "HKDSE"); + put(FINTS30.SepaLastschriftEinzelAendern, "HKDSL"); + put(FINTS30.SepaLastschriftEinzelLoeschen, "HKDSA"); + put(FINTS30.SepaLastschriftEinzelLoeschenB2B,"HKBSL"); + put(FINTS30.SepaLastschriftEinzelBestand, "HKDBS"); + put(FINTS30.SepaLastschriftEinzelBestandB2B,"HKBBS"); + put(FINTS30.SepaLastschriftSammelEinreichenCore1,"HKDMC"); + put(FINTS30.SepaLastschriftSammelEinreichenB2B, "HKBME"); + put(FINTS30.SepaLastschriftSammelEinreichen, "HKDME"); + put(FINTS30.SepaLastschriftSammelBestand, "HKDMB"); + put(FINTS30.SepaLastschriftSammelBestandB2B, "HKBMB"); + put(FINTS30.SepaLastschriftSammelLoeschen, "HKDML"); + put(FINTS30.SepaLastschriftSammelLoeschenB2B, "HKBML"); + put(FINTS30.KundenBankNachricht,"HKKDM"); + put(FINTS30.Vormerkumsaetze,"HKVMK"); + put(FINTS30.SepaUeberweisungEinzelTerminEinreichen,"HKCSE"); + put(FINTS30.SepaUeberweisungEinzelTerminBestand,"HKCSB"); + put(FINTS30.SepaUeberweisungEinzelTerminLoeschen,"HKCSL"); + put(FINTS30.SepaUeberweisungSammelTerminEinreichen,"HKCME"); + put(FINTS30.SepaUeberweisungSammelTerminBestand,"HKCMB"); + put(FINTS30.SepaUeberweisungSammelTerminLoeschen, "HKMSL"); + put(FINTS30.Identifikation, "HKIDN"); + }}; + + private static HashMap pinRequired = null; + private String permissions = ""; + + /** + * Liste der aktiven Geschäftsvorfälle + * @return FINTS30-Liste + */ + public static List getAllowedFINTS30() + { + return Arrays.asList( + FINTS30.SaldoAbfrage, + FINTS30.UmsatzAbfrage, + FINTS30.PinTanVerfahren, + FINTS30.PinTanZweiSchrittParameter, + FINTS30.SepaKontoVerbindungen, + FINTS30.SepaUeberweisungEinzelEinreichen, + FINTS30.SepaUeberweisungEinzelTerminEinreichen, + FINTS30.SepaUeberweisungEinzelTerminBestand, + FINTS30.SepaUeberweisungEinzelTerminLoeschen, + FINTS30.SepaUeberweisungSammelEinreichen, + FINTS30.SepaUeberweisungSammelTerminEinreichen, + FINTS30.SepaUeberweisungSammelTerminBestand, + FINTS30.SepaUeberweisungSammelTerminLoeschen, + FINTS30.SepaLastschriftEinzelEinreichen, + FINTS30.SepaLastschriftEinzelEinreichenCore1, + FINTS30.SepaLastschriftEinzelEinreichenB2B, + FINTS30.SepaLastschriftEinzelLoeschen, + FINTS30.SepaLastschriftEinzelLoeschenB2B, + FINTS30.SepaLastschriftEinzelAendern, + FINTS30.SepaLastschriftEinzelBestand, + FINTS30.SepaLastschriftEinzelBestandB2B, + FINTS30.SepaLastschriftSammelEinreichen, + FINTS30.SepaLastschriftSammelEinreichenCore1, + FINTS30.SepaLastschriftSammelEinreichenB2B, + FINTS30.SepaLastschriftSammelLoeschen, + FINTS30.SepaLastschriftSammelLoeschenB2B, + FINTS30.SepaLastschriftSammelBestand, + FINTS30.SepaLastschriftSammelBestandB2B, + FINTS30.PinAendern, + FINTS30.KundenBankNachricht, + FINTS30.Vormerkumsaetze + ); + } + + /** + * Liste mit HBCI-ParameterObjekten + * @return Liste + */ + public static List getBankParameters() + { + if (bankParameters == null) + { + TanProcessParameterObject iTAN = getiTanParameter(); + TanProcessParameterObject mTAN = getmTanParameter(); + TanProcessParameterObject iTANsms = getiTanSmsParameter(); + bankParameters = new ArrayList<>(); + bankParameters.add(new GlobalParameter("HICCMS", 1, 10, "1", "1", "1000:J:J", "")); // Sammel Einreichung + bankParameters.add(new GlobalParameter("HICCSS", 1, 1, "1", "2", "", "")); // Einzel Einreichung + //2 SEPA-Einzel-Lastschriften + bankParameters.add(new GlobalParameter("HIDSES", 1, 1, "1", "1", "1:14:1:14", "")); // Einreichen + bankParameters.add(new GlobalParameter("HIDSCS", 1, 1, "1", "1", "1:14:1:14", "")); // COR1 Einreichen + bankParameters.add(new GlobalParameter("HIDBSS", 1, 1, "1", "1", "J:N", "")); // Bestand + bankParameters.add(new GlobalParameter("HIDBSS", 1, 1, "1", "1", "J:N", "")); // Änderung + bankParameters.add(new GlobalParameter("HIDSAS", 1, 1, "1", "1", "1:14:1:14", "")); // Löschen + bankParameters.add(new GlobalParameter("HIDSLS", 1, 1, "1", "1", "J", "")); + //7 SEPA-Sammel-Lastschriften + bankParameters.add(new GlobalParameter("HIDMES", 1, 1, "1", "1", "1:14:1:14:1000:N:J", ""));// Einreichung + bankParameters.add(new GlobalParameter("HIDMCS", 1, 1, "1", "1", "100:N:N:1:14:1:14", ""));// COR1 Einreichen + bankParameters.add(new GlobalParameter("HIDMBS", 1, 1, "1", "1", "N:N", "")); // Bestand + bankParameters.add(new GlobalParameter("HIDMLS", 1, 1, "1", "1", "", "")); // Löschung + //11 Umsätze abrufen + bankParameters.add(new GlobalParameter("HIKAZS", 6, 1, "1", "1", "90:J:J", "")); + bankParameters.add(new GlobalParameter("HIKAZS", 7, 1, "1", "1", "90:J:J", "")); + bankParameters.add(new GlobalParameter("HIKAZS", 5, 1, "1", "", "90:J:N", "")); + //14 Saldo abrufen + bankParameters.add(new GlobalParameter("HISALS", 5, 1, "1", "", "", "")); + bankParameters.add(new GlobalParameter("HISALS", 6, 1, "1", "1", "", "")); + bankParameters.add(new GlobalParameter("HISALS", 7, 1, "1", "1", "", "")); + //17 SEPA-Parameter + bankParameters.add(new GlobalParameter("HISPAS", 1, 1, "1", "1", "J:N:N:sepade.pain.001.001.02.xsd:sepade.pain.001.002.02.xsd:sepade.pain.001.001.03.xsd:sepade.pain.001.002.03.xsd:sepade.pain.008.001.02.xsd:sepade.pain.008.002.02.xsd:sepade.pain.008.003.02.xsd", "")); + bankParameters.add(new GlobalParameter("HISPAS", 2, 1, "1", "1", "J:N:N:N:sepade.pain.001.001.02.xsd:sepade.pain.001.002.02.xsd:sepade.pain.001.001.03.xsd:sepade.pain.001.002.03.xsd:sepade.pain.008.001.02.xsd:sepade.pain.008.002.02.xsd:sepade.pain.008.003.02.xsd", "")); + //19 Überweisung IZV + bankParameters.add(new GlobalParameter("HIUEBS", 5, 1, "1", "2", "14:51:53:54:67:69", "")); + bankParameters.add(new GlobalParameter("HITANS", 6, 1, "1", "1", "J:N:1:" + mTAN.getElementVersion6String(), "")); + bankParameters.add(new GlobalParameter("HIPAES", 1, 1, "1", "1", "", ""));//PIN ändern + bankParameters.add(new GlobalParameter("HIKOMS", 4, 1, "1", "1", "", ""));//Kommunikationszugänge + bankParameters.add(new GlobalParameter("HIKDMS", 5, 1, "1", "1", "2048", "")); + bankParameters.add(new GlobalParameter("HIVMKS", 1, 1, "1", "1", "N:N", "")); + bankParameters.add(new GlobalParameter("HICSES", 1, 1, "1", "2", "1:14", "")); + bankParameters.add(new GlobalParameter("HICMES", 1, 10, "1", "2", "1:14:0:N:J", "")); + bankParameters.add(new GlobalParameter("HICSBS", 1, 1, "1", "1", "N:N", "")); + bankParameters.add(new GlobalParameter("HICMBS", 1, 1, "1", "1", "N:N", "")); + bankParameters.add(new GlobalParameter("HICSLS", 1, 1, "1", "2", "N" ,"")); + bankParameters.add(new GlobalParameter("HICMLS", 1, 1, "1", "2", "", "")); + bankParameters.add(new GlobalParameter("HIBBSS", 1, 1, "1", "1", "J:N", "")); //Bestand Firmeneinzellastschrift + bankParameters.add(new GlobalParameter("HIBMES", 1, 1, "1", "1", "1:14:1:14:1000:N:J", "")); //Einreichung Firmensammellastschrift + bankParameters.add(new GlobalParameter("HIBMLS", 1, 1, "1", "1", "", "")); //Firmensammellastschrift löschen + bankParameters.add(new GlobalParameter("HIBSES", 1, 1, "1", "2", "1:14:1:14", "")); //Einreichung Firmeneinzellastschrift + bankParameters.add(new GlobalParameter("HIBSLS", 1, 1, "1", "1", "J", "")); //Firmeneinzellastschrift löschen + } + return bankParameters; + } + + public static String getHBCIversion(FinTSVersionSwitch.FinTSVersion fintsVersion) + { + String hbciversion; + switch (fintsVersion) + { + case HBCI_VERSION_2_2: + hbciversion = "220"; + break; + default: + case FINTS_VERSION_3_0: + hbciversion = "300"; + break; + case FINTS_VERSION_4_0: + hbciversion = "400"; + break; + } + return hbciversion; + } + + /** + * HKXXX zu FINTS30 Geschäftsvorfall + * @param type FINTS30 + * @return String "HKXXX" + */ + public static String getGeschaeftsvorfall(FINTS30 type) + { + if (geschaeftsvorfaelle.containsKey(type)) + { + return geschaeftsvorfaelle.get(type); + } + return null; + } + + /** + * FINTS30 Geschäftsvorfall zum String + * @param value "HKXXX" + * @return FINTS30 Geschäftsvorfall + */ + public static FINTS30 getGeschaeftsvorfall(String value) + { + for (Map.Entry entry : geschaeftsvorfaelle.entrySet()) + { + if (value.equals(entry.getValue())) + { + return entry.getKey(); + } + } + return null; + } + + /** + * Liste "aller" FINTS30-Geschäftsvorfälle und ob für diese eine PIN-Eingabe benötigt wird + * @return Liste mit true/false-Values + */ + public static HashMap getPinRequired() + { + if (pinRequired == null) + { + pinRequired = new HashMap<>(); + pinRequired.put(FINTS30.SaldoAbfrage, false); + pinRequired.put(FINTS30.UmsatzAbfrage, false); + pinRequired.put(FINTS30.PinTanVerfahren, false); + pinRequired.put(FINTS30.PinAendern, true); + pinRequired.put(FINTS30.SepaKontoVerbindungen, false); + pinRequired.put(FINTS30.SepaUeberweisungEinzelEinreichen, true); + pinRequired.put(FINTS30.SepaUeberweisungEinzelTerminEinreichen,true); + pinRequired.put(FINTS30.SepaUeberweisungEinzelTerminBestand,false); + pinRequired.put(FINTS30.SepaUeberweisungEinzelTerminLoeschen,true); + pinRequired.put(FINTS30.SepaUeberweisungSammelEinreichen, true); + pinRequired.put(FINTS30.SepaUeberweisungSammelTerminEinreichen,true); + pinRequired.put(FINTS30.SepaUeberweisungSammelTerminBestand,false); + pinRequired.put(FINTS30.SepaUeberweisungSammelTerminLoeschen,true); + pinRequired.put(FINTS30.SepaLastschriftEinzelAendern, true); + pinRequired.put(FINTS30.SepaLastschriftEinzelEinreichen, true); + pinRequired.put(FINTS30.SepaLastschriftEinzelEinreichenCore1, true); + pinRequired.put(FINTS30.SepaLastschriftEinzelLoeschen, true); + pinRequired.put(FINTS30.SepaLastschriftEinzelBestand, false); + pinRequired.put(FINTS30.SepaLastschriftSammelEinreichen, true); + pinRequired.put(FINTS30.SepaLastschriftSammelEinreichenCore1,true); + pinRequired.put(FINTS30.SepaLastschriftSammelLoeschen, true); + pinRequired.put(FINTS30.SepaLastschriftSammelBestand, false); + pinRequired.put(FINTS30.KundenBankNachricht,false); + pinRequired.put(FINTS30.Vormerkumsaetze,false); + pinRequired.put(FINTS30.Identifikation, true); + } + return pinRequired; + } + + /** + * Generiert PIN-Parameter-Objekt für iTAN + * @return Object + */ + private static TanProcessParameterObject getiTanParameter() + { + TanProcessParameterObject iTanParameter = new TanProcessParameterObject(); + iTanParameter.setNameDesZweiSchrittVerfahrens("iTAN-Liste"); + iTanParameter.setBelegungsText("Erhaltene TAN"); + iTanParameter.setSicherheitsfunktion_kodiert(verfahren_iTan); + iTanParameter.setTechnischeIdentifikationTanVerfahren("iTAN"); + return iTanParameter; + } + + /** + * Generiert PIN-Parameter-Objekt für mTAN + * @return Object + */ + private static TanProcessParameterObject getmTanParameter() + { + TanProcessParameterObject mTanParameter = new TanProcessParameterObject(); + mTanParameter.setNameDesZweiSchrittVerfahrens("Mobile TAN"); + mTanParameter.setBelegungsText("Erhaltene TAN"); + mTanParameter.setSicherheitsfunktion_kodiert(verfahren_mTan); + mTanParameter.setTechnischeIdentifikationTanVerfahren("mTAN"); + return mTanParameter; + } + + /** + * Generiert PIN-Parameter-Objekt für iTAN per SMS + * @return Object + */ + private static TanProcessParameterObject getiTanSmsParameter() + { + TanProcessParameterObject iTanSmsParameter = new TanProcessParameterObject(); + iTanSmsParameter.setNameDesZweiSchrittVerfahrens("iTAN-Liste-Mobil"); + iTanSmsParameter.setBelegungsText("Erhaltene TAN"); + iTanSmsParameter.setSicherheitsfunktion_kodiert(verfahren_iTanSms); + iTanSmsParameter.setTechnischeIdentifikationTanVerfahren("iTANm"); + return iTanSmsParameter; + } + + /** + * Für die Generierung des Strings der Geschäftsvofälle pro konto + * @param type FINTS30, es sollten nur erlaubte hier auftrauchen + */ + public void addPermission(FINTS30 type) + { + String gvf = getGeschaeftsvorfall(type); + if (!permissions.equals("")) + { + permissions += "+"; + } + permissions += gvf + ":1"; + } + + /** + * Gibt den Geschäftsvorfallstring zurück + * @return String "HKXXX:1+HKXXX:1+..." + */ + public String toString() + { + return permissions; + } + + /** + * Liste aller Geschäftsvorfälle + */ + public enum FINTS30 + { + SaldoAbfrage, + UmsatzAbfrage, + PinTanVerfahren, + SepaKontoVerbindungen, + SepaUeberweisungEinzelEinreichen, + SepaUeberweisungEinzelTerminEinreichen, + SepaUeberweisungEinzelTerminBestand, + SepaUeberweisungEinzelTerminLoeschen, + SepaUeberweisungSammelEinreichen, + SepaUeberweisungSammelTerminEinreichen, + SepaUeberweisungSammelTerminBestand, + SepaUeberweisungSammelTerminLoeschen, + SepaLastschriftEinzelEinreichen, + SepaLastschriftEinzelLoeschen, + SepaLastschriftEinzelAendern, + SepaLastschriftEinzelEinreichenCore1, + SepaLastschriftEinzelBestand, + SepaLastschriftSammelEinreichenCore1, + SepaLastschriftSammelEinreichen, + SepaLastschriftSammelLoeschen, + SepaLastschriftSammelBestand, + PinTanZweiSchrittParameter, + PinAendern, + KundenBankNachricht, + Vormerkumsaetze, + SepaLastschriftEinzelLoeschenB2B, + SepaLastschriftEinzelEinreichenB2B, + SepaLastschriftEinzelBestandB2B, + SepaLastschriftSammelEinreichenB2B, + SepaLastschriftSammelBestandB2B, + SepaLastschriftSammelLoeschenB2B, + Identifikation + } + + /** + * Generelle Bankparameter + */ + public static class GlobalParameter + { + private final String segmentName; + private final Integer segmentVersion; + private final Integer maximaleAnzahlAuftraege; + private final String anzahlSingaturenMindestens; + private final String sicherheitsKlasse; + private final String fints_3_parameter; + private final String fints_4_parameter; + + GlobalParameter(String segmentName, + Integer segmentVersion, Integer maximaleAnzahlAuftraege, String anzahlSignaturenMindestens, + String sicherheitsKlasse, String fints_3_parameter, String fints_4_parameter) + { + this.segmentName = segmentName; + this.segmentVersion = segmentVersion; + this.maximaleAnzahlAuftraege = maximaleAnzahlAuftraege; + this.anzahlSingaturenMindestens = anzahlSignaturenMindestens; + this.sicherheitsKlasse = sicherheitsKlasse; + this.fints_3_parameter = fints_3_parameter; + this.fints_4_parameter = fints_4_parameter; + + } + + public String getFints_4_parameter() + { + return fints_4_parameter; + } + + public String getFints_3_parameter() + { + return fints_3_parameter; + } + + public String getSicherheitsKlasse() + { + return sicherheitsKlasse; + } + + public String getAnzahlSingaturenMindestens() + { + return anzahlSingaturenMindestens; + } + + public Integer getMaximaleAnzahlAuftraege() + { + return maximaleAnzahlAuftraege; + } + + public Integer getSegmentVersion() + { + return segmentVersion; + } + + public String getSegmentName() + { + return segmentName; + } + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/PinParameterObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/PinParameterObject.java new file mode 100644 index 0000000..516a51d --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/PinParameterObject.java @@ -0,0 +1,100 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.GeschaeftsvorfallspezifischePinTanInformation; + +import java.util.LinkedList; +import java.util.List; + +public class PinParameterObject { + private Integer maximaleAnzahlAuftraege; + private Integer anzahlSignaturenMindestens; + private Integer sicherheitsklasse = 0; + private Integer minPinLaenge; + private Integer maxPinLaenge; + private Integer maxTanLaenge; + private String textBelegungBenutzerkennung; + private String textBelegungKundenId; + private List tanInfo = new LinkedList(); + + public Integer getMaximaleAnzahlAuftraege() { + return maximaleAnzahlAuftraege; + } + + public void setMaximaleAnzahlAuftraege(Integer maximaleAnzahlAuftraege) { + this.maximaleAnzahlAuftraege = maximaleAnzahlAuftraege; + } + + public Integer getAnzahlSignaturenMindestens() { + return anzahlSignaturenMindestens; + } + + public void setAnzahlSignaturenMindestens(Integer anzahlSignaturenMindestens) { + this.anzahlSignaturenMindestens = anzahlSignaturenMindestens; + } + + public Integer getMinPinLaenge() { + return minPinLaenge; + } + + public void setMinPinLaenge(Integer minPinLaenge) { + this.minPinLaenge = minPinLaenge; + } + + public Integer getMaxPinLaenge() { + return maxPinLaenge; + } + + public void setMaxPinLaenge(Integer maxPinLaenge) { + this.maxPinLaenge = maxPinLaenge; + } + + public Integer getMaxTanLaenge() { + return maxTanLaenge; + } + + public void setMaxTanLaenge(Integer maxTanLaenge) { + this.maxTanLaenge = maxTanLaenge; + } + + public String getTextBelegungBenutzerkennung() { + return textBelegungBenutzerkennung; + } + + public void setTextBelegungBenutzerkennung(String textBelegungBenutzerkennung) { + this.textBelegungBenutzerkennung = textBelegungBenutzerkennung; + } + + public String getTextBelegungKundenId() { + return textBelegungKundenId; + } + + public void setTextBelegungKundenId(String textBelegungKundenId) { + this.textBelegungKundenId = textBelegungKundenId; + } + + public void addSegment(String segmentkennung, boolean tanErforderlich) { + GeschaeftsvorfallspezifischePinTanInformation geschaeftsvorfallspezifischePinTanInformation = new GeschaeftsvorfallspezifischePinTanInformation(segmentkennung, tanErforderlich); + tanInfo.add(geschaeftsvorfallspezifischePinTanInformation); + } + + public List getGeschaeftsvorfallspezifischePinTanInformationen() { + return tanInfo; + } + + public Integer getSicherheitsklasse() { + return sicherheitsklasse; + } + + public void setSicherheitsklasse(Integer sicherheitsklasse) { + this.sicherheitsklasse = sicherheitsklasse; + } + + public boolean isTanErforderlich(String segmentkennung) { + for (GeschaeftsvorfallspezifischePinTanInformation info: tanInfo) { + if (info.getSegmentkennung().equals(segmentkennung)) + return info.getTanErforderlich().equals("J"); + } + return true; + } + +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/RecipientAccountDataObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/RecipientAccountDataObject.java new file mode 100644 index 0000000..79bc03a --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/RecipientAccountDataObject.java @@ -0,0 +1,34 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +/** + * Enhält Daten für Empfängerkonten. (SEPA-Übertrag) + */ +public class RecipientAccountDataObject { + private String IBAN; + private String BIC; + private String name; + + public String getIBAN() { + return IBAN; + } + + public void setIBAN(String IBAN) { + this.IBAN = IBAN; + } + + public String getBIC() { + return BIC; + } + + public void setBIC(String BIC) { + this.BIC = BIC; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/ReturnDataObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/ReturnDataObject.java new file mode 100644 index 0000000..bc26078 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/ReturnDataObject.java @@ -0,0 +1,51 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +public class ReturnDataObject { + private boolean success = false; + private String message; + private String returnCode; + private Object additionalData = null; + + public ReturnDataObject(boolean success, String message) { + this(success, message, null); + } + + public ReturnDataObject(boolean success, String message, String code) { + this.success = success; + this.message = message; + this.returnCode = code; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getReturnCode() { + return returnCode; + } + + public void setReturnCode(String returnCode) { + this.returnCode = returnCode; + } + + public Object getAdditionalData() { + return additionalData; + } + + public ReturnDataObject setAdditionalData(Object additionalData) { + this.additionalData = additionalData; + return this; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/SaldoDataObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/SaldoDataObject.java new file mode 100644 index 0000000..85096fb --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/SaldoDataObject.java @@ -0,0 +1,68 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +public class SaldoDataObject { + private double gebuchterSaldo = 0d; + private Double vorgemerkterSaldo = null; + private Double verfuegbarerBetrag = null; + private Double kreditlinie = null; // verfügbarer Betrag - gebuchterSaldo + private String waehrung = "EUR"; // EUR ist standard für deutsche Konten + private Double verfuegbarTag=null; + private boolean isOverdrawAllowed = false; + + public double getGebuchterSaldo() { + return gebuchterSaldo; + } + + public void setGebuchterSaldo(double gebuchterSaldo) { + this.gebuchterSaldo = gebuchterSaldo; + } + + public Double getVorgemerkterSaldo() { + return vorgemerkterSaldo; + } + + public void setVorgemerkterSaldo(Double vorgemerkterSaldo) { + this.vorgemerkterSaldo = vorgemerkterSaldo; + } + + public Double getVerfuegbarerBetrag() { + return verfuegbarerBetrag; + } + + public void setVerfuegbarerBetrag(Double verfuegbarerBetrag) { + this.verfuegbarerBetrag = verfuegbarerBetrag; + } + + public Double getKreditlinie() { + return kreditlinie; + } + + public void setKreditlinie(Double kreditlinie) { + this.kreditlinie = kreditlinie; + } + + public String getWaehrung() { + return waehrung; + } + + public void setWaehrung(String waehrung) { + this.waehrung = waehrung; + } + public void setVerfuegbarTag(Double verfuegbarTag) + { + this.verfuegbarTag = verfuegbarTag; + } + + public Double getVerfuegbarTag() + { + return verfuegbarTag; + } + + public boolean isOverdrawAllowed() { + return isOverdrawAllowed; + } + + public void setOverdrawAllowed(boolean overdrawAllowed) { + isOverdrawAllowed = overdrawAllowed; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/TanProcessParameterObject.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/TanProcessParameterObject.java new file mode 100644 index 0000000..4cde799 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/TanProcessParameterObject.java @@ -0,0 +1,417 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects; + +/** + * Enthält Informationen zu einem TanProzess, die unter anderem in HITANS (FinTS 3.0) benutzt werden + *

+ * Damit der TanProzess funktionieren kann, müssen folgende Parameter gesetzt werden: + *

+ * * sicherheitsfunktion_kodiert + * * technischeIdentifikationTanVerfahren + * * nameDesZweiSchrittVerfahrens + * * belegungstext + */ +public class TanProcessParameterObject implements IDataObject { + + + private String sicherheitsfunktion_kodiert = ""; + + private String tanProzess = "1"; + + private String technischeIdentifikationTanVerfahren = ""; + + private String zkaTanVerfahren = ""; + + private String versionZkaTanVerfahren = ""; + + private String nameDesZweiSchrittVerfahrens = ""; + + private String maximaleLaengeDesEingabewertes = "10"; + + /* + * 1: numerisch + * 2: alphanumerisch + */ + private String erlaubtesFormat = "2"; + + private String belegungsText = ""; + + private String maximaleLaengeDesRueckgabewertes = "256"; + + private String anzahlUnterstuetzerTanListen = "1"; + + private String mehrfachTanErlaubt = "N"; + + /* + * 1: TAN nicht zeitversetzt / dialogübergreifend erlaubt; + * 2: TAN zeitversetzt / dialogübergreifend erlaubt; + * 3: beide Verfahren unterstützt; + * 4: nicht zutreffend + * + * Bei Prozessvariante 1 ist der Parameter immer mit „nicht zutreffend“ zu belegen, da hier generell keine zeitversetzte Verarbeitung möglich ist. + */ + private String tanZeitUndDialogBezug = "4"; + + /* + * 0: TAN-Listennummer darf nicht angegeben werden + * 1: TAN-Listennummer kann angegeben werden + * 2: TAN-Listennummer muss angegeben werden + */ + private String tanListenNummerErforderlich = "0"; + + private String auftragsStornoErlaubt = "N"; + + private String smsAbbuchungsKontoErforderlich = "0"; + + private String auftraggeberKontoErforderlich = "0"; + + private String challengeKlasseErforderlich = "N"; + + private String challengeBetragErforderlich = "N"; + + /* + * Über diesen BPD-Parameter erhält die Kundenseite die Information, + * dass im Datenelement „Challenge“ Formatsteuerzeichen enthalten sein können. + */ + private String challengeStrukturiert = "N"; + + /* + * 00: Initialisierungsverfahren mit Klartext-PIN ohne TAN + * 01: Verwendung analog der in [HHD] beschriebenen Schablone 01 – ver- schlüsselte PIN und ohne TAN + * 02: Verwendung analog der in [HHD] beschriebenen Schablone 02 – reser- viert, bei FinTS derzeit nicht verwendet + */ + private String initialisierungsModus = "00"; + + /* + * 0: Bezeichnung des TAN-Mediums darf nicht angegeben werden + * 1: Bezeichnung des TAN-Mediums kann angegeben werden + * 2: Bezeichnung des TAN-Mediums muss angegeben werden + */ + private String bezeichnungDesTanMediumsErforderlich = "0"; + + private String anzahlUnterstuetzterTanMedien = "1"; + + public String getElementVersion1String() { + + + StringBuilder sb = new StringBuilder(""); + sb.append(sicherheitsfunktion_kodiert + ":"); + sb.append(tanProzess + ":"); + sb.append(technischeIdentifikationTanVerfahren + ":"); + sb.append(nameDesZweiSchrittVerfahrens + ":"); + sb.append(maximaleLaengeDesEingabewertes + ":"); + sb.append(erlaubtesFormat + ":"); + sb.append(belegungsText + ":"); + sb.append(maximaleLaengeDesRueckgabewertes + ":"); + sb.append(anzahlUnterstuetzerTanListen + ":"); + sb.append(mehrfachTanErlaubt + ":"); + sb.append(tanZeitUndDialogBezug.equals("4") ? "N" : "J"); + return sb.toString(); + } + + public String getElementVersion2String() { + StringBuilder sb = new StringBuilder(""); + sb.append(sicherheitsfunktion_kodiert + ":"); + sb.append(tanProzess + ":"); + sb.append(technischeIdentifikationTanVerfahren + ":"); + sb.append(nameDesZweiSchrittVerfahrens + ":"); + sb.append(maximaleLaengeDesEingabewertes + ":"); + sb.append(erlaubtesFormat + ":"); + sb.append(belegungsText + ":"); + sb.append(maximaleLaengeDesRueckgabewertes + ":"); + sb.append(anzahlUnterstuetzerTanListen + ":"); + sb.append(mehrfachTanErlaubt + ":"); + sb.append(tanZeitUndDialogBezug + ":"); + sb.append(tanListenNummerErforderlich + ":"); + sb.append(auftragsStornoErlaubt + ":"); + sb.append(challengeKlasseErforderlich + ":"); + sb.append(challengeBetragErforderlich); + return sb.toString(); + } + + public String getElementVersion3String() { + StringBuilder sb = new StringBuilder(""); + sb.append(sicherheitsfunktion_kodiert + ":"); + sb.append(tanProzess + ":"); + sb.append(technischeIdentifikationTanVerfahren + ":"); + sb.append(nameDesZweiSchrittVerfahrens + ":"); + sb.append(maximaleLaengeDesEingabewertes + ":"); + sb.append(erlaubtesFormat + ":"); + sb.append(belegungsText + ":"); + sb.append(maximaleLaengeDesRueckgabewertes + ":"); + sb.append(anzahlUnterstuetzerTanListen + ":"); + sb.append(mehrfachTanErlaubt + ":"); + sb.append(tanZeitUndDialogBezug + ":"); + sb.append(tanListenNummerErforderlich + ":"); + sb.append(auftragsStornoErlaubt + ":"); + sb.append(challengeKlasseErforderlich + ":"); + sb.append(challengeBetragErforderlich + ":"); + sb.append(initialisierungsModus + ":"); + sb.append(bezeichnungDesTanMediumsErforderlich + ":"); + sb.append(anzahlUnterstuetzterTanMedien); + return sb.toString(); + } + + public String getElementVersion4String() { + StringBuilder sb = new StringBuilder(""); + sb.append(sicherheitsfunktion_kodiert + ":"); + sb.append(tanProzess + ":"); + sb.append(technischeIdentifikationTanVerfahren + ":"); + sb.append(zkaTanVerfahren + ":"); + sb.append(versionZkaTanVerfahren + ":"); + sb.append(nameDesZweiSchrittVerfahrens + ":"); + sb.append(maximaleLaengeDesEingabewertes + ":"); + sb.append(erlaubtesFormat + ":"); + sb.append(belegungsText + ":"); + sb.append(maximaleLaengeDesRueckgabewertes + ":"); + sb.append(anzahlUnterstuetzerTanListen + ":"); + sb.append(mehrfachTanErlaubt + ":"); + sb.append(tanZeitUndDialogBezug + ":"); + sb.append(tanListenNummerErforderlich + ":"); + sb.append(auftragsStornoErlaubt + ":"); + sb.append((smsAbbuchungsKontoErforderlich.equals("0") ? "N" : "J") + ":"); + sb.append(challengeKlasseErforderlich + ":"); + sb.append(challengeBetragErforderlich + ":"); + sb.append(challengeStrukturiert + ":"); + sb.append(initialisierungsModus + ":"); + sb.append(bezeichnungDesTanMediumsErforderlich + ":"); + sb.append(anzahlUnterstuetzterTanMedien); + return sb.toString(); + } + + public String getElementVersion5String() { + StringBuilder sb = new StringBuilder(""); + sb.append(sicherheitsfunktion_kodiert + ":"); + sb.append(tanProzess + ":"); + sb.append(technischeIdentifikationTanVerfahren + ":"); + sb.append(zkaTanVerfahren + ":"); + sb.append(versionZkaTanVerfahren + ":"); + sb.append(nameDesZweiSchrittVerfahrens + ":"); + sb.append(maximaleLaengeDesEingabewertes + ":"); + sb.append(erlaubtesFormat + ":"); + sb.append(belegungsText + ":"); + sb.append(maximaleLaengeDesRueckgabewertes + ":"); + sb.append(anzahlUnterstuetzerTanListen + ":"); + sb.append(mehrfachTanErlaubt + ":"); + sb.append(tanZeitUndDialogBezug + ":"); + sb.append(tanListenNummerErforderlich + ":"); + sb.append(auftragsStornoErlaubt + ":"); + sb.append(smsAbbuchungsKontoErforderlich + ":"); + sb.append(auftraggeberKontoErforderlich + ":"); + sb.append(challengeKlasseErforderlich + ":"); + sb.append(challengeStrukturiert + ":"); + sb.append(initialisierungsModus + ":"); + sb.append(bezeichnungDesTanMediumsErforderlich + ":"); + sb.append(anzahlUnterstuetzterTanMedien); + return sb.toString(); + } + + public String getElementVersion6String() { + StringBuilder sb = new StringBuilder(""); + sb.append(sicherheitsfunktion_kodiert + ":"); + sb.append(tanProzess + ":"); + sb.append(technischeIdentifikationTanVerfahren + ":"); + sb.append(zkaTanVerfahren + ":"); + sb.append(versionZkaTanVerfahren + ":"); + sb.append(nameDesZweiSchrittVerfahrens + ":"); + sb.append(maximaleLaengeDesEingabewertes + ":"); + sb.append(erlaubtesFormat + ":"); + sb.append(belegungsText + ":"); + sb.append(maximaleLaengeDesRueckgabewertes + ":"); + sb.append(mehrfachTanErlaubt + ":"); + sb.append(tanZeitUndDialogBezug + ":"); + sb.append(auftragsStornoErlaubt + ":"); + sb.append(smsAbbuchungsKontoErforderlich + ":"); + sb.append(auftraggeberKontoErforderlich + ":"); + sb.append(challengeKlasseErforderlich + ":"); + sb.append(challengeStrukturiert + ":"); + sb.append(initialisierungsModus + ":"); + sb.append(bezeichnungDesTanMediumsErforderlich + ":"); + sb.append("N:"); + sb.append(anzahlUnterstuetzterTanMedien); + return sb.toString(); + } + + public String getSicherheitsfunktion_kodiert() { + return sicherheitsfunktion_kodiert; + } + + public void setSicherheitsfunktion_kodiert(String sicherheitsfunktion_kodiert) { + this.sicherheitsfunktion_kodiert = sicherheitsfunktion_kodiert; + } + + public String getTanProzess() { + return tanProzess; + } + + public void setTanProzess(String tanProzess) { + this.tanProzess = tanProzess; + } + + public String getTechnischeIdentifikationTanVerfahren() { + return technischeIdentifikationTanVerfahren; + } + + public void setTechnischeIdentifikationTanVerfahren(String technischeIdentifikationTanVerfahren) { + this.technischeIdentifikationTanVerfahren = technischeIdentifikationTanVerfahren; + } + + public String getZkaTanVerfahren() { + return zkaTanVerfahren; + } + + public void setZkaTanVerfahren(String zkaTanVerfahren) { + this.zkaTanVerfahren = zkaTanVerfahren; + } + + public String getVersionZkaTanVerfahren() { + return versionZkaTanVerfahren; + } + + public void setVersionZkaTanVerfahren(String versionZkaTanVerfahren) { + this.versionZkaTanVerfahren = versionZkaTanVerfahren; + } + + public String getNameDesZweiSchrittVerfahrens() { + return nameDesZweiSchrittVerfahrens; + } + + public void setNameDesZweiSchrittVerfahrens(String nameDesZweiSchrittVerfahrens) { + this.nameDesZweiSchrittVerfahrens = nameDesZweiSchrittVerfahrens; + } + + public String getMaximaleLaengeDesEingabewertes() { + return maximaleLaengeDesEingabewertes; + } + + public void setMaximaleLaengeDesEingabewertes(String maximaleLaengeDesEingabewertes) { + this.maximaleLaengeDesEingabewertes = maximaleLaengeDesEingabewertes; + } + + public String getErlaubtesFormat() { + return erlaubtesFormat; + } + + public void setErlaubtesFormat(String erlaubtesFormat) { + this.erlaubtesFormat = erlaubtesFormat; + } + + public String getBelegungsText() { + return belegungsText; + } + + public void setBelegungsText(String belegungsText) { + this.belegungsText = belegungsText; + } + + public String getMaximaleLaengeDesRueckgabewertes() { + return maximaleLaengeDesRueckgabewertes; + } + + public void setMaximaleLaengeDesRueckgabewertes(String maximaleLaengeDesRueckgabewertes) { + this.maximaleLaengeDesRueckgabewertes = maximaleLaengeDesRueckgabewertes; + } + + public String getAnzahlUnterstuetzerTanListen() { + return anzahlUnterstuetzerTanListen; + } + + public void setAnzahlUnterstuetzerTanListen(String anzahlUnterstuetzerTanListen) { + this.anzahlUnterstuetzerTanListen = anzahlUnterstuetzerTanListen; + } + + public String getMehrfachTanErlaubt() { + return mehrfachTanErlaubt; + } + + public void setMehrfachTanErlaubt(String mehrfachTanErlaubt) { + this.mehrfachTanErlaubt = mehrfachTanErlaubt; + } + + public String getTanZeitUndDialogBezug() { + return tanZeitUndDialogBezug; + } + + public void setTanZeitUndDialogBezug(String tanZeitUndDialogBezug) { + this.tanZeitUndDialogBezug = tanZeitUndDialogBezug; + } + + public String getTanListenNummerErforderlich() { + return tanListenNummerErforderlich; + } + + public void setTanListenNummerErforderlich(String tanListenNummerErforderlich) { + this.tanListenNummerErforderlich = tanListenNummerErforderlich; + } + + public String getAuftragsStornoErlaubt() { + return auftragsStornoErlaubt; + } + + public void setAuftragsStornoErlaubt(String auftragsStornoErlaubt) { + this.auftragsStornoErlaubt = auftragsStornoErlaubt; + } + + public String getSmsAbbuchungsKontoErforderlich() { + return smsAbbuchungsKontoErforderlich; + } + + public void setSmsAbbuchungsKontoErforderlich(String smsAbbuchungsKontoErforderlich) { + this.smsAbbuchungsKontoErforderlich = smsAbbuchungsKontoErforderlich; + } + + public String getAuftraggeberKontoErforderlich() { + return auftraggeberKontoErforderlich; + } + + public void setAuftraggeberKontoErforderlich(String auftraggeberKontoErforderlich) { + this.auftraggeberKontoErforderlich = auftraggeberKontoErforderlich; + } + + public String getChallengeKlasseErforderlich() { + return challengeKlasseErforderlich; + } + + public void setChallengeKlasseErforderlich(String challengeKlasseErforderlich) { + this.challengeKlasseErforderlich = challengeKlasseErforderlich; + } + + public String getChallengeBetragErforderlich() { + return challengeBetragErforderlich; + } + + public void setChallengeBetragErforderlich(String challengeBetragErforderlich) { + this.challengeBetragErforderlich = challengeBetragErforderlich; + } + + public String getChallengeStrukturiert() { + return challengeStrukturiert; + } + + public void setChallengeStrukturiert(String challengeStrukturiert) { + this.challengeStrukturiert = challengeStrukturiert; + } + + public String getInitialisierungsModus() { + return initialisierungsModus; + } + + public void setInitialisierungsModus(String initialisierungsModus) { + this.initialisierungsModus = initialisierungsModus; + } + + public String getBezeichnungDesTanMediumsErforderlich() { + return bezeichnungDesTanMediumsErforderlich; + } + + public void setBezeichnungDesTanMediumsErforderlich(String bezeichnungDesTanMediumsErforderlich) { + this.bezeichnungDesTanMediumsErforderlich = bezeichnungDesTanMediumsErforderlich; + } + + public String getAnzahlUnterstuetzterTanMedien() { + return anzahlUnterstuetzterTanMedien; + } + + public void setAnzahlUnterstuetzterTanMedien(String anzahlUnterstuetzterTanMedien) { + this.anzahlUnterstuetzterTanMedien = anzahlUnterstuetzterTanMedien; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/database/banking2.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/database/banking2.java new file mode 100644 index 0000000..fe2d1b9 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/database/banking2.java @@ -0,0 +1,2773 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects.database; + +import net.petafuel.dbutils.DbHelper; +import net.petafuel.fuelifints.cryptography.aesencryption.AESUtil; +import net.petafuel.fuelifints.dataaccess.dataobjects.BankMessageObject; +import net.petafuel.fuelifints.dataaccess.dataobjects.PermissionDataObject; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.dataaccess.dataobjects.gateway.sms; +import net.petafuel.fuelifints.model.client.ClientProductInfo; +import net.petafuel.fuelifints.model.client.LegitimationInfo; +import net.petafuel.fuelifints.support.CkontoLocal; +import net.petafuel.fuelifints.support.Payments; +import net.petafuel.jsepa.model.Document; +import net.petafuel.mt94x.Konto; +import net.petafuel.mt94x.Satz; +import net.petafuel.mt94x.Umsatz; +import net.petafuel.mt94x.Writer; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.util.encoders.Base64; + +import javax.naming.NamingException; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.security.SecureRandom; +import java.sql.*; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.*; + +import static java.lang.Double.parseDouble; +import static java.lang.Integer.parseInt; +import static java.lang.String.format; +import static net.petafuel.fuelifints.support.Payments.convertKnrBlzToIBAN; +import static net.petafuel.fuelifints.support.Payments.derfNullSein; +import static org.apache.commons.lang3.StringUtils.*; + +/** + * delimiter $$ + * CREATE TABLE `publickeys3` ( + * `BENUTZERKENNUNG` int(9) unsigned DEFAULT '0', + * `PUBKEY` blob, + * `SCHLUESSELART` tinyint(4) unsigned DEFAULT NULL, + * `SCHLUESSELNUMMER` int(3) unsigned DEFAULT NULL, + * `SCHLUESSELVERSION` int(3) unsigned DEFAULT NULL, + * `FREIGESCHALTET` int(1) unsigned DEFAULT NULL, + * `MODULO` Text, + * `EXPONENT` varchar(30) DEFAULT NULL, + * `pubkeyid` int(10) unsigned NOT NULL AUTO_INCREMENT, + * PRIMARY KEY (`pubkeyid`), + * KEY `idx_benutzerkennung` (`BENUTZERKENNUNG`) + * ) ENGINE=InnoDB DEFAULT CHARSET=utf8$$ + *

+ * ALTER TABLE `messages_read` ADD COLUMN `benutzerkennung` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0' AFTER `loginid` + * , DROP PRIMARY KEY + * , ADD PRIMARY KEY (`messageid`, `loginid`, `benutzerkennung`) ; + *

+ * ALTER TABLE `vorabumsaetze` CHANGE COLUMN `GEGENKONTONR` `GEGENKONTONR` VARCHAR(45) NULL DEFAULT NULL + * , CHANGE COLUMN `GEGENKONTOBLZ` `GEGENKONTOBLZ` VARCHAR(11) NULL DEFAULT NULL ; + * ALTER TABLE `login_konten_permission` ADD COLUMN `BENUTZERKENNUNG` BIGINT(20) NOT NULL DEFAULT '0' AFTER `KONTOID` + * , DROP PRIMARY KEY + * , ADD PRIMARY KEY (`LOGINID`, `KONTOID`, `BENUTZERKENNUNG`) ; + *

+ * update login_konten_permission lkp set lkp.benutzerkennung=(select ref_benutzerkennung from logins l where l.loginid=lkp.loginid); + */ +@SuppressWarnings("deprecation") +public class banking2 { + private static final String seed = "412132343421"; + private static final Logger LOG = LogManager.getLogger(banking2.class); + public static SimpleDateFormat sdf_sql = new SimpleDateFormat("yyyy-MM-dd"); + private static SimpleDateFormat sdf_tan = new SimpleDateFormat("yyyyMMddHHmmss"); + private static String dbprofile = "banking2"; + private CkontoLocal ckonto; + private Charset charsetISO88591 = Charset.forName("ISO-8859-1"); + private Properties configuration; + private SecureRandom random = new SecureRandom(); + private boolean configCreateVorabumsaetze = false; + private HashMap> localMessageList = new HashMap<>(); + private HashMap localUserReference = new HashMap<>(); + private Boolean mailAvaible = null; + + /** + * Übergibt die Konfiguration mit + * + * @param configuration Konfiguration + */ + public banking2(Properties configuration) { + this.configuration = configuration; + configCreateVorabumsaetze = "true".equals(config(properties.createVorabumsaetze, "false")); + dbprofile = config(properties.bankCode, "12345678"); + LOG.info(format("createVorabumsätze ist %s", (configCreateVorabumsaetze ? "an" : "aus"))); + } + + private Connection getConnection() throws SQLException { + try { + return new DbHelper().getConnection(dbprofile); + } catch (NamingException e) { + LOG.warn(format("Fehler beim Verbindung holen: %s", e.getMessage()), e); + throw new SQLException(e); + } + } + + /** + * Lädt eine Einstellung aus der Konfiguration, falls nicht vorhanden, gibt es die defaultValue zurück + * + * @param property Einstellungs-Name + * @param defaultValue Vorgabe wert, falls nicht in der Konfig hinterlegt + * @return Konfigeinstellung oder defaultValue wenn nicht vorhanden + */ + public String config(String property, String defaultValue) { + return configuration.getProperty(property, defaultValue); + } + + private void configLog(String property) { + LOG.debug(format("config: %s: %s", property, configuration.getProperty(property, "[not-set]"))); + } + + /** + * Fügt eine neue Zeile in TempClientStuff ein + * + * @param dialogId Dialog-ID + * @param legitimationInfo LegitimationInfo + * @param clientProductInfo ClientProductInfo + * @return gibt die neue Kunden_System_ID zurück + */ + private String insertTempclientstaff(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo) { + String userSystemId = null; + Connection connection = null; + try { + String tempUserSystemid = generateTan(9); + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.tempclientstuff3.insert); + pS.setString(1, legitimationInfo.getUserId()); // Benutzerkennung + pS.setString(2, getCustomerIdByConfig(legitimationInfo)); // Kundenid + pS.setString(3, derfNullSein(clientProductInfo.getClientProductName())); // Kundenprodukt + pS.setString(4, derfNullSein(clientProductInfo.getClientProductVersion())); // Kundenprodukt-Version + pS.setString(5, tempUserSystemid); // Kundensystemid + pS.setString(6, dialogId); // dialogid + pS.execute(); + userSystemId = tempUserSystemid; + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim anlegen einer KundenSystemID: %s", e.getMessage()), legitimationInfo), e.getMessage()); + } finally { + new DbHelper().closeQuietly(connection); + } + return userSystemId; + } + + /** + * Prüft ob eine Kundenid gefragt wird + * + * @return if true, it is required + */ + public boolean isKundenIdNotRequired() { + return config("textKennung", "Benutzerkennung").equals(config("textKundenId", "Kunden-ID")) || config("textKundenId", "").equals(""); + } + + /** + * @param legitimationInfo legitimationInfo + * @return Kundenid + */ + public String getCustomerIdByConfig(LegitimationInfo legitimationInfo) { + return getCustomerIdByConfig(legitimationInfo.getUserId(), legitimationInfo.getCustomerId()); + } + + /** + * @param userId UserId + * @param fallbackId CustomerIdFallback + * @return gibt die Kundenid zurück + */ + private String getCustomerIdByConfig(String userId, String fallbackId) { + return isKundenIdNotRequired() ? getCustomerId(userId) : fallbackId; + } + + /** + * Generiert eine KundenSystemId und hinterlegt diese in Tempclientstuff + * + * @param dialogId Dialog-ID + * @param legitimationInfo LegitimationInfo + * @param clientProductInfo ClientProductInfo + * @return Kunden-System-ID + */ + public String getOrGenerateUserSystemId(String dialogId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo) { + String userSystemId = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.tempclientstuff3.select); + if (setPreparedStatementParams(pS, legitimationInfo, clientProductInfo, false)) { + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + userSystemId = resultSet.getString("kundensystemid"); + LOG.info(addTagUser(format("Kunden-System-ID gefunden: %s", userSystemId), legitimationInfo)); + clientProductInfo.setUserSystemId(userSystemId); + PreparedStatement pU = connection.prepareStatement(sql.tempclientstuff3.update_dialogid); + pU.setString(1, dialogId); + if (setPreparedStatementParams(pU, legitimationInfo, clientProductInfo, true)) { + pU.execute(); + } + } else { + LOG.info(addTagUser("Kunden-System-ID nicht gefunden - ProduktName:" + clientProductInfo.getClientProductName() + " - ProduktVersion: " + clientProductInfo.getClientProductVersion(), legitimationInfo)); + } + new DbHelper().closeQuietly(resultSet); + } + } catch (SQLException e) { + LOG.warn(addTagUser(format("KundenSystemID konnte nicht geholt werden: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + if (userSystemId == null) { + LOG.info(addTagUser("Kunden-System-ID wird angelegt", legitimationInfo)); + userSystemId = insertTempclientstaff(dialogId, legitimationInfo, clientProductInfo); + LOG.info(addTagUser(format("Kunden-System-ID: %s", userSystemId), legitimationInfo)); + } + return userSystemId; + } + + /** + * Entwertet einen iTAN/mTAN zum Dialog + * + * @param dialogId Dialog-ID + * @param legitimationInfo legitimansionsinformation + */ + public void devalueTan(String dialogId, LegitimationInfo legitimationInfo) { + LOG.info(addTagUser(format("Dialog-ID: %s", dialogId), legitimationInfo)); + String tanid = getTanId(dialogId, legitimationInfo); + if (tanid != null) { + Connection connection = null; + try { + connection = getConnection(); + if (isMobileTan(tanid)) { + PreparedStatement pS = connection.prepareStatement(sql.tanliste.mobil.update); + pS.setString(1, String.valueOf(unixTimeStamp())); + pS.setString(2, tanid.replace("-", "")); + pS.execute(); + } else { + PreparedStatement pS = connection.prepareStatement(sql.tanliste.update); + pS.setString(1, sdf_tan.format(new java.util.Date())); + pS.setString(2, tanid); + pS.execute(); + } + + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Entwerten der TAN-ID: %s, Fehler: %s", tanid, e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + } else { + LOG.warn(addTagUser(format("TAN zum Entwerten nicht wieder gefunden, Dialog-ID (%s) nicht gefunden?", dialogId), legitimationInfo)); + } + } + + /** + * Prüft ob eine TAN-ID für mTAN oder iTAN ist + * + * @param tanid TAN-ID + * @return true wenn mTAN + */ + private boolean isMobileTan(String tanid) { + return (tanid.contains("-")); + } + + /** + * Prüft ob die PIN Freigeschalten ist + * + * @param legitimationInfo LegitimationInfo + * @return true wenn freigeschalten + */ + public boolean isPinUsable(LegitimationInfo legitimationInfo) { + boolean returnBoolean = false; + Connection connection = null; + try { + connection = getConnection(); + String pin = getFieldByUserId("pin", legitimationInfo); + if (!pin.startsWith("!") && !pin.startsWith("*") && !pin.startsWith("#")) { + returnBoolean = true; + } + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Abfragen der PIN-Änderungsberechtigung: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnBoolean; + } + + /** + * Holt eine Kennung-Spalte aus der Datenbank + * + * @param field Feldname + * @param legitimationInfo LegitimationInfo + * @return Feldwert|null wenn nicht gefunden + */ + private String getFieldByUserId(String field, LegitimationInfo legitimationInfo) { + String returnString = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.kennungen.select); + pS.setString(1, legitimationInfo.getUserId()); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + returnString = resultSet.getString(field); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Holen der Kennung-Daten: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnString; + } + + public Map getPermittedOperations(LegitimationInfo legitimationInfo, String kontonummer) { + Map permittedOperations = new HashMap<>(); + try (Connection connection = getConnection()) { + PreparedStatement pS = connection.prepareStatement(sql.login.konten.permission.select); + pS.setString(1, legitimationInfo.getUserId()); + pS.setString(2, kontonummer); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + String ueberweisung = resultSet.getString(1); + String sdd = resultSet.getString(2); + String ade = resultSet.getString(3); + String avz = resultSet.getString(4); + String lastschrift = resultSet.getString(5); + permittedOperations.put("ueberweisung", "1".equals(ueberweisung)); + permittedOperations.put("sdd", "1".equals(sdd)); + permittedOperations.put("ade", "E".equals(ade)); + permittedOperations.put("avz", "1".equals(avz)); + permittedOperations.put("lastschrift", "1".equals(lastschrift)); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen der Berechtigung, Spalte für Benutzerkennung in 'login_konten_permission' nicht angelegt?: %s", e.getMessage()), legitimationInfo), e); + } + return permittedOperations; + } + + + /** + * Prüft in login_konten_permission ob die Spalte/das Feld den Wert 1 hat + * + * @param legitimationInfo LegitimationInfo + * @param kontonummer Kontonummer + * @param field Feldname + * @return gibt zurück, ob die Operation erlaubt ist + */ + public boolean isOperationPermit(LegitimationInfo legitimationInfo, String kontonummer, String field) { + boolean returnBoolean = false; + Connection connection = null; + String lpIsset = getLpPermission(legitimationInfo, field); + if (lpIsset != null) return lpIsset.equals("1"); + if (field != null) { + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.login.konten.permission.select); + pS.setString(1, legitimationInfo.getUserId()); + pS.setString(2, kontonummer); + ResultSet resultSet = pS.executeQuery(); + returnBoolean = resultSet.next() && resultSet.getString(field).trim().equals("1") && resultSet.getString("ade").trim().equals("E"); + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen der Berechtigung, Spalte für Benutzerkennung in 'login_konten_permission' nicht angelegt?: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + } + LOG.info(addTagUser(format("Berechtigung: %s:%s, Kontonummer: %s", field, (returnBoolean ? "true" : "false"), kontonummer), legitimationInfo)); + return returnBoolean; + } + + /** + * Generiert eine Auftragsreferenz zu dem Hashwert und hinterlegt diese in Tempclientstuff + * + * @param dialogId Dialog-ID + * @param auftragsHashwert AuftragsHashwert + * @return Auftragsreferenz + */ + public String generateAuftragsreferenz(String dialogId, byte[] auftragsHashwert, LegitimationInfo legitimationInfo) { + String Auftragsreferenz = generateAuftragsreferenz(auftragsHashwert, legitimationInfo); + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.tempclientstuff3.update_srvref_by_dialogId); + pS.setString(1, Auftragsreferenz); + pS.setString(2, dialogId); + pS.execute(); + + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Anlegen der Auftragsreferenz: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return Auftragsreferenz; + } + + /** + * generiert eine Auftragsreferenz zu dem Hashwert + * + * @param auftragsHashwert Auftragshashwert + * @return Auftragsreferenz + */ + private String generateAuftragsreferenz(byte[] auftragsHashwert, LegitimationInfo legitimationInfo) { + LOG.info(addTagUser(format("generateAuftragsreferenz: %s", (new String(auftragsHashwert))), legitimationInfo)); + long checksum = 0; + for (byte b : auftragsHashwert) { + checksum += Math.abs(b); + } + LOG.info(addTagUser(format("checksum: %s, Auftragsreferenz: %s", String.valueOf(checksum), (checksum % 1000000)), legitimationInfo)); + return "" + (checksum % 1000000); + } + + /** + * Holt die TAN-ID / mTAN-ID zu einem Dialog zurück + * + * @param dialogId Dialog-ID + * @param legitimationInfo LegitimationInfo + * @return TAN-ID/mTAN-ID, null wenn Dialog nicht gefunden wird + */ + @SuppressWarnings("UnusedParameters") + private String getTanId(String dialogId, LegitimationInfo legitimationInfo) { + String returnString = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement tcs = connection.prepareStatement(sql.tempclientstuff3.select_tanid_by_dialogid); + tcs.setString(1, dialogId); + ResultSet resultSet = tcs.executeQuery(); + if (resultSet.next()) { + returnString = resultSet.getString(sql.tempclientstuff3.col_tanid); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen der TAN-ID für den Dialog: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnString; + } + + /** + * Holt die alle freien TANs einer iTAN-Liste zurück + * + * @param tanlistennr TAN-Listen-Nummer + * @param legitimationInfo LegitimationInfo + * @return TAN-Liste mit freien TANs + */ + private List> getNextFreeTanFromlist(String tanlistennr, LegitimationInfo legitimationInfo) { + List> returnMapList = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement tanliste = connection.prepareStatement(sql.tanliste.select_unused_by_tanlistenid_kennung); + tanliste.setString(1, tanlistennr); + tanliste.setString(2, legitimationInfo.getUserId()); + ResultSet resultSet = tanliste.executeQuery(); + returnMapList = toMapList(resultSet); + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen der nächsten TAN aus der Liste %s: %s", tanlistennr, e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnMapList; + } + + public Integer getInt(String value) { +// LOG.debug("getInt"+value); + if (value != null) return parseInt(value); + return null; + } + + private Map toMap(ResultSet resultSet) { + Map returnMap = null; + if (resultSet != null) { + try { + ResultSetMetaData metaData = resultSet.getMetaData(); + returnMap = new HashMap<>(); + int count = metaData.getColumnCount(); + for (int i = 1; i < count + 1; i++) { + +// LOG.debug(StringUtils.lowerCase(metaData.getColumnName(i)) + ":"+metaData.getColumnLabel(i)+":" + resultSet.getString(i)); + returnMap.put(StringUtils.lowerCase(metaData.getColumnLabel(i)), resultSet.getString(i)); + } + } catch (SQLException e) { + LOG.warn(format("toMap: %s", e.getMessage()), e); + } + } + return returnMap; + } + + private List> toMapList(ResultSet resultSet) { + List> returnMapList = new ArrayList<>(); + if (resultSet != null) { + try { + while (resultSet.next()) { + returnMapList.add(toMap(resultSet)); + } + } catch (SQLException e) { + LOG.warn(format("toMapList: %s", e.getMessage()), e); + } + } + return returnMapList; + } + + /** + * Aktiviert eine neue TAN-Liste und gibt diese Zurück + * + * @param legitimationInfo LegitimationInfo + * @return SQL-Resultset + */ + private Map activateAndGetNextTanListe(LegitimationInfo legitimationInfo) { + Map returnMap = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement tanlistenlog = connection.prepareStatement(sql.tanliste.nlog.select_status); + tanlistenlog.setString(1, legitimationInfo.getUserId()); + tanlistenlog.setString(2, "0"); + ResultSet resultSet = tanlistenlog.executeQuery(); + if (resultSet.next()) { + String tanlistenid = resultSet.getString("tanlistenid"); + try { + PreparedStatement pSu = connection.prepareStatement(sql.tanliste.nlog.update_by_tanlistenid); + pSu.setString(1, tanlistenid); + pSu.execute(); + resultSet.beforeFirst(); + if (resultSet.next()) { + returnMap = toMap(resultSet); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException sqe1) { + LOG.warn(addTagUser(format("Fehler beim aktivieren der neuen TAN-Liste %s: %s", tanlistenid, sqe1.getMessage()), legitimationInfo), sqe1); + } + } + } catch (SQLException sqle2) { + LOG.warn(addTagUser(format("Fehler beim holen der inaktiven TAN-Listen für Kennung: %s", sqle2.getMessage()), legitimationInfo), sqle2); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnMap; + } + + /** + * Gibt eine TAN für iTAN-ID zurück + * + * @param tanId iTAN-ID + * @param legitimationInfo LegitimationInfo + * @return TAN (verschlüsselt) + */ + private String getTanById(String tanId, LegitimationInfo legitimationInfo) { + String returnString = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.tanliste.select_by_tanid); + pS.setString(1, tanId); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + returnString = resultSet.getString("tan"); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen der TAN zur TAN-ID: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnString; + } + + /** + * Holt eine TAN für mTAN-ID zurück + * + * @param tanId mTAN-ID + * @param legitimationInfo LegitimationInfo + * @return TAN (verschlüsselt) + */ + private String getTanByIdMobile(String tanId, LegitimationInfo legitimationInfo) { + String returnString = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.tanliste.mobil.select); + pS.setString(1, tanId); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + returnString = resultSet.getString("tan"); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen der mTAN zur mTAN-ID: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnString; + } + + /** + * Setzt eine TAN-ID zu einem Dialog + * + * @param dialogid Dialog-ID + * @param tanId TAN-ID (unsigned für iTAN, signed für mTAN) + * @param legitimationInfo LegitimationInfo + * @param clientProductInfo ClientProductInfo + * @return gibt bei Erfolg true zurück + */ + public boolean setTanId(String dialogid, String tanId, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo) { + Boolean returnBoolean = false; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.tempclientstuff3.update_tanid_by_kid); + pS.setString(1, tanId); + pS.setString(2, dialogid); + pS.setString(3, legitimationInfo.getUserId()); + pS.setString(4, getCustomerIdByConfig(legitimationInfo)); + pS.setString(5, derfNullSein(clientProductInfo.getClientProductName())); + pS.setString(6, derfNullSein(clientProductInfo.getClientProductVersion())); + pS.setString(7, clientProductInfo.getUserSystemId()); + pS.execute(); + returnBoolean = true; + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim setzen der TAN-ID: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnBoolean; + } + + /** + * Fügt einen Geschäftsvorfall in die Tabelle + * + * @param globalParameter PermissionDataObject.GlobalParameter + * @param bpdVersion BPD-Version + * @param hbciVersion HBCI-Version + */ + public void insertGeschaeftsvorfall(PermissionDataObject.GlobalParameter globalParameter, String bpdVersion, String hbciVersion) { + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pSi = connection.prepareStatement(sql.geschaeftsvorfaelle.insert); + pSi.setString(1, globalParameter.getSegmentName()); + pSi.setString(2, String.valueOf(globalParameter.getSegmentVersion())); + pSi.setString(3, String.valueOf(globalParameter.getMaximaleAnzahlAuftraege())); + pSi.setString(4, globalParameter.getAnzahlSingaturenMindestens()); //param1 + pSi.setString(5, globalParameter.getSicherheitsKlasse()); //param5 + pSi.setString(6, globalParameter.getFints_3_parameter()); //param3 + pSi.setString(7, globalParameter.getFints_4_parameter()); //param4 + pSi.setString(8, bpdVersion); + pSi.setString(9, hbciVersion); + pSi.execute(); + } catch (SQLException e) { + LOG.warn(format("Fehler beim Anlegen des Geschäftsvorfalls: %s", e.getMessage()), e); + } finally { + new DbHelper().closeQuietly(connection); + } + } + + /** + * Entfernt alle Geschäftsvorfälle zu einer BPD/HBCI-Version + * + * @param bpdversion BPD-Version + * @param hbciversion HBCI-Version + */ + public void resetGeschaeftsvorfaelle(String bpdversion, String hbciversion) { + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pSi = connection.prepareStatement(sql.geschaeftsvorfaelle.delete); + pSi.setString(1, bpdversion); + pSi.setString(2, hbciversion); + pSi.execute(); + } catch (SQLException e) { + LOG.warn(format("Fehler beim Zurücksetzen der Geschäftsvorfälle: %s", e.getMessage()), e); + } finally { + new DbHelper().closeQuietly(connection); + } + } + + /** + * Holt Vorabumsätze aus der Datenbank + * soll null zurückgeben, wenn keine Vorabumsätze vorhanden sind + * + * @param bankCode Bankleitzahl (für generierung) + * @param kontonummer Kontonummer + * @param legitimationInfo LegitimationInfo + * @param kontowaehrung Kontowährung + * @param vonDatum Start-Datum + * @param bisDatum End-Datum + * @return Byte-Array mit vorabumsätze + */ + public byte[] getNichtGebuchteUmsaetze(String bankCode, String kontonummer, LegitimationInfo legitimationInfo, String kontowaehrung, Date vonDatum, Date bisDatum) { + + byte[] returnBytes = null; + Konto konto = new Konto(); + konto.setBankleitzahl(bankCode); + konto.setNummer(kontonummer); + konto.setWaehrung(kontowaehrung); + Writer.setAddSeperator(true); + Writer writer = new Writer(konto); + Connection connection = null; + if (vonDatum == null) { + vonDatum = getMinDate(); + } + if (bisDatum == null) { + bisDatum = new Date(); + } + LOG.info(addTagUser(format("hole vorabumsätze für Kontonummer %s von %s bis %s", kontonummer, sdf_sql.format(vonDatum), sdf_sql.format(bisDatum)), legitimationInfo)); + boolean found = false; + try { + Satz s = new Satz(); + s.setKonto(konto); + String kontoid = getFieldByKontonummer(kontonummer, "kontoid", legitimationInfo); + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.vorabumsaetze.select); + pS.setString(1, kontoid); + pS.setDate(2, new java.sql.Date(vonDatum.getTime())); + pS.setDate(3, new java.sql.Date(bisDatum.getTime())); + ResultSet resultSet = pS.executeQuery(); + int umsatznummer = 0; + Konto umsatzKonto; + while (resultSet.next()) { + Umsatz u = new Umsatz(s, umsatznummer); + u.setBuchungsdatum(resultSet.getDate("buchungsdatum")); + u.setValuta(resultSet.getDate("buchungsdatum")); + u.setBetrag(resultSet.getDouble("betrag")); + u.setVerwendungszweck(resultSet.getString("vwz")); + umsatzKonto = new Konto(); + umsatzKonto.setInhaber(resultSet.getString("gegenname")); + if (resultSet.getString("gegenkonto").length() <= 10) { + umsatzKonto.setNummer(resultSet.getString("gegenkonto")); + umsatzKonto.setBankleitzahl(resultSet.getString("gegenblz")); + } else { + umsatzKonto.setBankleitzahl(""); + umsatzKonto.setNummer(""); + umsatzKonto.setIban(resultSet.getString("gegenkonto")); + umsatzKonto.setSwiftcode(resultSet.getString("gegenblz")); + } + u.setGegenkonto(umsatzKonto); + writer.addUmsatz(u); + umsatznummer++; + found = true; + } + LOG.info(addTagUser(format("gefundene Vorabumsätze: %s", String.valueOf(umsatznummer)), legitimationInfo)); + new DbHelper().closeQuietly(resultSet); + if (found) { + returnBytes = writer.write().getBytes(charsetISO88591); + } + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Holen der Vorabumsätze: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnBytes; + + } + + /** + * Holt die Benutzerkennung aus der Datenbank (kunden_benutzerkennungen) + * + * @param userId Kennung + * @return Kunden-ID, null wenn es keine dazu gibt + */ + public String getCustomerId(String userId) { + if (userId == null || userId.equals("")) { + return null; + } + + String returnString = localUserReference.getOrDefault(userId, null); + if (returnString == null) { + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.kunden.kennungen.select); + pS.setString(1, userId); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + returnString = resultSet.getString("kundenid"); + localUserReference.put(userId, returnString); + } else { + LOG.info(addTagUser("Kundenid zu Benutzerkennung nicht gefunden", userId, "")); + + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen der Kundenid zur Benutzerkennung: %s", e.getMessage()), userId, ""), e); + } finally { + new DbHelper().closeQuietly(connection); + } + LOG.info(addTagUser(format("Kunden-ID gefunden: %s", returnString), userId, "")); + } + return returnString; + } + + private boolean hasUserSystemId(ClientProductInfo clientProductInfo) { + return (!derfNullSein(clientProductInfo.getUserSystemId()).equals("")); + } + + /** + * Holt TempClientStuff? + * + * @param legitimationInfo LegitimationInfo + * @param clientProductInfo ClientProductInfo + * @return Dialog-ID? + */ + @SuppressWarnings("unused") + private String getTempClientStuff(LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo) { + Connection connection = null; + String dialogid = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(hasUserSystemId(clientProductInfo) ? sql.tempclientstuff3.select_kid : sql.tempclientstuff3.select); + setPreparedStatementParams(pS, legitimationInfo, clientProductInfo, hasUserSystemId(clientProductInfo)); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + dialogid = resultSet.getString("dialogid"); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen der Dialog-ID: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return dialogid; + } + + private boolean setPreparedStatementParams(PreparedStatement pS, LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo, boolean withKundenSystemId) { + try { + int params = pS.getParameterMetaData().getParameterCount(); + int len = (withKundenSystemId) ? 5 : 4; + int min = params - len; + pS.setString(min + 1, legitimationInfo.getUserId()); + pS.setString(min + 2, getCustomerIdByConfig(legitimationInfo)); + pS.setString(min + 3, derfNullSein(clientProductInfo.getClientProductName())); + pS.setString(min + 4, derfNullSein(clientProductInfo.getClientProductVersion())); + if (withKundenSystemId) { + pS.setString(min + 5, clientProductInfo.getUserSystemId()); + } + return true; + } catch (SQLException e) { + LOG.error(addTagUser(format("Fehler beim Setzen der Parameter für ein PreparedStatement: %s", e.getMessage()), legitimationInfo), e); + return false; + } + } + + /** + * Holt die aktiven TAN-Listen aus der Datenbank + * + * @param legitimationInfo Kennung + * @return SQL-Resultset aus der Datenbank, null wenn es keine gibt + */ + private Map getActiveTanListe(LegitimationInfo legitimationInfo) { + Map returnMap = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement tanlistenlog = connection.prepareStatement(sql.tanliste.nlog.select_status); + tanlistenlog.setString(1, legitimationInfo.getUserId()); + tanlistenlog.setString(2, "1"); + ResultSet resultSet = tanlistenlog.executeQuery(); + if (resultSet.next()) { + returnMap = toMap(resultSet); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen der aktiven TAN-Listen für Kennung: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnMap; + } + + /** + * Holt die Buchungstyp ID zum Textschlüssel aus der Datenbank + * + * @param textschluessel Textschlüssel + * @param returnString defaultValue + * @param legitimationInfo LegitimationInfo + * @return falls der Textschlüssel nicht existiert, gibts den defaultValue + */ + private String getBuchungstypid(String textschluessel, String returnString, LegitimationInfo legitimationInfo) { + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.buchungstypen.select); + pS.setString(1, textschluessel); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + returnString = resultSet.getString("buchungstypid"); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen der Buchungstypid zu Textschlüssel %s: %s", textschluessel, e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnString; + } + + /** + * Holt den nächsten Counter von Daueraufträge (für Sammler) + * + * @param legitimationInfo LegitimationInfo + * @param add ob der counter gezählt werden soll + * @return Sammler counter + */ + public int getNextCounterDauerauftraege(LegitimationInfo legitimationInfo, boolean add) { + int returnInt = (add) ? 1 : 0; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.dauerauftraege.select.counter); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + returnInt = resultSet.getInt("counter") + (add ? 1 : 0); + } + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehlen beim holen des nächsten DauerauftragsCounter: %s", e.getMessage()), legitimationInfo)); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnInt; + } + + /** + * Holt den nächsten DTAUS-Counter aus der Datenbank + * es wird ein Pseudo-Login verwendet, da der Counter-Login-Basiert ist + * + * @param legitimationInfo LegitimationInfo + * @param add soll er um 1 aufaddiert werden? + * @return Counter-Wert + */ + public int getNextCounterDTAUS(LegitimationInfo legitimationInfo, boolean add) { + String pseudoLogin = getPseudoLogin(legitimationInfo); + int returnInt = (add) ? 1 : 0; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.dtaus.select_counter); + pS.setString(1, pseudoLogin); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + returnInt = resultSet.getInt("counter") + (add ? 1 : 0); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen des nächsten Counters für %s: %s", pseudoLogin, e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnInt; + } + + private String getPseudoLogin(LegitimationInfo legitimationInfo) { + return "_" + legitimationInfo.getUserId(); + } + + /** + * Hilfsfunktion für Kunde + * + * @param field Feld aus der Kunden-Tabelle + * @param legitimationInfo legitimationInfo + * @return Feldwert + */ + private String getFieldByCustomerId(String field, LegitimationInfo legitimationInfo) { + String returnString = null; + Connection connection = null; + String customerIdByConfig = getCustomerIdByConfig(legitimationInfo); + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.kunden.select); + pS.setString(1, customerIdByConfig); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + returnString = resultSet.getString(field); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Holen der Kunden-Daten: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnString; + } + + /** + * Gibt ein Feld aus der Konten-Tabelle wieder + * + * @param kontonummer Kontonummer + * @param field Feldname + * @param legitimationInfo LegitimationInfo + * @return Feldwert + */ + public String getFieldByKontonummer(String kontonummer, String field, LegitimationInfo legitimationInfo) { + Map kontorow = getKontoRow(kontonummer, legitimationInfo); + if (kontorow != null) { + return kontorow.get(field); + } + LOG.info(addTagUser(format("Kontonummer %s nicht gefunden", kontonummer), legitimationInfo)); + return null; + } + + /** + * Prüft ob auf die Konten in der übergebenen Liste gebucht werden darf + * + * @param kontonummer Auftrags-Kontonummer + * @param kontenListe Liste der Empfänger-Konten (IBANs) + * @param transactionType Transaktionstyp (sct,sdd,b2b) + * @param legitimationInfo legitimationInfo + * @return gibt bei erlaubnis true zurück + */ + public ReturnDataObject checkEmpfaengerKonten(String kontonummer, HashMap kontenListe, tranactionTypes transactionType, LegitimationInfo legitimationInfo) { + + Map kontorow = getKontoRow(kontonummer, legitimationInfo); + if (kontorow != null) { + ReturnDataObject r = new ReturnDataObject(true, ""); + String preKonto = kontorow.get("pre_konto"); + String preBlz = kontorow.get("pre_blz"); + String preIban = kontorow.get("pre_iban"); + + if (preIban == null || preIban.equals("")) { + if (preKonto.length() <= 10 && preBlz.length() == 8) { + preIban = convertKnrBlzToIBAN(preKonto, preBlz); + } else if (preKonto.length() > 10) { + preIban = preKonto; + } else { + preIban = null; + } + } + if (preIban != null) { + for (String iban : kontenListe.keySet()) { + if (!iban.equals(preIban)) { + LOG.warn(addTagUser(format("Zielkonto (%s) != Gegenkonto (%s)", iban, preIban), legitimationInfo)); + r = new ReturnDataObject(false, format("Kontoverbindung (%s) ist für dieses Auftragskonto nicht zugelassen", iban)); + break; + } + } + } else { + for (String iban : kontenListe.keySet()) { + String bic = kontenListe.get(iban); + ReturnDataObject bicCheck = (config(properties.bicCheckEnabled, "true").equals("true")) ? checkBic(bic, transactionType, legitimationInfo) : new ReturnDataObject(true, ""); + if (!bicCheck.isSuccess()) { + LOG.warn(addTagUser(bicCheck.getMessage(), legitimationInfo)); + r = bicCheck; + break; + } + } + if (getCkonto().isActive()) { + for (String iban : kontenListe.keySet()) { + String bic = kontenListe.get(iban); + ReturnDataObject cKontoCheck = getCkonto().checkSepa(iban, bic, "true".equals(config(properties.cKontoSondercheckDe, ""))); + if (cKontoCheck != null && !cKontoCheck.isSuccess()) { + LOG.warn(addTagUser(cKontoCheck.getMessage(), legitimationInfo)); + r = cKontoCheck; + break; + } + } + } + } + return r; + } + LOG.info(addTagUser(format("Kontonummer: %s nicht gefunden", kontonummer), legitimationInfo)); + return new ReturnDataObject(false, format("Kontonummer: %s nicht gefunden", kontonummer), "9310"); + } + + private CkontoLocal getCkonto() { + if (this.ckonto == null) { + this.ckonto = new CkontoLocal( + config(properties.cKontoLocalKey, ""), + config(properties.cKontoLocalPath, ""), + config(properties.cKontoLocalCodes, "1,9") + ); + } + return this.ckonto; + } + + private Date getMinDate() { + Calendar c = Calendar.getInstance(); + c.set(Calendar.YEAR, 2000); + c.set(Calendar.MONTH, Calendar.JANUARY); + c.set(Calendar.DAY_OF_MONTH, 1); + return c.getTime(); + } + + /** + * Holt die gebuchten Umsätze aus der MT940-Tabelle + * + * @param kontonummer kontonummer + * @param vonDatum min-Datum + * @param bisDatum max-Datum + * @param legitimationInfo Legitimationsdatum + * @return Umsätze als Byte-Array + */ + public byte[] getGebuchteUmsaetze(String kontonummer, java.util.Date vonDatum, java.util.Date bisDatum, LegitimationInfo legitimationInfo) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + String kontoid = getFieldByKontonummer(kontonummer, "kontoid", legitimationInfo); + LOG.info(addTagUser(format("Konto-ID gefunden: %s", kontoid), legitimationInfo)); + String customerIdByConfig = getCustomerIdByConfig(legitimationInfo); + Double lastKontostand = getLastKontostandByBewegungen(kontoid, customerIdByConfig, legitimationInfo); + if (lastKontostand == null) { + lastKontostand = 0.00d; + } + SimpleDateFormat sdf = new SimpleDateFormat(); + sdf.applyPattern("yyyy-MM-dd"); + Connection connection = null; + boolean found = false; + try { + if (vonDatum == null) { + vonDatum = getMinDate(); + } + if (bisDatum == null) { + bisDatum = new java.util.Date(); + } + connection = getConnection(); + LOG.info(addTagUser(format("vonDatum: %s", sdf.format(vonDatum)), legitimationInfo)); + LOG.info(addTagUser(format("bisDatum: %s", sdf.format(bisDatum)), legitimationInfo)); + PreparedStatement pS = connection.prepareStatement(sql.mt940.select_by_id_date); + pS.setString(1, kontoid); + pS.setString(2, sdf.format(vonDatum)); + pS.setString(3, sdf.format(bisDatum)); + ResultSet resultSet = pS.executeQuery(); + found = isFound(byteArrayOutputStream, resultSet, legitimationInfo); + new DbHelper().closeQuietly(resultSet); + + if (!found) { + LOG.info(addTagUser(format("Keine Umsätze für das Konto: %s gefunden", kontonummer), legitimationInfo)); + } + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen der MT940-Sätze: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + try { + byteArrayOutputStream.flush(); + } catch (IOException e) { + LOG.warn(addTagUser(format("Fehler beim Flushen: %s", e.getMessage()), legitimationInfo), e); + } + if (found) { + return byteArrayOutputStream.toByteArray(); + } else { + byte[] lastMt940 = getLastMt940(kontoid, legitimationInfo); + if (lastMt940 != null) { + return lastMt940; + } else { + Date lastBuchungsdatumByBewegung = getLastBuchungsdatumByBewegung(kontoid, customerIdByConfig, legitimationInfo); + if (lastBuchungsdatumByBewegung != null) { + vonDatum = onDayBefore(lastBuchungsdatumByBewegung); + } else { + Date lastBuchungsdatumByVorab = getLastBuchungsdatumByVorab(kontoid, legitimationInfo); + if (lastBuchungsdatumByVorab != null) { + vonDatum = onDayBefore(lastBuchungsdatumByVorab); + } + } + } + } + return getEmptyMt940(kontonummer, lastKontostand, vonDatum).getBytes(); + } + + private boolean isFound(ByteArrayOutputStream byteArrayOutputStream, ResultSet resultSet, LegitimationInfo legitimationInfo) throws SQLException { + Blob blob; + boolean found = false; + while (resultSet.next()) { + blob = resultSet.getBlob("mt940"); + try { + byteArrayOutputStream.write(blob.getBytes(1, (int) blob.length())); + found = true; + } catch (IOException e) { + LOG.info(addTagUser(format("Unbekannter Fehler: %s", e.getMessage()), legitimationInfo), e); + } + } + return found; + } + + private Date onDayBefore(Date date) { + Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(Calendar.DATE, -1); + return c.getTime(); + } + + private String getBankCode() { + return config(properties.bankCode, "12345678"); + } + + private String getEmptyMt940(String kontonummer, Double lastKontostand, java.util.Date startDatum) { + Konto auftraggeberkonto = new Konto(); + auftraggeberkonto.setWaehrung("EUR"); + auftraggeberkonto.setNummer(kontonummer); + auftraggeberkonto.setBankleitzahl(getBankCode()); + Writer empty = new Writer(auftraggeberkonto, lastKontostand, 0, 0); + return empty.writeEmpty(startDatum); + } + + private byte[] getLastMt940(String kontoid, LegitimationInfo legitimationInfo) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + Connection connection = null; + boolean found = false; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.mt940.select_last); + pS.setString(1, kontoid); + ResultSet resultSet = pS.executeQuery(); + found = isFound(byteArrayOutputStream, resultSet, legitimationInfo); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen des letzten Umsatzes: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return ((found) ? byteArrayOutputStream.toByteArray() : null); + } + + /** + * @param legitimationInfo die LegitimationInfo + * @param auftraggeberKonto Kontonummer + * @param empfaengerKonto Empfänger-Kontonummer, wenn nicht vorhanden dann "" + * @param empfaengerBlz Empfänger-Bankleitzahl, wenn nicht vorhanden dann "" + * @param empfaengerName Empfänger-Name + * @param betrag Betrag ( > 0 wenn Gutschrift, <0 wenn Lastschrift) + * @param textSchluessel Siehe SQL-Tablle + * @param verwendungszweck Text + * @param TAN Verwendete TAN + * @param IBAN Empfänger-IBAN + * @param textSchluesselErgaenzung Text-Schlüssel-Ergänzung + * @param BIC Empfänger-BIC + * @param counter Counter (pro Login) + * @param lsArt unknown + * @param lsSequenz unknown + * @param lsDate unknown + * @param endToEnd endToEndID + * @param purposeCode purposeCode + * @return gibt zurück, ob die Eintragung erfolgreich war + */ + public Payments.Result inDtaus( + LegitimationInfo legitimationInfo, + String auftraggeberKonto, + String empfaengerKonto, + String empfaengerBlz, + String empfaengerName, + Double betrag, + String textSchluessel, + String verwendungszweck, + String TAN, String IBAN, + int textSchluesselErgaenzung, + String BIC, Integer counter, + String lsArt, String lsSequenz, Date lsDate, + String mandatsreferenz, String glaeubigerid, Date sign_date, + String currentTime, + String endToEnd, String purposeCode) { + if (lsDate == null) lsDate = new java.util.Date(); + if (sign_date == null) sign_date = new java.util.Date(); + if (currentTime == null) currentTime = new SimpleDateFormat("HH:mm:ss").format(new java.util.Date()); + Connection connection = null; + String auftraggeberName = getFieldByCustomerId("name", legitimationInfo); + String auftraggeberKontoInhaber = getFieldByKontonummer(auftraggeberKonto, "kontoinhaber", legitimationInfo); + String pseudoLogin = getPseudoLogin(legitimationInfo); + String buchungstypid = getBuchungstypid(textSchluessel, "0", legitimationInfo); + Gegenkonto gegenkonto = new Gegenkonto(empfaengerKonto, empfaengerBlz, IBAN, BIC); + empfaengerKonto = gegenkonto.getKontonummer(); + empfaengerBlz = gegenkonto.getBankleitzahl(); + IBAN = gegenkonto.getIBAN(); + BIC = gegenkonto.getBIC(); + + Payments.Result r; + try { + connection = getConnection(); + PreparedStatement pSi = connection.prepareStatement(sql.dtaus.insert, Statement.RETURN_GENERATED_KEYS); + pSi.setString(1, replaceNonSepaChars(empfaengerName)); + pSi.setString(2, empfaengerKonto); + pSi.setString(3, empfaengerBlz); + pSi.setDouble(4, betrag); + pSi.setString(5, replaceNonSepaChars(verwendungszweck)); + pSi.setString(6, replaceNonSepaChars(auftraggeberKontoInhaber)); + pSi.setString(7, auftraggeberKonto); + pSi.setString(8, replaceNonSepaChars(auftraggeberName)); + pSi.setString(9, pseudoLogin); + pSi.setString(10, derfNullSein(TAN)); + pSi.setString(11, "0"); + pSi.setString(12, buchungstypid); + pSi.setDate(13, new java.sql.Date(new java.util.Date().getTime())); + pSi.setString(14, currentTime); + pSi.setInt(15, counter); + pSi.setInt(16, textSchluesselErgaenzung); + pSi.setString(17, IBAN); + pSi.setString(18, BIC); + pSi.setString(19, lsArt); + pSi.setString(20, lsSequenz); + pSi.setDate(21, new java.sql.Date(lsDate.getTime())); + pSi.setString(22, mandatsreferenz); + pSi.setString(23, String.valueOf(unixTimeStamp(sign_date))); + pSi.setString(24, glaeubigerid); + pSi.setString(25, endToEnd); + pSi.setString(26, purposeCode); + boolean execute = pSi.execute(); + LOG.info(addTagUser(format("inDTAUS: %s", (execute ? "true" : "false")), legitimationInfo)); + r = new Payments.Result(true).readPrepared(pSi); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim anlegen einer Buchung: %s", e.getMessage()), legitimationInfo), e); + r = new Payments.Result(false).setError(e); + } finally { + new DbHelper().closeQuietly(connection); + } + return r; + } + + public Payments.Result inDauerauftraege(LegitimationInfo legitimationInfo, String auftraggeberKonto, String empfaengerKonto, + String empfaengerBlz, String empfaengerName, Double betrag, String textSchluessel, String verwendungszweck, + String TAN, String IBAN, String BIC, Integer counter, int turnus, + Date executionTime, + String currentTime, String endToEnd, String purposeCode) { + if (currentTime == null) currentTime = new SimpleDateFormat("HH:mm:ss").format(new java.util.Date()); + String benutzerkennung = legitimationInfo.getUserId(); + Connection connection = null; + String kundenName = getFieldByCustomerId("name", legitimationInfo); + String auftraggeberName = getFieldByKontonummer(auftraggeberKonto, "kontoinhaber", legitimationInfo); + String kontoid = getFieldByKontonummer(auftraggeberKonto, "kontoid", legitimationInfo); + String buchungstypid = getBuchungstypid(textSchluessel, "0", legitimationInfo); + Gegenkonto gegenkonto = new Gegenkonto(empfaengerKonto, empfaengerBlz, IBAN, BIC); + empfaengerKonto = gegenkonto.getKontonummer(); + empfaengerBlz = gegenkonto.getBankleitzahl(); + IBAN = gegenkonto.getIBAN(); + BIC = gegenkonto.getBIC(); + Payments.Result r; + try { + connection = getConnection(); + PreparedStatement pSi = connection.prepareStatement(sql.dauerauftraege.insert, Statement.RETURN_GENERATED_KEYS); + pSi.setString(1, replaceNonSepaChars(empfaengerName)); + pSi.setString(2, empfaengerKonto); + pSi.setString(3, empfaengerBlz); + pSi.setDouble(4, betrag); + pSi.setString(5, replaceNonSepaChars(verwendungszweck)); + pSi.setString(6, replaceNonSepaChars(auftraggeberName)); + pSi.setString(7, auftraggeberKonto); + pSi.setString(8, replaceNonSepaChars(kundenName)); + pSi.setString(9, benutzerkennung); + pSi.setString(10, "0"); // bearbeitet + pSi.setString(11, buchungstypid); + pSi.setDate(12, new java.sql.Date(new java.util.Date().getTime())); + pSi.setString(13, currentTime); + pSi.setDate(14, new java.sql.Date(executionTime.getTime())); + pSi.setInt(15, turnus); + pSi.setDate(16, null);// LastAction + pSi.setString(17, (counter == null ? null : String.valueOf(counter))); // Sammler + pSi.setString(18, kontoid); + pSi.setInt(19, 0); // Loginid + pSi.setString(20, TAN); // TAN + pSi.setString(21, IBAN); + pSi.setString(22, BIC); + pSi.setString(23, endToEnd); + pSi.setString(24, purposeCode); + boolean execute = pSi.execute(); + LOG.info(addTagUser(format("inDauerauftraege: %s", (execute ? "true" : "false")), legitimationInfo)); + r = new Payments.Result(true).readPrepared(pSi); + new DbHelper().closeQuietly(pSi); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim anlegen eines Dauerauftrags: %s", e.getMessage()), legitimationInfo), e); + r = new Payments.Result(false).setError(e); + } finally { + new DbHelper().closeQuietly(connection); + } + return r; + } + + + /** + * Trägt die Buchung in die Vorabumsätze ein + * + * @param kontonummer kontonummer + * @param legitimationInfo legitimationInfo + * @param amount Betrag + * @param isZulasten Überweisung = true, Lastschrift = false + * @param vwz Verwendungszweck + * @param creditorAgent EmpfängerName + * @param creditorIBAN EmpfängerIban + * @param creditorName EmpfängerBIC @return gibt zurück ob es erfolgreich war + */ + public Payments.Result inVorabumsaetze(String kontonummer, LegitimationInfo legitimationInfo, double amount, boolean isZulasten, String vwz, String creditorAgent, String creditorIBAN, String creditorName) { + if (!configCreateVorabumsaetze) { + LOG.info(addTagUser("Anlage von Vorabumsätze bei Einreichung deaktiviert", legitimationInfo)); + return new Payments.Result(false); + } + Connection connection = null; + SimpleDateFormat sdf = new SimpleDateFormat("dd.MM"); + if (isZulasten) { + amount *= (-1); + } + Payments.Result r; + try { + connection = getConnection(); + String kontoid = getFieldByKontonummer(kontonummer, "kontoid", legitimationInfo); + PreparedStatement pS = connection.prepareStatement(sql.vorabumsaetze.insert, Statement.RETURN_GENERATED_KEYS); + pS.setString(1, kontoid); + pS.setDate(2, new java.sql.Date(new java.util.Date().getTime())); + pS.setString(3, replaceNonSepaChars(vwz)); + pS.setDouble(4, amount); + pS.setString(5, sdf.format(new java.util.Date())); + pS.setString(6, replaceNonSepaChars(creditorName)); + pS.setString(7, creditorIBAN); + pS.setString(8, creditorAgent); + boolean execute = pS.execute(); + LOG.info(addTagUser(format("inVorab: %s", (execute ? "true" : "false")), legitimationInfo)); + r = new Payments.Result(true).readPrepared(pS); + new DbHelper().closeQuietly(pS); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Anlage von Vorabumsatz fehlgeschlagen: %s", e.getMessage()), legitimationInfo), e); + r = new Payments.Result(false).setError(e); + } finally { + new DbHelper().closeQuietly(connection); + } + return r; + } + + /** + * Aktualisiert die Kontostände + * + * @param betrag Betrag (absolute) + * @param isZulasten Ist der Betrag zu lasten? (z.B. Überweisung: true) + * @param kontonummer Kontonummer + * @param legitimationInfo LegitimationInfo + * @return gibt zurück, ob die Aktualisierung funktioniert hat + */ + public boolean updateSalden(Double betrag, Boolean isZulasten, String kontonummer, LegitimationInfo legitimationInfo) { + Boolean success = false; + Connection connection = null; + try { + betrag = (isZulasten) ? betrag * (-1) : betrag; + Double umsatz = (betrag * (-1)); + connection = getConnection(); + PreparedStatement pSk = connection.prepareStatement(sql.konten.update_saldo); + pSk.setDouble(1, umsatz); + pSk.setDouble(2, betrag); + pSk.setString(3, kontonummer); + pSk.execute(); + PreparedStatement pSlp = connection.prepareStatement(sql.login.permission.update_by_kennung); + pSlp.setDouble(1, umsatz); + pSlp.setString(2, legitimationInfo.getUserId()); + pSlp.execute(); + success = true; + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Update der Kontostände: %s", e.getMessage()), legitimationInfo), e); + success = false; + } finally { + new DbHelper().closeQuietly(connection); + } + return success; + } + + public Double getLpTageslimit(LegitimationInfo legitimationInfo) { + Double returnDouble = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.login.permission.select); + pS.setString(1, legitimationInfo.getUserId()); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + returnDouble = resultSet.getDouble("tageslimit") - resultSet.getDouble("tagesumsatz"); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Holen der Login_Permission für Kennung: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnDouble; + } + + /** + * Holt die Spalte aus Login_Permission + * + * @param legitimationInfo LegitimationInfo + * @param column spaltenName + * @return null wenn Spalte oder User nicht existiert + */ + private String getLpPermission(LegitimationInfo legitimationInfo, String column) { + String returnString = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.login.permission.select); + pS.setString(1, legitimationInfo.getUserId()); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + returnString = toMap(resultSet).get(column); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim abholen der login_permission: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnString; + } + + /** + * Prüft die eingegebene TAN vom Beuntzer + * + * @param dialogId Dialog-ID + * @param tan eingebene TAN + * @param legitimationInfo LegitimationInfo + * @return gibt zurück, ob die eingegeben TAN richtig ist + */ + public boolean checkTan(String dialogId, String tan, LegitimationInfo legitimationInfo) { + LOG.info(addTagUser("dialogid:" + dialogId, legitimationInfo)); + String tanIdString = getTanId(dialogId, legitimationInfo); + if (tanIdString != null) { + int tanId = parseInt(tanIdString); + String tanIdName = (tanId > 0) ? "TAN-ID" : "mTAN-ID"; + String dbTan = (tanId > 0) ? getTanById(tanIdString, legitimationInfo) : getTanByIdMobile(tanIdString.replace("-", ""), legitimationInfo); + if (dbTan != null && AESUtil.aesEncrypt(tan.getBytes()).equals(dbTan)) { + LOG.info(addTagUser(format("%s: %s korrekt", tanIdName, tanIdString.replace("-", "")), legitimationInfo)); + return true; + } else { + LOG.debug(addTagUser(format("(%s/%s)", dbTan, AESUtil.aesEncrypt((tan.getBytes())) + ")"), legitimationInfo)); + LOG.info(addTagUser(format("%s: %s falsch", tanIdName, tanIdString.replace("-", "")), legitimationInfo)); + } + } else { + LOG.warn(addTagUser(format("TAN-ID-Referenz zur Dialog-ID (%s) nicht gefunden", dialogId), legitimationInfo)); + } + return false; + } + + /** + * Überprüft die Eingegeben Benutzer-BIN + * + * @param userId Kennung + * @param pin PIN + * @return gibt zurück ob die PIN richtig ist + */ + public boolean checkUserPin(String userId, String pin) { + Connection connection = null; + Boolean returnBoolean = false; + LOG.info("creating db connection for pin check"); + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.kennungen.select); + pS.setString(1, userId); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + String dbPinEncoded = resultSet.getString("pin"); + LOG.debug(addTagUser(format("(%s/%s)", AESUtil.aesEncrypt((pin.getBytes())), dbPinEncoded), userId, "")); + switch (dbPinEncoded.substring(0, 1)) { + case "#": + returnBoolean = false; + break; + case "!": + returnBoolean = false; + break; + case "*": + returnBoolean = false; + break; + default: + returnBoolean = AESUtil.aesEncrypt((pin.getBytes())).equals(dbPinEncoded); + break; + } + if (!returnBoolean) { + LOG.info(addTagUser(format("PIN falsch: %s", AESUtil.aesEncrypt((pin.getBytes()))), userId, "")); + } + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen der PIN für Kennung: %s", e.getMessage()), userId, ""), e); + } finally { + new DbHelper().closeQuietly(connection); + } + + LOG.info("db check result {}", returnBoolean); + return returnBoolean; + } + + /** + * Holt die aktuelle BPD-Version aus der Datenbank + * + * @return die BPD-Version, 0 wenn nicht gefunden + */ + public int getCurrentBpdVersion() { + int bpdVersion = 0; + Map bpd = getBpd(); + if (bpd != null) { + bpdVersion = parseInt(bpd.get("version")); + LOG.info(format("gefundene BPD-Version: %s", bpdVersion)); + } else { + LOG.warn("Fehler beim holen der bpd.version: tabelle leer?"); + } + return bpdVersion; + } + + + /** + * Entschlüsselt PIN/TAN + * + * @param strToDecode PIN/TAN + * @return Entschlüsselte PIN/TAN + */ + public String decodeCA(String strToDecode) { + String decodedPwd = ""; + try { + int actualPwdIndex = 0; + for (int i = 0; i <= strToDecode.length() - 3; i += 3) { + // Get the chunk + int chunk = Integer.parseInt(strToDecode.substring(i, i + 3)); + chunk -= Integer.parseInt(seed.substring(actualPwdIndex++, actualPwdIndex)); + decodedPwd += (char) chunk; + } + } catch (Exception ex) { + decodedPwd = ""; + } + return decodedPwd; + } + + /** + * Holt alle Konten für den Benutzer aus der Datenbank + * + * @param legitimationInfo legitimationInfo + * @return Resultset|null wenn keine Konten vorhanden + */ + public List> getKonten(LegitimationInfo legitimationInfo) { + List> returnMapList = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.konten.select_by_kundenid); + pS.setString(1, getCustomerIdByConfig(legitimationInfo)); + ResultSet resultSet = pS.executeQuery(); + returnMapList = toMapList(resultSet); + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim holen der Konten: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnMapList; + } + + /** + * Speichert die neue PIN des Benutzers ab + * + * @param legitimationInfo LegitimationInfo + * @param newPin Neue PIN + * @return gibt den Erfolg des Speicherns zurück + */ + public boolean saveNewPin(LegitimationInfo legitimationInfo, String newPin) { + boolean returnBoolean = false; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.kennungen.update_pin); + pS.setString(1, AESUtil.aesEncrypt((newPin.getBytes()))); + pS.setString(2, legitimationInfo.getUserId()); + pS.execute(); + LOG.info(addTagUser("PIN erfolgreich geändert", legitimationInfo)); + addLocalMessage(legitimationInfo.getUserId(), "Neue PIN", "Ihre PIN wurde erfolgreich geändert"); + returnBoolean = true; + } catch (SQLException e) { + LOG.warn(addTagUser(format("Es trat ein Fehler beim Ändern der PIN auf: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnBoolean; + } + + /** + * Holt den letzten Kontostand aus konten_bewegungen + * + * @param kontoid Konto-ID + * @param customerId Kunden-ID + * @param legitimationInfo LegitimationInfo + * @return Kontostand, null wenn nicht vorhanden (z.B. wenn Konto neu) + */ + public Double getLastKontostandByBewegungen(String kontoid, String customerId, LegitimationInfo legitimationInfo) { + String gebuchterSaldoString = getFieldByBewegungen(customerId, kontoid, "kontostand", legitimationInfo); + return (gebuchterSaldoString == null) ? null : parseDouble(gebuchterSaldoString); + } + + private Date getLastBuchungsdatumByBewegung(String kontoid, String customerId, LegitimationInfo legitimationInfo) { + String letztesBuchungsdatum = getFieldByBewegungen(customerId, kontoid, "buchungsdatum", legitimationInfo); + try { + return (letztesBuchungsdatum == null) ? null : sdf_sql.parse(letztesBuchungsdatum); + } catch (ParseException e) { + LOG.warn(addTagUser(format("Fehler beim Parsen des Datums %s: %s", letztesBuchungsdatum, e.getMessage()), legitimationInfo), e); + } + return null; + } + + private Date getLastBuchungsdatumByVorab(String kontoid, LegitimationInfo legitimationInfo) { + String letztesBuchungsdatum = getFieldByVorab(kontoid, "buchungsdatum", legitimationInfo); + try { + return (letztesBuchungsdatum == null) ? null : sdf_sql.parse(letztesBuchungsdatum); + } catch (ParseException e) { + LOG.warn(addTagUser(format("Fehler beim Parsen des Datums %s: %s", letztesBuchungsdatum, e.getMessage()), legitimationInfo), e); + } + return null; + } + + private String getFieldByVorab(String kontoid, String field, LegitimationInfo legitimationInfo) { + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pSkb = connection.prepareStatement(sql.vorabumsaetze.select_last); + pSkb.setString(1, kontoid); + ResultSet resultSetKb = pSkb.executeQuery(); + if (resultSetKb.next()) { + return resultSetKb.getString(field); + } + resultSetKb.close(); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Abholen der letzten Konto-Bewegung: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return null; + } + + private String getFieldByBewegungen(String customerId, String kontoid, String field, LegitimationInfo legitimationInfo) { + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pSkb = connection.prepareStatement(sql.konten.bewegungen.select_by_kontoid); + pSkb.setString(1, kontoid); + pSkb.setString(2, customerId); + ResultSet resultSetKb = pSkb.executeQuery(); + if (resultSetKb.next()) { + return resultSetKb.getString(field); + } + resultSetKb.close(); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Abholen der letzten Konto-Bewegung: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return null; + } + + /** + * Holt eine Kontenzeile passend zur Kontonummer und Kunden-ID + * + * @param kontonummer Kontonummer + * @param legitimationInfo LegitimationInfo + * @return SQL-Resultset, null wenn es die Kombination nicht gibt + */ + public Map getKontoRow(String kontonummer, LegitimationInfo legitimationInfo) { + String customerId = getCustomerIdByConfig(legitimationInfo); + Map returnMap = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.konten.select); + pS.setString(1, kontonummer); + pS.setString(2, customerId); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + returnMap = toMap(resultSet); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Abholen der Kontoinformationen: Kontonummer: %s, Meldung: %s", kontonummer, e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnMap; + + } + + + /** + * @desc function handling the fetch of the sum of the prebooked revenues + * @param kontonummer + * @return + */ + public double getPreBookedRevenuesSum(String kontonummer) { + double sumPreBooked = 0; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.vorabumsaetze.sum_prebooked); + pS.setString(1, kontonummer); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + sumPreBooked = resultSet.getDouble(1); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn("failed to fetch the pre booked sum of revenues for account: {}", kontonummer, e); + } finally { + new DbHelper().closeQuietly(connection); + } + return sumPreBooked; + } + + /** + * Holt die SQL-Zeile aus der Datenbank + * + * @return falls null, fehlt die Zeile in der Datenbank + */ + public Map getBpd() { + Map returnMap = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement ps = connection.prepareStatement(sql.bpd.select); + ResultSet resultSet = ps.executeQuery(); + if (resultSet.next()) { + returnMap = toMap(resultSet); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException sqle) { + LOG.warn(format("Fehler beim holen der Bankparameter: %s", sqle.getMessage()), sqle); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnMap; + } + + /** + * Holt alle Geschäftsvorfälle aus der Datenbank + * + * @param bpdVersion BPD-Version + * @param hbciversion HBCI-Version + * @return SQL-Result der Geschäftsvorfälle + */ + public List> getGeschaeftsvorfaelle(String bpdVersion, String hbciversion) { + List> returnMapList = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.geschaeftsvorfaelle.select_by_version_bpd); + pS.setString(1, bpdVersion); + pS.setString(2, hbciversion); + ResultSet resultSet = pS.executeQuery(); + returnMapList = toMapList(resultSet); + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(format("Fehler beim Holen der GlobalParameter (HBCI: %s, BPD: %s): %s", hbciversion, bpdVersion, e.getMessage()), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnMapList; + } + + + /** + * gibt zurück ob iTAN per SMS für den Benutzer verfügbar ist + * + * @param legitimationInfo Kennung + * @return falls smsSystemId und Handynummer hinterlegt ist, true + */ + public boolean iTanSmsIsAvaible(LegitimationInfo legitimationInfo) { + return (parseInt(config(properties.smsSystemId, "0")) > 0 && getHandyNummer(legitimationInfo) != null); + } + + /** + * Prüft mTAN für den Benutzer verfügbar ist + * + * @param legitimationInfo LegitimationInfo + * @return falls alles richtig eingerichtet ist (Handynummer, smsSystemId & tanliste_mobil-Tabelle), gibt es hier ein true zurück + */ + public boolean mTanIsAvaible(LegitimationInfo legitimationInfo) { + boolean returnBoolean = false; + if (iTanSmsIsAvaible(legitimationInfo)) { + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.tanliste.mobil.select); + pS.setString(1, "0"); + pS.execute(); + returnBoolean = true; + } catch (SQLException e) { + LOG.info(addTagUser("mtan nicht verfügbar", legitimationInfo)); + } finally { + new DbHelper().closeQuietly(connection); + } + } + return returnBoolean; + } + + /** + * Holt die Handynummer aus der Login-Tabelle + * ref_benutzerkennung muss existieren und hinterlegt sein in der logins-Tabelle + * + * @param legitimationInfo LegitimationInfo + * @return gibt null zurück, falls die Handynummer leer ist oder nicht existiert + */ + private String getHandyNummer(LegitimationInfo legitimationInfo) { + String returnString = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.login.select_by_kennung); + pS.setString(1, legitimationInfo.getUserId()); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + if (!resultSet.getString("handy").equals("")) { + returnString = resultSet.getString("handy"); + } else { + LOG.info(addTagUser("handynummer leer", legitimationInfo)); + } + } else { + LOG.info(addTagUser("Handynummer-Suche: 'handy' für ref_benutzerkennung nicht gefunden", legitimationInfo)); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Es ist ein Fehler beim holen der Handynummer aufgetreten: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnString; + } + + /** + * Selectiert die nächste iTAN und gibt das als Result zurück + * Falls die tanPerSMS true, wird diese auch gleich als versendet markiert + * + * @param legitimationInfo legitimationInfo + * @param tanPerSMS wird die TAN per SMS verschickt? + * @return Selectierte TAN als SQL-Resultset + */ + public Map iTanGenerate(LegitimationInfo legitimationInfo, boolean tanPerSMS) { + String userId = legitimationInfo.getUserId(); + Map tlActiveRs = getActiveTanListe(legitimationInfo); + if (tlActiveRs == null) { + LOG.info(addTagUser("keine aktive TAN-Liste gefunden für Kennung", legitimationInfo)); + tlActiveRs = activateAndGetNextTanListe(legitimationInfo); + if (tlActiveRs != null && tlActiveRs.containsKey("tanlistenid")) { + addLocalMessage(userId, "Folge-TAN-Liste aktiviert", "Es wurde Ihre neue TAN-Liste mit der Nummer " + tlActiveRs.get("tanlistenid") + " aktiviert"); + LOG.info(addTagUser("nächste TAN-Liste erfolgreich aktiviert", legitimationInfo)); + } else { + LOG.info(addTagUser("keine neue TAN-Liste gefunden für Kennung", legitimationInfo)); + } + } + if (tlActiveRs != null && tlActiveRs.containsKey("tanlistenid")) { + LOG.info(addTagUser("aktive TAN-Liste gefunden für Kennung", legitimationInfo)); + String tanlistennr = tlActiveRs.get("tanlistenid"); + int cursor = 0; + List> tanliste_rS = getNextFreeTanFromlist(tanlistennr, legitimationInfo); + if (tanliste_rS.size() > 1) { + cursor = random.nextInt(tanliste_rS.size() - 1); + LOG.info(addTagUser("gehe zur Zufall:" + cursor, legitimationInfo)); + } + if (tanPerSMS) { + markTanSMS(tanliste_rS.get(cursor).get("tanid"), legitimationInfo); + } + return tanliste_rS.get(cursor); + } else { + return null; + } + } + + public String replaceNonSepaChars(String string) { + return derfNullSein(string) + .replaceAll("\u00fc", "u") // Alle kleinen ü + .replaceAll("\u00f6", "o") // Alle kleinen ü + .replaceAll("\u00e4", "a") // Alle kleinen ä + .replaceAll("\u00df", "s") // Alle ß + .replaceAll("\u00dc", "U") // Alle großen Ü + .replaceAll("\u00d6", "O") // Alle großen Ö + .replaceAll("\u00c4", "A") // Alle großen Ä + .replace("/[^a-zA-Z0-9\\. \\/\\+\\-\\?:\\(\\)$,]/", "."); + } + + /** + * Markiert eine iTAN als versendet per SMS + * + * @param tanid iTAN-ID + * @param legitimationInfo LegitimationInfo + * @return gibt erfolg zurück + */ + private boolean markTanSMS(String tanid, LegitimationInfo legitimationInfo) { + boolean returnBoolean = false; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.tanliste.update_sms); + pS.setString(1, String.valueOf(unixTimeStamp())); + pS.setString(2, tanid); + pS.execute(); + returnBoolean = true; + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim markieren der TAN per SMS: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnBoolean; + } + + /** + * Generiert eine mTAN + * + * @param legitimationInfo LegitimationInfo + * @return mTAN-ID + */ + public Long mTanGenerate(LegitimationInfo legitimationInfo) { + Long returnLong = null; + Connection connection = null; + int len = parseInt(config(properties.tanLen, "6")); + try { + + String mTan = generateTan(len); + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.tanliste.mobil.insert, Statement.RETURN_GENERATED_KEYS); + pS.setString(1, AESUtil.aesEncrypt((mTan.getBytes()))); + pS.setString(2, legitimationInfo.getUserId()); + pS.setString(3, String.valueOf(unixTimeStamp())); + pS.execute(); + ResultSet resultSet = pS.getGeneratedKeys(); + if (resultSet.next()) { + sms.status smsStatus = sendTan(legitimationInfo, "Ihre mTAN für Ihren HBCI-Auftrag lautet wie folgt:" + mTan); + switch (smsStatus) { + case okay: + case queued: + LOG.info(addTagUser("mtanid:" + resultSet.getLong(1), legitimationInfo)); + returnLong = resultSet.getLong(1); + break; + case fail: + LOG.info(addTagUser("mTan konnte nicht gesendet werden", legitimationInfo)); + break; + } + } else { + LOG.warn(addTagUser("Fehler beim anlegen einer Mobile-TAN", legitimationInfo), new Throwable()); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim generieren der mTAN: %s", e.getMessage()), legitimationInfo), e); + } catch (Exception e) { + LOG.warn(addTagUser(format("Fehler beim versenden der SMS: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnLong; + } + + /** + * Holt den Bankname aus der bpd-Tabelle + * + * @return Bankname + */ + private String getBankName() { + Map bpd = getBpd(); + return (bpd != null) ? bpd.get("bankname") : ""; + } + + /** + * Sendet eine Nachrichtan den benutzer + * + * @param legitimationInfo LegitimationInfo + * @param message Nachrichten-Text + * @return gibt zurück, ob die SMS erfolgreich versendet wurde + */ + public sms.status sendTan(LegitimationInfo legitimationInfo, String message) { + String smsSystemId = config(properties.smsSystemId, "0"); + String configGatewayUrl = config(properties.smsGatewayUrl, "http://yoursmsgateway.url/yousmsgateway.php"); + String handynummer = getHandyNummer(legitimationInfo); + String absender = abbreviate(capitalize(getBankName()).replace(" ", ""), 11); + + if (parseInt(smsSystemId) > 0 && handynummer != null) { + sms gateway = new sms(smsSystemId, configGatewayUrl); + gateway.setConfigSubSystemId("_" + legitimationInfo); + try { + return gateway.doSendSMS(handynummer, message, absender); + } catch (Exception e) { + LOG.warn(addTagUser("Fehler beim Versenden der SMS", legitimationInfo), e); + } + } else if (!(parseInt(smsSystemId) > 0)) { + LOG.warn(addTagUser("mTanSystemId nicht gesetzt", legitimationInfo)); + } else { + LOG.info(addTagUser("Handynummer leer?", legitimationInfo)); + } + return sms.status.fail; + } + + /** + * Generiert eine PIN/TAN mit der geforderten Länge + * + * @param len Länge der PIN/TAN + * @return PIN/TAN mit gewünschter Länge + */ + private String generateTan(int len) { + int min = parseInt(rightPad("8", len, "9")); + int add = parseInt(rightPad("1", len, "0")); + return String.valueOf(random.nextInt(min) + add); + } + + /** + * generiert einen Unix-Timestamp + * + * @return php like time() + */ + private long unixTimeStamp() { + return System.currentTimeMillis() / 1000; + } + + private long unixTimeStamp(Date signDate) { + long millis = signDate.getTime(); + if (millis > 0) { + return millis / 1000; + } + return 0; + } + + /** + * Speichert den Schlüssel von einem Benutzer/Bank + * + * @param userId Kennung + * @param publicKey Schlüssel als Base64 + * @param art Schlüsselart + * @param version Schlüsselnummer + * @param nummer Schlüsselversion (RDH10/9) + * @param modulo Modulus + * @param exponent Exponent + * @param freigeschaltet ist der Schlüssel gleich freigeschalten? (sollte nur beim Bankschlüssel sein) + * @return gibt den Erfolg des speicherns zurück + */ + public boolean saveKey(String userId, String publicKey, int art, int version, int nummer, String modulo, String exponent, boolean freigeschaltet) { + boolean returnBoolean = false; + Connection connection = null; + try { + LOG.info(addTagUser("key.base64:" + publicKey, userId, "")); + connection = getConnection(); + Blob blob = connection.createBlob(); + blob.setBytes(1, Base64.decode(publicKey)); + PreparedStatement pS = connection.prepareStatement(sql.publickeys3.insert); + pS.setString(1, userId); + pS.setBlob(2, blob); + pS.setString(3, String.valueOf(art)); + pS.setString(4, String.valueOf(nummer)); + pS.setString(5, String.valueOf(version)); + pS.setString(6, (freigeschaltet ? "1" : "0")); // freigeschaltet + pS.setString(7, modulo); + pS.setString(8, exponent); + pS.execute(); + returnBoolean = true; + LOG.info(addTagUser("Schlüssel erfolgreich in Datenbank geschrieben für Kennung", userId, "")); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Speichern des PublicKeys Kennung/Art/Version/Nummer: %s/%s/%s", String.valueOf(art), String.valueOf(version), String.valueOf(nummer)), userId, ""), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnBoolean; + } + + /** + * Prüft ob ein Benutzer/Bank-Schlüssel existiert, freigeschalten spiel keine rolle + * + * @param userId Kennung + * @param schluesselArt Schlüsselart + * @param schluesselNummer Schlüsselnummer (RDH10/9) + * @param schluesselVersion Schlüsselversion + * @return gibt zurück ob der angefragte Schlüssel hinterlegt ist + */ + public boolean isKey(String userId, int schluesselArt, int schluesselNummer, int schluesselVersion) { + boolean returnBoolean = false; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.publickeys3.select_wo); + pS.setString(1, userId); + pS.setString(2, String.valueOf(schluesselArt)); + pS.setString(3, String.valueOf(schluesselNummer)); + pS.setString(4, String.valueOf(schluesselVersion)); + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + LOG.info(addTagUser("Schlüssel (Anonym) gefunden für Kennung", userId, "")); + returnBoolean = true; + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Allgemeiner Fehler beim Holen eines PublicKeys: %s", e.getMessage()), userId, ""), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnBoolean; + } + + /** + * Holt den Benutzer/Bank-Schlüssel aus der Datenbank + * + * @param userId kennung + * @param schluesselArt Schlüsselart + * @param schluesselNummer Schlüsselnummer (RDH10/RDH9 etc) + * @param schluesselVersion Schlüsselversion + * @param freigeschaltet Muss der Schlüssel freigeschaltet sein? + * @return Schlüssel als Base64 + */ + public String getKey(String userId, int schluesselArt, int schluesselNummer, int schluesselVersion, boolean freigeschaltet) { + String returnString = null; + Connection connection = null; + try { + String isfree = (freigeschaltet) ? "1" : "0"; + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement((schluesselVersion < 0) ? sql.publickeys3.select_wo_version : sql.publickeys3.select); + pS.setString(1, userId); + pS.setString(2, String.valueOf(schluesselArt)); + if (schluesselVersion > 0) { + pS.setString(3, String.valueOf(schluesselNummer)); + pS.setString(4, String.valueOf(schluesselVersion)); + pS.setString(5, isfree); + } else { + pS.setString(3, String.valueOf(schluesselNummer)); + pS.setString(4, isfree); + } + ResultSet resultSet = pS.executeQuery(); + if (resultSet.next()) { + Blob pubkey = resultSet.getBlob("pubkey"); + byte[] returnBytes = pubkey.getBytes(1, (int) pubkey.length()); + LOG.info(addTagUser(format("key.base64: %s", new String(Base64.encode(returnBytes))), userId, "")); + returnString = new String(Base64.encode(returnBytes)); + LOG.info(addTagUser("Schlüssel gefunden für Kennung", userId, "")); + } else { + LOG.info(addTagUser(format("Angefragten Schlüssel nicht gefunden für Art/Version/Nummer: %s/%s/%s", String.valueOf(schluesselArt), String.valueOf(schluesselVersion), String.valueOf(schluesselNummer)), userId, "")); + } + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Allgemeiner Fehler beim Holen eines PublicKeys: %s", e.getMessage()), userId, ""), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnString; + } + + /** + * Holt alle Bank-Nachrichten aus der Datenbank + * + * @param legitimationInfo LegitimationInfo + * @return Liste aller Bank-Nachrichten + */ + public List> getBankMessages(LegitimationInfo legitimationInfo) { + List> returnMapList = null; + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.messages.select_unread); + pS.setString(1, legitimationInfo.getUserId()); + ResultSet resultSet = pS.executeQuery(); + returnMapList = toMapList(resultSet); + new DbHelper().closeQuietly(resultSet); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Unerwarteter Fehler bei holen der ungelesenen Nachrichten, existiert Spalte BenutzeruserId in messages_read? %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnMapList; + } + + /** + * Setzt alle Nachrichten für den Benutzer als Gelesen + * + * @param messageIds Liste der Nachrichten-IDs + * @param legitimationInfo LegitimationInfo + * @return gibt erfolg zurück + */ + public boolean setBankMessagesRead(List messageIds, LegitimationInfo legitimationInfo) { + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.messages.read.insert); + for (String messageId : messageIds) { + pS.setString(1, messageId); + pS.setString(2, legitimationInfo.getUserId()); + pS.execute(); + } + pS.close(); + return true; + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim gelesen-markieren von Nachrichten: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return false; + } + + /** + * Setzt alle Lokalen Nachrichten für den Benutzer zurück + * + * @param userId Kennung + */ + public void resetLocalMessageList(String userId) { + localMessageList.remove(userId); + localUserReference.remove(userId); + } + + /** + * Fügt eine Nachricht für den Benutzer hinzu + * + * @param userId Kennung + * @param bankMessageObject Nachricht + */ + private void addLocalMessage(String userId, BankMessageObject bankMessageObject) { + localMessageList.putIfAbsent(userId, new ArrayList<>()); + localMessageList.get(userId).add(bankMessageObject); + } + + public boolean isMailAvaible() { + if (mailAvaible == null) { + configLog(properties.mailEmpfaenger); + configLog(properties.mailSubject); + configLog(properties.mailSmtpHost); + configLog(properties.mailSmtpUserName); + configLog(properties.mailSmtpPassword); + mailAvaible = ( + !config(properties.mailEmpfaenger, "").isEmpty() && + !config(properties.mailSubject, "").isEmpty() && + !config(properties.mailSmtpHost, "").isEmpty() && + !config(properties.mailSmtpUserName, "").isEmpty() && + !config(properties.mailSmtpPassword, "").isEmpty() + ); + } + return mailAvaible; + } + + private int parseIntNull(String intvalue, int defaultIntValue) { + if (intvalue == null || intvalue.equals("")) return defaultIntValue; + return parseInt(intvalue); + } + + private int parseIntNull(String intvalue) { + return parseIntNull(intvalue, 0); + } + + public int getLatestUpd(LegitimationInfo legitimationInfo) { + int returnInt = 1; + int lastnn = parseIntNull(getFieldByUserId("lastnn", legitimationInfo)); + LOG.debug(addTagUser(format("upd:lastnn: %s", lastnn), legitimationInfo)); + Connection connection = null; + int lastupdversion = parseIntNull(getFieldByCustomerId("updversion", legitimationInfo)); + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.login.konten.permission.calc_checksum); + pS.setString(1, legitimationInfo.getUserId()); + ResultSet resultSet = pS.executeQuery(); + int newLastNN = 1; + + if (resultSet.next()) { + newLastNN = parseIntNull(resultSet.getString("checksum")); + } + LOG.debug(addTagUser(format("upd:calcNN: %s", newLastNN), legitimationInfo)); + if (lastnn != newLastNN || lastupdversion == 0) { + LOG.info(addTagUser(format("UPD(%s)-Update erforderlich: %s > %s", lastupdversion, lastnn, newLastNN), legitimationInfo)); + PreparedStatement pSu = connection.prepareStatement(sql.kennungen.update_lastnn_checksum); + pSu.setString(1, String.valueOf(newLastNN)); + pSu.setString(2, legitimationInfo.getUserId()); + pSu.execute(); + PreparedStatement pSc = connection.prepareStatement(sql.kunden.update_upd); + pSc.setString(1, String.valueOf(lastupdversion + 1)); + pSc.setString(2, getCustomerIdByConfig(legitimationInfo)); + pSc.execute(); + } + returnInt = parseIntNull(getFieldByCustomerId("updversion", legitimationInfo)); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim generieren der Latest UPD: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return returnInt; + } + + public boolean isUpdVersionCurrent(LegitimationInfo legitimationInfo, int updVersion) { + LOG.debug(addTagUser(format("kundenid: %s", getCustomerIdByConfig(legitimationInfo)), legitimationInfo)); + LOG.debug(addTagUser(format("angefragte UPD-Version: %s", updVersion), legitimationInfo)); + if (legitimationInfo.getCustomerId().equals("9999999999")) { + return false; + } + int latestUpd = parseIntNull(getFieldByCustomerId("updversion", legitimationInfo), 1); + return (latestUpd == updVersion); + } + + public void addLocalMessage(String userId, String subject, String message) { + addLocalMessage(userId, new BankMessageObject(subject, message)); + } + + /** + * Holt alle Nachrichten für die Kennung + * + * @param userId Kennung + * @return List aller Lokaler Nachrichten + */ + public List getLocalMessageList(String userId) { + if (localMessageList.get(userId) == null) { + return new ArrayList<>(); + } + return localMessageList.get(userId); + } + + @SuppressWarnings("UnusedParameters") + public LinkedList getTerminzahlungen(LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo, boolean isSammler, Payments.versions version) { + LinkedList documents = new LinkedList<>(); + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(((isSammler) ? sql.dauerauftraege.select.termin_sammel : sql.dauerauftraege.select.termin_single)); + pS.setString(1, legitimationInfo.getUserId()); + ResultSet resultSet = pS.executeQuery(); + LinkedList paymentEntries = new LinkedList<>(); + Payments.AuftragsId auftragsIdLast = new Payments.AuftragsId(""); + String bic = config(properties.bankBic, "").trim().replace(" ", ""); + String blz = config(properties.bankCode, "").trim().replace(" ", ""); + while (resultSet.next()) { + Payments.AuftragsId auftragsIdNew = Payments.AuftragsId.generate(isSammler, legitimationInfo.getUserId(), resultSet.getString("auftragsid"), resultSet.getString("sammler")); + if (!auftragsIdNew.equals(auftragsIdLast) && paymentEntries.size() > 0) { + documents.add(Payments.paymentsToSepa(paymentEntries, auftragsIdLast.toString(), (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")).format(new Date()) + auftragsIdLast, version)); + paymentEntries = new LinkedList<>(); + } + paymentEntries.add(Payments.PaymentEntry.fromDauerautrag(resultSet, bic, blz)); + auftragsIdLast = auftragsIdNew; + } + if (paymentEntries.size() > 0) { + documents.add(Payments.paymentsToSepa(paymentEntries, auftragsIdLast.toString(), (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")).format(new Date()) + auftragsIdLast, version)); + } + new DbHelper().closeQuietly(pS); + new DbHelper().closeQuietly(resultSet); + LOG.info(addTagUser(format("isSammler: %s", isSammler), legitimationInfo)); + LOG.info(addTagUser(format("Terminzahlungen gefunden: %s", documents.size()), legitimationInfo)); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Holen der Daueraufträge: %s", e.getMessage()), legitimationInfo), e); + documents = null; + } finally { + new DbHelper().closeQuietly(connection); + } + return documents; + } + + @SuppressWarnings("UnusedParameters") + public Payments.Result deleteTerminzahlung(LegitimationInfo legitimationInfo, ClientProductInfo clientProductInfo, String auftragsidentifikation, boolean isSammler) { + Connection connection = null; + Payments.Result payRes = null; + try { + connection = getConnection(); + String start = (isSammler) ? Payments.AuftragsId.markSammler : Payments.AuftragsId.markEinzel; + String[] parts = auftragsidentifikation.split(Payments.AuftragsId.separator); + if (parts.length != 3 || !parts[0].equals(start) || !parts[1].equals(legitimationInfo.getUserId())) { + return new Payments.Result(false); + } + String auftragsidOrSammler = parts[2]; + PreparedStatement pS = connection.prepareStatement((isSammler) ? sql.dauerauftraege.select.sammel : sql.dauerauftraege.select.single); + pS.setString(1, legitimationInfo.getUserId()); + pS.setString(2, auftragsidOrSammler); + ResultSet rs = pS.executeQuery(); + if (rs.next()) { + String sqlDelete = (isSammler) ? sql.dauerauftraege.delete_sammel : sql.dauerauftraege.delete_single; + PreparedStatement pSdelete = connection.prepareStatement(sqlDelete); + pSdelete.setString(1, legitimationInfo.getUserId()); + pSdelete.setString(2, auftragsidOrSammler); + pSdelete.execute(); + new DbHelper().closeQuietly(pS); + new DbHelper().closeQuietly(rs); + payRes = new Payments.Result(true); + } else { + payRes = new Payments.Result(false); + } + } catch (SQLException e) { + payRes = new Payments.Result(false).setError(e); + } finally { + new DbHelper().closeQuietly(connection); + } + return payRes; + } + + private ReturnDataObject checkBic(String bic, tranactionTypes transactionType, LegitimationInfo legitimationInfo) { + Connection connection = null; + ReturnDataObject r = new ReturnDataObject(true, ""); + if (bic.length() != 8 && bic.length() != 11) { + return new ReturnDataObject(false, format("BIC (%s) hat eine ungültige Länge", bic), "9930"); + } + String bic_8 = (bic.length() == 8) ? bic : StringUtils.mid(bic, 0, 8); + String bic_11 = (bic.length() == 11) ? bic : null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.sepa.select); + String allowed = null; + if (bic_11 != null) { + pS.setString(1, bic_11); + ResultSet rs_11 = pS.executeQuery(); + if (rs_11.next()) { + allowed = rs_11.getString(transactionType.toString()); + } + new DbHelper().closeQuietly(rs_11); + } + if (allowed == null) { + pS.setString(1, bic_8); + ResultSet rs_8 = pS.executeQuery(); + if (rs_8.next()) { + allowed = rs_8.getString(transactionType.toString()); + } + new DbHelper().closeQuietly(rs_8); + } + if (allowed == null) { + r = new ReturnDataObject(false, format("BIC (%s) wird nicht unterstützt", bic), "9930"); + } else if (!allowed.equals("1")) { + r = new ReturnDataObject(false, format("BIC (%s) unterstützt nicht: %s", bic, transactionType.toString().toUpperCase()), "9930"); + } + new DbHelper().closeQuietly(pS); + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Prüfen der BIC (%s): %s", bic, e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return r; + } + + public ReturnDataObject checkPurposeCode(String purposeCode, LegitimationInfo legitimationInfo) { + ReturnDataObject r = new ReturnDataObject(true, ""); + if (Objects.equals(purposeCode, "")) { + return r; + } + if (purposeCode.length() != 4) { + return new ReturnDataObject(false, format("PurposeCode '%s' ist inhaltlich ungültig", purposeCode), "9930"); + } + Connection connection = null; + try { + connection = getConnection(); + PreparedStatement pS = connection.prepareStatement(sql.purposecodes.select); + pS.setString(1, purposeCode); + ResultSet rs = pS.executeQuery(); + Boolean allowed = rs.next(); + new DbHelper().closeQuietly(rs); + if (!allowed) { + r = new ReturnDataObject(false, format("PurposeCode '%s' ist nicht zugelassen", purposeCode), "9930"); + } + } catch (SQLException e) { + LOG.warn(addTagUser(format("Fehler beim Prüfen der BIC: %s", e.getMessage()), legitimationInfo), e); + } finally { + new DbHelper().closeQuietly(connection); + } + return r; + } + + + public static String addTagUser(String message, LegitimationInfo legitimationInfo) { + return legitimationInfo != null ? addTagUser(message, legitimationInfo.getUserId(), legitimationInfo.getCustomerId()) : message; + } + + public static String addTagUser(String message, String userId, String customerId) { + return format("[%s:%s] %s", userId, customerId, message); + } + + /** + * Entspricht den Spalten-Namen in der SEPA-Tabelle + */ + public enum tranactionTypes { + sct, sdd, b2b + } + + private static final class sql { + private static final class bpd { + private static final String select = "SELECT version AS version, blz AS blz, bankname AS bankname, anzgesch AS anzgesch, lang AS lang, hbciversion AS hbciversion,maxsize AS maxsize FROM bpd"; + } + + private static final class buchungstypen { + private static final String select = "SELECT buchungstypid AS buchungstypid FROM buchungstypen WHERE buchungstypid=?"; + } + + private static final class dtaus { + private static final String insert = "INSERT INTO dtaus (empfaenger,empfaengerkonto,empfaengerblz,betrag,verwendungszweck,auftraggeber,auftraggeberkonto,kunde,login,tan,bearbeitet,buchungstypid,datum,uhrzeit,counter,textschluesselergaenzung,iban,bic,ls_art,ls_sequenz,ls_duedate,mandatsreferenz,date_sign,glaeubigerid,end_to_end,purposecode) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + private static final String select_counter = "SELECT ifnull(max(counter),0) AS counter FROM dtaus WHERE login=?"; + + } + + private static final class dauerauftraege { + private static final String insert = "INSERT INTO dauerauftraege (empfaenger, empfaengerkonto, empfaengerblz, betrag, verwendungszweck, auftraggeber, auftraggeberkonto, kunde, benutzerkennung, bearbeitet, buchungstypid, datum, uhrzeit, ausfuehrungsdatum, turnus, lastaction, sammler, kontoid, loginid, tan, iban, bic, end_to_end, purposecode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + private static final String delete_single = "DELETE FROM dauerauftraege WHERE benutzerkennung=? AND auftragsid=?"; + private static final String delete_sammel = "DELETE FROM dauerauftraege WHERE benutzerkennung=? AND sammler=?"; + + private static class select { + private static final String counter = "SELECT ifnull(max(sammler),0) as counter FROM dauerauftraege"; + private static final String termin_single = "SELECT * FROM dauerauftraege WHERE benutzerkennung=? AND turnus='6' AND ifnull(sammler,0) = 0 ORDER BY sammler"; + private static final String termin_sammel = "SELECT * FROM dauerauftraege WHERE benutzerkennung=? AND turnus='6' AND ifnull(sammler,0) > 0 ORDER BY auftragsid"; + private static final String single = "SELECT * FROM dauerauftraege WHERE benutzerkennung=? AND auftragsid=?"; + private static final String sammel = "SELECT * FROM dauerauftraege WHERE benutzerkennung=? AND sammler=?"; + } + + } + + private static final class geschaeftsvorfaelle { + private static final String select_by_version_bpd = "SELECT bezeichnung AS segmentname,segmentversion AS segmentversion,maxanzahl AS maximaleanzahlauftraege,param1 AS anzahlsignaturenmindestens,param5 AS sicherheitsklasse,param3 AS fints_3_parameter,param4 AS fints_4_parameter FROM geschaeftsvorfaelle WHERE bpdversion=? AND hbciversion=?"; + private static final String insert = "INSERT INTO geschaeftsvorfaelle (bezeichnung,segmentversion,maxanzahl,param1,param5,param3,param4,bpdversion,hbciversion) VALUES (?,?,?,?,?,?,?,?,?)"; + private static final String delete = "DELETE FROM geschaeftsvorfaelle WHERE bpdversion=? AND hbciversion=?"; + + } + + private static final class tempclientstuff3 { + private static final String select = "SELECT dialogid AS dialogid,kundensystemid AS kundensystemid,tanid as tanid, kontrollref as kontrollref FROM tempclientstuff3 WHERE benutzerkennung=? AND kundenid=? AND produkt=? AND produktversion=?"; + private static final String select_kid = "SELECT dialogid AS dialogid, tanid as tanid, kontrollref as kontrollref FROM tempclientstuff3 WHERE benutzerkennung=? AND kundenid=? AND produkt=? AND produktversion=? and kundensystemid=?"; + private static final String select_tanid_by_dialogid = "SELECT tanid AS tanid FROM tempclientstuff3 WHERE dialogid=?"; + private static final String insert = "INSERT tempclientstuff3 (benutzerkennung,kundenid,produkt,produktversion,kundensystemid,dialogid) VALUES (?,?,?,?,?,?)"; + private static final String update_dialogid = "UPDATE tempclientstuff3 SET dialogid=? WHERE benutzerkennung=? AND kundenid=? AND produkt=? AND produktversion=?"; + private static final String update_srvref_by_dialogId = "UPDATE tempclientstuff3 SET kontrollref=? WHERE dialogid=?"; + private static final String update_tanid_by_kid = "UPDATE tempclientstuff3 SET tanid=?, dialogid=? WHERE benutzerkennung=? and kundenid=? and produkt=? and produktversion=? and kundensystemid=?"; + private static final String col_tanid = "tanid"; + } + + private static final class kennungen { + private static final String select = "SELECT pin AS pin,lastnn AS lastnn FROM benutzerkennungen WHERE benutzerkennung=?"; + private static final String update_pin = "UPDATE benutzerkennungen SET pin=? WHERE benutzerkennung=?"; + private static final String update_lastnn_checksum = "UPDATE benutzerkennungen SET lastnn=? WHERE benutzerkennung=?"; + } + + private static final class konten { + private static final String select = "SELECT k.kontoid AS kontoid, k.kontoinhaber AS kontoinhaber, k.kreditlinie_aktiv AS kl_aktiv,k.kreditlinie AS vb, k.kontostand AS kontostand,k.virtkontostand AS virtkontostand, k.ueberziehen AS ueberziehen,k.kontoart AS kontoart,k.tageslimit AS tageslimit,k.tagesumsatz AS tagesumsatz,k.konto_pre AS pre_konto, k.blz_pre AS pre_blz, k.iban_pre as pre_iban, k.empfaenger_pre AS pre_name FROM konten k,kunden_konten kk WHERE kk.kontoid=k.kontoid AND k.kontonummer=? AND kk.kundenid=?"; + private static final String select_by_kundenid = "SELECT k.kontoid AS kontoid, k.kontonummer AS kontonummer, k.kontoinhaber AS kontoinhaber,k.kontoart AS kontoart, k.tageslimit AS tageslimit, k.tagesumsatz AS tagesumsatz FROM kunden_konten kk, konten k WHERE kk.kundenid=? AND kk.kontoid=k.kontoid"; //s + private static final String update_saldo = "UPDATE konten SET tagesumsatz=tagesumsatz+?, virtkontostand=virtkontostand+? WHERE kontonummer=?"; + + private static final class bewegungen { + private static final String select_by_kontoid = "SELECT kb.kontostand AS kontostand, buchungsdatum as buchungsdatum FROM konten_bewegungen kb, kunden_konten kk WHERE kk.kontoid=kb.kontoid AND kk.kontoid=? AND kk.kundenid=? ORDER BY bewegungsid DESC LIMIT 1"; + } + + } + + private static final class kunden { + private static final String select = "SELECT name AS name, updversion AS updversion, stammnummer AS stammnummer,sachbearbeiter AS sachbearbeiter FROM kunden WHERE kundenid=?"; + private static final String update_upd = "UPDATE kunden SET updversion=? WHERE kundenid=?"; + + private static final class kennungen { + private static final String select = "SELECT kundenid AS kundenid FROM kunden_benutzerkennungen WHERE benutzerkennung=?"; //s + } + } + + private static final class login { + private static final String select_by_kennung = "SELECT handy AS handy FROM logins WHERE ref_benutzerkennung=?"; + + private static final class permission { + private static final String select = "SELECT tageslimit AS tageslimit, tagesumsatz AS tagesumsatz,lastschrift AS lastschrift, ueberweisung AS ueberweisung FROM login_permission WHERE benutzerkennung=?"; + private static final String update_by_kennung = "UPDATE login_permission SET tagesumsatz=tagesumsatz+? WHERE benutzerkennung=?"; + } + + private static final class konten { + private static final class permission { + private static final String select = "SELECT lkp.ueberweisung AS ueberweisung, lkp.sdd AS sdd, lkp.ade AS ade, lkp.azv AS azv, lkp.lastschrift as lastschrift FROM login_konten_permission lkp, konten k WHERE k.kontoid=lkp.kontoid AND lkp.benutzerkennung=? AND k.kontonummer=?"; //s + private static final String calc_checksum = "SELECT sum(ueberweisung+(lastschrift*10)+(abbuchung*100)+(dtaus*1000)+(sdd*10000)) AS checksum FROM login_konten_permission WHERE benutzerkennung=?"; + } + } + } + + private static final class messages { + private static final String select_unread = "SELECT messageid AS messageid,subject AS subject,text AS message FROM messages WHERE messageid NOT IN (SELECT messageid FROM messages_read WHERE benutzerkennung=?)"; + + private static final class read { + private static final String insert = "INSERT INTO messages_read (messageid,loginid,benutzerkennung) VALUES (?,0,?)"; + } + } + + private static final class mt940 { + private static final String select_by_id_date = "SELECT mt940 AS mt940, mt940id AS id FROM mt940 WHERE kontoid=? AND buchungstag >=? AND buchungstag<=? order by mt940id"; + private static final String select_last = "SELECT mt940 AS mt940, mt940id AS id FROM mt940 WHERE kontoid=? order by mt940id desc limit 1"; + } + + private static final class publickeys3 { + private static final String insert = "INSERT INTO publickeys3 (benutzerkennung,pubkey,schluesselart,schluesselnummer,schluesselversion,freigeschaltet, modulo,exponent) VALUES (?,?,?,?,?,?,?,?)"; + private static final String select = "SELECT pubkey AS pubkey FROM publickeys3 WHERE benutzerkennung=? AND schluesselart=? AND schluesselnummer=? AND schluesselversion=? AND freigeschaltet=?"; + private static final String select_wo_version = "SELECT pubkey AS pubkey FROM publickeys3 WHERE benutzerkennung=? AND schluesselart=? AND schluesselnummer=? AND freigeschaltet=? ORDER BY schluesselnummer DESC"; + private static final String select_wo = "SELECT pubkey AS pubkey FROM publickeys3 WHERE benutzerkennung=? AND schluesselart=? AND schluesselnummer=? AND schluesselversion=?"; + + } + + private static final class purposecodes { + private static final String select = "Select purposecode from purposecodes where purposecode=?"; + } + + private static final class tanliste { + private static final String update = "UPDATE tanliste SET used=1, used_at=? WHERE tanid=?"; + private static final String select_by_tanid = "SELECT tan AS tan FROM tanliste WHERE tanid=?"; + private static final String update_sms = "UPDATE tanliste SET tan_per_sms=? WHERE tanid=?"; + private static final String select_unused_by_tanlistenid_kennung = "SELECT tanid AS tanid, tan AS tan, reihenfolge AS reihenfolge, ref_tanlistenlogid AS tanlistenid FROM tanliste WHERE ref_tanlistenlogid=? AND benutzerkennung=? AND used='0' AND (tan_per_sms IS null OR tan_per_sms ='0')"; + + private static final class mobil { + private static final String insert = "INSERT INTO tanliste_mobil (mtan,used,loginid,benutzerkennung,request) VALUES (?,0,0,?,?)"; + private static final String select = "SELECT mtan AS tan FROM tanliste_mobil WHERE mtanid=?"; + private static final String update = "UPDATE tanliste_mobil SET used=? WHERE mtanid=?"; + + } + + private static final class nlog { + private static final String update_by_tanlistenid = "UPDATE tanlistenlog SET freigeschaltet=1 WHERE tanlistenlogid=?"; + private static final String select_status = "SELECT tl.tanlistenlogid AS tanlistenid,sum(if(used='0',0,1)) AS free,sum(if(used='0',1,0)) AS used, tl.freigeschaltet AS active FROM tanliste t,tanlistenlog tl WHERE t.ref_tanlistenlogid = tl.tanlistenlogid AND t.benutzerkennung = tl.ref_benutzerkennung AND benutzerkennung=? AND tl.freigeschaltet=? GROUP BY tl.tanlistenlogid HAVING sum(if(used='0',1,0)) > 0 ORDER BY tl.tanlistenlogid"; + } + } + + private static final class vorabumsaetze { + private static final String insert = "INSERT INTO vorabumsaetze (kontoid,buchungsdatum,primanota,verwendungszweck,betrag,kontostand,wert,refimportid,gegenkontoname,gegenkontonr,gegenkontoblz) VALUES (?,?,'000',?,?,0,?,0,?,?,?)"; + private static final String select = "SELECT buchungsdatum AS buchungsdatum,verwendungszweck AS vwz, betrag AS betrag, gegenkontoname AS gegenname, gegenkontonr AS gegenkonto, gegenkontoblz AS gegenblz FROM vorabumsaetze WHERE kontoid=? and buchungsdatum >=? and buchungsdatum <=?"; + private static final String select_last = "SELECT buchungsdatum AS buchungsdatum,verwendungszweck AS vwz, betrag AS betrag, gegenkontoname AS gegenname, gegenkontonr AS gegenkonto, gegenkontoblz AS gegenblz FROM vorabumsaetze WHERE kontoid=? order by bewegungsid desc limit 1"; + private static final String sum_prebooked = "select SUM(v.BETRAG) from vorabumsaetze v join konten k ON v.KONTOID = k.KONTOID where k.KONTONUMMER=? order by BEWEGUNGSID desc ;"; + } + + private static final class sepa { + private static final String select = "SELECT swiftcode,sct,sdd,b2b FROM sepa WHERE swiftcode=?"; + } + } + + + /** + * Mögliche Bank Parameter + */ + @SuppressWarnings("WeakerAccess") + public class properties { + public static final String pinSignatureCount = "pinSignatureCount"; + public static final String pinMaxEntry = "pinMaxEntry"; + public static final String pinClass = "pinClass"; + public static final String pinMin = "pinMin"; + public static final String pinMax = "pinMax"; + public static final String columnSct = "columnSct"; + public static final String columnSdd = "columnSdd"; + public static final String smsSystemId = "smsSystemId"; + public static final String smsGatewayUrl = "smsGatewayUrl"; + public static final String tanLen = "tanLen"; + public static final String bankCode = "bankCode"; + public static final String createVorabumsaetze = "createVorabumsaetze"; + public static final String communikationUrls = "communikationUrls"; + public static final String mailSender = "mail.sender"; + public static final String mailEmpfaenger = "mail.empfaenger"; + public static final String mailSubject = "mail.betreff"; + public static final String mailSmtpHost = "mail.smtp.host"; + @SuppressWarnings("unused") + public static final String mailSmtpPort = "mail.smtp.port"; + public static final String mailSmtpUserName = "mail.smtp.username"; + public static final String mailSmtpPassword = "mail.smtp.password"; + public static final String bankBic = "bankBic"; + public static final String cutOffHour = "sepa.sdd.cutOff.hour"; + public static final String cutOffMinute = "sepa.sdd.cutOff.minute"; + public static final String daysToAdd = "sepa.sdd.cuttOff.daystoadd"; + public static final String sddDueDateAdd = "sepa.sdd.duedateadd"; + public static final String allowedB2B = "sepa.sdd.b2ballowed"; + public static final String cKontoLocalPath = "ckonto.local.path"; + public static final String cKontoLocalKey = "ckonto.local.key"; + public static final String cKontoLocalCodes = "ckonto.local.codes"; + public static final String checkSonderzeichen = "check.sonderzeichen"; + public static final String cKontoSondercheckDe = "ckonto.sondercheck.de"; + public static final String avActive = "av.active"; + public static final String bicCheckEnabled = "sepa.bic.check.enabled"; + public static final String bicSaveEnabled = "sepa.bic.save.enabled"; + public static final String checkPurposeCodeSCT = "sepa.purpose.check.sct.enabled"; + public static final String checkPurposeCodeSDD = "sepa.purpose.check.sdd.enabled"; + public static final String checkCore1 = "sepa.cor1.enabled"; + } + + private class Gegenkonto { + private String Kontonummer; + private String Bankleitzahl; + private String IBAN; + private String BIC; + + Gegenkonto(String Kontonummer, String Bankleitzahl, String IBAN, String BIC) { + this.Kontonummer = (Kontonummer != null) ? Kontonummer : ""; + this.Bankleitzahl = (Bankleitzahl != null) ? Bankleitzahl : ""; + this.IBAN = (IBAN != null) ? IBAN : ""; + this.BIC = BIC != null ? BIC : ""; + if (!this.IBAN.isEmpty()) { + this.Bankleitzahl = ""; + this.Kontonummer = ""; + } else { + this.IBAN = ""; + this.BIC = ""; + } + } + + String getKontonummer() { + return Kontonummer; + } + + String getBankleitzahl() { + return Bankleitzahl; + } + + String getIBAN() { + return IBAN; + } + + String getBIC() { + return (config(properties.bicSaveEnabled, "true").equals("true") ? BIC : ""); + } + } + + +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/gateway/email.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/gateway/email.java new file mode 100644 index 0000000..0d19242 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/gateway/email.java @@ -0,0 +1,42 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects.gateway; + + +import javax.mail.*; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import java.util.Arrays; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class email +{ + + private final Session session; + + public email(String smtphost, final String username, final String password) + { + Properties props = new Properties(); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.host", smtphost); + session = Session.getInstance(props, + new javax.mail.Authenticator() + { + protected PasswordAuthentication getPasswordAuthentication() + { + return new PasswordAuthentication(username, password); + } + }); + } + + public void sendMail(String absender, String empfaenger, String subject, String text) throws MessagingException + { + Message message = new MimeMessage(session); + message.setFrom(new InternetAddress(absender)); + message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(empfaenger)); + message.setSubject(subject); + message.setText(text); + Transport.send(message); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/gateway/sms.java b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/gateway/sms.java new file mode 100644 index 0000000..cb3e518 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/dataaccess/dataobjects/gateway/sms.java @@ -0,0 +1,166 @@ +package net.petafuel.fuelifints.dataaccess.dataobjects.gateway; + +import org.bouncycastle.util.encoders.Base64; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.Charset; + +import static org.apache.commons.lang3.StringUtils.isNumeric; +import static org.apache.commons.lang3.StringUtils.left; + +public class sms +{ + public static enum status + { + fail, + okay, + queued + } + private Charset charsetUTF8 = Charset.forName("UTF-8"); + private String configGatewayUrl = "http://yoursmsgateway.url/yousmsgateway.php"; + private String configSystemId = ""; + private String configSubSystemId = null; + private String configGatewayId = null; + private String configNotBefore = null; + + public sms(String configSystemId, String configGatewayUrl) + { + this.configSystemId = configSystemId; + this.configGatewayUrl = configGatewayUrl; + } + + /** + * Sendete eine SMS ab + * @param handyNummer Handynummer + * @param message Nachricht + * @param absender Absender (max 11 Zeichen Text oder Handynummer) + * @return gibt zurück, ob die SMS erfolgreich versendet wurde + * @throws Exception + */ + public status doSendSMS(String handyNummer, String message, String absender) throws Exception + { + if (absender != null && absender.isEmpty()) absender = null; + if (configSystemId.isEmpty()) + { + throw new Exception("Keine System-ID übergeben"); + } + return doSendNew(handyNummer, message, absender); + } + + /** + * SMS-Funktion zum versenden Über das SMS-Gateway + * + * @param handynummer Handynummer + * @param message Nachricht + * @param absender Absender + * @return boolean + * @throws Exception + */ + private status doSendNew(String handynummer, String message, String absender) throws Exception + { + + + if (absender != null) + { + absender = isNumeric(absender) ? formatHandynummer(absender, false) : left(absender,11); + } + URL url = new URL(configGatewayUrl); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + + connection.setRequestProperty("systemid", configSystemId); + connection.setRequestProperty("target", formatHandynummer(handynummer, true)); + connection.setRequestProperty("text", new String(Base64.encode(message.getBytes(charsetUTF8)))); + if (absender != null) + { + connection.setRequestProperty("sender", new String(Base64.encode(absender.getBytes(charsetUTF8)))); + } + if (configSubSystemId != null) + { + connection.setRequestProperty("subsystemCustomer", configSubSystemId); + } + if (configGatewayId != null) + { + connection.setRequestProperty("gateway", configGatewayId); + } + if (configNotBefore != null) + { + connection.setRequestProperty("notbefore", configNotBefore); + } + connection.setUseCaches(false); + connection.connect(); + if (connection.getResponseCode() == 200) + { + BufferedReader rd = new BufferedReader(new InputStreamReader(connection.getInputStream())); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = rd.readLine()) != null) + { + sb.append(line).append("\n"); + } + if(sb.toString().contains("OK")) + { + return status.okay; + } + else if(sb.toString().contains("QUEUED")) + { + return status.queued; + } + } + return status.fail; + } + + /** + * Formatiert die Handynummer richtig + * + * @param handynummer Handynummer + * @param international soll die Nummer am ende International sein? + * @return Handynummer, formatiert + */ + private String formatHandynummer(String handynummer, boolean international) + { + handynummer = handynummer.replace("+", "00"); + if (!handynummer.startsWith("00")) + { + handynummer = "0049" + handynummer.substring(1); + } + if (!international) + { + handynummer = handynummer.replace("0049", "0"); + } + return handynummer; + } + + /** + * Setzen der SubsystemID (für Abrechnung innerhalb eines Systems) + * + * @param configSubSystemId Subsystem-ID oder auch CustomerId + */ + public void setConfigSubSystemId(String configSubSystemId) + { + this.configSubSystemId = configSubSystemId; + } + + /** + * Bevorzugte Gateway-ID (eigentlich nur zum Testen) + * + * @param configGatewayId Gateway-ID + */ + public void setConfigGatewayId(String configGatewayId) + { + this.configGatewayId = configGatewayId; + } + + /** + * Nicht senden vor ... + * + * @param configNotBefore MindestDatum + */ + public void setConfigNotBefore(String configNotBefore) + { + this.configNotBefore = configNotBefore; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/exceptions/DependencyResolveException.java b/src/main/java/net/petafuel/fuelifints/exceptions/DependencyResolveException.java new file mode 100644 index 0000000..a69448b --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/exceptions/DependencyResolveException.java @@ -0,0 +1,27 @@ +package net.petafuel.fuelifints.exceptions; + +public class DependencyResolveException extends Exception { + + private String errorCode = "9000"; + + public DependencyResolveException() { + super(); + } + + public DependencyResolveException(String message) { + super(message); + } + + public DependencyResolveException(String message, String errorCode) { + super(message); + this.errorCode = errorCode; + } + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/exceptions/DeprecatedVersionHKTAN.java b/src/main/java/net/petafuel/fuelifints/exceptions/DeprecatedVersionHKTAN.java new file mode 100644 index 0000000..d0654f3 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/exceptions/DeprecatedVersionHKTAN.java @@ -0,0 +1,4 @@ +package net.petafuel.fuelifints.exceptions; + +public class DeprecatedVersionHKTAN extends Exception { +} diff --git a/src/main/java/net/petafuel/fuelifints/exceptions/ElementParseException.java b/src/main/java/net/petafuel/fuelifints/exceptions/ElementParseException.java new file mode 100644 index 0000000..332fa48 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/exceptions/ElementParseException.java @@ -0,0 +1,16 @@ +package net.petafuel.fuelifints.exceptions; + +/** + * Exception für Probleme, die beim Parsen einer Nachricht auftreten können + */ + +public class ElementParseException extends Exception { + + public ElementParseException(String reason) { + super(reason); + } + + public ElementParseException(Exception e) { + super(e); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/exceptions/HBCISyntaxException.java b/src/main/java/net/petafuel/fuelifints/exceptions/HBCISyntaxException.java new file mode 100644 index 0000000..db5dd8d --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/exceptions/HBCISyntaxException.java @@ -0,0 +1,7 @@ +package net.petafuel.fuelifints.exceptions; + +public class HBCISyntaxException extends Exception { + public HBCISyntaxException(Exception ex) { + super(ex); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/exceptions/HBCIValidationException.java b/src/main/java/net/petafuel/fuelifints/exceptions/HBCIValidationException.java new file mode 100644 index 0000000..947fa93 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/exceptions/HBCIValidationException.java @@ -0,0 +1,34 @@ +package net.petafuel.fuelifints.exceptions; + +import net.petafuel.fuelifints.protocol.fints3.segments.HIRMS; + +public class HBCIValidationException extends Exception { + + private HIRMS rueckmeldung; + private boolean showDetails = true; + + public HBCIValidationException(String reason) { + super(reason); + } + + public HBCIValidationException(String reason, boolean showDetails) { + super(reason); + this.showDetails = showDetails; + } + + public HIRMS getRueckmeldung() { + return rueckmeldung; + } + + public void setRueckmeldung(HIRMS rueckmeldung) { + this.rueckmeldung = rueckmeldung; + } + + public boolean isShowDetails() { + return showDetails; + } + + public void setShowDetails(boolean showDetails) { + this.showDetails = showDetails; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/exceptions/UnregisteredClientProduct.java b/src/main/java/net/petafuel/fuelifints/exceptions/UnregisteredClientProduct.java new file mode 100644 index 0000000..14889b3 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/exceptions/UnregisteredClientProduct.java @@ -0,0 +1,4 @@ +package net.petafuel.fuelifints.exceptions; + +public class UnregisteredClientProduct extends Exception { +} diff --git a/src/main/java/net/petafuel/fuelifints/management/CommunicationManagement.java b/src/main/java/net/petafuel/fuelifints/management/CommunicationManagement.java new file mode 100644 index 0000000..ad3a559 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/management/CommunicationManagement.java @@ -0,0 +1,114 @@ +package net.petafuel.fuelifints.management; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.management.AttributeChangeNotification; +import javax.management.MBeanNotificationInfo; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Properties; + +public class CommunicationManagement extends NotificationBroadcasterSupport implements CommunicationManagementMBean { + private static final Logger LOG = LogManager.getLogger(CommunicationManagement.class); + public static final String DIALOG_TIMEOUT_MILLIS = "dialog.timeout.millis"; + private boolean nioMode = false; + private int socketPort = 3000; + private int sslPort = 443; + private String server_ip = ""; + + private long sequenceNumber = 1; + + public CommunicationManagement() { + //Versuche, default Einstellungen aus einer property-Datei zu laden: + Properties properties = new Properties(); + try { + BufferedInputStream bis = new BufferedInputStream(new FileInputStream("config/fuelifints.properties")); + properties.load(bis); + bis.close(); + System.getProperties().setProperty("hbci.security.protocols", properties.getProperty("hbci.security.protocols")); + System.getProperties().setProperty(DIALOG_TIMEOUT_MILLIS, properties.getProperty(DIALOG_TIMEOUT_MILLIS)); + socketPort = Integer.parseInt(properties.getProperty("rdh_port")); + sslPort = Integer.parseInt(properties.getProperty("ssl_port")); + server_ip = properties.getProperty("server_ip"); + } catch (FileNotFoundException e) { + LOG.error(e.getMessage(), e); + } catch (IOException e) { + LOG.error(e.getMessage(), e); + } + } + + @Override + public boolean isNioMode() { + return nioMode; + } + + @Override + public void setNioMode(boolean nioMode) { + boolean oldmode = this.nioMode; + this.nioMode = nioMode; + + Notification n = new AttributeChangeNotification(this, sequenceNumber++, + System.currentTimeMillis(), "Niomode changed", "nioMode", "boolean", + oldmode, this.nioMode); + sendNotification(n); + } + + @Override + public MBeanNotificationInfo[] getNotificationInfo() { + String[] types = new String[]{ + AttributeChangeNotification.ATTRIBUTE_CHANGE + }; + + String name = AttributeChangeNotification.class.getName(); + String description = "An attribute of this MBean has changed"; + MBeanNotificationInfo info = + new MBeanNotificationInfo(types, name, description); + return new MBeanNotificationInfo[]{info}; + } + + @Override + public int getSocketPort() { + return socketPort; + } + + @Override + public void setSocketPort(int socketPort) { + int oldPort = this.socketPort; + this.socketPort = socketPort; + + Notification n = new AttributeChangeNotification(this, sequenceNumber++, + System.currentTimeMillis(), "Socket Port changed", "socketPort", "int", + oldPort, this.socketPort); + sendNotification(n); + + } + + @Override + public int getSslPort() { + return sslPort; + } + + @Override + public void setSslPort(int sslPort) { + int oldPort = this.sslPort; + this.sslPort = sslPort; + + Notification n = new AttributeChangeNotification(this, sequenceNumber++, + System.currentTimeMillis(), "SSL Port changed", "sslPort", "int", + oldPort, this.sslPort); + sendNotification(n); + } + + public String getServer_ip() { + return server_ip; + } + + public void setServer_ip(String server_ip) { + this.server_ip = server_ip; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/management/CommunicationManagementMBean.java b/src/main/java/net/petafuel/fuelifints/management/CommunicationManagementMBean.java new file mode 100644 index 0000000..036f874 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/management/CommunicationManagementMBean.java @@ -0,0 +1,12 @@ +package net.petafuel.fuelifints.management; + +public interface CommunicationManagementMBean { + boolean isNioMode(); + void setNioMode(boolean nioMode); + + int getSocketPort(); + void setSocketPort(int socketPort); + + int getSslPort(); + void setSslPort(int sslPort); +} diff --git a/src/main/java/net/petafuel/fuelifints/model/Dialog.java b/src/main/java/net/petafuel/fuelifints/model/Dialog.java new file mode 100644 index 0000000..fdca014 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/model/Dialog.java @@ -0,0 +1,236 @@ +package net.petafuel.fuelifints.model; + +import net.petafuel.fuelifints.communication.FinTSCommunicationHandler; +import net.petafuel.fuelifints.model.client.ClientProductInfo; +import net.petafuel.fuelifints.model.client.LegitimationInfo; +import net.petafuel.fuelifints.model.client.TransactionInfo; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Hashalgorithmus; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; + +public class Dialog { + + private FinTSCommunicationHandler.CommunicationChannel communicationChannel; + private ArrayList messages = new ArrayList(); + private Message currentMessage; + private int taskId; + private String dialogId; + private long timeStamp; + private boolean lastMessage; //Gibt an, ob das serverseitig die letzte Nachricht ist (FinTS 3: HKEND) + private SecurityMethod securityMethod; + private String bankId; + private String userId; + private int keyVersion; + private int keyNumber; + private LegitimationInfo legitimationsInfo = new LegitimationInfo(); + private ClientProductInfo clientProductInfo; + private Integer messageNumber = 1; + private TransactionInfo transactionInfo; + private ErrorInfo errorInfo; + private String sicherheitsKontrollReferenz; + + public Dialog() { + //generiere zufällige, neue DialogId: + SecureRandom random = new SecureRandom(); + this.dialogId = new BigInteger(130, random).toString(32); + //legitimationsInfo = new LegitimationInfo(); + clientProductInfo = new ClientProductInfo(); + errorInfo = new ErrorInfo(); + timeStamp = System.currentTimeMillis(); + } + + /** + * Fügt dem Dialog eine neue Nachricht hinzu. + * Diese Nachricht wird automatisch auch als aktuelle Nachricht (currentMessage) markiert + * + * @param message Nachricht, die dem Dialog hinzugefügt werden soll + */ + public void addMessage(Message message) { + updateTimeStamp(); + if (!this.messages.contains(message)) { + this.messages.add(message); + this.setCurrentMessage(message); + } + } + + /** + * Liefert die Dialog Id des Dialogs zurück + * + * @return Dialog Id des Dialog + */ + public String getDialogId() { + return dialogId; + } + + /** + * Setzt die Dialog Id des Dialogs auf den übergebenen Wert + * + * @param dialogId Neue Dialog Id, die im Dialog gesetzt werden soll + */ + public void setDialogId(String dialogId) { + updateTimeStamp(); + this.dialogId = dialogId; + } + + /** + * Zwei Dialog Objekte sind gleich, wenn ihre Dialog Ids übereinstimmen + * + * @param o Objekt, das mit dem aktuellen Dialog verglichen werden soll + * @return true, wenn das Vergleichobjekt auch ein Dialog ist und die Dialog Ids übereinstimmen, ansonsten false + */ + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Dialog)) return false; + + Dialog dialog = (Dialog) o; + + if (dialogId != null ? !dialogId.equals(dialog.dialogId) : dialog.dialogId != null) return false; + + return true; + } + + @Override + public int hashCode() { + return dialogId != null ? dialogId.hashCode() : 0; + } + + public int getTaskId() { + return taskId; + } + + public void setTaskId(int taskId) { + this.taskId = taskId; + updateTimeStamp(); + } + + public Message getCurrentMessage() { + return currentMessage; + } + + public void setCurrentMessage(Message currentMessage) { + updateTimeStamp(); + this.currentMessage = currentMessage; + } + + public String getBankId() { + return bankId; + } + + public String getUserId() { + return userId; + } + + public int getKeyVersion() { + return keyVersion; + } + + public int getKeyNumber() { + return keyNumber; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public void setBankId(String bankId) { + if (this.bankId != null || bankId == null) { + return; + } + this.bankId = bankId; + } + + public void setLegitimationInfo(LegitimationInfo legitimationInfo) { + this.legitimationsInfo = legitimationInfo; + } + + public LegitimationInfo getLegitimationsInfo() { + return legitimationsInfo; + } + + public ClientProductInfo getClientProductInfo() { + return clientProductInfo; + } + + public void setClientProductInfo(ClientProductInfo clientProductInfo) { + this.clientProductInfo = clientProductInfo; + } + + public Integer getMessageNumber() { + return messageNumber; + } + + public void setMessageNumber(Integer messageNumber) { + this.messageNumber = messageNumber; + } + + public void increaseMessageNumber() { + messageNumber++; + } + + public void setTransactionInfo(TransactionInfo transactionInfo) { + this.transactionInfo = transactionInfo; + } + + public TransactionInfo getTransactionInfo() { + return transactionInfo; + } + + public ErrorInfo getErrorInfo() { + return errorInfo; + } + + public void setErrorInfo(ErrorInfo errorInfo) { + this.errorInfo = errorInfo; + } + + public FinTSCommunicationHandler.CommunicationChannel getCommunicationChannel() { + return communicationChannel; + } + + public void setCommunicationChannel(FinTSCommunicationHandler.CommunicationChannel communicationChannel) { + this.communicationChannel = communicationChannel; + switch (communicationChannel) { + case SSL: + legitimationsInfo.setSecurityMethod(SecurityMethod.PIN_1); + legitimationsInfo.setHashalgorithmus(new Hashalgorithmus("1:999:1".getBytes())); + break; + case SOCKET: + if (legitimationsInfo.getSecurityMethod() == null) { + legitimationsInfo.setSecurityMethod(SecurityMethod.RDH_10); + } + legitimationsInfo.setHashalgorithmus(new Hashalgorithmus("1:6:1".getBytes())); + break; + } + } + + public boolean isLastMessage() { + return lastMessage; + } + + public void setLastMessage(boolean lastMessage) { + this.lastMessage = lastMessage; + } + + public String getSicherheitsKontrollReferenz() { + return sicherheitsKontrollReferenz; + } + + public void setSicherheitsKontrollReferenz(String sicherheitsKontrollReferenz) { + this.sicherheitsKontrollReferenz = sicherheitsKontrollReferenz; + } + + private void updateTimeStamp() { + timeStamp = System.currentTimeMillis(); + } + + public long getTimeStamp() { + if(transactionInfo != null) { + updateTimeStamp(); + } + return timeStamp; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/model/ErrorInfo.java b/src/main/java/net/petafuel/fuelifints/model/ErrorInfo.java new file mode 100644 index 0000000..45d6a8d --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/model/ErrorInfo.java @@ -0,0 +1,25 @@ +package net.petafuel.fuelifints.model; + +import net.petafuel.fuelifints.protocol.fints3.segments.HIRMS; + +public class ErrorInfo { + + private boolean errorOccured = false; + private HIRMS errorSegment; + + public boolean isErrorOccured() { + return errorOccured; + } + + public void setErrorOccured(boolean errorOccured) { + this.errorOccured = errorOccured; + } + + public HIRMS getErrorSegment() { + return errorSegment; + } + + public void setErrorSegment(HIRMS errorSegment) { + this.errorSegment = errorSegment; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/model/IDependentElement.java b/src/main/java/net/petafuel/fuelifints/model/IDependentElement.java new file mode 100644 index 0000000..2164b2e --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/model/IDependentElement.java @@ -0,0 +1,4 @@ +package net.petafuel.fuelifints.model; + +public interface IDependentElement { +} \ No newline at end of file diff --git a/src/main/java/net/petafuel/fuelifints/model/IExecutableElement.java b/src/main/java/net/petafuel/fuelifints/model/IExecutableElement.java new file mode 100644 index 0000000..49d4f25 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/model/IExecutableElement.java @@ -0,0 +1,16 @@ +package net.petafuel.fuelifints.model; + +import java.util.List; + +public interface IExecutableElement { + + public static enum StatusCode { + OK, INFO, WARNING, ERROR + } + + public StatusCode execute(Dialog dialog); + + public List getReplyMessageElements(); + + public IMessageElement getStatusElement(); +} diff --git a/src/main/java/net/petafuel/fuelifints/model/IMessageElement.java b/src/main/java/net/petafuel/fuelifints/model/IMessageElement.java new file mode 100644 index 0000000..58fdc36 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/model/IMessageElement.java @@ -0,0 +1,14 @@ +package net.petafuel.fuelifints.model; + +import net.petafuel.fuelifints.exceptions.ElementParseException; +import net.petafuel.fuelifints.exceptions.HBCIValidationException; + +/** + * Dieses Interface liefert die Basismethoden, die für jedes Nachrichtenelement nötig sind. + */ +public interface IMessageElement { + + public byte[] getBytes(); + public void parseElement() throws ElementParseException; + public boolean validate() throws HBCIValidationException; +} diff --git a/src/main/java/net/petafuel/fuelifints/model/Message.java b/src/main/java/net/petafuel/fuelifints/model/Message.java new file mode 100644 index 0000000..d3206f5 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/model/Message.java @@ -0,0 +1,126 @@ +package net.petafuel.fuelifints.model; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.HIRMS; +import net.petafuel.fuelifints.protocol.fints3.segments.Segment; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class Message { + + private static final Logger LOG = LogManager.getLogger(Message.class); + + private int messageNumber; + private String messageId; + private int taskId; + private List messageElements; + private SecurityMethod securityMethod; + private String userId; + private String bankId; + private int segmentCount; + private List replyElements = new ArrayList<>(); + + public Message(int taskId, List messageElements, String dialogId) { + this.taskId = taskId; + this.messageElements = messageElements; + this.messageId = dialogId; + } + + @Override + public String toString() { + return "Message{" + + "messageId=" + messageId + + ", messageNumber=" + messageNumber + + '}'; + } + + public String getMessageId() { + return messageId; + } + + public void executeGVs() { + + } + + public List getMessageElements() { + return messageElements; + } + + public int getTaskId() { + return taskId; + } + + public void setTaskId(int taskId) { + this.taskId = taskId; + } + + public void setSecurityMethod(SecurityMethod securityMethod) { + this.securityMethod = securityMethod; + } + + public SecurityMethod getSecurityMethod() { + return securityMethod; + } + + public byte[] getMessageBytes() { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + LOG.debug("IMessageElementsSize: {}", messageElements.size()); + for (int i = 0; i < messageElements.size(); i++) { + IMessageElement messageElement = messageElements.get(i); + if (messageElement != null) { + try { + byteArrayOutputStream.write(((Segment) messageElement).getHbciEncoded()); + byteArrayOutputStream.flush(); + } catch (IOException ex) { + LOG.error("IOException during creating bytes of message..", ex); + return new byte[0]; + } + byteArrayOutputStream.write('\''); + try { + byteArrayOutputStream.flush(); + } catch (IOException e) { + LOG.error("IOException during creating bytes of message..", e); + return new byte[0]; + } + } + } + return byteArrayOutputStream.toByteArray(); + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserId() { + return userId; + } + + public void setBankId(String bankId) { + this.bankId = bankId; + } + + public String getBankId() { + return bankId; + } + + public void setSegmentCount(int segmentCount) { + this.segmentCount = segmentCount; + } + + public int getSegmentCount() { + return segmentCount; + } + + public void addReplyElement(HIRMS hirms) { + replyElements.add(hirms); + } + + public List getReplyElements() { + return replyElements; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/model/client/ClientProductInfo.java b/src/main/java/net/petafuel/fuelifints/model/client/ClientProductInfo.java new file mode 100644 index 0000000..d736c5d --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/model/client/ClientProductInfo.java @@ -0,0 +1,114 @@ +package net.petafuel.fuelifints.model.client; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import java.io.BufferedReader; +import java.io.FileReader; + +public class ClientProductInfo { + + private static final Logger LOG = LogManager.getLogger(ClientProductInfo.class); + + private String userSystemId; + private Integer currentClientBPDVersion; + private Integer currentClientUPDVersion; + private Integer dialogLanguage; + private String clientProductName; + private String clientProductVersion; + private boolean hksynRequired; + + public void setUserSystemId(String userSystemId) { + this.userSystemId = userSystemId; + } + + public String getUserSystemId() { + return userSystemId; + } + + public void setCurrentClientBPDVersion(Integer currentClientBPDVersion) { + this.currentClientBPDVersion = currentClientBPDVersion; + } + + public Integer getCurrentClientBPDVersion() { + return currentClientBPDVersion; + } + + public void setCurrentClientUPDVersion(Integer currentClientUPDVersion) { + this.currentClientUPDVersion = currentClientUPDVersion; + } + + public Integer getCurrentClientUPDVersion() { + return currentClientUPDVersion; + } + + public void setDialogLanguage(Integer dialogLanguage) { + this.dialogLanguage = dialogLanguage; + } + + public Integer getDialogLanguage() { + return dialogLanguage; + } + + public void setClientProductName(String clientProductName) { + this.clientProductName = clientProductName; + } + + public String getClientProductName() { + return clientProductName; + } + + public void setClientProductVersion(String clientProductVersion) { + this.clientProductVersion = clientProductVersion; + } + + public String getClientProductVersion() { + return clientProductVersion; + } + + public void setHksynRequired() { + hksynRequired = true; + } + + public void hksynRequested() { + hksynRequired = false; + } + + public boolean hksynRequired() { + return hksynRequired; + } + + public boolean validateClientProductInfo() { + + if (this.clientProductName == null || this.clientProductName.equals("")) { + return false; + } + + try (BufferedReader csvReader = new BufferedReader(new FileReader(System.getProperty("productinfo.csv.filepath")))) { + String row; + int startRow = Integer.parseInt(System.getProperty("productinfo.csv.startRow", "10")); + int pointer = 0; + while ((row = csvReader.readLine()) != null) { + + // Anfang der CSV Datei überspringen + if (pointer < startRow) { + pointer++; + continue; + } + + String[] data = row.split(";"); + + // Produktbezeichnung mit dem Eintrag in der Liste vergleichen + if (this.clientProductName.equals(data[2])) { + return true; + } + } + } catch (ArrayIndexOutOfBoundsException invalidIndex){ + LOG.error("Fehler beim Parsen der CSV-Datei - unerwartetes Format"); + } catch (Exception e) { + LOG.error(e.getMessage()); + } + + return false; + } + +} diff --git a/src/main/java/net/petafuel/fuelifints/model/client/LegitimationInfo.java b/src/main/java/net/petafuel/fuelifints/model/client/LegitimationInfo.java new file mode 100644 index 0000000..a6e0341 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/model/client/LegitimationInfo.java @@ -0,0 +1,130 @@ +package net.petafuel.fuelifints.model.client; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Hashalgorithmus; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class LegitimationInfo { + private static final Logger LOG = LogManager.getLogger(LegitimationInfo.class); + public static final String CUSTOMER_ANONYM = "9999999999"; + + private SecurityMethod securityMethod; + private String userId; //Benutzerkennung + private String customerId; //Kunden-ID + private String bankId; + + protected String tanResponse; + + private boolean userIdentified; + private Hashalgorithmus hashalgorithmus; + + private String sicherheitsfunktion = "2"; + + private boolean anonymousAccount = false; + private boolean userKeysSubmitted = false; + private boolean isStrongAuthenticated = false; + + public LegitimationInfo() { + + } + + public LegitimationInfo(LegitimationInfo legitimationInfo) { + this.securityMethod = legitimationInfo.securityMethod; + this.userId = legitimationInfo.userId; + this.customerId = legitimationInfo.customerId; + this.bankId = legitimationInfo.bankId; + this.userIdentified = legitimationInfo.userIdentified; + this.hashalgorithmus = legitimationInfo.hashalgorithmus; + this.sicherheitsfunktion = legitimationInfo.sicherheitsfunktion; + this.anonymousAccount = legitimationInfo.anonymousAccount; + this.userKeysSubmitted = legitimationInfo.userKeysSubmitted; + } + + public void setUserId(String userId) { + this.anonymousAccount = userId.equals(CUSTOMER_ANONYM); + LOG.debug("Setting new userId: {}", userId); + this.userId = userId; + } + + public String getUserId() { + return userId; + } + + public void setUserIdentified(boolean userIdentified) { + this.userIdentified = userIdentified; + } + + public boolean isUserIdentified() { + return userIdentified; + } + + public void setSecurityMethod(SecurityMethod securityMethod) { + this.securityMethod = securityMethod; + } + + public SecurityMethod getSecurityMethod() { + return securityMethod; + } + + public String getBankId() { + return bankId; + } + + public void setBankId(String bankId) { + this.bankId = bankId; + } + + public String getCustomerId() { + return customerId; + } + + public void setCustomerId(String customerId) { + this.anonymousAccount = customerId.equals(CUSTOMER_ANONYM); + this.customerId = customerId; + } + + public void setHashalgorithmus(Hashalgorithmus hashalgorithmus) { + this.hashalgorithmus = hashalgorithmus; + } + + public Hashalgorithmus getHashalgorithmus() { + return hashalgorithmus; + } + + public String getSicherheitsfunktion() { + return sicherheitsfunktion; + } + + public void setSicherheitsfunktion(String sicherheitsfunktion) { + this.sicherheitsfunktion = sicherheitsfunktion; + } + + public boolean isAnonymousAccount() { + return anonymousAccount; + } + + public boolean userKeysSubmitted() { + return userKeysSubmitted; + } + + public void setUserKeysSubmitted(boolean submitted) { + userKeysSubmitted = submitted; + } + + public void setTanResponse(String tanResponse) { + this.tanResponse = tanResponse; + } + + public String getTanResponse() { + return tanResponse; + } + + public boolean isStrongAuthenticated() { + return isStrongAuthenticated; + } + + public void setStrongAuthenticated(boolean strongAuthenticated) { + this.isStrongAuthenticated = strongAuthenticated; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/model/client/TransactionInfo.java b/src/main/java/net/petafuel/fuelifints/model/client/TransactionInfo.java new file mode 100644 index 0000000..340b7dd --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/model/client/TransactionInfo.java @@ -0,0 +1,107 @@ +package net.petafuel.fuelifints.model.client; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +public class TransactionInfo { + + private byte[] auftragsHashwert; + private Date gueltigkeitsDatum; + private String auftragsreferenz; + private String tanProzess; + private String tanListennummer; + private String challenge; + private List parameterChallengeKlasse; + private boolean tanUsed = false; + + public byte[] getAuftragsHashwert() { + return auftragsHashwert; + } + + public void setAuftragsHashwert(byte[] auftragsHashwert) { + this.auftragsHashwert = auftragsHashwert; + } + + public Date getGueltigkeitsDatum() { + return gueltigkeitsDatum; + } + + public void setGueltigkeitsDatum(Date gueltigkeitsDatum) { + this.gueltigkeitsDatum = gueltigkeitsDatum; + } + + public String getAuftragsreferenz() { + return auftragsreferenz; + } + + public void setAuftragsreferenz(String auftragsreferenz) { + this.auftragsreferenz = auftragsreferenz; + } + + public String getTanProzess() { + return tanProzess; + } + + public void setTanProzess(String tanProzess) { + this.tanProzess = tanProzess; + } + + public void setTanListennummer(String tanListennummer) { + this.tanListennummer = tanListennummer; + } + + public String getTanListennummer() { + return tanListennummer; + } + + public void setChallenge(String challenge) { + this.challenge = challenge; + } + + public String getChallenge() { + return challenge; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TransactionInfo that = (TransactionInfo) o; + + if (!Arrays.equals(auftragsHashwert, that.auftragsHashwert)) return false; + if (auftragsreferenz != null ? !auftragsreferenz.equals(that.auftragsreferenz) : that.auftragsreferenz != null) + return false; + if (gueltigkeitsDatum != null ? !gueltigkeitsDatum.equals(that.gueltigkeitsDatum) : that.gueltigkeitsDatum != null) + return false; + if (tanListennummer != null ? !tanListennummer.equals(that.tanListennummer) : that.tanListennummer != null) + return false; + if (tanProzess != null ? !tanProzess.equals(that.tanProzess) : that.tanProzess != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = gueltigkeitsDatum != null ? gueltigkeitsDatum.hashCode() : 0; + result = 31 * result + (auftragsreferenz != null ? auftragsreferenz.hashCode() : 0); + return result; + } + + public List getParameterChallengeKlasse() { + return parameterChallengeKlasse; + } + + public void setParameterChallengeKlasse(List parameterChallengeKlasse) { + this.parameterChallengeKlasse = parameterChallengeKlasse; + } + + public boolean isTanUsed() { + return tanUsed; + } + + public void setTanUsed(boolean tanUsed) { + this.tanUsed = tanUsed; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/FinTSPayload.java b/src/main/java/net/petafuel/fuelifints/protocol/FinTSPayload.java new file mode 100644 index 0000000..5547312 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/FinTSPayload.java @@ -0,0 +1,32 @@ +package net.petafuel.fuelifints.protocol; + +/** + * Klasse um die Informationen aus einer Clientnachricht zu speichern und an den entsprechenden FinTSController zur + * weiteren Auswertung weiter zu reichen + */ +public class FinTSPayload { + + private byte[] payload; + private int taskId; + + public FinTSPayload(byte[] payload, int taskId) { + this.payload = payload; + this.taskId = taskId; + } + + public int getTaskId() { + return taskId; + } + + public void setTaskId(int taskId) { + this.taskId = taskId; + } + + public byte[] getPayload() { + return payload; + } + + public void setPayload(byte[] payload) { + this.payload = payload; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/IFinTSDecryptor.java b/src/main/java/net/petafuel/fuelifints/protocol/IFinTSDecryptor.java new file mode 100644 index 0000000..0ae92d3 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/IFinTSDecryptor.java @@ -0,0 +1,11 @@ +package net.petafuel.fuelifints.protocol; + +import net.petafuel.fuelifints.model.Dialog; + +public interface IFinTSDecryptor extends Runnable { + + public void setPayload(FinTSPayload payload); + + public void setDialog(Dialog dialog); + +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/IFinTSEncryptor.java b/src/main/java/net/petafuel/fuelifints/protocol/IFinTSEncryptor.java new file mode 100644 index 0000000..667a5bd --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/IFinTSEncryptor.java @@ -0,0 +1,8 @@ +package net.petafuel.fuelifints.protocol; + +import net.petafuel.fuelifints.model.Dialog; + +public interface IFinTSEncryptor extends Runnable{ + + public void setDialog(Dialog dialog); +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/IFinTSExecutor.java b/src/main/java/net/petafuel/fuelifints/protocol/IFinTSExecutor.java new file mode 100644 index 0000000..e423d3f --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/IFinTSExecutor.java @@ -0,0 +1,11 @@ +package net.petafuel.fuelifints.protocol; + +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.Message; + +public interface IFinTSExecutor extends Runnable { + + public Message execute(); + + public void setDialog(Dialog dialog); +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/IFinTSInjector.java b/src/main/java/net/petafuel/fuelifints/protocol/IFinTSInjector.java new file mode 100644 index 0000000..a08e4c0 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/IFinTSInjector.java @@ -0,0 +1,10 @@ +package net.petafuel.fuelifints.protocol; + +import net.petafuel.fuelifints.model.Dialog; + +/** + * FFTS-27 - Kann raus! + */ +public interface IFinTSInjector extends Runnable { + public void setDialog(Dialog dialog); +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/IFinTSParser.java b/src/main/java/net/petafuel/fuelifints/protocol/IFinTSParser.java new file mode 100644 index 0000000..d77eca6 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/IFinTSParser.java @@ -0,0 +1,18 @@ +package net.petafuel.fuelifints.protocol; + +import net.petafuel.fuelifints.HBCIParseException; +import net.petafuel.fuelifints.exceptions.DeprecatedVersionHKTAN; +import net.petafuel.fuelifints.exceptions.HBCIValidationException; +import net.petafuel.fuelifints.exceptions.UnregisteredClientProduct; +import net.petafuel.fuelifints.model.Dialog; + +public interface IFinTSParser extends Runnable { + + public void setPayload(FinTSPayload payload); + + public void setDialog(Dialog dialog); + + public void parseAndValidateRequest(int taskId, byte[] request) throws HBCIParseException, SegmentNotSupportedException, HBCIValidationException, DeprecatedVersionHKTAN, UnregisteredClientProduct; + + public String getDialogId(); +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/IFinTSReplyThread.java b/src/main/java/net/petafuel/fuelifints/protocol/IFinTSReplyThread.java new file mode 100644 index 0000000..dae2259 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/IFinTSReplyThread.java @@ -0,0 +1,11 @@ +package net.petafuel.fuelifints.protocol; + +import net.petafuel.fuelifints.FinTSServer; +import net.petafuel.fuelifints.model.Dialog; + +public interface IFinTSReplyThread extends Runnable { + + public void setDialog(Dialog dialog); + + public void setFinTSServer(FinTSServer finTSServer); +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/SegmentNotSupportedException.java b/src/main/java/net/petafuel/fuelifints/protocol/SegmentNotSupportedException.java new file mode 100644 index 0000000..8962d21 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/SegmentNotSupportedException.java @@ -0,0 +1,9 @@ +package net.petafuel.fuelifints.protocol; + +public class SegmentNotSupportedException extends Exception { + + public SegmentNotSupportedException(String segmentName) { + super(segmentName + " not supported"); + } + +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Controller.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Controller.java new file mode 100644 index 0000000..d9c45cd --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Controller.java @@ -0,0 +1,479 @@ +package net.petafuel.fuelifints.protocol.fints3; + +import net.petafuel.fuelifints.ExecutorManager; +import net.petafuel.fuelifints.FinTSServer; +import net.petafuel.fuelifints.HBCIParseException; +import net.petafuel.fuelifints.IFinTSController; +import net.petafuel.fuelifints.communication.FinTSCommunicationHandler; +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.PinParameterObject; +import net.petafuel.fuelifints.exceptions.DependencyResolveException; +import net.petafuel.fuelifints.management.CommunicationManagement; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.Message; +import net.petafuel.fuelifints.model.client.LegitimationInfo; +import net.petafuel.fuelifints.model.client.TransactionInfo; +import net.petafuel.fuelifints.protocol.FinTSPayload; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.HIRMG; +import net.petafuel.fuelifints.protocol.fints3.segments.HIRMS; +import net.petafuel.fuelifints.protocol.fints3.segments.HNHBK; +import net.petafuel.fuelifints.protocol.fints3.segments.HNHBS; +import net.petafuel.fuelifints.protocol.fints3.segments.SegmentUtil; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Bezugsnachricht; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungNational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.support.ByteSplit; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.jce.provider.JDKMessageDigest; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +public class FinTS3Controller implements IFinTSController { + + private final long dialogTimeout; + private static final Logger LOG = LogManager.getLogger(FinTS3Controller.class); + + private final List

runningDialogs = new ArrayList(); + private static FinTS3Controller ourInstance; + private ExecutorManager executor = new ExecutorManager(); + + private DataAccessFacade accessFacade; + private FinTSServer finTSServer; + + private Thread dialogCleanupThread; + + /** + * private (Singleton) Constructor + */ + private FinTS3Controller() { + dialogTimeout = Long.parseLong(System.getProperty(CommunicationManagement.DIALOG_TIMEOUT_MILLIS, "10000")); + dialogCleanupThread = new Thread(new Runnable() { + @Override + public void run() { + while (!Thread.interrupted()) { + try { + Thread.sleep(2 * 60 * 1000); + } catch (InterruptedException e) { + //ignored + } + synchronized (runningDialogs) { + int countBefore = runningDialogs.size(); + Iterator iterator = runningDialogs.iterator(); + while (iterator.hasNext()) { + Dialog dialog = iterator.next(); + if ((System.currentTimeMillis() - dialog.getTimeStamp()) > dialogTimeout) { + iterator.remove(); + } + } + if (countBefore != runningDialogs.size()) { + LOG.debug("cleaned up {} Dialog(s)", (countBefore - runningDialogs.size())); + } + } + } + } + }); + dialogCleanupThread.setDaemon(true); + dialogCleanupThread.setName("DialogCleanupThread"); + dialogCleanupThread.start(); + } + + public static FinTS3Controller getInstance() { + if (ourInstance == null) + ourInstance = new FinTS3Controller(); + return ourInstance; + } + + /** + * Für Tests, um nach dem Test die Insatnz vom FinTSController los zu werden. + */ + public static void removeInstance() { + ourInstance = null; + } + + @Override + public boolean newRequest(final FinTSPayload payload, FinTSCommunicationHandler.CommunicationChannel channel) { + //Dialog ID holen, neue Message erstellen, neue oder existierenden Dialog suchen + String dialogId = "0"; + Integer nachrichtenNummer = 1; + try { + //Dialog ID steht in HNHBK, Element Nummer 4: + List segments = ByteSplit.split(payload.getPayload(), ByteSplit.MODE_SEGMENT); + if (segments.size() > 0) { + HNHBK hnhbk = new HNHBK(segments.get(0)); + hnhbk.parseElement(); + dialogId = hnhbk.getDialogId(); + nachrichtenNummer = hnhbk.getNachrichtennummer(); + + Message requestMessage = new Message(payload.getTaskId(), new ArrayList(), hnhbk.getDialogId()); + Dialog dialog = getNewOrExistingDialog(hnhbk.getDialogId()); + + dialog.setCommunicationChannel(channel); + dialog.addMessage(requestMessage); + LOG.info(dialog); + /* + if (dialog.getMessageNumber() != 1 && (dialog.getMessageNumber() != hnhbk.getNachrichtennummer() - 1)) { + replyWithError(payload.getTaskId(), "9010::Unbekannter Nachrichtenaufbau"); + return false; + } + */ + + dialog.setMessageNumber(hnhbk.getNachrichtennummer()); + FinTS3Decryptor decryptor = new FinTS3Decryptor(); + this.executor.addToDecryptionQueue(dialog, payload, decryptor); + return true; + } + } catch (Exception e) { + LOG.error("Interner Fehler", e); + } + + HNHBK hnhbk = new HNHBK(new byte[0]); + hnhbk.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HNHBK.class).setSegmentVersion(3).setSegmentNumber(1).build()); + hnhbk.setDialog_id(dialogId); + hnhbk.setNachrichtennummer(nachrichtenNummer); + hnhbk.setBezugsnachricht(new Bezugsnachricht((dialogId + ":" + nachrichtenNummer).getBytes())); + hnhbk.setNachrichtengroesse("012345678901"); + hnhbk.setHbci_version(300); + HIRMG hirmg = new HIRMG(new byte[0]); + hirmg.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentVersion(2).setSegmentKennung(HIRMG.class).setSegmentNumber(2).build()); + hirmg.setRueckmeldung(Rueckmeldung.getRueckmeldung("9800")); + HIRMS hirms = new HIRMS(new byte[0]); + hirms.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentVersion(2).setSegmentKennung(HIRMS.class).setSegmentNumber(3).build()); + hirms.addRueckmeldung(Rueckmeldung.getRueckmeldung("9951")); + HNHBS hnhbs = new HNHBS(new byte[0]); + hnhbs.setNachrichtennummer(nachrichtenNummer); + hnhbs.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentVersion(1).setSegmentNumber(4).setSegmentKennung(HNHBS.class).build()); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try { + byteArrayOutputStream.write(hnhbk.getHbciEncoded()); + byteArrayOutputStream.write('\''); + byteArrayOutputStream.write(hirmg.getHbciEncoded()); + byteArrayOutputStream.write('\''); + byteArrayOutputStream.write(hirms.getHbciEncoded()); + byteArrayOutputStream.write('\''); + byteArrayOutputStream.write(hnhbs.getHbciEncoded()); + byteArrayOutputStream.write('\''); + byteArrayOutputStream.flush(); + } catch (IOException e) { + //unrepairable error + } + + byte[] respondBytes = byteArrayOutputStream.toByteArray(); + int length = respondBytes.length; + byte[] lengthBytes = String.format("%012d", length).getBytes(); + System.arraycopy(lengthBytes, 0, respondBytes, 10, 12); + String responseString = "error"; + try { + responseString = new String(respondBytes, "ISO-8859-1"); + } catch (UnsupportedEncodingException e) { + LOG.error("Exception", e); + } + + Thread t = new Thread() { + @Override + public void run() { + //Fehler beim Parsen der Dialog ID - Fehler zurückgeben, weitere Verarbeitung abbrechen + replyWithError(payload.getTaskId(), getName()); + } + }; + t.setName(responseString); + t.start(); + + return false; + } + + /** + * For testing purposes + * + * @param manager new Executormanager to be used + */ + @Override + public void setExecutor(ExecutorManager manager) { + this.executor = manager; + } + + private Dialog getNewOrExistingDialog(String requestId) throws HBCIParseException { + if (requestId == null) + throw new HBCIParseException("no dialogid given"); + if (requestId.equals("0")) { + //Neue Nachricht, MessageId wurde bisher noch nicht gesetzt. + Dialog newDialog = new Dialog(); + synchronized (runningDialogs) { + runningDialogs.add(newDialog); + } + return newDialog; + } else { + synchronized (runningDialogs) { + for (Dialog currentDialog : runningDialogs) { + if (currentDialog.getDialogId().equals(requestId)) + return currentDialog; + } + } + } + throw new HBCIParseException("Unknown dialogId"); + } + + private Dialog getExistingDialog(int taskId) { + synchronized (runningDialogs) { + for (Dialog currentDialog : runningDialogs) { + if (currentDialog.getTaskId() == taskId) + return currentDialog; + } + } + return null; + } + + private void updateDialog(Dialog dialog) { + + } + + public void updateLegitimationInfo(String requestId, LegitimationInfo legitimationInfo) { + synchronized (runningDialogs) { + for (Dialog currentDialog : runningDialogs) { + if (currentDialog.getDialogId().equals(requestId)) + currentDialog.setLegitimationInfo(legitimationInfo); + } + } + } + + /** + * Called by {@link net.petafuel.fuelifints.protocol.fints3.FinTS3Decryptor} when decrypting is done (iof necessary) + * + * @param payload {@link FinTSPayload} Object containing the decrypted payload + */ + @Override + public void finishedDecryption(FinTSPayload payload, Dialog dialog) { + FinTS3Parser parser = new FinTS3Parser(); + executor.addToParseQueue(dialog, payload, parser); + } + + /** + * Called by {@link net.petafuel.fuelifints.protocol.fints3.FinTS3Parser} when it finished its parsing + * + * @param taskId taskId of FinTS request + * @param currentDialog + */ + @Override + public void finishedParsing(int taskId, Dialog currentDialog) { + LOG.trace("finishedParsing called; taskId: {}", taskId); + currentDialog.setTaskId(taskId); + if (currentDialog.getCurrentMessage().getUserId() != null) { + currentDialog.setUserId(currentDialog.getCurrentMessage().getUserId()); + } + currentDialog.setBankId(currentDialog.getCurrentMessage().getBankId()); + executor.addToExecuteQueue(currentDialog, new FinTS3Executor()); + } + + @Override + public void injectDependencies(IMessageElement elem, Dialog dialog) throws DependencyResolveException { + LOG.debug("Injecting dependencies for {}", elem.getClass().getSimpleName()); + // Inject dependencies into elem + if (dialog == null) { + throw new DependencyResolveException("Dialog nicht vorhanden."); + } + if (dialog.getLegitimationsInfo() == null) { + throw new DependencyResolveException("Legitimationsinfo nicht gesetzt."); + } + if (dialog.getClientProductInfo() == null) { + throw new DependencyResolveException("ClientProductInfo nicht gesetzt."); + } + + //zu Beginn die Requirement durchgehen und prüfen + if (elem.getClass().getAnnotation(Requires.class) != null) { + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + Requires.Requirement[] requirements = elem.getClass().getAnnotation(Requires.class).value(); + for (Requires.Requirement requirement : requirements) { + switch (requirement) { + case EXECUTION_ALLOWED: //Prüft, ob die Ausführung des Segments für den angemeldeten Benutzer erlaubt ist + String accountId = ""; + for (Field f : elem.getClass().getDeclaredFields()) { + //Gehe alle Felder durch, um zu schauen, ob eventuell eine Auftraggeberkontonummer gesetzt ist: + if (f.getAnnotation(ApplicantAccount.class) != null) { + f.setAccessible(true); + try { + Object o = f.get(elem); + if (o != null) { + if (o instanceof KontoverbindungNational) + accountId = ((KontoverbindungNational) o).getKontonummer(); + else if (o instanceof KontoverbindungInternational) + accountId = SegmentUtil.ibanToAccountNr(((KontoverbindungInternational) o).getIban()); + } + } catch (IllegalAccessException e) { + LOG.error("Problem accessing " + f.getName() + " in Element " + elem.getClass().getSimpleName()); + throw new DependencyResolveException("Auftrag konnte aus technischen Gründen nicht verarbeitet werden.", "9999"); + } + + } + } + if ((!accountId.equals("") && !dataAccessFacade.operationAllowedForAccount(dialog.getLegitimationsInfo(), accountId, elem.getClass())) + || !dataAccessFacade.operationAllowedForAccount(dialog.getLegitimationsInfo(), elem.getClass())) { + throw new DependencyResolveException("Benutzer hat keine Auftragsberechtigung", "9380"); + } + break; + case TAN: //Prüfe, ob die Ausführung eine TAN benötigt und wenn ja, ob die transactioninfos stimmen: + PinParameterObject ppo = dataAccessFacade.getPinParameter(); + + // TAN ist nur erforderlich falls + // 1. Das Segment TAN erfordert und + // 2. der Kunde noch nicht stark authenifiziert wurde + if (ppo.isTanErforderlich(elem.getClass().getSimpleName()) && !dialog.getLegitimationsInfo().isStrongAuthenticated()) { + //für diesen Geschäftsvorfall wird eine TAN benötigt + if (dialog.getLegitimationsInfo().getSecurityMethod() == SecurityMethod.PIN_1 || dialog.getLegitimationsInfo().getSecurityMethod() == SecurityMethod.PIN_2) { + + TransactionInfo transactionInfo = dialog.getTransactionInfo(); + if (transactionInfo == null) + throw new DependencyResolveException("Verarbeitung nicht möglich", "9010"); + String hashalgorithmus = dialog.getLegitimationsInfo().getHashalgorithmus().getHashalgorithmus(); + byte[] generatedHashFromSegment = new byte[0]; + //das Abschließende Semikolon fehlt + byte[] segmentBytes = new byte[elem.getBytes().length + 1]; + System.arraycopy(elem.getBytes(), 0, segmentBytes, 0, elem.getBytes().length); + segmentBytes[elem.getBytes().length] = '\''; + if (hashalgorithmus.equals("999")) { + //RIPEMD-160 + JDKMessageDigest.RIPEMD160 digest = new JDKMessageDigest.RIPEMD160(); + generatedHashFromSegment = digest.digest(segmentBytes); + } else if (hashalgorithmus.equals("1")) { + //SHA-1 + try { + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + generatedHashFromSegment = digest.digest(segmentBytes); + } catch (NoSuchAlgorithmException e) { + //never thrown SHA-1 is supported + } + } + if (!Arrays.equals(transactionInfo.getAuftragsHashwert(), generatedHashFromSegment)) { + LOG.error("Auftragshashwert stimmt nicht mit errechnetem Hashwert überein"); + LOG.error("Die Überweisung wird abgelehnt"); + throw new DependencyResolveException("Verarbeitung nicht möglich", "9010"); + } + transactionInfo.setTanUsed(true); + } + + } + break; + case KUNDENSYSTEM_ID: + String userSystemId = dialog.getClientProductInfo().getUserSystemId(); + if (userSystemId == null || (userSystemId.equals("0") && !dialog.getLegitimationsInfo().isAnonymousAccount())) { + throw new DependencyResolveException("Kundensystem-Id fehlerhaft oder nicht vorhanden: '" + userSystemId + "'"); + } + break; + case USER_IDENTIFIED: + if (!dialog.getLegitimationsInfo().isUserIdentified()) { + throw new DependencyResolveException("Nutzer nicht identifiziert."); + } + break; + + } + } + } + + //setze benötigte Attribute + } + + @Deprecated + public void finishedInjecting(int taskId) { + Dialog currentDialog = getExistingDialog(taskId); + if (currentDialog == null) { + } + executor.addToExecuteQueue(currentDialog, new FinTS3Executor()); + } + + /** + * Called by {@link Dialog) after execution of FinTS request is finished + * + * @param taskId taskId of FinTS request + */ + @Override + public void finishedExecuting(Message msg) { + LOG.trace("finishedExecuting called; taskId: {}", msg.getTaskId()); + Dialog currentDialog = getExistingDialog(msg.getTaskId()); + if (currentDialog == null) { + LOG.error("Dialog konnte nicht gefunden werden."); + replyWithError(msg.getTaskId(), "Could not find dialog."); + } else { + currentDialog.setCurrentMessage(msg); + FinTS3Encryptor finTS3Encryptor = new FinTS3Encryptor(); + finTS3Encryptor.setDialog(currentDialog); + executor.addToEncryptionQueue(currentDialog, finTS3Encryptor); + } + } + + @Override + public void finishedEncryption(Message msg) { + LOG.trace("finishedEncryption called; taskId: {}", msg.getTaskId()); + Dialog currentDialog = getExistingDialog(msg.getTaskId()); + if (currentDialog == null) { + LOG.error("Dialog konnte nicht gefunden werden."); + replyWithError(msg.getTaskId(), "Could not find dialog."); + return; + } + + currentDialog.addMessage(msg); + FinTS3ReplyThread replyThread = new FinTS3ReplyThread(); + executor.addToReplyQueue(currentDialog, finTSServer, replyThread); + + } + + public void replyWithError(int taskId, HIRMS hirms) { + Dialog dialog = getExistingDialog(taskId); + if (dialog != null && hirms != null) { + List messageElements = new LinkedList(); + messageElements.add(hirms); + Message errorMessage = new Message(taskId, messageElements, dialog.getDialogId()); + dialog.setCurrentMessage(errorMessage); + dialog.addMessage(errorMessage); + FinTS3Encryptor encryptor = new FinTS3Encryptor(); + encryptor.setDialog(dialog); + executor.addToEncryptionQueue(dialog, encryptor); + } + } + + public void replyWithError(int taskid, String error) { + if (error != null && finTSServer != null) { + finTSServer.respond(taskid, error.getBytes(), true); + } + } + + @Override + public int getRunningDialogCount() { + synchronized (runningDialogs) { + return runningDialogs.size(); + } + } + + public void setAccessFacade(DataAccessFacade accessFacade) { + this.accessFacade = accessFacade; + } + + @Override + public void setFinTSServer(FinTSServer finTSServer) { + this.finTSServer = finTSServer; + } + + @Override + public FinTSServer getFinTSServer() { + return this.finTSServer; + } + + public ExecutorManager getExecutorManager() { + return executor; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Decryptor.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Decryptor.java new file mode 100644 index 0000000..72bba12 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Decryptor.java @@ -0,0 +1,262 @@ +package net.petafuel.fuelifints.protocol.fints3; + +import net.petafuel.fuelifints.cryptography.Cryptography; +import net.petafuel.fuelifints.cryptography.CryptographyFactory; +import net.petafuel.fuelifints.exceptions.ElementParseException; +import net.petafuel.fuelifints.exceptions.HBCISyntaxException; +import net.petafuel.fuelifints.exceptions.HBCIValidationException; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.protocol.FinTSPayload; +import net.petafuel.fuelifints.protocol.IFinTSDecryptor; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.HNHBK; +import net.petafuel.fuelifints.protocol.fints3.segments.HNHBS; +import net.petafuel.fuelifints.protocol.fints3.segments.HNVSD; +import net.petafuel.fuelifints.protocol.fints3.segments.HNVSK; +import net.petafuel.fuelifints.protocol.fints3.segments.SegmentUtil; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Sicherheitsprofil; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Verschluesselungsalgorithmus; +import net.petafuel.fuelifints.support.ByteSplit; +import net.petafuel.fuelifints.support.LogHelper; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.UnsupportedEncodingException; +import java.util.List; + +/** + * - prüft die Syntax der Steuersegmente + * - entschlüsselt die Nachricht + * - prüft die Signatur + *

+ * Diese Klasse tastet die eigentlichen Aufträge nicht an, das wird später vom Parser erledigt + */ +public class FinTS3Decryptor implements IFinTSDecryptor { + + private static final Logger LOG = LogManager.getLogger(FinTS3Decryptor.class); + private FinTSPayload payload; + private Dialog dialog; + private HNHBK hnhbk; + private HNHBS hnhbs; + private HNVSK hnvsk; + + @Override + public void run() { + //3 Schritte: + //Nachrichtenkopf & Nachrichtenabschluss prüfen (HNHBK & HNHBS) FIXED + //Verschlüsselungskopf prüfen (HNVSK) -> später irgendwann bei RDH auch entschlüsseln FIXED + //Signatur überprüfen (HNSHK, HNSHA) -> für RDH den Hashwert checken (später), für PIN/TAN die PIN, ev. TAN checken WONT-FIX + // + //Diese Logik steckt zur Zeit noch (teilweise) in protocol.fints3.segments.SegmentManager und muss da raus + + List segments = null; + try { + segments = ByteSplit.split(payload.getPayload(), ByteSplit.MODE_SEGMENT); + } catch (HBCISyntaxException e) { + throw new RuntimeException(e); + } + try { + LOG.trace("Anzahl segmente: " + segments.size() + " von Auftrag:"); + for (byte[] seg : segments) { + LOG.trace(LogHelper.removeSignatureInformation(new String(seg, "ISO-8859-1"))); + } + } catch (UnsupportedEncodingException e) { + } + boolean messageSizeValid = false; + try { + messageSizeValid = messageSizeValid || checkMessageSize(segments.get(0)); + } catch (ElementParseException e) { + LOG.error("ElementParseException", e); + } + + if (messageSizeValid) { + LOG.trace("MessageSize valid"); + } else { + LOG.error("MessageSize invalid", new HBCIValidationException("Nachrichtenlänge ungültig.")); + throw new RuntimeException(new HBCIValidationException("Nachrichtenlänge ungültig.")); + } + + try { + checkHnhbk(); + } catch (HBCIValidationException ex) { + throw new RuntimeException(ex); + } + + boolean messageNumberValid = false; + try { + messageNumberValid = messageNumberValid || checkHnhbs(segments.get(segments.size() - 1)); + } catch (UnsupportedEncodingException e) { + LOG.error("UnsupportedEncodingException", e); + throw new RuntimeException(e); + } catch (ElementParseException e) { + LOG.error("ElementParseException", e); + throw new RuntimeException(e); + } + + if (messageNumberValid) { + LOG.trace("MessageNumber valid"); + } else { + LOG.error("MessageNumber invalid", new HBCIValidationException("Nachrichtenummer in HNHBK und HNHBS stimmen nicht überein.")); + throw new RuntimeException(new HBCIValidationException("Nachrichtenummer in HNHBK und HNHBS stimmen nicht überein.")); + } + + if (SegmentUtil.getSegmentName(segments.get(1)).equals("HNVSK")) { + /* + * Verschlüsselte Nachricht erhalten. + */ + LOG.trace("Check if message is PinTan or HBCI (encrypted)."); + String securityMethod = ""; + try { + securityMethod = getSecurityMethod(segments.get(1)); + } catch (ElementParseException e) { + throw new RuntimeException(e); + } + Cryptography cryptography = CryptographyFactory.getCryptography(SecurityMethod.valueOf(securityMethod.replace('-', '_'))); + byte[] encrypted = new byte[0]; + try { + encrypted = getEncryptedBytes(segments.get(2)); + } catch (UnsupportedEncodingException e) { + //ISO-8859-1 is supported + } catch (ElementParseException e) { + throw new RuntimeException(e); + } + byte[] decryptedSegments = cryptography.decrypt(encrypted, getCypherKey(securityMethod), hnvsk.getSchluesselname().getKreditsinstitutkennung().getKreditinstitutscode()); + LOG.trace("Finished decrypting..."); + dialog.setBankId(hnvsk.getSchluesselname().getKreditsinstitutkennung().getKreditinstitutscode()); + + try { + LOG.trace("Decrypted Segments: {}", LogHelper.removeSignatureInformation(new String(decryptedSegments, "ISO-8859-1"))); + } catch (UnsupportedEncodingException e) { + LOG.error("UnsupportedEncodingException", e); + } + + // + 2 weil das Segment-Trennzeichen (') in HNHBK und HNHBS abgeschnitten ist. + int newPayloadLength = hnhbk.getBytes().length + hnhbs.getBytes().length + decryptedSegments.length + 2; + byte[] newPayload = new byte[newPayloadLength]; + int offset = 0; + System.arraycopy(hnhbk.getBytes(), 0, newPayload, offset, hnhbk.getBytes().length); + offset += hnhbk.getBytes().length; + newPayload[offset] = '\''; + offset++; + System.arraycopy(decryptedSegments, 0, newPayload, offset, decryptedSegments.length); + offset += decryptedSegments.length; + System.arraycopy(hnhbs.getBytes(), 0, newPayload, offset, hnhbs.getBytes().length); + offset += hnhbs.getBytes().length; + newPayload[offset] = '\''; + + try { + LOG.info("Auftragsnachricht: " + LogHelper.removeSignatureInformation(new String(newPayload, "ISO-8859-1"))); + } catch (UnsupportedEncodingException e) { + LOG.error("UnsupportedEncodingException", e); + } + + payload = new FinTSPayload(newPayload, payload.getTaskId()); + + } else { + //anonyme anfragen müsse nicht angefasst werden -> payload unverändert weiter reichen + } + FinTS3Controller.getInstance().finishedDecryption(payload, dialog); + } + + private byte[] getEncryptedBytes(byte[] bytes) throws UnsupportedEncodingException, ElementParseException { + //String hnvsdString = new String(bytes,0,bytes.length-1,"ISO-8859-1"); + HNVSD hnvsd = new HNVSD(bytes); + hnvsd.parseElement(); + byte[] encryptedDE = hnvsd.getVerschluesselteDaten(); + //LOG.debug("EncryptedDE: " + new String(encryptedDE, "ISO-8859-1")); + int offset = 1; + for (; offset < encryptedDE.length; offset++) { + if (encryptedDE[offset] == 0x40) { + LOG.trace("Found binary character at: {}", offset); + offset++; + break; + } + } + byte[] data = new byte[encryptedDE.length - offset]; + LOG.trace("EncryptedDataLength: {}", data.length); + System.arraycopy(encryptedDE, offset, data, 0, data.length); + return data; + } + + private byte[] getCypherKey(String securityMethod) { + if (securityMethod.equals("PIN-1") || securityMethod.equals("PIN-2")) { + /* + * Daten in HNVSD werden hier nicht verschlüsselt. + */ + return new byte[0]; + } else { + Verschluesselungsalgorithmus verschluesselungsalgorithmus = hnvsk.getVerschluesselungsalgorithmus(); + byte[] keyDEG = verschluesselungsalgorithmus.getWertAlgorithmusparameterSchluessel(); + //LOG.debug(new String(keyDEG)); + int index = 1; + for (; index < keyDEG.length; index++) { + if (keyDEG[index] == 0x40) { + index++; + break; + } + } + + byte[] encryptedKey = new byte[keyDEG.length - index]; + System.arraycopy(keyDEG, index, encryptedKey, 0, encryptedKey.length); + return encryptedKey; + } + } + + private String getSecurityMethod(byte[] bytes) throws ElementParseException { + //String hnvskString = new String(bytes,0,bytes.length-1,"ISO-8859-1"); + hnvsk = new HNVSK(bytes); + hnvsk.parseElement(); + Sicherheitsprofil sicherheitsprofil = hnvsk.getSicherheitsprofil(); + return sicherheitsprofil.getSicherheitsverfahren() + "-" + sicherheitsprofil.getSicherheitsverfahrensversion(); + } + + private void checkHnhbk() throws HBCIValidationException { + if (hnhbk != null) { + int nachrichtennummer = hnhbk.getNachrichtennummer(); + if (nachrichtennummer <= 0) { + HBCIValidationException exception = new HBCIValidationException("Nachrichtennummer <= 0."); + LOG.error("Nachrichtennummer falsch.", exception); + throw exception; + } + if (hnhbk.getBezugsnachricht() != null) { + LOG.error("Bezugsnachricht vom Kunden gesetzt. Abbruch.", new HBCIValidationException("Bezugsnachricht vom Kunden gesetzt. Abbruch.")); + throw new HBCIValidationException("Bezugsnachricht vom Kunden gesetzt. Abbruch."); + } + } + } + + private boolean checkHnhbs(byte[] segment) throws UnsupportedEncodingException, ElementParseException { + //String hnhbsString = new String(segment,0,segment.length-1,"ISO-8859-1"); + //LOG.info(hnhbsString); + hnhbs = new HNHBS(segment); + hnhbs.parseElement(); + int nachrichtennummerHnhbk = hnhbk.getNachrichtennummer(); + int nachrichtennummerHnhbs = hnhbs.getNachrichtennummer(); + LOG.trace("Nachrichtennummer HNHBK: " + nachrichtennummerHnhbk + " Nachrichtennummer HNHBS: " + nachrichtennummerHnhbs); + return nachrichtennummerHnhbk == nachrichtennummerHnhbs; + } + + private boolean checkMessageSize(byte[] segment) throws ElementParseException { + //String hnhbkSting = new String(segment,0,segment.length-1,"ISO-8859-1"); + //LOG.info(hnhbkSting); + hnhbk = new HNHBK(segment); + hnhbk.parseElement(); + String messageSize = hnhbk.getNachrichtengroesse(); + //int payloadSize = (new String(payload.getPayload(),"ISO-8859-1")).length(); + //LOG.info("Parsed MessageSize: "+ messageSize +" parsed int: "+ Integer.parseInt(messageSize)+ " payload size: "+ payloadSize); + return Integer.parseInt(messageSize) == payload.getPayload().length/*(new String(payload.getPayload(),"ISO-8859-1")).length()*/; + } + + @Override + public void setPayload(FinTSPayload payload) { + this.payload = payload; + } + + public Dialog getDialog() { + return dialog; + } + + public void setDialog(Dialog dialog) { + this.dialog = dialog; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Encryptor.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Encryptor.java new file mode 100644 index 0000000..2c83215 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Encryptor.java @@ -0,0 +1,180 @@ +package net.petafuel.fuelifints.protocol.fints3; + +import net.petafuel.fuelifints.cryptography.Cryptography; +import net.petafuel.fuelifints.cryptography.CryptographyFactory; +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.Message; +import net.petafuel.fuelifints.model.client.ClientProductInfo; +import net.petafuel.fuelifints.model.client.LegitimationInfo; +import net.petafuel.fuelifints.protocol.IFinTSEncryptor; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.AbstractElement; +import net.petafuel.fuelifints.protocol.fints3.segments.HIRMG; +import net.petafuel.fuelifints.protocol.fints3.segments.HIRMS; +import net.petafuel.fuelifints.protocol.fints3.segments.HNVSD; +import net.petafuel.fuelifints.protocol.fints3.segments.HNVSK; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Schluesselname; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.SicherheitsdatumUndUhrzeit; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Sicherheitsidentifikationsdetails; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Sicherheitsprofil; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Verschluesselungsalgorithmus; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.GregorianCalendar; +import java.util.LinkedList; +import java.util.List; + +/** + * Verschlüsselt die Antwortnachrichten. + * Fügt die Segmente HNVSK und HNVSD dazu und füllt diese entsprechend. + */ +public class FinTS3Encryptor implements IFinTSEncryptor { + private static final Logger LOG = LogManager.getLogger(FinTS3Encryptor.class); + + private Dialog dialog; + + @Override + public void setDialog(Dialog dialog) { + this.dialog = dialog; + } + + @Override + public void run() { + if (dialog.getLegitimationsInfo().isAnonymousAccount()/* || dialog.getLegitimationsInfo().userKeysSubmitted()*/) { + /* + * Anonyme Nachricht erhalten, keine verschlüsselung nötig. + */ + dialog.getLegitimationsInfo().setUserKeysSubmitted(false); + FinTS3Controller.getInstance().finishedEncryption(dialog.getCurrentMessage()); + return; + } + + SecurityMethod securityMethod = dialog.getLegitimationsInfo().getSecurityMethod(); + LOG.debug("Encrypting with securitymethod: {}", securityMethod); + Cryptography cryptography = CryptographyFactory.getCryptography(securityMethod); + + Message message = dialog.getCurrentMessage(); + byte[] encryptionKey = cryptography.generateKey(); + + LOG.debug("EncryptionKey length: {}", encryptionKey.length); + + byte[] toEncrypt = message.getMessageBytes(); + LOG.debug("ToEncryptLength: {}, ToEncrypt: {}", toEncrypt.length, new String(toEncrypt)); + byte[] encryptedMessage = cryptography.encryptMessage(toEncrypt, encryptionKey, dialog.getBankId()); + LOG.debug("encryptedMessageLength: {}", encryptedMessage.length); + byte[] encryptedKey = cryptography.encryptEncryptionKey(encryptionKey, dialog.getBankId(), dialog.getUserId()); + if (encryptedKey.length == 0) { + //Fehler + List elements = new LinkedList(); + + HIRMG hirmg = new HIRMG(new byte[0]); + hirmg.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMG.class).setSegmentNumber(2).setSegmentVersion(2).build()); + hirmg.setRueckmeldung(Rueckmeldung.getRueckmeldung("9999")); + elements.add(hirmg); + HIRMS hirms = new HIRMS(new byte[0]); + hirms.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentVersion(2).setSegmentNumber(3).setSegmentKennung(HIRMS.class).build()); + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode("9999"); + rueckmeldung.setRueckmeldungstext("Bei der Verarbeitung ist ein Fehler aufgetreten."); + hirms.addRueckmeldung(rueckmeldung); + elements.add(hirms); + + Message message1 = new Message(message.getTaskId(), elements, dialog.getDialogId()); + message1.setSegmentCount(message.getSegmentCount()); + FinTS3Controller.getInstance().finishedEncryption(message1); + return; + } + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try { + byteArrayOutputStream.write('@'); + byteArrayOutputStream.write(Integer.toString(encryptedMessage.length).getBytes("ISO-8859-1")); + byteArrayOutputStream.write('@'); + byteArrayOutputStream.write(encryptedMessage); + } catch (IOException ex) { + } + HNVSD hnvsd = new HNVSD(new byte[0]); + //hnvsd.setSegmentkopf(new Segmentkopf("HNVSD:999:1".getBytes())); + hnvsd.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HNVSD.class).setSegmentNumber(999).setSegmentVersion(1).build()); + hnvsd.setVerschluesselteDaten(byteArrayOutputStream.toByteArray()); + List elements = new LinkedList(); + elements.add(hnvsd); + + HNVSK hnvsk = new HNVSK(new byte[0]); + //hnvsk.setSegmentkopf(new Segmentkopf("HNVSK:998:3".getBytes())); + hnvsk.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HNVSK.class).setSegmentNumber(998).setSegmentVersion(3).build()); + + Sicherheitsprofil sicherheitsprofil = new Sicherheitsprofil(securityMethod.getHbciDEG()); + hnvsk.setSicherheitsprofil(sicherheitsprofil); + if (securityMethod == SecurityMethod.PIN_1 || securityMethod == SecurityMethod.PIN_2) + hnvsk.setSicherheitsfunktion("998"); + else + hnvsk.setSicherheitsfunktion("4"); + hnvsk.setSicherheitslieferant("1"); + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + ClientProductInfo clientProductInfo = dialog.getClientProductInfo(); + LegitimationInfo legitimationInfo = dialog.getLegitimationsInfo(); + + String userSystemId = null; + try { + userSystemId = dataAccessFacade == null ? "0" : dataAccessFacade.getUserSystemId(dialog.getDialogId(), legitimationInfo, clientProductInfo); + } catch (Exception e) { + LOG.error(e.getMessage()); + } + Sicherheitsidentifikationsdetails sicherheitsidentifikationsdetails = new Sicherheitsidentifikationsdetails(("2::" + (userSystemId == null ? 0 : userSystemId)).getBytes()); + hnvsk.setSicherheitsidentifikationsdetails(sicherheitsidentifikationsdetails); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd:HHmmss"); + GregorianCalendar gregorianCalendar = (GregorianCalendar) GregorianCalendar.getInstance(); + SicherheitsdatumUndUhrzeit sicherheitsdatumUndUhrzeit = new SicherheitsdatumUndUhrzeit(("1:" + simpleDateFormat.format(gregorianCalendar.getTime())).getBytes()); + hnvsk.setSicherheitsdatumUndUhrzeit(sicherheitsdatumUndUhrzeit); + byteArrayOutputStream.reset(); + try { + byteArrayOutputStream.write('2'); + byteArrayOutputStream.write(':'); + byteArrayOutputStream.write(getHbciFormat(securityMethod.getOperationsModusVerschluesselung())); + byteArrayOutputStream.write(':'); + byteArrayOutputStream.write(getHbciFormat(securityMethod.getVerschluesselungsAlgorithmus())); + byteArrayOutputStream.write(':'); + byteArrayOutputStream.write('@'); + byteArrayOutputStream.write(Integer.toString(encryptedKey.length).getBytes()); + byteArrayOutputStream.write('@'); + byteArrayOutputStream.write(encryptedKey); + byteArrayOutputStream.write(":6:1".getBytes()); + byteArrayOutputStream.flush(); + } catch (IOException ex) { + } + Verschluesselungsalgorithmus verschluesselungsalgorithmus = new Verschluesselungsalgorithmus(byteArrayOutputStream.toByteArray()); + hnvsk.setVerschluesselungsalgorithmus(verschluesselungsalgorithmus); + String userId = dialog.getLegitimationsInfo().getUserId(); + if (userId != null) { + if (userId.isEmpty() && !dialog.getUserId().isEmpty()) { + userId = dialog.getUserId(); + } + } else { + userId = "0"; + } + + Schluesselname schluesselname = new Schluesselname(("280:" + dialog.getBankId() + ":" + AbstractElement.generateEscapedVersion(userId) + ":V:" + securityMethod.getVersionNumber() + ":1").getBytes()); + + hnvsk.setSchluesselname(schluesselname); + hnvsk.setKomprimierungsfunktion("0"); + + elements.add(0, hnvsk); + Message message1 = new Message(message.getTaskId(), elements, dialog.getDialogId()); + message1.setSegmentCount(message.getSegmentCount()); + FinTS3Controller.getInstance().finishedEncryption(message1); + } + + private byte[] getHbciFormat(int hbciCode) { + return Integer.toString(hbciCode).getBytes(StandardCharsets.ISO_8859_1); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Executor.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Executor.java new file mode 100644 index 0000000..5fc2f59 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Executor.java @@ -0,0 +1,342 @@ +package net.petafuel.fuelifints.protocol.fints3; + +import net.petafuel.fuelifints.cryptography.SignatureHelper; +import net.petafuel.fuelifints.cryptography.SignatureHelperFactory; +import net.petafuel.fuelifints.exceptions.DependencyResolveException; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.Message; +import net.petafuel.fuelifints.model.client.LegitimationInfo; +import net.petafuel.fuelifints.protocol.IFinTSExecutor; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.HIISA; +import net.petafuel.fuelifints.protocol.fints3.segments.HIRMG; +import net.petafuel.fuelifints.protocol.fints3.segments.HIRMS; +import net.petafuel.fuelifints.protocol.fints3.segments.HNSHA; +import net.petafuel.fuelifints.protocol.fints3.segments.HNSHK; +import net.petafuel.fuelifints.protocol.fints3.segments.Segment; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Schluesselname; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.SicherheitsdatumUndUhrzeit; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Sicherheitsidentifikationsdetails; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Sicherheitsprofil; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Signaturalgorithmus; +import net.petafuel.fuelifints.support.SegmentComparator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.GregorianCalendar; +import java.util.List; + +public class FinTS3Executor implements IFinTSExecutor { + + private static final Logger LOG = LogManager.getLogger(FinTS3Executor.class); + + private Dialog dialog; + + @Override + public Message execute() { + //iterate through last requests' segments to find gvs and their dependencies: + + Message msg = this.dialog.getCurrentMessage(); + List messageElements = msg.getMessageElements(); + + //AntwortSegmente + List replyElements = msg.getReplyElements(); + + boolean successful = false; + boolean warning = false; + boolean error = false; + + /* Prüfe, dass bisher kein Fehler aufgetreten ist. + * Fehler können an dieser Stelle bereits beim Entschlüsseln, der Signaturprüfung und beim Parsen der Segmente aufgetreten sein + */ + if (dialog.getErrorInfo() != null && !dialog.getErrorInfo().isErrorOccured()) { + for (IMessageElement currentElement : messageElements) { + if (currentElement instanceof IDependentElement) {// This segment has dependencies + try { + FinTS3Controller.getInstance().injectDependencies(currentElement, this.dialog); + } catch (DependencyResolveException e) { + LOG.error("DependencyResolveException", e); + HIRMS hirms = new HIRMS(new byte[0]); + hirms.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(((Segment) currentElement).getSegmentkopf().getSegmentNummer()).build()); + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode("9000"); + rueckmeldung.setRueckmeldungstext("Verarbeitung nicht möglich"); + hirms.addRueckmeldung(rueckmeldung); + if (e.getErrorCode() != null && !e.getErrorCode().equals("")) { + Rueckmeldung exceptionMeldung = new Rueckmeldung(new byte[0]); + exceptionMeldung.setRueckmeldungscode(e.getErrorCode()); + exceptionMeldung.setRueckmeldungstext(e.getMessage()); + hirms.addRueckmeldung(exceptionMeldung); + } + replyElements.add(hirms); + error = true; + successful = false; + break; + } + } + /** + * Gehe über alle Auftrag-Segmente und führe sie aus. + * Jedes ausführbare Segment liefert einen StatusCode zurück der angibt, + * ob die Ausführung erfolgreich, Warnungen enthielt oder fehlerhaft war. + * + * Falls ein Fehler aufgetreten ist, dann wird abgebrochen, bei Warnungen kann + * weiter gearbeitet werden. + */ + if (currentElement instanceof IExecutableElement) { + + IExecutableElement.StatusCode statusCode = ((IExecutableElement) currentElement).execute(dialog); + List segmentReplyElements = ((IExecutableElement) currentElement).getReplyMessageElements(); + LOG.info("adding new reply element for element " + currentElement.getClass().getSimpleName() + " replyElement " + segmentReplyElements); + if (segmentReplyElements != null && segmentReplyElements.size() > 0) { + replyElements.addAll(segmentReplyElements); + } + switch (statusCode) { + case OK: + successful = true; + break; + case INFO: + //INFO kann wie WARNING behandelt werden.. + case WARNING: + warning = true; + break; + case ERROR: + LOG.error("could not execute: {}", currentElement); + if (currentElement != null && ((IExecutableElement) currentElement).getStatusElement() != null) + LOG.error("error message: {}", new String(((IExecutableElement) currentElement).getStatusElement().getBytes())); + error = true; + break; + } + //füge alle Statuselemente hinzu, sie werden später sortiert. + IMessageElement statusElement = ((IExecutableElement) currentElement).getStatusElement(); + if (statusElement != null && ((HIRMS) statusElement).getRueckmeldung() != null) { + replyElements.add(statusElement); + } + if (error) { + //Abbruch? + break; + } + } + } + } else { + error = true; + if (dialog.getErrorInfo().getErrorSegment() != null) { + replyElements.add(dialog.getErrorInfo().getErrorSegment()); + } + } + //erstelle HIRMG anhand der bisherigen Rückmeldungen + HIRMG hirmg = buildHIRMG(successful, warning, error); + replyElements.add(0, hirmg); + + + return new Message(msg.getTaskId(), replyElements, msg.getMessageId()); + } + + @Override + public void setDialog(Dialog dialog) { + this.dialog = dialog; + } + + @Override + public void run() { + Message message = this.execute(); + + //HIRMS Segmente müssen zu Beginn der Nachricht stehen (jedoch nach HIRMG) + List replySegments = message.getMessageElements(); + Collections.sort(replySegments, new SegmentComparator()); + + boolean containsHiisa = false; + for (IMessageElement messageElement : replySegments) { + if (messageElement instanceof HIISA) { + containsHiisa = true; + break; + } + } + + if (!containsHiisa && dialog.getLegitimationsInfo().isAnonymousAccount() && dialog.getLegitimationsInfo().getSecurityMethod() != SecurityMethod.PIN_1 && dialog.getLegitimationsInfo().getSecurityMethod() != SecurityMethod.PIN_2) { + //keine Bankschlüssel und Anonymer Dialog + //es wird keine Signatur benötigt + setSegmentNumbers(replySegments, 2); + + message.setSegmentCount(replySegments.size() + 1); + FinTS3Controller.getInstance().finishedExecuting(message); + return; + } + + LegitimationInfo legitimationInfo = dialog.getLegitimationsInfo(); + if (legitimationInfo.getCustomerId() != null && legitimationInfo.isAnonymousAccount() && (legitimationInfo.getSecurityMethod() == SecurityMethod.PIN_1 || legitimationInfo.getSecurityMethod() == SecurityMethod.PIN_2) || (dialog.getErrorInfo() != null && dialog.getErrorInfo().isErrorOccured())) { + //es wird keine Signatur benötigt, alle Segmente beginnen bei Nummer 2 (anonym PIN/TAN) + setSegmentNumbers(replySegments, 2); + } else { + //es wird eine Signatur benötigt, alle Segmente beginnen bei Nummer 3 + setSegmentNumbers(replySegments, 3); + createSignature(replySegments); + } + message.setSegmentCount(replySegments.size() + 1); + FinTS3Controller.getInstance().finishedExecuting(message); + } + + private void setSegmentNumbers(List replySegments, int startNumber) { + for (IMessageElement iMessageElement : replySegments) { + Field[] fields = iMessageElement.getClass().getDeclaredFields(); + Field segmentkopfField = null; + for (Field f : fields) { + f.setAccessible(true); + if (f.getType().equals(Segmentkopf.class)) { + segmentkopfField = f; + } + } + if (segmentkopfField == null) { + throw new RuntimeException("could not set Segmentnumber."); + } else { + try { + Segmentkopf segmentkopf = (Segmentkopf) segmentkopfField.get(iMessageElement); + + segmentkopf.setSegmentNummer(startNumber++); + } catch (IllegalAccessException e) { + //setAccessible(true) + } + } + } + } + + private HIRMG buildHIRMG(boolean successful, boolean warning, boolean error) { + HIRMG hirmg = new HIRMG(new byte[0]); + Segmentkopf segmentkopf;// = new Segmentkopf("HIRMG:0:2".getBytes()); + segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMG.class).setSegmentVersion(2).build(); + hirmg.setSegmentkopf(segmentkopf); + try { + Rueckmeldung rueckmeldung; + if (successful && warning) { + //3000er HIRMG + rueckmeldung = new Rueckmeldung("3060::Teilweise liegen Warnungen oder Hinweise vor.".getBytes("ISO-8859-1")); + } else if (successful && error) { + //9000er HIRMG + rueckmeldung = new Rueckmeldung("9910::Die Nachricht konnte nicht ausgeführt werden.".getBytes("ISO-8859-1")); + } else if (!successful && !warning && error) { + //9000er HIRMG + rueckmeldung = new Rueckmeldung("9910::Die Nachricht konnte nicht ausgeführt werden.".getBytes("ISO-8859-1")); + } else if (!successful && (warning | error)) { + //3000er HIRMG + rueckmeldung = new Rueckmeldung("3920::Es liegen Warnungen oder Fehler vor.".getBytes("ISO-8859-1")); + } else { + //0000er HIRMG + rueckmeldung = new Rueckmeldung("0010::Auftrag erfolgreich entgegengenommen.".getBytes("ISO-8859-1")); + } + hirmg.setRueckmeldung(rueckmeldung); + } catch (UnsupportedEncodingException e) { + //ISO-8859-1 is supported + } + return hirmg; + } + + //TODO generieren von Segmente besser anpassen + private void createSignature(List messageElements) { + Segmentkopf segmentkopf; + /* + HIRMG hirmg = new HIRMG(new byte[0]); + segmentkopf = new Segmentkopf("HIRMG:3:2".getBytes()); + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode("0010"); + rueckmeldung.setRueckmeldungstext("Nachricht fehlerfrei entgegengenommen."); + hirmg.setSegmentkopf(segmentkopf); + hirmg.setRueckmeldung(rueckmeldung); + messageElements.add(0, hirmg); + */ + SecurityMethod securityMethod = dialog.getLegitimationsInfo().getSecurityMethod(); + LOG.debug(dialog.getLegitimationsInfo().getSecurityMethod()); + if (securityMethod == null) { + securityMethod = SecurityMethod.RDH_10; + } + SignatureHelper signatureHelper = SignatureHelperFactory.getSignatureHelper(securityMethod); + dialog.setSicherheitsKontrollReferenz(String.valueOf((int) Math.abs(Math.random() * Integer.MAX_VALUE))); + String sicherheitskontrollreferenz = dialog.getSicherheitsKontrollReferenz(); + HNSHK hnshk = new HNSHK(new byte[0]); + //segmentkopf = new Segmentkopf("HNSHK:2:4".getBytes()); + segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(HNSHK.class).setSegmentNumber(2).setSegmentVersion(4).build(); + hnshk.setSegmentkopf(segmentkopf); + Sicherheitsprofil sicherheitsprofil = new Sicherheitsprofil(securityMethod.getHbciDEG()); + hnshk.setSicherheitsprofil(sicherheitsprofil); + hnshk.setSicherheitsfunktion(dialog.getLegitimationsInfo().getSicherheitsfunktion()); + hnshk.setSicherheitskontrollreferenz(sicherheitskontrollreferenz); + hnshk.setSicherheitsapplikationskontrollbereich("1"); + hnshk.setSicherheitslieferantenrolle("1"); + Sicherheitsidentifikationsdetails sicherheitsidentifikationsdetails = new Sicherheitsidentifikationsdetails("2".getBytes()); + hnshk.setSicherheitsidentifikationsdetails(sicherheitsidentifikationsdetails); + hnshk.setSicherheitsreferenznummer(1000); + GregorianCalendar gregorianCalendar = (GregorianCalendar) GregorianCalendar.getInstance(); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd:HHmmss"); + SicherheitsdatumUndUhrzeit sicherheitsdatumUndUhrzeit = new SicherheitsdatumUndUhrzeit(("1:" + simpleDateFormat.format(gregorianCalendar.getTime())).getBytes()); + hnshk.setSicherheitsdatumUndUhrzeit(sicherheitsdatumUndUhrzeit); + + if (securityMethod == SecurityMethod.PIN_1 || securityMethod == SecurityMethod.PIN_2) { + hnshk.setHashalgorithmus(dialog.getLegitimationsInfo().getHashalgorithmus()); + Schluesselname schluesselname = new Schluesselname(("280:" + dialog.getBankId() + ":100:S:0:0").getBytes()); //Default Schlüsselname für PIN/TAN Kommunikation + hnshk.setSchluesselname(schluesselname); + hnshk.setSignaturalgorithmus(new Signaturalgorithmus("6:10:16".getBytes())); + } else { + hnshk.setHashalgorithmus(dialog.getLegitimationsInfo().getHashalgorithmus()); + Schluesselname schluesselname = new Schluesselname(("280:" + dialog.getBankId() + ":0:S:1:1").getBytes()); //TODO Schlüsselname aus der facade + hnshk.setSchluesselname(schluesselname); + hnshk.setSignaturalgorithmus(new Signaturalgorithmus("6:10:19".getBytes())); + } + + int segmentcount = 3; + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try { + byteArrayOutputStream.write(hnshk.getHbciEncoded()); + byteArrayOutputStream.write('\''); + for (IMessageElement iMessageElement : messageElements) { + if (iMessageElement != null) { + segmentcount++; + byteArrayOutputStream.write(((Segment) iMessageElement).getHbciEncoded()); + byteArrayOutputStream.write('\''); + } + } + byteArrayOutputStream.flush(); + } catch (IOException ex) { + + } + + byte[] toSign = byteArrayOutputStream.toByteArray(); + + try { + LOG.debug("toSign: {}", new String(toSign, "ISO-8859-1")); + } catch (UnsupportedEncodingException e) { + LOG.error("UnsupportedEncodingException", e); + } + + HNSHA hnsha = new HNSHA(new byte[0]); + //segmentkopf = new Segmentkopf(("HNSHA:" + (segmentcount) + ":2").getBytes()); + segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(HNSHA.class).setSegmentNumber(segmentcount).setSegmentVersion(2).build(); + hnsha.setSegmentkopf(segmentkopf); + hnsha.setSicherheitskontrollreferenz(sicherheitskontrollreferenz); + byte[] signature = signatureHelper.sign(toSign, hnshk); + if (signature != null) { + byteArrayOutputStream.reset(); + try { + byteArrayOutputStream.write('@'); + for (char s : Integer.toString(signature.length).toCharArray()) { + byteArrayOutputStream.write(s); + } + byteArrayOutputStream.write('@'); + byteArrayOutputStream.write(signature); + byteArrayOutputStream.flush(); + } catch (IOException ex) { + } + hnsha.setValidierungsresultat(byteArrayOutputStream.toByteArray()); + } + messageElements.add(0, hnshk); + messageElements.add(hnsha); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Injector.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Injector.java new file mode 100644 index 0000000..035aa04 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Injector.java @@ -0,0 +1,23 @@ +package net.petafuel.fuelifints.protocol.fints3; + +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.protocol.IFinTSInjector; + +/** + * FFTS-27: Kann raus! + */ +@Deprecated +public class FinTS3Injector implements IFinTSInjector { + + private Dialog dialog; + + @Override + public void run() { + FinTS3Controller.getInstance().finishedInjecting(dialog.getTaskId()); + } + + @Override + public void setDialog(Dialog dialog) { + this.dialog = dialog; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Parser.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Parser.java new file mode 100644 index 0000000..77b2d35 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3Parser.java @@ -0,0 +1,313 @@ +package net.petafuel.fuelifints.protocol.fints3; + +import net.petafuel.fuelifints.HBCIParseException; +import net.petafuel.fuelifints.cryptography.SignatureHelper; +import net.petafuel.fuelifints.cryptography.SignatureHelperFactory; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.exceptions.DeprecatedVersionHKTAN; +import net.petafuel.fuelifints.exceptions.ElementParseException; +import net.petafuel.fuelifints.exceptions.HBCIValidationException; +import net.petafuel.fuelifints.exceptions.UnregisteredClientProduct; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.client.ClientProductInfo; +import net.petafuel.fuelifints.model.client.LegitimationInfo; +import net.petafuel.fuelifints.protocol.FinTSPayload; +import net.petafuel.fuelifints.protocol.IFinTSParser; +import net.petafuel.fuelifints.protocol.SegmentNotSupportedException; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.HIRMS; +import net.petafuel.fuelifints.protocol.fints3.segments.HKIDN; +import net.petafuel.fuelifints.protocol.fints3.segments.HKTAN; +import net.petafuel.fuelifints.protocol.fints3.segments.HKVVB; +import net.petafuel.fuelifints.protocol.fints3.segments.HNSHA; +import net.petafuel.fuelifints.protocol.fints3.segments.HNSHK; +import net.petafuel.fuelifints.protocol.fints3.segments.SegmentManager; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * - parsed die Auftragelemente (IMessageElement), die sich im Request befinden + *

+ *

+ * Entschlüsselung und Verifizierung der Signatur erfolt nicht hier, sondern im IFinTSDecryptor + * Es kann davon ausgegangen werden, dass die Nachricht fehlerfrei entschlüsselt und die Signatur verifiziert wurde + */ + +public class FinTS3Parser implements IFinTSParser, Runnable { + + private static final Logger LOG = LogManager.getLogger(FinTS3Parser.class); + private Dialog dialog; + private byte[] request; + private ArrayList elements; + private int taskId; + private String dialogId = ""; + + public int getTaskId() { + return this.taskId; + } + + @Override + public void setPayload(FinTSPayload payload) { + this.request = payload.getPayload(); + this.taskId = payload.getTaskId(); + } + + @Override + public void parseAndValidateRequest(int taskId, byte[] request) throws HBCIParseException, SegmentNotSupportedException, HBCIValidationException, DeprecatedVersionHKTAN, UnregisteredClientProduct { + + //split request in segment objects: + this.elements = SegmentManager.getSegments(request); + HNSHK hnshk = null; + HNSHA hnsha = null; + List signedSegments = new LinkedList(); + + boolean containsHKTAN = false; + boolean containsHKIDN = false; + boolean isClientMissingInformation = false; + + for (IMessageElement messageElement : elements) { + if (messageElement instanceof HKVVB) { + boolean isValidClientProductInfo = false; + try { + ((HKVVB) messageElement).parseElement(); + } catch (Exception e) { + LOG.error("Parsen von HKVVB für die Produktbezeichnung ist fehlgeschlagen."); + } + + String produktbezeichnung = ((HKVVB) messageElement).getProduktbezeichnung(); + + // Prüfung ob die Produktbezeichung registriert ist + ClientProductInfo clientProductInfo = new ClientProductInfo(); + clientProductInfo.setClientProductName(produktbezeichnung); + + isValidClientProductInfo = clientProductInfo.validateClientProductInfo(); + + LOG.info("Anfragendes Clientproduct {} ist gültig: {}", produktbezeichnung, isValidClientProductInfo); + + // Falls die Prüfung nicht deaktiviert wurde und die Überprüfung fehlgeschlagen ist + if (System.getProperty("productinfo.csv.check").equals("true") && ! isValidClientProductInfo) { + throw new UnregisteredClientProduct(); + } + } + + if (messageElement instanceof HNSHK && ((HNSHK) messageElement).getSicherheitsfunktion().equals("999")) { + isClientMissingInformation = true; + } + + if (messageElement instanceof HKIDN) { + containsHKIDN = true; + HKIDN hkidn = ((HKIDN) messageElement); + dialog.setBankId(hkidn.getBankId()); + } + + try { + if(messageElement instanceof HKTAN) { + containsHKTAN = true; + if (((HKTAN) messageElement).getVersion() < 6) { + throw new DeprecatedVersionHKTAN(); + } + } + } catch (ElementParseException e) { + throw new HBCIParseException(e); + } + + if (messageElement instanceof HNSHK) { + hnshk = (HNSHK) messageElement; + dialog.setBankId(hnshk.getSchluesselname().getKreditsinstitutkennung().getKreditinstitutscode()); + } + if (messageElement instanceof HNSHA) { + hnsha = (HNSHA) messageElement; + break; + } + //die Segmente zwischen HNSHK und HNVSK werden nur zur Signaturprüfung herangezogen + //deshalb ist es an dieser Stelle möglich nachdem HNSHK gefunden wurde, alle Segmente + //die anschließend folgen bis hin zu HNSHA in die Signaturprüfung mit aufzunehmen + if (hnshk != null) { + signedSegments.add(messageElement.getBytes()); + } + } + + LOG.info("Anonyme Anfrage: {}", dialog.getLegitimationsInfo().isAnonymousAccount()); + LOG.info("HKIDN vorhanden: {}", containsHKIDN); + LOG.info("HKTAN vorhanden: {}", containsHKTAN); + + if (isClientMissingInformation) + dialog.getLegitimationsInfo().setUserId(LegitimationInfo.CUSTOMER_ANONYM); + + boolean isPin = dialog + .getLegitimationsInfo() + .getSecurityMethod() + .isPIN(); + + if (containsHKIDN + && !containsHKTAN + && !dialog.getLegitimationsInfo().isAnonymousAccount() + && !isClientMissingInformation + && isPin + && !dialog.getLegitimationsInfo().isStrongAuthenticated() + && !dialog.getLegitimationsInfo().getSicherheitsfunktion().equals("999") + ) { + LOG.error("Deprecated version or missing HKTAN"); + throw new DeprecatedVersionHKTAN(); + } + + if (hnshk != null && hnsha == null) { + throw new HBCIValidationException("Signaturabschluss fehlt."); + } + + //bei anonymend dialog wird nicht signiert, deshalb wird hier geprüft + //ob die Signatur verifiziert werden muss + if (hnshk != null) { + ReturnDataObject validSignature = new ReturnDataObject(false, ""); + try { + validSignature = validateSignature(hnshk, hnsha, signedSegments); + } catch (UnsupportedEncodingException e) { + //ignored ISO-8859-1 is supported + } catch (ElementParseException e) { + LOG.error(e.getMessage()); + } + + if (!validSignature.isSuccess()) { + switch (SecurityMethod.valueOf(hnshk.getSicherheitsprofil().getSicherheitsverfahren() + "_" + hnshk.getSicherheitsprofil().getSicherheitsverfahrensversion())) { + case PIN_1: + case PIN_2: + + HIRMS hirms = new HIRMS(new byte[0]); + hirms.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(hnsha.getSegmentkopf().getSegmentNummer()).build()); + HBCIValidationException exception; + if (validSignature.getMessage().equals("")) { + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("9910"); + hirms.addRueckmeldung(rueckmeldung); + + exception = new HBCIValidationException("PIN/TAN ungültig."); + } else { + Rueckmeldung rueckmeldung = new Rueckmeldung(("9910::" + validSignature.getMessage()).getBytes()); + hirms.addRueckmeldung(rueckmeldung); + exception = new HBCIValidationException(validSignature.getMessage()); + } + exception.setRueckmeldung(hirms); + throw exception; + case RDH_9: + case RDH_10: + throw new HBCIValidationException("Verarbeitung nicht möglich.", false); + default: + throw new HBCIValidationException("Kein unterstützes Sicherheitsverfahren verwendet. Signatur ungültig."); + } + } + if (validSignature.getReturnCode() != null) { + HIRMS hirms = new HIRMS(new byte[0]); + hirms.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(hnsha.getSegmentkopf().getSegmentNummer()).build()); + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung(validSignature.getReturnCode()); + hirms.addRueckmeldung(rueckmeldung); + dialog.getCurrentMessage().addReplyElement(hirms); + } + } + LOG.info("Signaturprüfung erfolgreich."); + + dialog.getCurrentMessage().getMessageElements().addAll(elements); + + if (hnshk != null) { + dialog.getCurrentMessage().setSecurityMethod(SecurityMethod.valueOf(hnshk.getSicherheitsprofil().getSicherheitsverfahren() + "_" + hnshk.getSicherheitsprofil().getSicherheitsverfahrensversion())); + dialog.getCurrentMessage().setUserId(hnshk.getSchluesselname().getBenutzerkennung()); + dialog.getCurrentMessage().setBankId(hnshk.getSchluesselname().getKreditsinstitutkennung().getKreditinstitutscode()); + } + } + + /** + * Prüft die Signatur-Segmente einer Nachricht. + * + * @param hnshk Signaturkopf + * @param hnsha Signaturabschluss + * @param segments signierte Segmente + * @return true falls Signatur übereinstimmt, andernfalls false. + */ + private ReturnDataObject validateSignature(HNSHK hnshk, HNSHA hnsha, List segments) throws UnsupportedEncodingException, ElementParseException, HBCIValidationException { + SignatureHelper signatureHelper = SignatureHelperFactory.getSignatureHelper(SecurityMethod.valueOf(hnshk.getSicherheitsprofil().getSicherheitsverfahren() + "_" + hnshk.getSicherheitsprofil().getSicherheitsverfahrensversion())); + int sigLength = 0; + for (byte[] segment : segments) { + sigLength += segment.length; + } + byte[] signedData = new byte[sigLength + segments.size()]; + int offset = 0; + for (byte[] segment : segments) { + System.arraycopy(segment, 0, signedData, offset, segment.length); + offset += segment.length; + signedData[offset++] = 0x27; + } + LOG.debug("Data to check Singature: {}", new String(signedData, "ISO-8859-1")); + return signatureHelper.validateSignature(signedData, hnshk, hnsha, dialog); + } + + public String getDialogId() { + return dialogId; + } + + @Override + public void run() { + try { + if (request != null) { + parseAndValidateRequest(taskId, request); + } + } catch (HBCIParseException | SegmentNotSupportedException e) { + HIRMS error = new HIRMS(new byte[0]); + error.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).build()); + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("9000"); + error.addRueckmeldung(rueckmeldung); + dialog.getErrorInfo().setErrorOccured(true); + dialog.getErrorInfo().setErrorSegment(error); + } catch (HBCIValidationException hbciValidationException) { + + if (hbciValidationException.isShowDetails()) { + HIRMS error; + if (hbciValidationException.getRueckmeldung() == null) { + error = new HIRMS(new byte[0]); + error.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setSegmentNumber(0).build()); + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("9999"); + error.addRueckmeldung(rueckmeldung); + rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungstext(hbciValidationException.getMessage()); + rueckmeldung.setRueckmeldungscode("9999"); + error.addRueckmeldung(rueckmeldung); + } else { + error = hbciValidationException.getRueckmeldung(); + } + dialog.getErrorInfo().setErrorSegment(error); + } + dialog.getErrorInfo().setErrorOccured(true); + } catch (DeprecatedVersionHKTAN e) { + HIRMS error; + error = new HIRMS(new byte[0]); + error.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setSegmentNumber(0).setBezugssegment(2).build()); + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("9075"); + error.addRueckmeldung(rueckmeldung); + dialog.getErrorInfo().setErrorOccured(true); + dialog.getErrorInfo().setErrorSegment(error); + } catch (UnregisteredClientProduct e) { + HIRMS error = new HIRMS(new byte[0]); + error.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setSegmentNumber(0).setBezugssegment(2).build()); + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("9078"); + error.addRueckmeldung(rueckmeldung); + dialog.getErrorInfo().setErrorOccured(true); + dialog.getErrorInfo().setErrorSegment(error); + } catch (Exception e) { + LOG.error("Unbekannte Exception: {}", e.getMessage()); + } + FinTS3Controller.getInstance().finishedParsing(taskId, dialog); + } + + public Dialog getDialog() { + return dialog; + } + + public void setDialog(Dialog dialog) { + this.dialog = dialog; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3ReplyThread.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3ReplyThread.java new file mode 100644 index 0000000..10f6d71 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/FinTS3ReplyThread.java @@ -0,0 +1,116 @@ +package net.petafuel.fuelifints.protocol.fints3; + +import net.petafuel.fuelifints.FinTSServer; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.IFinTSReplyThread; +import net.petafuel.fuelifints.protocol.fints3.segments.HNHBK; +import net.petafuel.fuelifints.protocol.fints3.segments.HNHBS; +import net.petafuel.fuelifints.protocol.fints3.segments.Segment; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Bezugsnachricht; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.util.encoders.Base64; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public class FinTS3ReplyThread implements IFinTSReplyThread { + + private static final Logger LOG = LogManager.getLogger(FinTS3ReplyThread.class); + + private Dialog dialog; + private FinTSServer finTSServer; + + @Override + public void run() { + LOG.trace("Respond element count: {}", dialog.getCurrentMessage().getMessageElements().size()); + + HNHBK hnhbk = new HNHBK(new byte[0]); + HNHBS hnhbs = new HNHBS(new byte[0]); + Segmentkopf segmentkopf;// = new Segmentkopf("HNHBK:1:3".getBytes()); + segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(HNHBK.class).setSegmentNumber(1).setSegmentVersion(3).build(); + hnhbk.setSegmentkopf(segmentkopf); + + if (dialog.getMessageNumber() == 1 && dialog.getDialogId().isEmpty()) { + String dialogId = generateDialogId(); + dialog.setDialogId(dialogId); + } + hnhbk.setDialog_id(dialog.getDialogId()); + hnhbk.setHbci_version(300); + hnhbk.setNachrichtennummer(dialog.getMessageNumber()); + hnhbk.setNachrichtengroesse("012345678901"); + hnhbk.setBezugsnachricht(new Bezugsnachricht((dialog.getDialogId() + ":" + dialog.getMessageNumber()).getBytes())); + + //segmentkopf = new Segmentkopf(("HNHBS:"+(dialog.getCurrentMessage().getSegmentCount()+1)+":1").getBytes()); + segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(HNHBS.class).setSegmentNumber(dialog.getCurrentMessage().getSegmentCount() + 1).setSegmentVersion(1).build(); + hnhbs.setSegmentkopf(segmentkopf); + hnhbs.setNachrichtennummer(dialog.getMessageNumber()); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + + try { + byteArrayOutputStream.write(hnhbk.getHbciEncoded()); + byteArrayOutputStream.write('\''); + } catch (IOException e) { + LOG.error("IOException", e); + } + for (IMessageElement iMessageElement : dialog.getCurrentMessage().getMessageElements()) { + if (iMessageElement != null) { + try { + byteArrayOutputStream.write(((Segment) iMessageElement).getHbciEncoded()); + byteArrayOutputStream.write('\''); + byteArrayOutputStream.flush(); + } catch (IOException e) { + LOG.error("IOException", e); + } + } else { + LOG.error("iMessageElement is null"); + } + } + try { + byteArrayOutputStream.write(hnhbs.getHbciEncoded()); + byteArrayOutputStream.write('\''); + byteArrayOutputStream.flush(); + } catch (IOException e) { + LOG.error("IOException", e); + } + byte[] respondBytes = byteArrayOutputStream.toByteArray(); + int length = respondBytes.length; + byte[] lengthBytes = String.format("%012d", length).getBytes(); + System.arraycopy(lengthBytes, 0, respondBytes, 10, 12); + LOG.trace("response length: {}", length); + //try { + LOG.debug("Response: {}", new String(respondBytes)); + //} catch (UnsupportedEncodingException e) { + //} + finTSServer.respond(dialog.getTaskId(), respondBytes, dialog.isLastMessage()); + } + + private String generateDialogId() { + try { + MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); + SecureRandom random = new SecureRandom(); + byte[] bytes = messageDigest.digest(new BigInteger(130, random).toString(32).getBytes()); + LOG.trace("Generated DialogId: {} length: {}", new String(Base64.encode(bytes)), (new String(Base64.encode(bytes))).length()); + return (new String(Base64.encode(bytes))).replace('+', '-'); + } catch (NoSuchAlgorithmException e) { + } + return "fallbackMessage#"; + } + + @Override + public void setDialog(Dialog dialog) { + this.dialog = dialog; + } + + @Override + public void setFinTSServer(FinTSServer finTSServer) { + this.finTSServer = finTSServer; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/annotations/ApplicantAccount.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/annotations/ApplicantAccount.java new file mode 100644 index 0000000..3896686 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/annotations/ApplicantAccount.java @@ -0,0 +1,16 @@ +package net.petafuel.fuelifints.protocol.fints3.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * ApplicantAccount sollte in IExecutableElements bei der Kontoverbindung des Auftraggebers gesetzt werden. + * Anhand dieser Annotation wird geprüft, ob die Ausführung des entsprechenden Segments für die entsprechende + * Kontoverbindung erlaubt ist. + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ApplicantAccount { +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/annotations/Dependency.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/annotations/Dependency.java new file mode 100644 index 0000000..da106de --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/annotations/Dependency.java @@ -0,0 +1,12 @@ +package net.petafuel.fuelifints.protocol.fints3.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Dependency { + Class dependency(); +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/annotations/Inject.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/annotations/Inject.java new file mode 100644 index 0000000..a617a60 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/annotations/Inject.java @@ -0,0 +1,4 @@ +package net.petafuel.fuelifints.protocol.fints3.annotations; + +public @interface Inject { +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/annotations/Requires.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/annotations/Requires.java new file mode 100644 index 0000000..865ea52 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/annotations/Requires.java @@ -0,0 +1,19 @@ +package net.petafuel.fuelifints.protocol.fints3.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Requires { + enum Requirement { + EXECUTION_ALLOWED, + KUNDENSYSTEM_ID, + USER_IDENTIFIED, + TAN + } + + Requirement[] value(); +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/model/SecurityMethod.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/model/SecurityMethod.java new file mode 100644 index 0000000..5e86970 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/model/SecurityMethod.java @@ -0,0 +1,69 @@ +package net.petafuel.fuelifints.protocol.fints3.model; + +public enum SecurityMethod { + + PIN_1(1, 10, 16, 2, 6, 13, 2), + PIN_2(2, 10, 16, 2, 6, 13, 2), + RDH_9(9, 10, 19, 6, 6, 13, 18), + RDH_10(10, 10, 19, 6, 6, 13, 2), + RAH_9(9, 10, 19, 6, 6, 14, 18), + RAH_10(10, 10, 19, 6, 6, 14, 2); + + private final int number; + + private int signaturAlgorithmus; + private int operationsModusSignatur; + private int verwendungSignaturAlgorithmus; + private int hashAlgorithmus; + private int verschluesselungsAlgorithmus; + private int operationsModusVerschluesselung; + + private SecurityMethod(int number, int signaturAlgorithmus, int operationsModusSignatur, int verwendungSignaturAlgorithmus, int hashAlgorithmus, int verschluesselungsAlgorithmus, int operationsModusVerschluesselung) { + this.number = number; + this.signaturAlgorithmus = signaturAlgorithmus; + this.operationsModusSignatur = operationsModusSignatur; + this.verwendungSignaturAlgorithmus = verwendungSignaturAlgorithmus; + this.hashAlgorithmus = hashAlgorithmus; + this.verschluesselungsAlgorithmus = verschluesselungsAlgorithmus; + this.operationsModusVerschluesselung = operationsModusVerschluesselung; + } + + public byte[] getHbciDEG() { + String asString = this.toString(); + return asString.replace('_', ':').getBytes(); + } + + + public int getVersionNumber() { + return number; + } + + public int getSignaturAlgorithmus() { + return signaturAlgorithmus; + } + + public int getOperationsModusSignatur() { + return operationsModusSignatur; + } + + public int getVerwendungSignaturAlgorithmus() { + return verwendungSignaturAlgorithmus; + } + + public int getHashAlgorithmus() { + return hashAlgorithmus; + } + + public int getVerschluesselungsAlgorithmus() { + return verschluesselungsAlgorithmus; + } + + public int getOperationsModusVerschluesselung() { + return operationsModusVerschluesselung; + } + + public boolean isPIN() { + return this.number == PIN_1.number || this.number == PIN_2.number; + } + +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/AbstractElement.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/AbstractElement.java new file mode 100644 index 0000000..8017f61 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/AbstractElement.java @@ -0,0 +1,202 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.DatenElementGruppe; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; +import net.petafuel.fuelifints.support.FieldComparator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public abstract class AbstractElement implements IMessageElement { + + + private static final Logger LOG = LogManager.getLogger(AbstractElement.class); + + private boolean stopSkippingUnusedElements = false; + + public void setStopSkippingUnusedElements() { + stopSkippingUnusedElements = true; + } + + public static String generateEscapedVersion(String s) { + String escapedString = ""; + if (s != null) { + for (char c : s.toCharArray()) { + switch (c) { + case ':': + case '+': + case '@': + case '?': + case '\'': + escapedString += '?'; + } + escapedString += c; + } + } + return escapedString; + } + + public static ElementDescription getMatchingElementDescription(Field f, int segmentVersion) { + Element element; + if ((element = f.getAnnotation(Element.class)) != null) { + for (ElementDescription description : element.description()) { + if (description.segmentVersion() == segmentVersion || + description.segmentVersion() == 0) + return description; + } + } + return null; + } + + public byte[] getHbciEncoded() { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + List degs = new LinkedList(); + + int segmentVersion = -1; + if (this instanceof Segment) { + Segmentkopf segmentkopf = ((Segment) this).getSegmentkopf(); + if (segmentkopf != null) { + segmentVersion = segmentkopf.getSegmentVersion(); + } + } + + Field[] declaredFields = getClass().getDeclaredFields(); + + for (Field f : declaredFields) { + f.setAccessible(true); + if (getMatchingElementDescription(f, segmentVersion) != null) + degs.add(f); + } + Collections.sort(degs, new FieldComparator(segmentVersion)); + try { + removeUnusedDE(degs, segmentVersion); + for (int i = 0; i < degs.size(); i++) { + Field f = degs.get(i); + if (f.getAnnotation(bin.class) != null) { + Object value = f.get(this); + if (value == null && getMatchingElementDescription(f, segmentVersion).status() == ElementDescription.StatusCode.M) { + throw new RuntimeException("Muss Feld nicht gesetzt: " + f.getName() + " typ: " + f.getType().getSimpleName()); + } + if (value != null) + byteArrayOutputStream.write((byte[]) f.get(this)); + } else if (f.getAnnotation(num.class) != null) { + Object value = f.get(this); + if (value == null && getMatchingElementDescription(f, segmentVersion).status() == ElementDescription.StatusCode.M) { + throw new RuntimeException("Muss Feld von Klasse " + this.getClass().getSimpleName() + " nicht gesetzt: " + f.getName() + " typ: " + f.getType().getSimpleName()); + } + if (value != null) { + byteArrayOutputStream.write(Integer.toString((Integer) value).getBytes("ISO-8859-1")); + } + } else if (AbstractElement.class.isAssignableFrom(f.getType())) { + AbstractElement abstractElement = (AbstractElement) f.get(this); + if (abstractElement == null) { + if (getMatchingElementDescription(f, segmentVersion).status() == ElementDescription.StatusCode.M) { + LOG.debug(f.getName()); + throw new RuntimeException("Muss Feld nicht gesetzt: " + f.getName() + " typ: " + f.getType().getSimpleName()); + } else if (this instanceof DatenElementGruppe) { + //falls noch weitere Felder folgen müssen für optionale DEGs alle felder "leer gesetzt werden" sprich es fehlen noch separator character + if (i < degs.size() - 1) { + //anzahl elemente - 1 für letzte automatisch gesetzte trennzeichen + int count = f.getType().getDeclaredFields().length - 1; + for (int j = 0; j < count; j++) { + byteArrayOutputStream.write(':'); + } + } + } + } else { + byteArrayOutputStream.write(abstractElement.getHbciEncoded()); + } + } else if (List.class.isAssignableFrom(f.getType())) { + List list = (List) f.get(this); + + /* Alle List-Typen die kein AbstractElement sind (DEGs/DEs die öfter vorkommen + * müssen einen Typ besitzen der sich als String ohne Fehler darstellen lässt. + */ + if (list != null) { + for (int j = 0; j < list.size(); j++) { + Object o = list.get(j); + if (AbstractElement.class.isAssignableFrom(o.getClass())) { + AbstractElement abstractElement = (AbstractElement) o; + byteArrayOutputStream.write(abstractElement.getHbciEncoded()); + } else { + Type type = f.getGenericType(); + ParameterizedType parameterizedType = (ParameterizedType) type; + type = parameterizedType.getActualTypeArguments()[0]; + if (type.equals(String.class)) { + byteArrayOutputStream.write(o.toString().getBytes("ISO-8859-1")); + } else if (type.equals(byte[].class)) { + byteArrayOutputStream.write((byte[]) o); + } + } + if (j < list.size() - 1) { + byteArrayOutputStream.write(getSeparatorCharacter()); + } + } + } + } else { + /* + * es sollten lediglich noch Strings übrig bleiben + */ + Object value = f.get(this); + if (value == null && getMatchingElementDescription(f, segmentVersion).status() == ElementDescription.StatusCode.M) { + throw new RuntimeException("Muss Feld nicht gesetzt.: " + f.getName()); + } + if (value != null) + byteArrayOutputStream.write(generateEscapedVersion(f.get(this).toString()).getBytes("ISO-8859-1")); + } + if (i < degs.size() - 1) { + byteArrayOutputStream.write(getSeparatorCharacter()); + } + } + byteArrayOutputStream.flush(); + } catch (IOException e) { + LOG.error("IOException", e); + } catch (IllegalAccessException e) { + LOG.error("IllegalAccessException", e); + } + + return byteArrayOutputStream.toByteArray(); + } + + protected abstract char getSeparatorCharacter(); + + private void removeUnusedDE(List degs, int segmentVersion) throws IllegalAccessException { + if (stopSkippingUnusedElements) { + return; + } + List toRemove = new LinkedList(); + for (int i = degs.size() - 1; i >= 0; i--) { + Field f = degs.get(i); + if (getMatchingElementDescription(f, segmentVersion).status() == ElementDescription.StatusCode.C || + getMatchingElementDescription(f, segmentVersion).status() == ElementDescription.StatusCode.O) { + if (f.get(this) == null || ((f.get(this) instanceof String) && f.get(this).equals(""))) { + toRemove.add(f); + } else { + /* + * falls es mehrere optionale Felder gibt und eines davon gesetzt ist müssen die anderen leer bleiben + * also abbrechen + */ + break; + } + } else { + /* + * falls ein pflichtfeld gefunden wird abbrechen + */ + break; + } + } + degs.removeAll(toRemove); + } + +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/DIKKU.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/DIKKU.java new file mode 100644 index 0000000..aec1a0f --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/DIKKU.java @@ -0,0 +1,125 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KreditkartenUmsatz; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Saldo; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +import java.util.List; + +/** + * Name: Kreditkartenumsätze rückmelden + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: DIKKU + * Bezugssegment: DKKKU + * Version: 1 + * Anzahl: 1 + */ +public class DIKKU extends Segment { + + @Element(description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element(description = {@ElementDescription(number = 2,length = -19)}) + @an + private String kreditkartennummer; + + @Element(description = {@ElementDescription(number = 3,status = ElementDescription.StatusCode.O)}) + @id + private String kreditkartenkontonummer; + + @Element(description = {@ElementDescription(number = 4,status = ElementDescription.StatusCode.O)}) + @id + private String kundennummer; + + @Element(description = {@ElementDescription(number = 5)}) + private Saldo aktuellerSaldo; + + @Element(description = {@ElementDescription(number = 6,status = ElementDescription.StatusCode.O)}) + @dat + private String datumLetzteAbrechnung; + + @Element(description = {@ElementDescription(number = 7,status = ElementDescription.StatusCode.O)}) + @dat + private String vorraussichtlichesAbrechnungsdatum; + + @Element(description = {@ElementDescription(number = 8,status = ElementDescription.StatusCode.O)}) + private List umsaetze; + + public DIKKU(byte[] message) { + super(message); + } + + public DIKKU(int segmentVersion, int bezugssegment) { + super(new byte[0]); + segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(DIKKU.class).setSegmentVersion(segmentVersion).setBezugssegment(bezugssegment).build(); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public String getKreditkartennummer() { + return kreditkartennummer; + } + + public void setKreditkartennummer(String kreditkartennummer) { + this.kreditkartennummer = kreditkartennummer; + } + + public String getKreditkartenkontonummer() { + return kreditkartenkontonummer; + } + + public void setKreditkartenkontonummer(String kreditkartenkontonummer) { + this.kreditkartenkontonummer = kreditkartenkontonummer; + } + + public String getKundennummer() { + return kundennummer; + } + + public void setKundennummer(String kundennummer) { + this.kundennummer = kundennummer; + } + + public Saldo getAktuellerSaldo() { + return aktuellerSaldo; + } + + public void setAktuellerSaldo(Saldo aktuellerSaldo) { + this.aktuellerSaldo = aktuellerSaldo; + } + + public String getDatumLetzteAbrechnung() { + return datumLetzteAbrechnung; + } + + public void setDatumLetzteAbrechnung(String datumLetzteAbrechnung) { + this.datumLetzteAbrechnung = datumLetzteAbrechnung; + } + + public String getVorraussichtlichesAbrechnungsdatum() { + return vorraussichtlichesAbrechnungsdatum; + } + + public void setVorraussichtlichesAbrechnungsdatum(String vorraussichtlichesAbrechnungsdatum) { + this.vorraussichtlichesAbrechnungsdatum = vorraussichtlichesAbrechnungsdatum; + } + + public List getUmsaetze() { + return umsaetze; + } + + public void setUmsaetze(List umsaetze) { + this.umsaetze = umsaetze; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/DKKKU.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/DKKKU.java new file mode 100644 index 0000000..0f0e132 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/DKKKU.java @@ -0,0 +1,256 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.CreditCardRevenueDataObject; +import net.petafuel.fuelifints.dataaccess.dataobjects.SaldoDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Betrag; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.BetragSollHabenKennung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungNational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KreditkartenUmsatz; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Saldo; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Transaktionsbeschreibung; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.txt; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; + +/** + * Name: Kreditkartenumsätze anfordern + * Typ: Segment + * Kennung: DKKKU + * Bezugssegment: - + * Version 1 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class DKKKU extends Segment implements IDependentElement, IExecutableElement { + + @Element(description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element(description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.C)}) + @ApplicantAccount + private KontoverbindungNational kontoverbindungAuftraggeber; + + @Element(description = {@ElementDescription(number = 3, length = -19)}) + @an + private String kreditkartennummer; + + @Element(description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C)}) + @id + private String kreditkartenkontonummer; + + @Element(description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.C)}) + @id + private String kundennummer; + + @Element(description = {@ElementDescription(number = 6, status = ElementDescription.StatusCode.O)}) + @dat + private String vonDatum; + + @Element(description = {@ElementDescription(number = 7, status = ElementDescription.StatusCode.O)}) + @dat + private String bisDatum; + + @Element(description = {@ElementDescription(number = 8, status = ElementDescription.StatusCode.C, length = -4)}) + @num + private Integer maximaleAnzahlEintraege; + + @Element(description = {@ElementDescription(number = 9, status = ElementDescription.StatusCode.C, length = -35)}) + @an + private String aufsetzpunkt; + + private HIRMS statusElement; + private List replyElements; + + public DKKKU(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungNational getKontoverbindungAuftraggeber() { + return kontoverbindungAuftraggeber; + } + + public void setKontoverbindungAuftraggeber(KontoverbindungNational kontoverbindungAuftraggeber) { + this.kontoverbindungAuftraggeber = kontoverbindungAuftraggeber; + } + + public String getKreditkartennummer() { + return kreditkartennummer; + } + + public void setKreditkartennummer(String kreditkartennummer) { + this.kreditkartennummer = kreditkartennummer; + } + + public String getKreditkartenkontonummer() { + return kreditkartenkontonummer; + } + + public void setKreditkartenkontonummer(String kreditkartenkontonummer) { + this.kreditkartenkontonummer = kreditkartenkontonummer; + } + + public String getKundennummer() { + return kundennummer; + } + + public void setKundennummer(String kundennummer) { + this.kundennummer = kundennummer; + } + + public String getVonDatum() { + return vonDatum; + } + + public void setVonDatum(String vonDatum) { + this.vonDatum = vonDatum; + } + + public String getBisDatum() { + return bisDatum; + } + + public void setBisDatum(String bisDatum) { + this.bisDatum = bisDatum; + } + + public Integer getMaximaleAnzahlEintraege() { + return maximaleAnzahlEintraege; + } + + public void setMaximaleAnzahlEintraege(Integer maximaleAnzahlEintraege) { + this.maximaleAnzahlEintraege = maximaleAnzahlEintraege; + } + + public String getAufsetzpunkt() { + return aufsetzpunkt; + } + + public void setAufsetzpunkt(String aufsetzpunkt) { + this.aufsetzpunkt = aufsetzpunkt; + } + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + + boolean operationAllowed = dataAccessFacade.operationAllowedForAccount(dialog.getLegitimationsInfo(), kreditkartennummer, getClass()); + + if (!operationAllowed) { + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("9380"); + statusElement.addRueckmeldung(rueckmeldung); + return StatusCode.ERROR; + } + + + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd"); + Date von = null; + Date bis = null; + try { + if (vonDatum != null && !vonDatum.isEmpty()) { + von = simpleDateFormat.parse(vonDatum); + if (bisDatum != null && !bisDatum.isEmpty()) { + bis = simpleDateFormat.parse(bisDatum); + } + } + } catch (ParseException e) { + //ignored sollten bereits geprüft sein + } + + List revenues = dataAccessFacade.getCreditCardRevenueData(kreditkartennummer, von, bis, dialog.getLegitimationsInfo()); + SaldoDataObject saldoDataObject = dataAccessFacade.getSaldo(kreditkartennummer,dialog.getLegitimationsInfo()); + if(revenues == null || revenues.isEmpty()) { + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("3010"); + statusElement.addRueckmeldung(rueckmeldung); + return StatusCode.WARNING; + } + DIKKU dikku = new DIKKU(segmentkopf.getSegmentVersion(), segmentkopf.getSegmentNummer()); + + Betrag betrag = new Betrag(new byte[0]); + betrag.setWert(String.format(Locale.GERMAN, "%.2f", Math.abs(saldoDataObject.getGebuchterSaldo()))); + betrag.setWaehrung(saldoDataObject.getWaehrung()); + + Saldo saldo = new Saldo(new byte[0]); + saldo.setBetrag(betrag); + String sollHabenKennzeichen = "C"; + if(saldoDataObject.getGebuchterSaldo() < 0) { + sollHabenKennzeichen = "D"; + } + saldo.setSollHabenKennzeichen(sollHabenKennzeichen); + saldo.setDatum(simpleDateFormat.format(new Date())); + + dikku.setAktuellerSaldo(saldo); + if(kontoverbindungAuftraggeber != null) { + dikku.setKreditkartenkontonummer(kontoverbindungAuftraggeber.getKontonummer()); + } + dikku.setKundennummer(dialog.getLegitimationsInfo().getCustomerId()); + dikku.setKreditkartennummer(kreditkartennummer); + List umsaetze = new LinkedList<>(); + for(CreditCardRevenueDataObject creditCardRevenueDataObject : revenues) { + KreditkartenUmsatz kreditkartenUmsatz = new KreditkartenUmsatz(new byte[0]); + kreditkartenUmsatz.setGetaetigtVon(creditCardRevenueDataObject.getCreditCardNumber()); + kreditkartenUmsatz.setBelegDatum(simpleDateFormat.format(creditCardRevenueDataObject.getVoucherDate())); + kreditkartenUmsatz.setBuchungsDatum(simpleDateFormat.format(creditCardRevenueDataObject.getBookingDate())); + BetragSollHabenKennung buchungsbetrag = new BetragSollHabenKennung(new byte[0]); + buchungsbetrag.setWert(String.format(Locale.GERMAN, "%.2f", Math.abs(creditCardRevenueDataObject.getAmount()))); + buchungsbetrag.setWaehrung(saldoDataObject.getWaehrung()); + buchungsbetrag.setSollHabenKennung("C"); + if(creditCardRevenueDataObject.getAmount() < 0) { + buchungsbetrag.setSollHabenKennung("D"); + } + kreditkartenUmsatz.setBuchungsBetrag(buchungsbetrag); + Transaktionsbeschreibung transaktionsbeschreibung = new Transaktionsbeschreibung(new byte[0]); + transaktionsbeschreibung.setTransaktionsbeschreibungsgrundtext(creditCardRevenueDataObject.getPurpose()); + kreditkartenUmsatz.setTransaktionsbeschreibung(transaktionsbeschreibung); + kreditkartenUmsatz.setUmsatzAbgerechnet(creditCardRevenueDataObject.isBilled() ? "J" : "N"); + umsaetze.add(kreditkartenUmsatz); + } + + dikku.setUmsaetze(umsaetze); + replyElements = new LinkedList<>(); + replyElements.add(dikku); + statusElement.addRueckmeldung(Rueckmeldung.getRueckmeldung("0020")); + return StatusCode.OK; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/Element.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/Element.java new file mode 100644 index 0000000..c997604 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/Element.java @@ -0,0 +1,11 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import java.lang.annotation.*; + +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Element { + + ElementDescription[] description(); +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/ElementDescription.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/ElementDescription.java new file mode 100644 index 0000000..678d539 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/ElementDescription.java @@ -0,0 +1,19 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import java.lang.annotation.*; + +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ElementDescription { + + enum StatusCode {M, O, C} + + int number(); + + StatusCode status() default StatusCode.M; + + int length() default 0; + + int segmentVersion() default 0; +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICAZ.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICAZ.java new file mode 100644 index 0000000..55a1e05 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICAZ.java @@ -0,0 +1,98 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.GebuchteCamtUmsaetze; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; + +import java.util.List; + +/** + * Name: Kontoumsätze rückmelden/Zeitraum camt + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HICAZ + * Bezugssegment: HKCAZ + * Version: 1 + * Anzahl: n + * Sender: Kreditinstitut + */ +public class HICAZ extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element(description = {@ElementDescription(number = 2)}) + private KontoverbindungInternational kontoverbindungInternational; + + @Element(description = @ElementDescription(number = 3, length = -256)) + @an + private String camtDescriptor; + + @Element(description = @ElementDescription(number = 4)) + private GebuchteCamtUmsaetze gebuchteCamtUmsaetze; + + @Element(description = @ElementDescription(number = 5, status = ElementDescription.StatusCode.O)) + @bin + private byte[] nichtGebuchteCamtUmsaetze; + + + public HICAZ(byte[] message) { + super(message); + } + + public HICAZ(String camtDescriptor, List gebuchteUmsaetze, byte[] nichtGebuchteUmsaetze, Integer segmentNummer, Integer segmentVersion) { + super(new byte[0]); + segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(getClass()).setSegmentVersion(segmentVersion).setBezugssegment(segmentNummer).build(); + this.camtDescriptor = camtDescriptor; + if (gebuchteUmsaetze != null && gebuchteUmsaetze.size() > 0) { + gebuchteCamtUmsaetze = new GebuchteCamtUmsaetze(new byte[0]); + for (byte[] b : gebuchteUmsaetze) { + gebuchteCamtUmsaetze.addCamtUmsatz(SegmentUtil.wrapBinary(b)); + } + } + nichtGebuchteCamtUmsaetze = SegmentUtil.wrapBinary(nichtGebuchteUmsaetze); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public String getCamtDescriptor() { + return camtDescriptor; + } + + public void setCamtDescriptor(String camtDescriptor) { + this.camtDescriptor = camtDescriptor; + } + + public GebuchteCamtUmsaetze getGebuchteCamtUmsaetze() { + return gebuchteCamtUmsaetze; + } + + public void setGebuchteCamtUmsaetze(GebuchteCamtUmsaetze gebuchteCamtUmsaetze) { + this.gebuchteCamtUmsaetze = gebuchteCamtUmsaetze; + } + + public byte[] getNichtGebuchteCamtUmsaetze() { + return nichtGebuchteCamtUmsaetze; + } + + public void setNichtGebuchteCamtUmsaetze(byte[] nichtGebuchteCamtUmsaetze) { + this.nichtGebuchteCamtUmsaetze = nichtGebuchteCamtUmsaetze; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICMB.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICMB.java new file mode 100644 index 0000000..eab7d7d --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICMB.java @@ -0,0 +1,69 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Betrag; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +public class HICMB extends Segment{ + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + @Element( + description = {@ElementDescription(number = 2)}) + private String auftragsidentifikation; + @Element( + description = {@ElementDescription(number = 3)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + @Element( + description = {@ElementDescription(number = 4)}) + @dat + private String einreichungsdatum; + @Element( + description = {@ElementDescription(number = 5)}) + @dat + private String ausfuehrungsdatum; + @Element( + description = {@ElementDescription(number = 6)}) + @num + private int anzahlAuftraege; + @Element( + description = {@ElementDescription(number = 7)}) + private Betrag summeDerBetrage; + + public HICMB(byte[] message) { + super(message); + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public void setEinreichungsdatum(String einreichungsdatum) { + this.einreichungsdatum = einreichungsdatum; + } + + public void setAusfuehrungsdatum(String ausfuehrungsdatum) { + this.ausfuehrungsdatum = ausfuehrungsdatum; + } + + public void setAnzahlAuftraege(int anzahlAuftraege) { + this.anzahlAuftraege = anzahlAuftraege; + } + + public void setSummeDerBetrage(Betrag summeDerBetrage) { + this.summeDerBetrage = summeDerBetrage; + } + + public void setAuftragsidentifikation(String auftragsidentifikation) { + this.auftragsidentifikation = auftragsidentifikation; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICME.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICME.java new file mode 100644 index 0000000..19edfb3 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICME.java @@ -0,0 +1,30 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; + +public class HICME extends Segment { + @Element(description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + @Element(description = {@ElementDescription(number = 2,status = ElementDescription.StatusCode.O)}) + private String auftragsidentifikation; + + public HICME(byte[] message) { + super(message); + } + + public void setAuftragsidentifikation(String auftragsidentifikation) { + this.auftragsidentifikation = auftragsidentifikation; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public String getAuftragsidentifikation() { + return auftragsidentifikation; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICSB.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICSB.java new file mode 100644 index 0000000..dbcedb9 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICSB.java @@ -0,0 +1,97 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; + +public class HICSB extends Segment { + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + @Element( + description = {@ElementDescription(number = 3)}) + @an + private String sepaDescriptor; + + @Element( + description = {@ElementDescription(number = 4)}) + @bin + private byte[] sepaPainMessage; + + @Element( + description = {@ElementDescription(number = 5, length = -99, status = ElementDescription.StatusCode.C)}) + private String auftragsIdentifikation; + @Element( + description = {@ElementDescription(number = 6, status = ElementDescription.StatusCode.O)}) + private String loeschbar; + @Element( + description = {@ElementDescription(number = 7, status = ElementDescription.StatusCode.O)}) + private String aenderbar; + + public HICSB(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public String getSepaDescriptor() { + return sepaDescriptor; + } + + public void setSepaDescriptor(String sepaDescriptor) { + this.sepaDescriptor = sepaDescriptor; + } + + public byte[] getSepaPainMessage() { + return sepaPainMessage; + } + + public void setSepaPainMessage(byte[] sepaPainMessage) { + this.sepaPainMessage = sepaPainMessage; + } + + public String getAuftragsIdentifikation() { + return auftragsIdentifikation; + } + + public void setAuftragsIdentifikation(String auftragsIdentifikation) { + this.auftragsIdentifikation = auftragsIdentifikation; + } + + public String getLoeschbar() { + return loeschbar; + } + + public void setLoeschbar(String loeschbar) { + this.loeschbar = loeschbar; + } + + public String getAenderbar() { + return aenderbar; + } + + public void setAenderbar(String aenderbar) { + this.aenderbar = aenderbar; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICSE.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICSE.java new file mode 100644 index 0000000..11686a1 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICSE.java @@ -0,0 +1,29 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; + +public class HICSE extends Segment { + @Element(description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + @Element(description = {@ElementDescription(number = 2,status = ElementDescription.StatusCode.O)}) + private String auftragsidentifikation; + public HICSE(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public String getAuftragsidentifikation() { + return auftragsidentifikation; + } + + public void setAuftragsidentifikation(String auftragsidentifikation) { + this.auftragsidentifikation = auftragsidentifikation; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICUB.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICUB.java new file mode 100644 index 0000000..0b5f932 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HICUB.java @@ -0,0 +1,51 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.AngabenEmpfaengerkonten; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; + +import java.util.List; + +/** + * Name: Empfängerkontenbestand rückmelden + * Kennung: HICUB + * Version: 1 + * Sender: Kreditinstitut + */ +public class HICUB extends Segment { + + @Element(description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + @Element(description = {@ElementDescription(number = 2)}) + private KontoverbindungInternational kontoverbindungInternational; + @Element(description = {@ElementDescription(number = 3)}) + private List angabenEmpfaengerkonten; + + public HICUB(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public List getAngabenEmpfaengerkonten() { + return angabenEmpfaengerkonten; + } + + public void setAngabenEmpfaengerkonten(List angabenEmpfaengerkonten) { + this.angabenEmpfaengerkonten = angabenEmpfaengerkonten; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIISA.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIISA.java new file mode 100644 index 0000000..60189ad --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIISA.java @@ -0,0 +1,104 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.OeffentlicherSchluessel; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Schluesselname; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Zertifikat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Name: Übermittlung eines öffentlichen Schlüssels + * Typ: Segment + * Sender: Kreditinstitut + */ +public class HIISA extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, length = 1)}) + @code(restrictions = {"1"}) + private String nachrichtenbeziehungKodiert; + + @Element( + description = {@ElementDescription(number = 3, length = 0)}) + @id + private String austauschkontrollereferenz; + + @Element( + description = {@ElementDescription(number = 4, length = -4)}) + @num + private Integer nachrichtenreferenznummer; + + @Element( + description = {@ElementDescription(number = 5, length = -3)}) + @code(restrictions = {"224"}) + private String bezeichnerFunktionstyp; + + @Element( + description = {@ElementDescription(number = 6)}) + private Schluesselname schluesselname; + + @Element( + description = {@ElementDescription(number = 7)}) + private OeffentlicherSchluessel oeffentlicherSchluessel; + + @Element( + description = {@ElementDescription(number = 8, status = ElementDescription.StatusCode.O)}) + private Zertifikat zertifikat; + + public HIISA(byte[] message) { + super(message); + } + + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public void setNachrichtenbeziehungKodiert(String nachrichtenbeziehungKodiert) { + this.nachrichtenbeziehungKodiert = nachrichtenbeziehungKodiert; + } + + public void setAustauschkontrollereferenz(String austauschkontrollereferenz) { + this.austauschkontrollereferenz = austauschkontrollereferenz; + } + + public void setNachrichtenreferenznummer(Integer nachrichtenreferenznummer) { + this.nachrichtenreferenznummer = nachrichtenreferenznummer; + } + + public void setBezeichnerFunktionstyp(String bezeichnerFunktionstyp) { + this.bezeichnerFunktionstyp = bezeichnerFunktionstyp; + } + + public void setSchluesselname(Schluesselname schluesselname) { + this.schluesselname = schluesselname; + } + + public void setOeffentlicherSchluessel(OeffentlicherSchluessel oeffentlicherSchluessel) { + this.oeffentlicherSchluessel = oeffentlicherSchluessel; + } + + public void setZertifikat(Zertifikat zertifikat) { + this.zertifikat = zertifikat; + } + + @Override + public String toString() { + return "HIISA{" + + "segmentkopf=" + segmentkopf + + ", nachrichtenbeziehungKodiert='" + nachrichtenbeziehungKodiert + '\'' + + ", austauschkontrollereferenz='" + austauschkontrollereferenz + '\'' + + ", nachrichtenreferenznummer=" + nachrichtenreferenznummer + + ", bezeichnerFunktionstyp='" + bezeichnerFunktionstyp + '\'' + + ", schluesselname=" + schluesselname + + ", oeffentlicherSchluessel=" + oeffentlicherSchluessel + + ", zertifikat=" + zertifikat + + '}'; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIKAZ.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIKAZ.java new file mode 100644 index 0000000..8b8c476 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIKAZ.java @@ -0,0 +1,62 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; + +/** + * Name: Kontoumsätze rückmelden/Zeitraum + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HIKAZ + * Bezugssegment: HKKAZ + * Version: 6 + * Anzahl: n + * Sender: Kreditinstitut + */ +public class HIKAZ extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @bin + private byte[] gebuchteUmsaetze; + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.O)}) + @bin + private byte[] nichtGebuchteUmsaetze; + + public HIKAZ(byte[] gebuchteUmsaetze, byte[] nichtGebuchteUmsaetze, int bezugsSegment, int segmentversion) { + super(new byte[0]); + segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(this.getClass()).setSegmentVersion(segmentversion).setBezugssegment(bezugsSegment).build(); + this.gebuchteUmsaetze = SegmentUtil.wrapBinary(gebuchteUmsaetze); + this.nichtGebuchteUmsaetze = SegmentUtil.wrapBinary(nichtGebuchteUmsaetze); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public byte[] getNichtGebuchteUmsaetze() { + return nichtGebuchteUmsaetze; + } + + public void setNichtGebuchteUmsaetze(byte[] nichtGebuchteUmsaetze) { + this.nichtGebuchteUmsaetze = nichtGebuchteUmsaetze; + } + + public byte[] getGebuchteUmsaetze() { + return gebuchteUmsaetze; + } + + public void setGebuchteUmsaetze(byte[] gebuchteUmsaetze) { + this.gebuchteUmsaetze = gebuchteUmsaetze; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIKIM.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIKIM.java new file mode 100644 index 0000000..e0a8949 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIKIM.java @@ -0,0 +1,61 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.txt; + +/** + * Name: Kreditinstitutsmeldung + * Typ: Segment + * Segmentart: Administration + * Kennung: HIKIM + * Bezugssegment: - + * Sender: Kreditinstitut + */ +public class HIKIM extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, length = -35)}) + @an + private String betreff; + + @Element( + description = {@ElementDescription(number = 3, length = -2048)}) + @txt + private String freitextmeldung; + + public HIKIM(String betreff, String freitextmeldung) { + super(new byte[0]); + segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(getClass()).setSegmentVersion(2).build(); + this.betreff = betreff; + this.freitextmeldung = freitextmeldung; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public String getBetreff() { + return betreff; + } + + public void setBetreff(String betreff) { + this.betreff = betreff; + } + + public String getFreitextmeldung() { + return freitextmeldung; + } + + public void setFreitextmeldung(String freitextmeldung) { + this.freitextmeldung = freitextmeldung; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIKOM.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIKOM.java new file mode 100644 index 0000000..609f835 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIKOM.java @@ -0,0 +1,92 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.dataobjects.KommunikationsParameterData; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Kik; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KommunikationsParameter; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; + +import java.util.LinkedList; +import java.util.List; + +/** + * Name: Kommunikationszugang rückmelden + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HIKOM + * Bezugssegment: HKKOM + * Version: 4 + * Sender: Kreditinstitut + */ +public class HIKOM extends Segment { + + @Element(description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element(description = {@ElementDescription(number = 2)}) + private Kik kreditinstitutskennung; + + /** + * Standardsprache + * Es ist ein Sprachkennzeichen einzustellen, welches Standardsprache und + * -zeichensatz des Kreditinstituts festlegt (s. auch DE „Dialogsprache“). Die- + * ses Kennzeichen bestimmt, mit welchem Zeichensatz die Dialoginitialisie- + * rungsnachricht des Kunden gebildet werden muss. Nach dieser Nachricht + * verliert die Standardsprache ihre Gültigkeit, da der Kunde in der Dialoginitia + * lisierung die Dialogsprache wählt, welche evtl. von der Standardsprache ab- + * weicht. + * Codierung: + * 1: Deutsch, Code ‚de’ (German), Subset Deutsch, Codeset 1 (Latin 1) + * 2: Englisch, Code ‚en’ (English), Subset Englisch, Codeset 1 (Latin 1) + * 3: Französisch, Code ‚fr’ (French), Subset Französisch, Codeset 1 (Latin 1) + */ + @Element(description = {@ElementDescription(number = 3)}) + private Integer standardsprache; + + @Element(description = {@ElementDescription(number = 4)}) + private List kommunikationsParameter; + + public HIKOM(int segmentVersion, int bezugssegment, KommunikationsParameterData kommunikationsParameterData) { + super(new byte[0]); + segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(HIKOM.class).setSegmentVersion(segmentVersion).setBezugssegment(bezugssegment).build(); + standardsprache = 1; + kreditinstitutskennung = new Kik(new byte[0]); + kreditinstitutskennung.setKreditinstitutscode(kommunikationsParameterData.getBankId()); + kreditinstitutskennung.setLaenderkennzeichen("280"); + kommunikationsParameter = new LinkedList<>(); + for (String kommunikationszugangsAdresse : kommunikationsParameterData.getKommunikationszugangsAdressen()) { + kommunikationsParameter.add(new KommunikationsParameter(kommunikationszugangsAdresse)); + } + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public Kik getKreditinstitutskennung() { + return kreditinstitutskennung; + } + + public void setKreditinstitutskennung(Kik kreditinstitutskennung) { + this.kreditinstitutskennung = kreditinstitutskennung; + } + + public Integer getStandardsprache() { + return standardsprache; + } + + public void setStandardsprache(Integer standardsprache) { + this.standardsprache = standardsprache; + } + + public List getKommunikationsParameter() { + return kommunikationsParameter; + } + + public void setKommunikationsParameter(List kommunikationsParameter) { + this.kommunikationsParameter = kommunikationsParameter; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIRMG.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIRMG.java new file mode 100644 index 0000000..44abf58 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIRMG.java @@ -0,0 +1,40 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; + +/** + * Name: Rückmeldung zur Gesamtnachricht + * Typ: Segment + * Sender: Kreditinstitut + */ +public class HIRMG extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + private Rueckmeldung rueckmeldung; + + public HIRMG(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public Rueckmeldung getRueckmeldung() { + return rueckmeldung; + } + + public void setRueckmeldung(Rueckmeldung rueckmeldung) { + this.rueckmeldung = rueckmeldung; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIRMS.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIRMS.java new file mode 100644 index 0000000..4561a6b --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIRMS.java @@ -0,0 +1,51 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; + +import java.util.LinkedList; +import java.util.List; + +/** + * Name: Rückmeldungen zu Segmenten + * Typ: Segment + * Sender: Kreditinstitut + */ +public class HIRMS extends Segment { + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + private List rueckmeldung; + + public HIRMS(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public List getRueckmeldung() { + return rueckmeldung; + } + + public void setRueckmeldung(List rueckmeldung) { + this.rueckmeldung = rueckmeldung; + } + + public void addRueckmeldung(Rueckmeldung rueckmeldung) { + if (this.rueckmeldung == null) { + this.rueckmeldung = new LinkedList(); + } + if (rueckmeldung != null) { + this.rueckmeldung.add(rueckmeldung); + } + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HISAL.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HISAL.java new file mode 100644 index 0000000..c02fd98 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HISAL.java @@ -0,0 +1,179 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.*; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; + +/** + * Name: Saldenrückmeldung + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HISAL + * Bezugssegment: HKSAL + * Version: 6 + * Anzahl: n + * Sender: Kreditinstitut + */ +public class HISAL extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, segmentVersion = 6), + @ElementDescription(number = 2, segmentVersion = 5)}) + private KontoverbindungNational kontoverbindungAuftraggeber; + + @Element( + description = {@ElementDescription(number = 2, segmentVersion = 7)}) + private KontoverbindungInternational kontoverbindungInternational; + + @Element( + description = {@ElementDescription(number = 3)}) + private String kontoproduktBezeichnung; + + @Element( + description = {@ElementDescription(number = 4)}) + private String kontowaehrung; + + @Element( + description = {@ElementDescription(number = 5)}) + private Saldo gebuchterSaldo; + + @Element( + description = {@ElementDescription(number = 6, status = ElementDescription.StatusCode.O)}) + private Saldo saldoDerVorgemerktenUmsaetze; + + @Element( + description = {@ElementDescription(number = 7, status = ElementDescription.StatusCode.O)}) + private Betrag kreditlinie; + + @Element( + description = {@ElementDescription(number = 8, status = ElementDescription.StatusCode.O)}) + private Betrag verfuegbarerBetrag; + + @Element( + description = {@ElementDescription(number = 9, status = ElementDescription.StatusCode.O)}) + private Betrag bereitsVerfuegterBetrag; + + @Element( + description = {@ElementDescription(number = 10, status = ElementDescription.StatusCode.O)}) + private Betrag ueberziehung; + + @Element( + description = {@ElementDescription(number = 11, status = ElementDescription.StatusCode.O)}) + private Zeitstempel buchungszeitpunkt; + + @Element( + description = {@ElementDescription(number = 12, status = ElementDescription.StatusCode.C, length = 8)}) + @dat + private String faelligkeit; + + public HISAL(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungNational getKontoverbindungAuftraggeber() { + return kontoverbindungAuftraggeber; + } + + public void setKontoverbindungAuftraggeber(KontoverbindungNational kontoverbindungAuftraggeber) { + this.kontoverbindungAuftraggeber = kontoverbindungAuftraggeber; + } + + public String getKontoproduktBezeichnung() { + return kontoproduktBezeichnung; + } + + public void setKontoproduktBezeichnung(String kontoproduktBezeichnung) { + this.kontoproduktBezeichnung = kontoproduktBezeichnung; + } + + public String getKontowaehrung() { + return kontowaehrung; + } + + public void setKontowaehrung(String kontowaehrung) { + this.kontowaehrung = kontowaehrung; + } + + public Saldo getGebuchterSaldo() { + return gebuchterSaldo; + } + + public void setGebuchterSaldo(Saldo gebuchterSaldo) { + this.gebuchterSaldo = gebuchterSaldo; + } + + public Saldo getSaldoDerVorgemerktenUmsaetze() { + return saldoDerVorgemerktenUmsaetze; + } + + public void setSaldoDerVorgemerktenUmsaetze(Saldo saldoDerVorgemerktenUmsaetze) { + this.saldoDerVorgemerktenUmsaetze = saldoDerVorgemerktenUmsaetze; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public Betrag getKreditlinie() { + return kreditlinie; + } + + public void setKreditlinie(Betrag kreditlinie) { + this.kreditlinie = kreditlinie; + } + + public Betrag getVerfuegbarerBetrag() { + return verfuegbarerBetrag; + } + + public void setVerfuegbarerBetrag(Betrag verfuegbarerBetrag) { + this.verfuegbarerBetrag = verfuegbarerBetrag; + } + + public Betrag getBereitsVerfuegterBetrag() { + return bereitsVerfuegterBetrag; + } + + public void setBereitsVerfuegterBetrag(Betrag bereitsVerfuegterBetrag) { + this.bereitsVerfuegterBetrag = bereitsVerfuegterBetrag; + } + + public Betrag getUeberziehung() { + return ueberziehung; + } + + public void setUeberziehung(Betrag ueberziehung) { + this.ueberziehung = ueberziehung; + } + + public Zeitstempel getBuchungszeitpunkt() { + return buchungszeitpunkt; + } + + public void setBuchungszeitpunkt(Zeitstempel buchungszeitpunkt) { + this.buchungszeitpunkt = buchungszeitpunkt; + } + + public String getFaelligkeit() { + return faelligkeit; + } + + public void setFaelligkeit(String faelligkeit) { + this.faelligkeit = faelligkeit; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HISPA.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HISPA.java new file mode 100644 index 0000000..a120866 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HISPA.java @@ -0,0 +1,55 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungZvInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; + +import java.util.LinkedList; +import java.util.List; + +/** + * Name: SEPA-Kontoverbindung rückmelden + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HISPA + * Bezugssegment: HKSPA + * Version: 1 + * Anzahl: 1 + * Sender: Kreditinstitut + */ +public class HISPA extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.O)}) + private List sepaKontoverbindungen; + + public HISPA(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public List getSepaKontoverbindungen() { + return sepaKontoverbindungen; + } + + public void setSepaKontoverbindungen(List sepaKontoverbindungen) { + this.sepaKontoverbindungen = sepaKontoverbindungen; + } + + public void addSepaKontoverbindung(KontoverbindungZvInternational sepaKontoverbindung) { + if (this.sepaKontoverbindungen == null) { + this.sepaKontoverbindungen = new LinkedList(); + } + this.sepaKontoverbindungen.add(sepaKontoverbindung); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HISYN.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HISYN.java new file mode 100644 index 0000000..647d611 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HISYN.java @@ -0,0 +1,81 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Name: Synchronisierungsantwortnachricht + * Typ: Segment + * Sender: Kreditinstitut + */ +public class HISYN extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, length = -30)}) + @id + private String kundensystemId; + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.C, length = -4)}) + @num + private Integer nachrichtennummer; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C, length = -16)}) + @num + private Integer sicherheitsreferenznummerSignierschluessel; + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -16)}) + @num + private Integer sicherheitsreferenznummerDigitaleSignatur; + + public HISYN(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public String getKundensystemId() { + return kundensystemId; + } + + public void setKundensystemId(String kundensystemId) { + this.kundensystemId = kundensystemId; + } + + public Integer getNachrichtennummer() { + return nachrichtennummer; + } + + public void setNachrichtennummer(Integer nachrichtennummer) { + this.nachrichtennummer = nachrichtennummer; + } + + public Integer getSicherheitsreferenznummerSignierschluessel() { + return sicherheitsreferenznummerSignierschluessel; + } + + public void setSicherheitsreferenznummerSignierschluessel(Integer sicherheitsreferenznummerSignierschluessel) { + this.sicherheitsreferenznummerSignierschluessel = sicherheitsreferenznummerSignierschluessel; + } + + public Integer getSicherheitsreferenznummerDigitaleSignatur() { + return sicherheitsreferenznummerDigitaleSignatur; + } + + public void setSicherheitsreferenznummerDigitaleSignatur(Integer sicherheitsreferenznummerDigitaleSignatur) { + this.sicherheitsreferenznummerDigitaleSignatur = sicherheitsreferenznummerDigitaleSignatur; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HITAN.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HITAN.java new file mode 100644 index 0000000..20a234a --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HITAN.java @@ -0,0 +1,171 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.GueltigkeitsdatumUndUhrzeitChallenge; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; + +/** + * Name: Zwei-Schritt-TAN-Einreichung Rückmeldung + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HITAN + * Bezugssegment: HKTAN + * Segmentversion: 1 + * Anzahl: 1 + * Sender: Kreditinstitut + */ +public class HITAN extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, length = 1)}) + @code(restrictions = {"1", "2", "3", "4"}) + private String tanProzess; + + @Element( + description = {@ElementDescription(number = 3, length = -256)}) + @bin + private byte[] auftragsHashwert; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C, length = -35)}) + @an + private String auftragsreferenz; + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -256)}) + @an + private String challenge; + + + @Element( + description = { + @ElementDescription(number = 6, status = ElementDescription.StatusCode.C, segmentVersion = 4), + @ElementDescription(number = 6, status = ElementDescription.StatusCode.C, segmentVersion = 5), + @ElementDescription(number = 6, status = ElementDescription.StatusCode.C, segmentVersion = 6) + } + ) + @bin + private byte[] challengeHHD_UC; + + @Element( + description = { + @ElementDescription(number = 6, status = ElementDescription.StatusCode.O, segmentVersion = 1), + @ElementDescription(number = 6, status = ElementDescription.StatusCode.O, segmentVersion = 2), + @ElementDescription(number = 6, status = ElementDescription.StatusCode.O, segmentVersion = 3), + @ElementDescription(number = 7, status = ElementDescription.StatusCode.O, segmentVersion = 4), + @ElementDescription(number = 7, status = ElementDescription.StatusCode.O, segmentVersion = 5), + @ElementDescription(number = 7, status = ElementDescription.StatusCode.O, segmentVersion = 6) + } + ) + private GueltigkeitsdatumUndUhrzeitChallenge gueltigkeitsdatumUndUhrzeitChallenge; + + @Element( + description = { + @ElementDescription(number = 7, status = ElementDescription.StatusCode.C, length = -20, segmentVersion = 1), + @ElementDescription(number = 7, status = ElementDescription.StatusCode.C, length = -20, segmentVersion = 2), + @ElementDescription(number = 7, status = ElementDescription.StatusCode.C, length = -20, segmentVersion = 3), + @ElementDescription(number = 8, status = ElementDescription.StatusCode.C, length = -20, segmentVersion = 4), + @ElementDescription(number = 8, status = ElementDescription.StatusCode.C, length = -20, segmentVersion = 5) + } + ) + @an + private String tanListennummer; //Muss nur gesetzt werden, wenn das Institut mehr als eine aktive Tan Liste unterstützt + + @Element( + description = {@ElementDescription(number = 8, status = ElementDescription.StatusCode.C, length = -99, segmentVersion = 1)}) + @an + private String tanZusatzinformationen; + + @Element( + description = {@ElementDescription(number = 8, status = ElementDescription.StatusCode.C, length = -99, segmentVersion = 2), + @ElementDescription(number = 8, status = ElementDescription.StatusCode.C, length = -99, segmentVersion = 3), + @ElementDescription(number = 9, status = ElementDescription.StatusCode.C, length = -99, segmentVersion = 4), + @ElementDescription(number = 9, status = ElementDescription.StatusCode.C, length = -99, segmentVersion = 5)}) + @an + private String ben; //Wird nur bei Tan Prozess = 2 gesetzt + + @Element( + description = { + @ElementDescription(number = 9, status = ElementDescription.StatusCode.C, length = -32, segmentVersion = 3), + @ElementDescription(number = 10, status = ElementDescription.StatusCode.C, length = -32, segmentVersion = 4), + @ElementDescription(number = 10, status = ElementDescription.StatusCode.C, length = -32, segmentVersion = 5), + @ElementDescription(number = 8, status = ElementDescription.StatusCode.C, length = -32, segmentVersion = 6) + } + ) + @an + private String bezeichnungDesTanMediums; + + public HITAN(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public String getTanProzess() { + return tanProzess; + } + + public void setTanProzess(String tanProzess) { + this.tanProzess = tanProzess; + } + + public byte[] getAuftragsHashwert() { + return auftragsHashwert; + } + + public void setAuftragsHashwert(byte[] auftragsHashwert) { + this.auftragsHashwert = auftragsHashwert; + } + + public String getAuftragsreferenz() { + return auftragsreferenz; + } + + public void setAuftragsreferenz(String auftragsreferenz) { + this.auftragsreferenz = auftragsreferenz; + } + + public String getChallenge() { + return challenge; + } + + public void setChallenge(String challenge) { + this.challenge = challenge; + } + + public GueltigkeitsdatumUndUhrzeitChallenge getGueltigkeitsdatumUndUhrzeitChallenge() { + return gueltigkeitsdatumUndUhrzeitChallenge; + } + + public void setGueltigkeitsdatumUndUhrzeitChallenge(GueltigkeitsdatumUndUhrzeitChallenge gueltigkeitsdatumUndUhrzeitChallenge) { + this.gueltigkeitsdatumUndUhrzeitChallenge = gueltigkeitsdatumUndUhrzeitChallenge; + } + + public String getTanListennummer() { + return tanListennummer; + } + + public void setTanListennummer(String tanListennummer) { + this.tanListennummer = tanListennummer; + } + + public String getTanZusatzinformationen() { + return tanZusatzinformationen; + } + + public void setTanZusatzinformationen(String tanZusatzinformationen) { + this.tanZusatzinformationen = tanZusatzinformationen; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIVMK.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIVMK.java new file mode 100644 index 0000000..00728cc --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HIVMK.java @@ -0,0 +1,34 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; + +/** + * Name: Vormerkposten anfordern + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HIVMK + * Bezugssegment: HKVMK + * Version: 1 + * Anzahl: n + * Sender: Kreditinstitut + */ +public class HIVMK extends Segment{ + + @Element( + description = {@ElementDescription(number = 1)} + ) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)} + ) + @bin + private byte[] nichtGebuchteUmsaetze; + + public HIVMK(byte[] nichtGebuchteUmsaetze, Integer segmentNummer, Integer segmentVersion) { + super(new byte[0]); + segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(getClass()).setSegmentVersion(segmentVersion).setBezugssegment(segmentNummer).build(); + this.nichtGebuchteUmsaetze = SegmentUtil.wrapBinary(nichtGebuchteUmsaetze); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBBS.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBBS.java new file mode 100644 index 0000000..28c62ca --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBBS.java @@ -0,0 +1,7 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +public class HKBBS extends HKDBS { + public HKBBS(byte[] message) { + super(message); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBMB.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBMB.java new file mode 100644 index 0000000..cec7f1e --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBMB.java @@ -0,0 +1,7 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +public class HKBMB extends HKDMB { + public HKBMB(byte[] message) { + super(message); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBME.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBME.java new file mode 100644 index 0000000..210dca8 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBME.java @@ -0,0 +1,7 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +public class HKBME extends HKDMC { + public HKBME(byte[] message) { + super(message); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBML.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBML.java new file mode 100644 index 0000000..227ecdd --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBML.java @@ -0,0 +1,4 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +public class HKBML extends HKDML { +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBSE.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBSE.java new file mode 100644 index 0000000..4f62432 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBSE.java @@ -0,0 +1,7 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +public class HKBSE extends HKDSC{ + public HKBSE(byte[] message) { + super(message); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBSL.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBSL.java new file mode 100644 index 0000000..27299cb --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKBSL.java @@ -0,0 +1,4 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +public class HKBSL { +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCAZ.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCAZ.java new file mode 100644 index 0000000..c984c00 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCAZ.java @@ -0,0 +1,141 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.UnterstuetzteCamtMessages; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.jn; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +/** + * Name: Kontoumsätze anfordern/Zeitraum camt + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKCAZ + * Bezugssegment: - + * Version: 1 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED}) +public class HKCAZ extends Segment implements IExecutableElement, IDependentElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element(description = {@ElementDescription(number = 2)}) + private KontoverbindungInternational kontoverbindungInternational; + + @Element(description = { + @ElementDescription(number = 3) + }) + private UnterstuetzteCamtMessages unterstuetzteCamtMessages; + + @Element(description = @ElementDescription(number = 4)) + @jn + private String alleKonten; + + @Element(description = @ElementDescription(number = 5,status = ElementDescription.StatusCode.O)) + @dat + private String vonDatum; + + @Element(description = @ElementDescription(number = 6,status = ElementDescription.StatusCode.O)) + @dat + private String bisDatum; + + @Element(description = @ElementDescription(number = 7,status = ElementDescription.StatusCode.C,length = -4)) + @num + private Integer maximaleAnzahlEintraege; + + @Element(description = @ElementDescription(number = 8, status = ElementDescription.StatusCode.C,length = -35)) + @an + private String aufsetzpunkt; + + + private HIRMS statusElement; + private List replyMessageElements; + + public HKCAZ(byte[] message) { + super(message); + } + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + boolean operationAllowed; + String accountNr = ""; + accountNr = SegmentUtil.ibanToAccountNr(kontoverbindungInternational.getIban()); + operationAllowed = dataAccessFacade.operationAllowedForAccount(dialog.getLegitimationsInfo(), SegmentUtil.ibanToAccountNr(kontoverbindungInternational.getIban()), this.getClass()); + + + if (!operationAllowed) { + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("9380"); + statusElement.addRueckmeldung(rueckmeldung); + return StatusCode.ERROR; + } + + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd"); + Date vonDatum = null; + Date bisDatum = null; + if (this.vonDatum != null) { + try { + vonDatum = simpleDateFormat.parse(this.vonDatum); + if (this.bisDatum != null && this.bisDatum.isEmpty()) { + bisDatum = simpleDateFormat.parse(this.bisDatum); + } + } catch (ParseException e) { + //ignored sollten bereits geprüft sein + } + } + + List gebuchteUmsaetze = dataAccessFacade.getGebuchteCamtUmsaetze(accountNr, vonDatum, bisDatum, dialog.getLegitimationsInfo(), unterstuetzteCamtMessages.getCamtDescriptor().get(0)); + + + if ((gebuchteUmsaetze == null || gebuchteUmsaetze.size() == 0)) { + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("3010"); + statusElement.addRueckmeldung(rueckmeldung); + return StatusCode.WARNING; + } + byte[] nichtGebuchteUmsaetze = dataAccessFacade.getNichtGebuchteCamtUmsaetze(accountNr, vonDatum, bisDatum, dialog.getLegitimationsInfo()); + HICAZ hicaz = new HICAZ(unterstuetzteCamtMessages.getCamtDescriptor().get(0),gebuchteUmsaetze, nichtGebuchteUmsaetze, getSegmentkopf().getSegmentNummer(), getSegmentkopf().getSegmentVersion()); + hicaz.setKontoverbindungInternational(kontoverbindungInternational); + replyMessageElements = new LinkedList<>(); + replyMessageElements.add(hicaz); + + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("0020"); + statusElement.addRueckmeldung(rueckmeldung); + + return StatusCode.OK; + } + + @Override + public List getReplyMessageElements() { + return replyMessageElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } + +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCCM.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCCM.java new file mode 100644 index 0000000..5f0f18c --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCCM.java @@ -0,0 +1,155 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Betrag; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.jn; + +import java.util.List; + +/** + * Name: SEPA-Sammelüberweisung + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKCCM + * Bezugssegment: - + * Version: 1 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKCCM extends Segment implements IExecutableElement, IDependentElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + + @Element( + description = {@ElementDescription(number = 3)}) + private Betrag betrag; + + @Element( + description = {@ElementDescription(number = 4, length = 1)}) + @jn + private String einzelbuchungGewuenscht; + + @Element( + description = {@ElementDescription(number = 5, length = -256)}) + @an + private String sepaDescriptor; + + @Element( + description = {@ElementDescription(number = 6)}) + @bin + private byte[] sepaPainMessage; + + public HKCCM(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public Betrag getBetrag() { + return betrag; + } + + public void setBetrag(Betrag betrag) { + this.betrag = betrag; + } + + public String getEinzelbuchungGewuenscht() { + return einzelbuchungGewuenscht; + } + + public void setEinzelbuchungGewuenscht(String einzelbuchungGewuenscht) { + this.einzelbuchungGewuenscht = einzelbuchungGewuenscht; + } + + public String getSepaDescriptor() { + return sepaDescriptor; + } + + public void setSepaDescriptor(String sepaDescriptor) { + this.sepaDescriptor = sepaDescriptor; + } + + public byte[] getSepaPainMessage() { + return sepaPainMessage; + } + + public void setSepaPainMessage(byte[] sepaPainMessage) { + this.sepaPainMessage = sepaPainMessage; + } + + + private HIRMS statusElement; + private List replyElements; + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + + ReturnDataObject transactionSubmitted = dataAccessFacade.submitNewTransaction(dialog.getLegitimationsInfo(), sepaDescriptor, SegmentUtil.getBytes(getSepaPainMessage()), true); + StatusCode statusCode; + if (transactionSubmitted.isSuccess()) { + //die Überweisung wurde angenommen + statusCode = StatusCode.OK; + } else { + //die Überweisung wurde abgelehnt + statusCode = StatusCode.ERROR; + } + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode(transactionSubmitted.getReturnCode()); + rueckmeldung.setRueckmeldungstext(transactionSubmitted.getMessage()); + statusElement.addRueckmeldung(rueckmeldung); + //entferne TansactionInfo object vom Dialog + dialog.setTransactionInfo(null); + return statusCode; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCCS.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCCS.java new file mode 100644 index 0000000..37bc923 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCCS.java @@ -0,0 +1,134 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.client.TransactionInfo; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import org.bouncycastle.jce.provider.JDKMessageDigest; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.List; + +/** + * Name: SEPA Einzelüberweisung + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKCCS + * Bezugssegment: - + * Version: 1 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKCCS extends Segment implements IExecutableElement, IDependentElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + + @Element( + description = {@ElementDescription(number = 3, length = -256)}) + @an + private String sepaDescriptor; + + @Element( + description = {@ElementDescription(number = 4)}) + @bin + private byte[] sepaPainMessage; + + public HKCCS(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public String getSepaDescriptor() { + return sepaDescriptor; + } + + public void setSepaDescriptor(String sepaDescriptor) { + this.sepaDescriptor = sepaDescriptor; + } + + public byte[] getSepaPainMessage() { + return sepaPainMessage; + } + + public void setSepaPainMessage(byte[] sepaPainMessage) { + this.sepaPainMessage = sepaPainMessage; + } + + private HIRMS statusElement; + private List replyElements; + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + + ReturnDataObject transactionSubmitted = dataAccessFacade.submitNewTransaction(dialog.getLegitimationsInfo(), sepaDescriptor, SegmentUtil.getBytes(getSepaPainMessage()), false); + StatusCode statusCode; + if (transactionSubmitted.isSuccess()) { + //die Überweisung wurde angenommen + statusCode = StatusCode.OK; + } else { + //die Überweisung wurde abgelehnt + statusCode = StatusCode.ERROR; + } + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode(transactionSubmitted.getReturnCode()); + rueckmeldung.setRueckmeldungstext(transactionSubmitted.getMessage()); + statusElement.addRueckmeldung(rueckmeldung); + //entferne TansactionInfo object vom Dialog + dialog.setTransactionInfo(null); + return statusCode; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCMB.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCMB.java new file mode 100644 index 0000000..0e768d1 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCMB.java @@ -0,0 +1,95 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Betrag; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.support.Payments; +import net.petafuel.jsepa.model.Document; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.UnsupportedEncodingException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; + +/** + * Terminierte SEPA Überweisung Sammel Bestand + */ +public class HKCMB extends HKCSB { + + private static final Logger LOG = LogManager.getLogger(HKCMB.class); + + private HIRMS statusElement; + + public HKCMB(byte[] message) { + super(message); + } + LinkedList hicmbs = null; + @Override + public StatusCode execute(Dialog dialog) { + SimpleDateFormat output = new SimpleDateFormat("yyyyMMdd"); + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(getSegmentkopf().getSegmentNummer()).build()); + Payments.versions version = Payments.getBestVersion(getUnterstuetzteSepaPainMessages()); + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + LinkedList terminzahlungSammelBestand = dataAccessFacade.getTerminzahlungen(dialog.getLegitimationsInfo(), dialog.getClientProductInfo(),true, version); + Betrag betrag = new Betrag(new byte[0]); + betrag.setWaehrung("EUR"); + + if (terminzahlungSammelBestand == null || terminzahlungSammelBestand.size() == 0) { + try { + statusElement.addRueckmeldung(new Rueckmeldung("3010::Es liegen keine Einträge vor".getBytes("ISO-8859-1"))); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + return StatusCode.OK; + } else { + hicmbs= new LinkedList<>(); + LOG.info("Terminzahlungen-Sammler gefunden: {}", terminzahlungSammelBestand.size()); + for(Document d: terminzahlungSammelBestand) + { + try { + HICMB hicmb = new HICMB(new byte[0]); + hicmb.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HICMB.class).setSegmentVersion(1).setBezugssegment(getSegmentkopf().getSegmentNummer()).build()); + hicmb.setKontoverbindungInternational(getKontoverbindungInternational()); + hicmb.setEinreichungsdatum(output.format(Payments.creationFormat.parse(d.getCctInitiation().getGrpHeader().getCreationTime()))); + hicmb.setAusfuehrungsdatum(output.format(Payments.executionFormat.parse(d.getCctInitiation().getPmtInfos().get(0).getRequestedExecutionDate()))); + hicmb.setAnzahlAuftraege(d.getCctInitiation().getGrpHeader().getNoOfTransactions()); + betrag.setWert(String.format(Locale.GERMAN, "%.2f", d.getCctInitiation().getGrpHeader().getControlSum())); + hicmb.setSummeDerBetrage(betrag); + hicmb.setAuftragsidentifikation(d.getCctInitiation().getPmtInfos().get(0).getPmtInfId()); + hicmbs.add(hicmb); + } catch (ParseException e) { + LOG.info("Einrichtungsdatum: {}", d.getCctInitiation().getGrpHeader().getCreationTime()); + LOG.info("Ausführungsdatum: {}", d.getCctInitiation().getPmtInfos().get(0).getRequestedExecutionDate()); + LOG.warn(e.getMessage(),e); + hicmbs = null; + try { + statusElement.addRueckmeldung(new Rueckmeldung("9210::Fehler beim Erstellen der Bestandsdaten".getBytes("ISO-8859-1"))); + } catch (UnsupportedEncodingException e1) { + throw new RuntimeException(e1); + } + return StatusCode.ERROR; + } + } + } + statusElement.addRueckmeldung(Rueckmeldung.getRueckmeldung("0020")); + return StatusCode.OK; + } + @Override + public List getReplyMessageElements() { + return hicmbs; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCME.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCME.java new file mode 100644 index 0000000..3023888 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCME.java @@ -0,0 +1,111 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import net.petafuel.fuelifints.support.Payments; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +/** + * Terminierte SEPA Überweisung Sammel Einreichen + */ +public class HKCME extends Segment implements IExecutableElement { + + private static final Logger LOG = LogManager.getLogger(HKCME.class); + + + @Element(description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element(description = {@ElementDescription(number = 2)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + + @Element(description = {@ElementDescription(number = 3,status = ElementDescription.StatusCode.C)}) + private List summenfeld; + + @Element(description = {@ElementDescription(number = 4,status = ElementDescription.StatusCode.C)} ) + private String einzelbuchungerlaubt; + + @Element(description = {@ElementDescription(number = 5)}) + @an + private String sepaDescriptor; + + @Element(description = {@ElementDescription(number = 6)}) + @bin + private byte[] sepaPainMessage; + private HIRMS statusElement; + private List hicmes = null; + + public HKCME(byte[] message) { + super(message); + } + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(getSegmentkopf().getSegmentNummer()).build()); + /* + * Reiche die Sepa Pain Message an die DataAccessFacade zur weiteren Auswertung weiter: + */ + LOG.info("Terminüberweisung bekommen"); + LOG.info(new String(getSepaPainMessage())); + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + ReturnDataObject transactionSubmitted = dataAccessFacade.submitNewTransactionSchedule(dialog.getLegitimationsInfo(), getSepaDescriptor(), SegmentUtil.getBytes(getSepaPainMessage()), true); + LOG.info("Terminüberweisung verarbeitet: {}", transactionSubmitted.getMessage()); + Object id = transactionSubmitted.getAdditionalData(); + if(id != null && id instanceof Payments.AuftragsId) { + HICME hicme = new HICME(new byte[0]); + hicme.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HICME.class).setSegmentVersion(1).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + hicme.setAuftragsidentifikation(id.toString()); + this.hicmes = new ArrayList<>(); + this.hicmes.add(hicme); + } + StatusCode statusCode; + if (transactionSubmitted.isSuccess()) { + //die Lastschrift wurde angenommen + statusCode = StatusCode.OK; + } else { + //die Lastschrift wurde abgelehnt + statusCode = StatusCode.ERROR; + } + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode(transactionSubmitted.getReturnCode()); + rueckmeldung.setRueckmeldungstext(transactionSubmitted.getMessage()); + statusElement.addRueckmeldung(rueckmeldung); + //entferne TansactionInfo object vom Dialog + dialog.setTransactionInfo(null); + return statusCode; + } + + @Override + public List getReplyMessageElements() { + return this.hicmes; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } + + public String getSepaDescriptor() { + return sepaDescriptor; + } + + public byte[] getSepaPainMessage() { + return sepaPainMessage; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCML.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCML.java new file mode 100644 index 0000000..b8d150c --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCML.java @@ -0,0 +1,131 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Betrag; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +import java.util.List; + +/** + * Terminierte SEPA Überweisung Sammel Löschen + */ +public class HKCML extends Segment implements IExecutableElement { + @Element(description = {@ElementDescription(number = 1, status = ElementDescription.StatusCode.M)}) + private Segmentkopf segmentkopf; + @Element(description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.M)}) + private String auftragsidentifikation; + @Element(description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.O)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + @Element(description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.O)}) + @dat + private String einreichungsdatum; + @Element(description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.O)}) + @dat + private String ausfuehrungsdatum; + @Element(description = {@ElementDescription(number = 6, status = ElementDescription.StatusCode.O)}) + @num + private String anzahlderauftraege; + @Element(description = {@ElementDescription(number = 7, status = ElementDescription.StatusCode.O)}) + private Betrag summederbetraege; + private HIRMS statusElement = null; + + public HKCML(byte[] message) { + super(message); + } + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(getSegmentkopf().getSegmentNummer()).build()); + ReturnDataObject deleteTransaction = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()).deleteTerminzahlung(dialog.getLegitimationsInfo(), dialog.getClientProductInfo(), getAuftragsidentifikation(), true); + StatusCode statusCode; + if(deleteTransaction.isSuccess()) { + statusCode = StatusCode.OK; + } else { + statusCode = StatusCode.ERROR; + } + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode(deleteTransaction.getReturnCode()); + rueckmeldung.setRueckmeldungstext(deleteTransaction.getMessage()); + statusElement.addRueckmeldung(rueckmeldung); + //entferne TansactionInfo object vom Dialog + dialog.setTransactionInfo(null); + return statusCode; + } + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public String getAuftragsidentifikation() { + return auftragsidentifikation; + } + + public void setAuftragsidentifikation(String auftragsidentifikation) { + this.auftragsidentifikation = auftragsidentifikation; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public String getEinreichungsdatum() { + return einreichungsdatum; + } + + public void setEinreichungsdatum(String einreichungsdatum) { + this.einreichungsdatum = einreichungsdatum; + } + + public String getAusfuehrungsdatum() { + return ausfuehrungsdatum; + } + + public void setAusfuehrungsdatum(String ausfuehrungsdatum) { + this.ausfuehrungsdatum = ausfuehrungsdatum; + } + + public String getAnzahlderauftraege() { + return anzahlderauftraege; + } + + public void setAnzahlderauftraege(String anzahlderauftraege) { + this.anzahlderauftraege = anzahlderauftraege; + } + + public Betrag getSummederbetraege() { + return summederbetraege; + } + + public void setSummederbetraege(Betrag summederbetraege) { + this.summederbetraege = summederbetraege; + } + + + + @Override + public List getReplyMessageElements() { + return null; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCSB.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCSB.java new file mode 100644 index 0000000..033d1d1 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCSB.java @@ -0,0 +1,150 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.UnterstuetzteCamtMessages; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; +import net.petafuel.fuelifints.support.Payments; +import net.petafuel.jsepa.SEPAWriter; +import net.petafuel.jsepa.exception.SEPAWriteException; +import net.petafuel.jsepa.model.Document; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.UnsupportedEncodingException; +import java.util.LinkedList; +import java.util.List; + +/** + * Terminierte SEPA Überweisung Einzel Bestand + */ +public class HKCSB extends Segment implements IExecutableElement { + + private static final Logger LOG = LogManager.getLogger(HKCSB.class); + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + + @Element( + description = {@ElementDescription(number = 3, length = -99)}) + private List unterstuetzteSepaPainMessages; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C)}) + @dat + private String vonDatum; + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.C)}) + private String bisDatum; + + @Element( + description = {@ElementDescription(number = 6, status = ElementDescription.StatusCode.C, length = -4)}) + @num + private Integer maximaleAnzahlAuftraege; + + @Element( + description = {@ElementDescription(number = 7, status = ElementDescription.StatusCode.C, length = -35)}) + private String aufsetzpunkt; + private HIRMS statusElement = null; + + public HKCSB(byte[] message) { + super(message); + } + LinkedList hicsbs = null; + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + Payments.versions version = Payments.getBestVersion(getUnterstuetzteSepaPainMessages()); + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + LinkedList terminzahlungEinzelBestand = dataAccessFacade.getTerminzahlungen(dialog.getLegitimationsInfo(), dialog.getClientProductInfo(),false, version); + if (terminzahlungEinzelBestand == null || terminzahlungEinzelBestand.size() == 0) { + try { + statusElement.addRueckmeldung(new Rueckmeldung("3010::Es liegen keine Einträge vor".getBytes("ISO-8859-1"))); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + return StatusCode.OK; + } else { + hicsbs = new LinkedList<>(); + String sepaDescriptor = version.getDescriptor(); + for(Document d: terminzahlungEinzelBestand) + { + try { + SEPAWriter s = new SEPAWriter(d); + HICSB hicsb = new HICSB(new byte[0]); + hicsb.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HICSB.class).setSegmentVersion(1).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + hicsb.setSepaDescriptor(sepaDescriptor); + hicsb.setKontoverbindungInternational(kontoverbindungInternational); + hicsb.setSepaPainMessage(SegmentUtil.wrapBinary(s.writeSEPA())); + hicsb.setAuftragsIdentifikation(d.getCctInitiation().getPmtInfos().get(0).getPmtInfId()); + hicsbs.add(hicsb); + } catch (SEPAWriteException e) { + LOG.warn("Fehler beim Erstellen der Bestandsdaten",e); + try { + statusElement.addRueckmeldung(new Rueckmeldung("9210::Fehler beim Erstellen der Bestandsdaten".getBytes("ISO-8859-1"))); + } catch (UnsupportedEncodingException e1) { + throw new RuntimeException(e1); + } + hicsbs = null; + return StatusCode.ERROR; + } + } + } + statusElement.addRueckmeldung(Rueckmeldung.getRueckmeldung("0020")); + return StatusCode.OK; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public List getUnterstuetzteSepaPainMessages() { + return unterstuetzteSepaPainMessages; + } + + public String getVonDatum() { + return vonDatum; + } + + public String getBisDatum() { + return bisDatum; + } + + public Integer getMaximaleAnzahlAuftraege() { + return maximaleAnzahlAuftraege; + } + + public String getAufsetzpunkt() { + return aufsetzpunkt; + } + + @Override + public List getReplyMessageElements() { + return hicsbs; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCSE.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCSE.java new file mode 100644 index 0000000..118c069 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCSE.java @@ -0,0 +1,119 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import net.petafuel.fuelifints.support.Payments; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +/** + * Terminierte SEPA Überweisung Einzel Einreichen + */ +public class HKCSE extends Segment implements IExecutableElement, IDependentElement { + + private static final Logger LOG = LogManager.getLogger(HKCSE.class); + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + + @Element( + description = {@ElementDescription(number = 3)}) + @an + private String sepaDescriptor; + + @Element( + description = {@ElementDescription(number = 4)}) + @bin + private byte[] sepaPainMessage; + private HIRMS statusElement; + + + public HKCSE(byte[] message) { + super(message); + } + List hicses = null; + @Override + public StatusCode execute(Dialog dialog) + { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(getSegmentkopf().getSegmentNummer()).build()); + + /* + * Reiche die Sepa Pain Message an die DataAccessFacade zur weiteren Auswertung weiter: + */ + LOG.info("Terminüberweisung bekommen"); + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + ReturnDataObject transactionSubmitted = dataAccessFacade.submitNewTransactionSchedule(dialog.getLegitimationsInfo(), getSepaDescriptor(), SegmentUtil.getBytes(getSepaPainMessage()), false); + + LOG.info("Terminüberweisung verarbeitet: {}", transactionSubmitted.getMessage()); + StatusCode statusCode; + if (transactionSubmitted.isSuccess()) { + //die Lastschrift wurde angenommen + hicses = new ArrayList<>(); + HICSE hicse = new HICSE(new byte[0]); + hicse.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HICSE.class).setSegmentVersion(1).setBezugssegment(getSegmentkopf().getSegmentNummer()).build()); + Object id = transactionSubmitted.getAdditionalData(); + if(id != null && id instanceof Payments.AuftragsId) { + hicse.setAuftragsidentifikation(id.toString()); + } + hicses.add(hicse); + statusCode = StatusCode.OK; + } else { + //die Lastschrift wurde abgelehnt + statusCode = StatusCode.ERROR; + } + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode(transactionSubmitted.getReturnCode()); + rueckmeldung.setRueckmeldungstext(transactionSubmitted.getMessage()); + statusElement.addRueckmeldung(rueckmeldung); + //entferne TansactionInfo object vom Dialog + dialog.setTransactionInfo(null); + return statusCode; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public String getSepaDescriptor() { + return sepaDescriptor; + } + + public byte[] getSepaPainMessage() { + return sepaPainMessage; + } + + @Override + public List getReplyMessageElements() { + return hicses; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCSL.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCSL.java new file mode 100644 index 0000000..7e23eef --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCSL.java @@ -0,0 +1,109 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; + +import java.util.List; + +/** + * Terminierte SEPA Überweisung Einzel Löschen + */ +public class HKCSL extends Segment implements IExecutableElement { + @Element(description = {@ElementDescription(number = 1, status = ElementDescription.StatusCode.M)}) + private Segmentkopf segmentkopf; + @Element(description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.M)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + @Element(description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.C)}) + @an + private String sepaDescriptor; + @Element(description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C)}) + @bin + private byte[] sepaMessage; + @Element(description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.M)}) + private String auftragsidentifikation; + + public HKCSL(byte[] message) { + super(message); + } + + + private HIRMS statusElement = null; + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(getSegmentkopf().getSegmentNummer()).build()); + ReturnDataObject deleteTransaction = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()).deleteTerminzahlung(dialog.getLegitimationsInfo(), dialog.getClientProductInfo(), getAuftragsidentifikation(), false); + StatusCode statusCode; + if(deleteTransaction.isSuccess()) { + statusCode = StatusCode.OK; + } else { + statusCode = StatusCode.ERROR; + } + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode(deleteTransaction.getReturnCode()); + rueckmeldung.setRueckmeldungstext(deleteTransaction.getMessage()); + statusElement.addRueckmeldung(rueckmeldung); + //entferne TansactionInfo object vom Dialog + dialog.setTransactionInfo(null); + return statusCode; + } + + @Override + public List getReplyMessageElements() { + return null; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public String getSepaDescriptor() { + return sepaDescriptor; + } + + public void setSepaDescriptor(String sepaDescriptor) { + this.sepaDescriptor = sepaDescriptor; + } + + public byte[] getSepaMessage() { + return sepaMessage; + } + + public void setSepaMessage(byte[] sepaMessage) { + this.sepaMessage = sepaMessage; + } + + public String getAuftragsidentifikation() { + return auftragsidentifikation; + } + + public void setAuftragsidentifikation(String auftragsidentifikation) { + this.auftragsidentifikation = auftragsidentifikation; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCUB.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCUB.java new file mode 100644 index 0000000..bb21962 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKCUB.java @@ -0,0 +1,122 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.RecipientAccountDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.*; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; +import net.petafuel.fuelifints.support.Payments; +import net.petafuel.jsepa.SEPAWriter; +import net.petafuel.jsepa.exception.SEPAWriteException; +import net.petafuel.jsepa.model.Document; +import net.petafuel.mt94x.Konto; + +import java.io.UnsupportedEncodingException; +import java.util.LinkedList; +import java.util.List; + +/** + * Name: Bestand Empfängerkonten anfordern + * Kennung: HKCUB + * Version: 1 + * Sender: Kunde + */ +public class HKCUB extends Segment implements IExecutableElement { + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.C, length = -4)}) + @num + private Integer maximaleAnzahlAuftraege; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C, length = -35)}) + private String aufsetzpunkt; + private HIRMS statusElement = null; + + public HKCUB(byte[] message) { + super(message); + } + + LinkedList hicubList = null; + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + List empfaengerBestand = dataAccessFacade.getEmpfaengerkontenbestand(dialog.getLegitimationsInfo(), kontoverbindungInternational); + if (empfaengerBestand == null || empfaengerBestand.size() == 0) { + try { + statusElement.addRueckmeldung(new Rueckmeldung("3010::Es liegen keine Einträge vor".getBytes("ISO-8859-1"))); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + return StatusCode.OK; + } else { + hicubList = new LinkedList<>(); + HICUB hicub = new HICUB(new byte[0]); + hicub.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HICUB.class).setSegmentVersion(1).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + hicub.setKontoverbindungInternational(kontoverbindungInternational); + List angabenEmpfaengerkontenList = new LinkedList<>(); + for (RecipientAccountDataObject recipientAccountDataObject : empfaengerBestand) { + KontoverbindungInternational ktie = new KontoverbindungInternational(new byte[0]); + ktie.setIban(recipientAccountDataObject.getIBAN()); + ktie.setBic(recipientAccountDataObject.getBIC()); + AngabenEmpfaengerkonten ae = new AngabenEmpfaengerkonten(new byte[0]); + if(recipientAccountDataObject.getName() != null && !recipientAccountDataObject.getName().isEmpty()) { + Kik kik = new Kik(new byte[0]); + kik.setKreditinstitutscode(""); + kik.setLaenderkennzeichen(""); + kik.setStopSkippingUnusedElements(); + ktie.setStopSkippingUnusedElements(); + ktie.setKreditsinstitutskennung(kik); + ae.setName1Empfaenger(recipientAccountDataObject.getName()); + } + ae.setKontoverbindungInternationalEmpfaenger(ktie); + angabenEmpfaengerkontenList.add(ae); + } + hicub.setAngabenEmpfaengerkonten(angabenEmpfaengerkontenList); + hicubList.add(hicub); + } + statusElement.addRueckmeldung(Rueckmeldung.getRueckmeldung("0020")); + return StatusCode.OK; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public Integer getMaximaleAnzahlAuftraege() { + return maximaleAnzahlAuftraege; + } + + public String getAufsetzpunkt() { + return aufsetzpunkt; + } + + @Override + public List getReplyMessageElements() { + return hicubList; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDBS.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDBS.java new file mode 100644 index 0000000..d1b2e4a --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDBS.java @@ -0,0 +1,159 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.DirectDebitDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +import java.io.UnsupportedEncodingException; +import java.util.List; + +/** + * Name: Bestand terminierter SEPA-Einzellastschriften anfordern + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKDBS + * Bezugssegment: - + * Version: 1 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKDBS extends Segment implements IExecutableElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + + @Element( + description = {@ElementDescription(number = 3, length = -99)}) + private List unterstuetzteSepaPainMessages; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C)}) + @dat + private String vonDatum; + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.C)}) + private String bisDatum; + + @Element( + description = {@ElementDescription(number = 6, status = ElementDescription.StatusCode.C, length = -4)}) + @num + private Integer maximaleAnzahlAuftraege; + + @Element( + description = {@ElementDescription(number = 7, status = ElementDescription.StatusCode.C, length = -35)}) + private String aufsetzpunkt; + + public HKDBS(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public List getUnterstuetzteSepaPainMessages() { + return unterstuetzteSepaPainMessages; + } + + public void setUnterstuetzteSepaPainMessages(List unterstuetzteSepaPainMessages) { + this.unterstuetzteSepaPainMessages = unterstuetzteSepaPainMessages; + } + + public String getVonDatum() { + return vonDatum; + } + + public void setVonDatum(String vonDatum) { + this.vonDatum = vonDatum; + } + + public String getBisDatum() { + return bisDatum; + } + + public void setBisDatum(String bisDatum) { + this.bisDatum = bisDatum; + } + + public Integer getMaximaleAnzahlAuftraege() { + return maximaleAnzahlAuftraege; + } + + public void setMaximaleAnzahlAuftraege(Integer maximaleAnzahlAuftraege) { + this.maximaleAnzahlAuftraege = maximaleAnzahlAuftraege; + } + + public String getAufsetzpunkt() { + return aufsetzpunkt; + } + + public void setAufsetzpunkt(String aufsetzpunkt) { + this.aufsetzpunkt = aufsetzpunkt; + } + + private HIRMS statusElement; + private List replyElements; + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + List lastschriftBestand = dataAccessFacade.getEinzelLastschriftBestand(dialog.getLegitimationsInfo()); + if (lastschriftBestand == null || lastschriftBestand.size() == 0) { + try { + statusElement.addRueckmeldung(new Rueckmeldung("3010::Es liegen keine Einträge vor".getBytes("ISO-8859-1"))); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + return StatusCode.INFO; + } else { + //TODO einbauen! + } + + return StatusCode.OK; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDMB.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDMB.java new file mode 100644 index 0000000..06d96db --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDMB.java @@ -0,0 +1,148 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.DirectDebitDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +import java.io.UnsupportedEncodingException; +import java.util.List; + +/** + * Name: Bestand terminierter SEPA-Sammellastschriften anfordern + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKDMB + * Bezugssegment: - + * Version: 1 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKDMB extends Segment implements IExecutableElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.C)}) + @dat + private String vonDatum; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C)}) + private String bisDatum; + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -4)}) + @num + private Integer maximaleAnzahlAuftraege; + + @Element( + description = {@ElementDescription(number = 6, status = ElementDescription.StatusCode.C, length = -35)}) + private String aufsetzpunkt; + + public HKDMB(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public String getVonDatum() { + return vonDatum; + } + + public void setVonDatum(String vonDatum) { + this.vonDatum = vonDatum; + } + + public String getBisDatum() { + return bisDatum; + } + + public void setBisDatum(String bisDatum) { + this.bisDatum = bisDatum; + } + + public Integer getMaximaleAnzahlAuftraege() { + return maximaleAnzahlAuftraege; + } + + public void setMaximaleAnzahlAuftraege(Integer maximaleAnzahlAuftraege) { + this.maximaleAnzahlAuftraege = maximaleAnzahlAuftraege; + } + + public String getAufsetzpunkt() { + return aufsetzpunkt; + } + + public void setAufsetzpunkt(String aufsetzpunkt) { + this.aufsetzpunkt = aufsetzpunkt; + } + + private HIRMS statusElement; + private List replyElements; + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + + + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + List lastschriftBestand = dataAccessFacade.getSammelLastschriftBestand(dialog.getLegitimationsInfo()); + if (lastschriftBestand == null || lastschriftBestand.size() == 0) { + try { + statusElement.addRueckmeldung(new Rueckmeldung("3010::Es liegen keine Eintraege vor".getBytes("ISO-8859-1"))); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + return StatusCode.INFO; + } else { + //TODO einbauen! + } + return StatusCode.ERROR; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDMC.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDMC.java new file mode 100644 index 0000000..864ff02 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDMC.java @@ -0,0 +1,22 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; + +/** + * Name: Terminierte SEPA-COR1-Sammellastschrift einreichen + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKDMC + * Bezugssegment: - + * Version: 1 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKDMC extends HKDME { + public HKDMC(byte[] message) { + super(message); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDME.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDME.java new file mode 100644 index 0000000..ce4a4a0 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDME.java @@ -0,0 +1,166 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Betrag; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.jn; + +import java.util.List; + +/** + * Name: Einreichung terminierter SEPA-Sammellastschrift + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKDME + * Bezugssegment: - + * Version: 1 + * Sender: Kunde + */ + +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKDME extends Segment implements IExecutableElement, IDependentElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.C)} + ) + private Betrag summenfeld; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C)} + ) + @jn + private String einzelbuchungGewuenscht; + + + @Element( + description = {@ElementDescription(number = 5)}) + @an + private String sepaDescriptor; + + @Element( + description = {@ElementDescription(number = 6)}) + @bin + private byte[] sepaPainMessage; + + public HKDME(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public String getSepaDescriptor() { + return sepaDescriptor; + } + + public void setSepaDescriptor(String sepaDescriptor) { + this.sepaDescriptor = sepaDescriptor; + } + + public byte[] getSepaPainMessage() { + return sepaPainMessage; + } + + public void setSepaPainMessage(byte[] sepaPainMessage) { + this.sepaPainMessage = sepaPainMessage; + } + + private HIRMS statusElement; + private List replyElements; + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + /* + * SFirm Fix: + * SFirm schickt die Sepa Pain Message falsch! + */ + String sepaFile = new String(sepaPainMessage).replaceAll("pain.008.001.01", "CstmrDrctDbtInitn").replaceAll("OthrId", "Othr"); + sepaPainMessage = sepaFile.getBytes(); + + /* + * Reiche die Sepa Pain Message an die DataAccessFacade zur weiteren Auswertung weiter: + */ + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + ReturnDataObject transactionSubmitted = dataAccessFacade.submitNewTransaction(dialog.getLegitimationsInfo(), sepaDescriptor, SegmentUtil.getBytes(getSepaPainMessage()), true); + StatusCode statusCode; + if (transactionSubmitted.isSuccess()) { + //die Lastschrift wurde angenommen + statusCode = StatusCode.OK; + } else { + //die Lastschrift wurde abgelehnt + statusCode = StatusCode.ERROR; + } + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode(transactionSubmitted.getReturnCode()); + rueckmeldung.setRueckmeldungstext(transactionSubmitted.getMessage()); + statusElement.addRueckmeldung(rueckmeldung); + //entferne TansactionInfo object vom Dialog + dialog.setTransactionInfo(null); + return statusCode; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } + + public Betrag getSummenfeld() { + return summenfeld; + } + + public void setSummenfeld(Betrag summenfeld) { + this.summenfeld = summenfeld; + } + + public String getEinzelbuchungGewuenscht() { + return einzelbuchungGewuenscht; + } + + public void setEinzelbuchungGewuenscht(String einzelbuchungGewuenscht) { + this.einzelbuchungGewuenscht = einzelbuchungGewuenscht; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDML.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDML.java new file mode 100644 index 0000000..55d3aa8 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDML.java @@ -0,0 +1,4 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +public class HKDML { +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDSC.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDSC.java new file mode 100644 index 0000000..5fb7952 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDSC.java @@ -0,0 +1,22 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; + +/** + * Name: Terminierte SEPA-COR1-Einzellastschrift einreichen + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKDSC + * Bezugssegment: - + * Version: 1 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKDSC extends HKDSE { + public HKDSC(byte[] message) { + super(message); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDSE.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDSE.java new file mode 100644 index 0000000..57dfa2a --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKDSE.java @@ -0,0 +1,142 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.client.TransactionInfo; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import org.bouncycastle.jce.provider.JDKMessageDigest; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.List; + +/** + * Name: Terminierte SEPA-Einzellastschrift einreichen + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKDSE + * Bezugssegment: - + * Version: 1 + * Sender: Kunde + */ + +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKDSE extends Segment implements IExecutableElement, IDependentElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + + @Element( + description = {@ElementDescription(number = 3)}) + @an + private String sepaDescriptor; + + @Element( + description = {@ElementDescription(number = 4)}) + @bin + private byte[] sepaPainMessage; + + public HKDSE(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public String getSepaDescriptor() { + return sepaDescriptor; + } + + public void setSepaDescriptor(String sepaDescriptor) { + this.sepaDescriptor = sepaDescriptor; + } + + public byte[] getSepaPainMessage() { + return sepaPainMessage; + } + + public void setSepaPainMessage(byte[] sepaPainMessage) { + this.sepaPainMessage = sepaPainMessage; + } + + private HIRMS statusElement; + private List replyElements; + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + /* + * SFirm Fix: + * SFirm schickt die Sepa Pain Message falsch! + */ + String sepaFile = new String(sepaPainMessage).replaceAll("pain.008.001.01", "CstmrDrctDbtInitn").replaceAll("OthrId", "Othr"); + sepaPainMessage = sepaFile.getBytes(); + + /* + * Reiche die Sepa Pain Message an die DataAccessFacade zur weiteren Auswertung weiter: + */ + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + ReturnDataObject transactionSubmitted = dataAccessFacade.submitNewTransaction(dialog.getLegitimationsInfo(), sepaDescriptor, SegmentUtil.getBytes(getSepaPainMessage()), false); + StatusCode statusCode; + if (transactionSubmitted.isSuccess()) { + //die Lastschrift wurde angenommen + statusCode = StatusCode.OK; + } else { + //die Lastschrift wurde abgelehnt + statusCode = StatusCode.ERROR; + } + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode(transactionSubmitted.getReturnCode()); + rueckmeldung.setRueckmeldungstext(transactionSubmitted.getMessage()); + statusElement.addRueckmeldung(rueckmeldung); + //entferne TansactionInfo object vom Dialog + dialog.setTransactionInfo(null); + return statusCode; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKEND.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKEND.java new file mode 100644 index 0000000..41972b7 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKEND.java @@ -0,0 +1,67 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; + +import java.util.List; + +/** + * Name: Dialogende + * Typ: Segment + * Segmentart: Administration + * Sender: Kunde + */ +public class HKEND extends Segment implements IExecutableElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, length = -30)}) + @id + private String dialogId; + + public HKEND(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public String getDialogId() { + return dialogId; + } + + public void setDialogId(String dialogId) { + this.dialogId = dialogId; + } + + @Override + public StatusCode execute(Dialog dialog) { + dialog.setLastMessage(true); //HKEND ist immer die letzte Servernachricht in einem Dialog + return StatusCode.OK; + } + + @Override + public List getReplyMessageElements() { + return null; + } + + @Override + public IMessageElement getStatusElement() { + HIRMS statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + statusElement.addRueckmeldung(Rueckmeldung.getRueckmeldung("0100")); + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKIDN.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKIDN.java new file mode 100644 index 0000000..fbabee2 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKIDN.java @@ -0,0 +1,122 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.client.LegitimationInfo; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Kik; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +import java.util.List; + +/** + * Name: Identifikation + * Typ: Segment + * Segmentart: Administration + * Bezugssegment: - + * Sender: Kunde + */ +public class HKIDN extends Segment implements IExecutableElement, IDependentElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + private Kik kreditinstitutskennung; + + @Element( + description = {@ElementDescription(number = 3)}) + @id + private String kunden_id; + + @Element( + description = {@ElementDescription(number = 4)}) + @id + private String kundensystem_id; + + @Element( + description = {@ElementDescription(number = 5, + length = 1)}) + @num + private Integer kundensystem_status; + + public HKIDN(byte[] message) { + super(message); + } + + + private HIRMS statusElement; + + //Example + // HKIDN:5:2+280:10020030+12345+2+1' + + @Override + public String toString() { + return "HKIDN{" + + "segmentkopf=" + segmentkopf + + ", kreditinstitutskennung=" + kreditinstitutskennung + + ", kunden_id='" + kunden_id + '\'' + + ", kundensystem_id='" + kundensystem_id + '\'' + + ", kundensystem_status='" + kundensystem_status + '\'' + + '}'; + } + + @Override + public StatusCode execute(Dialog dialog) { + dialog.setBankId(kreditinstitutskennung.getKreditinstitutscode()); + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + if(dataAccessFacade == null) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + Rueckmeldung rueckmeldung = new Rueckmeldung(null); + rueckmeldung.setRueckmeldungscode("9999"); + rueckmeldung.setRueckmeldungstext("BLZ nicht bekannt."); + statusElement.addRueckmeldung(rueckmeldung); + + return StatusCode.ERROR; + } + LegitimationInfo legitimationInfo = dialog.getLegitimationsInfo(); + legitimationInfo.setCustomerId(kunden_id); + if ("9999999999".equals(kunden_id)) { + legitimationInfo.setUserId(kunden_id); + } + legitimationInfo.setUserIdentified(dataAccessFacade.isCustomerIdValid(dialog.getLegitimationsInfo().getUserId(), kunden_id)); + + if(!legitimationInfo.isUserIdentified()) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + Rueckmeldung rueckmeldung = new Rueckmeldung(null); + rueckmeldung.setRueckmeldungscode("9010"); + rueckmeldung.setRueckmeldungstext("Benutzerkennung oder Kunden-ID unbekannt."); + statusElement.addRueckmeldung(rueckmeldung); + return StatusCode.ERROR; + } + + legitimationInfo.setBankId(kreditinstitutskennung.getKreditinstitutscode()); + dialog.getClientProductInfo().setUserSystemId(kundensystem_id); + return StatusCode.OK; + } + + @Override + public List getReplyMessageElements() { + return null; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } + + public String getBankId() { + return kreditinstitutskennung.getKreditinstitutscode(); + } +} + diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKISA.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKISA.java new file mode 100644 index 0000000..4009a78 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKISA.java @@ -0,0 +1,196 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.cryptography.KeyManager; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.OeffentlicherSchluessel; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Schluesselname; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Sicherheitsprofil; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Zertifikat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.PublicKey; +import java.security.interfaces.RSAPublicKey; +import java.util.LinkedList; +import java.util.List; + +/** + * Name: Anforderung eines öffentlichen Schlüssels + * Typ: Segment + * Segmentart: Administration + * Kennung: HKISA + * Bezugssegment: - + * Sender: Kunde + */ +public class HKISA extends Segment implements IExecutableElement { + + private static final Logger LOG = LogManager.getLogger(HKISA.class); + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, length = 1)}) + @code(restrictions = {"2"}) + private String nachrichtenbeziehungKodiert; + + @Element( + description = {@ElementDescription(number = 3, length = -3)}) + @code(restrictions = {"124"}) + private String bezeichnerFunktionstyp; + + @Element( + description = {@ElementDescription(number = 4)}) + private Sicherheitsprofil sicherheitsprofil; + + @Element( + description = {@ElementDescription(number = 5)}) + private Schluesselname schluesselname; + + @Element( + description = {@ElementDescription(number = 6, status = ElementDescription.StatusCode.O)}) + private Zertifikat zertifikat; + + + private List resultElements = new LinkedList(); + + public HKISA(byte[] message) { + super(message); + } + + private KeyManager keyManager; + + @Override + public StatusCode execute(Dialog dialog) { + LOG.info("HKISA execute called"); + keyManager = KeyManager.getInstance(schluesselname.getKreditsinstitutkennung().getKreditinstitutscode()); + if (keyManager != null) { + + HIISA hiisa = new HIISA(new byte[0]); + try { + //if(schluesselname.getSchluesselart().equals("S")) { + //hiisa.setSegmentkopf(new Segmentkopf(("HIISA:0:3:" + segmentkopf.getSegmentNummer()).getBytes("ISO-8859-1"))); + hiisa.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIISA.class).setSegmentVersion(3).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + //} else { + // hiisa.setSegmentkopf(new Segmentkopf(("HIISA:0:3:"+segmentkopf.getSegmentNummer()).getBytes("ISO-8859-1"))); + //} + hiisa.setNachrichtenbeziehungKodiert("1"); + hiisa.setAustauschkontrollereferenz("0"); + hiisa.setNachrichtenreferenznummer(1234); + hiisa.setBezeichnerFunktionstyp("224"); + Schluesselname schluesselname = new Schluesselname(new byte[0]); + schluesselname.setKreditsinstitutkennung(this.schluesselname.getKreditsinstitutkennung()); + schluesselname.setBenutzerkennung("0"); + schluesselname.setSchluesselart(this.schluesselname.getSchluesselart()); + schluesselname.setSchluesselnummer(1); + schluesselname.setSchluesselversion(1); + hiisa.setSchluesselname(schluesselname); + + SecurityMethod securityMethod = SecurityMethod.valueOf(this.sicherheitsprofil.getSicherheitsverfahren() + "_" + this.sicherheitsprofil.getSicherheitsverfahrensversion()); + + dialog.getLegitimationsInfo().setSecurityMethod(securityMethod); + + OeffentlicherSchluessel oeffentlicherSchluessel = new OeffentlicherSchluessel(new byte[0]); + oeffentlicherSchluessel.setVerwendungszweckOeffentlicherSchluessel(this.schluesselname.getSchluesselart().equals("V") ? "5" : "6"); + String operationsmodusKodiert = ""; + if (this.schluesselname.getSchluesselart().equals("V")) { + switch (securityMethod) { + case RAH_10: + case RDH_10: + operationsmodusKodiert = "2"; + break; + case RAH_9: + case RDH_9: + operationsmodusKodiert = "18"; + } + } else { + switch (securityMethod) { + case RAH_9: + case RAH_10: + case RDH_9: + case RDH_10: + operationsmodusKodiert = "19"; + break; + } + } + oeffentlicherSchluessel.setOpertationsmodusKodiert(operationsmodusKodiert); + oeffentlicherSchluessel.setVerfahrenBenutzer("10"); + PublicKey publicKey = keyManager.getPublicKey(securityMethod, this.schluesselname.getSchluesselart()); + RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; + BigInteger publicModulus = rsaPublicKey.getModulus(); + byte[] pubModBytes; + if(publicModulus.toByteArray()[0] == 0) { + //führende Null entfernen falls vorhanden + pubModBytes = new byte[publicModulus.toByteArray().length - 1]; + System.arraycopy(publicModulus.toByteArray(), 1, pubModBytes, 0, pubModBytes.length); + } else { + pubModBytes = publicModulus.toByteArray(); + } + LOG.info("pubModBytes {}", pubModBytes.length); + byte[] publicModValue = new byte[pubModBytes.length + 2 + Integer.toString(pubModBytes.length).length()]; + publicModValue[0] = '@'; + int i = 1; + for (byte b : Integer.toString(pubModBytes.length).getBytes("ISO-8859-1")) { + publicModValue[i++] = b; + } + publicModValue[i] = '@'; + System.arraycopy(pubModBytes, 0, publicModValue, i + 1, pubModBytes.length); + oeffentlicherSchluessel.setModulus(publicModValue); + oeffentlicherSchluessel.setBezeichnerModulus("12"); + + BigInteger publicExponent = rsaPublicKey.getPublicExponent(); + byte[] pubExpBytes = publicExponent.toByteArray(); + byte[] publicExponentValue = new byte[pubExpBytes.length + 2 + Integer.toString(pubExpBytes.length).length()]; + publicExponentValue[0] = '@'; + i = 1; + for (byte b : Integer.toString(pubExpBytes.length).getBytes("ISO-8859-1")) { + publicExponentValue[i++] = b; + } + publicExponentValue[i] = '@'; + System.arraycopy(pubExpBytes, 0, publicExponentValue, i + 1, pubExpBytes.length); + oeffentlicherSchluessel.setExponent(publicExponentValue); + oeffentlicherSchluessel.setBezeichnerExponent("13"); + + hiisa.setOeffentlicherSchluessel(oeffentlicherSchluessel); + } catch (UnsupportedEncodingException e) { + } + LOG.info(hiisa); + resultElements.add(hiisa); + return StatusCode.OK; + } + LOG.error("could not load keymanager in HKISA"); + return StatusCode.ERROR; + } + + @Override + public List getReplyMessageElements() { + LOG.info("getReplyMessageElements called"); + return resultElements; + } + + @Override + public IMessageElement getStatusElement() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String toString() { + return "HKISA{" + + "segmentkopf=" + segmentkopf + + ", nachrichtenbeziehungKodiert='" + nachrichtenbeziehungKodiert + '\'' + + ", bezeichnerFunktionstyp='" + bezeichnerFunktionstyp + '\'' + + ", sicherheitsprofil=" + sicherheitsprofil + + ", schluesselname=" + schluesselname + + ", zertifikat=" + zertifikat + + ", keyManager=" + keyManager + + '}'; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKKAZ.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKKAZ.java new file mode 100644 index 0000000..c8f962a --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKKAZ.java @@ -0,0 +1,210 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungNational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.jn; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +/** + * Name: Kontoumsätze anfordern/Zeitraum + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKKAZ + * Bezugssegment: - + * Version: 6 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKKAZ extends Segment implements IExecutableElement, IDependentElement { + + @Element( + description = {@ElementDescription(number = 1)} + ) + private Segmentkopf segmentkopf; + @Element( + description = {@ElementDescription(number = 2, segmentVersion = 6), + @ElementDescription(number = 2, segmentVersion = 4), + @ElementDescription(number = 2, segmentVersion = 5)}) + @ApplicantAccount + private KontoverbindungNational kontoverbindungNational; + @Element( + description = {@ElementDescription(number = 2, segmentVersion = 7)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungAuftraggeber; + @Element( + description = {@ElementDescription(number = 3, length = 1)}) + @jn + private String alleKonten; + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.O)}) + @dat + private String vonDatum; + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.O)}) + @dat + private String bisDatum; + @Element( + description = {@ElementDescription(number = 6, status = ElementDescription.StatusCode.C, length = -4)}) + @num + private Integer maximaleAnzahlEintraege; + @Element( + description = {@ElementDescription(number = 7, status = ElementDescription.StatusCode.C, length = -35)}) + @an + private String Aufsetzpunkt; + private HIRMS statusElement; + private List replyMessageElements; + + public HKKAZ(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungInternational getKontoverbindungAuftraggeber() { + return kontoverbindungAuftraggeber; + } + + public void setKontoverbindungAuftraggeber(KontoverbindungInternational kontoverbindungAuftraggeber) { + this.kontoverbindungAuftraggeber = kontoverbindungAuftraggeber; + } + + public String getAlleKonten() { + return alleKonten; + } + + public void setAlleKonten(String alleKonten) { + this.alleKonten = alleKonten; + } + + public String getVonDatum() { + return vonDatum; + } + + public void setVonDatum(String vonDatum) { + this.vonDatum = vonDatum; + } + + public String getBisDatum() { + return bisDatum; + } + + public void setBisDatum(String bisDatum) { + this.bisDatum = bisDatum; + } + + public Integer getMaximaleAnzahlEintraege() { + return maximaleAnzahlEintraege; + } + + public void setMaximaleAnzahlEintraege(Integer maximaleAnzahlEintraege) { + this.maximaleAnzahlEintraege = maximaleAnzahlEintraege; + } + + public String getAufsetzpunkt() { + return Aufsetzpunkt; + } + + public void setAufsetzpunkt(String aufsetzpunkt) { + Aufsetzpunkt = aufsetzpunkt; + } + + public KontoverbindungNational getKontoverbindungNational() { + return kontoverbindungNational; + } + + public void setKontoverbindungNational(KontoverbindungNational kontoverbindungNational) { + this.kontoverbindungNational = kontoverbindungNational; + } + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + boolean operationAllowed; + String accountNr = ""; + if (segmentkopf.getSegmentVersion() < 7) { + accountNr = kontoverbindungNational.getKontonummer(); + operationAllowed = dataAccessFacade.operationAllowedForAccount(dialog.getLegitimationsInfo(), kontoverbindungNational.getKontonummer(), this.getClass()); + } else { + accountNr = SegmentUtil.ibanToAccountNr(kontoverbindungAuftraggeber.getIban()); + operationAllowed = dataAccessFacade.operationAllowedForAccount(dialog.getLegitimationsInfo(), SegmentUtil.ibanToAccountNr(kontoverbindungAuftraggeber.getIban()), this.getClass()); + } + + if (!operationAllowed) { + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("9380"); + statusElement.addRueckmeldung(rueckmeldung); + return StatusCode.ERROR; + } + + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd"); + Date vonDatum = null; + Date bisDatum = null; + if (this.vonDatum != null) { + try { + vonDatum = simpleDateFormat.parse(this.vonDatum); + if (this.bisDatum != null && !this.bisDatum.isEmpty()) { + bisDatum = simpleDateFormat.parse(this.bisDatum); + } + } catch (ParseException e) { + //ignored sollten bereits geprüft sein + } + } + + byte[] gebuchteUmsaetze = dataAccessFacade.getGebuchteUmsaetze(accountNr, vonDatum, bisDatum, dialog.getLegitimationsInfo()); + + + if ((gebuchteUmsaetze == null || gebuchteUmsaetze.length == 0)) { + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("3010"); + statusElement.addRueckmeldung(rueckmeldung); + return StatusCode.WARNING; + } + byte[] nichtGebuchteUmsaetze = dataAccessFacade.getNichtGebuchteUmsaetze(accountNr, vonDatum, bisDatum, dialog.getLegitimationsInfo()); + HIKAZ hikaz = new HIKAZ(gebuchteUmsaetze, nichtGebuchteUmsaetze, getSegmentkopf().getSegmentNummer(), getSegmentkopf().getSegmentVersion()); + replyMessageElements = new LinkedList(); + replyMessageElements.add(hikaz); + + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("0020"); + statusElement.addRueckmeldung(rueckmeldung); + + return StatusCode.OK; + } + + @Override + public List getReplyMessageElements() { + return replyMessageElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKKDM.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKKDM.java new file mode 100644 index 0000000..26ec617 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKKDM.java @@ -0,0 +1,155 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungNational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; + +import java.util.List; + +/** + * Freitextmeldungen + * + * Dem Kunden wird die Möglichkeit gegeben, eine unstrukturierte, unformatierte Mel- + * dung (Freitext) an das Kreditinstitut zu senden. Gegenstand der Freitextmeldung + * können alle Aufträge sein, zu denen (noch) keine expliziten Geschäftsvorfälle exis- + * tieren. Ferner können per Kundenmeldung beliebige Informationen ohne Auftrags- + * charakter an das Kreditinstitut übermittelt werden. + * Da der Kunde auch Aufträge in der Meldung übermitteln kann, ist die allgemeine + * Kundenmeldung stets zu signieren. + * Die Behandlung der Meldung sowie die Reaktion auf die Meldung sind kreditinsti- + * tutsspezifisch und den entsprechenden Bedingungen des Kunde-Bank-Verhältnis- + * ses zu entnehmen. Insbesondere sollten mit Hilfe der Freitextmeldung keine zeitkri- + * tischen Aufträge gesendet werden. + * + * Die Kundenmeldung soll nicht die Funktionalität eines Email- + * Dienstes (Adressierung, Anhängen von Anlagen, Formatierungs- + * möglichkeiten etc.) bieten, sondern diesen ergänzen. Es ist dem + * Kundenprodukt freigestellt, über die Kundenmeldung hinaus zu + * sätz- + * lich die Mailkomponente des jeweiligen Online-Dienstes anzubieten. + * + * Kundenmeldungen sollten im Kundenprodukt gespeichert werden, + * um dem Kunden auch zu einem späteren Zeitpunkt einen Zugriff auf + * seine Meldungen zu ermöglichen. + * + * Der Kunde erhält lediglich eine Bestätigung des Eingangs seiner Meldung, jedoch + * keine Rückmeldung bzgl. der Bearbeitung oder Ausführung (sofern es sich um ei- + * nen Auftrag handelt). Ebenso sind keine weiteren Informationen über den Verarbei- + * tungsvorgang im Statusprotokoll abrufbar. + * + * Format + * Name: Kundenmeldung + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKKDM + * Bezugssegment: - + * Version: 5 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED}) +public class HKKDM extends Segment implements IExecutableElement,IDependentElement +{ + + @Element(description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element(description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.O)}) + private KontoverbindungNational kontoverbindungAuftraggeber; + + @Element(description = {@ElementDescription(number = 3, length = -2048)}) + @an + private String freitextmeldung; + + @Element(description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.O, length = -35)}) + @an + private String betreff; + + @Element(description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.O, length = -35)}) + @an + private String empfaengerangaben; + + public HKKDM(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungNational getKontoverbindungAuftraggeber() { + return kontoverbindungAuftraggeber; + } + + public void setKontoverbindungAuftraggeber(KontoverbindungNational kontoverbindungAuftraggeber) { + this.kontoverbindungAuftraggeber = kontoverbindungAuftraggeber; + } + + public String getFreitextmeldung() { + return freitextmeldung; + } + + public void setFreitextmeldung(String freitextmeldung) { + this.freitextmeldung = freitextmeldung; + } + + public String getBetreff() { + return betreff; + } + + public void setBetreff(String betreff) { + this.betreff = betreff; + } + + public String getEmpfaengerangaben() { + return empfaengerangaben; + } + + public void setEmpfaengerangaben(String empfaengerangaben) { + this.empfaengerangaben = empfaengerangaben; + } + + private HIRMS statusElement; + + @Override + public StatusCode execute(Dialog dialog) { + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + boolean accepted = dataAccessFacade.userMessageSubmitted(dialog.getLegitimationsInfo(),kontoverbindungAuftraggeber,freitextmeldung,betreff,empfaengerangaben); + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(getSegmentkopf().getSegmentNummer()).build()); + StatusCode result; + Rueckmeldung rueckmeldung; + if(accepted) { + rueckmeldung = Rueckmeldung.getRueckmeldung("0010"); + result = StatusCode.OK; + } else { + rueckmeldung = Rueckmeldung.getRueckmeldung("9210"); + result = StatusCode.ERROR; + } + statusElement.addRueckmeldung(rueckmeldung); + return result; + } + + @Override + public List getReplyMessageElements() { + return null; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKKOM.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKKOM.java new file mode 100644 index 0000000..5f5747e --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKKOM.java @@ -0,0 +1,64 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Kik; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; + +import java.util.LinkedList; +import java.util.List; + +/** + * Name: Kommunikationszugang anfordern + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKKOM + * Bezugssegment: - + * Version: 4 + */ +public class HKKOM extends Segment implements IExecutableElement { + + @Element(description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element(description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.O)}) + private Kik vonKreditinstitutskennung; + + @Element(description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.O)}) + private Kik bisKreditinstitutskennung; + + @Element(description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.O)}) + private Integer maximaleAnzahlEintraege; + + @Element(description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.C)}) + private String aufsetzpunkt; + + public HKKOM(byte[] message) { + super(message); + } + + private List replyElements; + private HIRMS statusElement; + + @Override + public StatusCode execute(Dialog dialog) { + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + HIKOM hikom = new HIKOM(segmentkopf.getSegmentVersion(), segmentkopf.getSegmentNummer(), dataAccessFacade.getKommunikationsParameterData(dialog.getBankId())); + replyElements = new LinkedList<>(); + replyElements.add(hikom); + return StatusCode.OK; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKPAE.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKPAE.java new file mode 100644 index 0000000..88136ff --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKPAE.java @@ -0,0 +1,96 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.client.TransactionInfo; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import org.bouncycastle.jce.provider.JDKMessageDigest; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.List; + +/** + * Name: PIN ändern + * Typ:Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKPAE + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKPAE extends Segment implements IExecutableElement, IDependentElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, + length = -99)}) + @an + private String pin; + + private HIRMS statusElement; + + public HKPAE(byte[] message) { + super(message); + } + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + + + //TAN und Hashwert ok + + ReturnDataObject transactionSubmitted = dataAccessFacade.submitChangePin(dialog.getLegitimationsInfo(), this.pin); + if (transactionSubmitted.isSuccess()) { + //die Pinänderung wurde angenommen + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("0020"); + statusElement.addRueckmeldung(rueckmeldung); + //entferne TansactionInfo object vom Dialog + rueckmeldung = Rueckmeldung.getRueckmeldung("0090"); + statusElement.addRueckmeldung(rueckmeldung); + dialog.setTransactionInfo(null); + return StatusCode.OK; + } else { + //die Pinänderung wurde abgelehnt + + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("9942"); + statusElement.addRueckmeldung(rueckmeldung); + //TAN hingegen war ok + rueckmeldung = Rueckmeldung.getRueckmeldung("0090"); + statusElement.addRueckmeldung(rueckmeldung); + //entferne TansactionInfo object vom Dialog + dialog.setTransactionInfo(null); + + return StatusCode.ERROR; + } + } + + @Override + public List getReplyMessageElements() { + return null; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSAK.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSAK.java new file mode 100644 index 0000000..aaeb5da --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSAK.java @@ -0,0 +1,196 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.cryptography.KeyManager; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.OeffentlicherSchluessel; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Schluesselname; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Sicherheitsprofil; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Zertifikat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.RSAPublicKeySpec; +import java.util.LinkedList; +import java.util.List; + +/** + * Name: Änderung eines öffentlichen Schlüssels + * Typ: Segment + * Segmentart: Administration + * Kennung: HKSAK + * Bezugssegment: - + * Sender: Kunde + */ +public class HKSAK extends Segment implements IExecutableElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, length = 1)}) + @code(restrictions = {"2"}) + private String nachrichtenbeziehungKodiert; + + @Element( + description = {@ElementDescription(number = 3, length = -3)}) + @code(restrictions = {"112"}) + private String funktionstypBezeichner; + + @Element( + description = {@ElementDescription(number = 4)}) + private Sicherheitsprofil sicherheitsprofil; + + @Element( + description = {@ElementDescription(number = 5)}) + private Schluesselname schluesselname; + + @Element( + description = {@ElementDescription(number = 6)}) + private OeffentlicherSchluessel oeffentlicherSchluessel; + + @Element( + description = {@ElementDescription(number = 7, status = ElementDescription.StatusCode.O)}) + private Zertifikat zertifikat; + + private List replyElements; + private HIRMS statusElement; + + public HKSAK(byte[] message) { + super(message); + } + + @Override + public StatusCode execute(Dialog dialog) { + StatusCode statusCode; + replyElements = new LinkedList(); + statusElement = new HIRMS(new byte[0]); + Segmentkopf sk;// = new Segmentkopf(("HIRMS:0:2:" + segmentkopf.getSegmentNummer()).getBytes()); + sk = Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build(); + Rueckmeldung rueckmeldung; + statusElement.setSegmentkopf(sk); + if (oeffentlicherSchluessel != null) { + KeyManager keyManager = KeyManager.getInstance(schluesselname.getKreditsinstitutkennung().getKreditinstitutscode()); + SecurityMethod securityMethod = SecurityMethod.valueOf(sicherheitsprofil.getSicherheitsverfahren() + "_" + sicherheitsprofil.getSicherheitsverfahrensversion()); + BigInteger modulus = new BigInteger(1, SegmentUtil.getBytes(oeffentlicherSchluessel.getModulus())); + BigInteger exponent = new BigInteger(1, SegmentUtil.getBytes(oeffentlicherSchluessel.getExponent())); + RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulus, exponent); + try { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); + if (keyManager.addUserPublicKey(schluesselname.getBenutzerkennung(), securityMethod, schluesselname.getSchluesselart(), schluesselname.getSchluesselversion(), schluesselname.getSchluesselnummer(), publicKey)) { + if (schluesselname.getSchluesselart().equals("S")) { + statusElement.addRueckmeldung(new Rueckmeldung("3310::Ini-Brief erforderlich.".getBytes("ISO-8859-1"))); + rueckmeldung = new Rueckmeldung("0010::Entgegengenommen.".getBytes("ISO-8859-1")); + statusCode = StatusCode.OK; + } else { + rueckmeldung = new Rueckmeldung("0010::Entgegengenommen.".getBytes("ISO-8859-1")); + statusCode = StatusCode.OK; + } + } else { + //Schlüsseleintraguung in die Datenbank fehlgschlagen -> Schlüssel bereits vorhanden + //Fehlermeldung darf laut Doku aus Sicherheitsgründen nicht begründet werden! + rueckmeldung = new Rueckmeldung("9010::Auftrag abgelehnt.".getBytes("ISO-8859-1")); + statusCode = StatusCode.ERROR; + } + + } catch (NoSuchAlgorithmException e) { + //return false; + rueckmeldung = new Rueckmeldung("9000::Kann nicht verarbeitet werden.".getBytes()); + statusCode = StatusCode.ERROR; + } catch (InvalidKeySpecException e) { + try { + rueckmeldung = new Rueckmeldung("9010::Verarbeitung nicht möglich.".getBytes("ISO-8859-1")); + statusCode = StatusCode.ERROR; + } catch (UnsupportedEncodingException e1) { + throw new RuntimeException(e); + } + } catch (UnsupportedEncodingException e) { + //ISO-8859-1 is supported + throw new RuntimeException(e); + } + } else { + rueckmeldung = new Rueckmeldung("9160:6:Fehlt.".getBytes()); + statusCode = StatusCode.ERROR; + } + statusElement.addRueckmeldung(rueckmeldung); + //replyElements.add(statusElement); + return statusCode; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public String getNachrichtenbeziehungKodiert() { + return nachrichtenbeziehungKodiert; + } + + public void setNachrichtenbeziehungKodiert(String nachrichtenbeziehungKodiert) { + this.nachrichtenbeziehungKodiert = nachrichtenbeziehungKodiert; + } + + public String getFunktionstypBezeichner() { + return funktionstypBezeichner; + } + + public void setFunktionstypBezeichner(String funktionstypBezeichner) { + this.funktionstypBezeichner = funktionstypBezeichner; + } + + public Sicherheitsprofil getSicherheitsprofil() { + return sicherheitsprofil; + } + + public void setSicherheitsprofil(Sicherheitsprofil sicherheitsprofil) { + this.sicherheitsprofil = sicherheitsprofil; + } + + public Schluesselname getSchluesselname() { + return schluesselname; + } + + public void setSchluesselname(Schluesselname schluesselname) { + this.schluesselname = schluesselname; + } + + public OeffentlicherSchluessel getOeffentlicherSchluessel() { + return oeffentlicherSchluessel; + } + + public void setOeffentlicherSchluessel(OeffentlicherSchluessel oeffentlicherSchluessel) { + this.oeffentlicherSchluessel = oeffentlicherSchluessel; + } + + public Zertifikat getZertifikat() { + return zertifikat; + } + + public void setZertifikat(Zertifikat zertifikat) { + this.zertifikat = zertifikat; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSAL.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSAL.java new file mode 100644 index 0000000..4995ba6 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSAL.java @@ -0,0 +1,199 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.SaldoDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.*; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.jn; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; + +/** + * Name: Saldenabfrage + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKSAL + * Bezugssegment: - + * Version: 6 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKSAL extends Segment implements IDependentElement, IExecutableElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + @Element( + description = {@ElementDescription(number = 2, segmentVersion = 6), + @ElementDescription(number = 2, segmentVersion = 3), + @ElementDescription(number = 2, segmentVersion = 4), + @ElementDescription(number = 2, segmentVersion = 5)}) + @ApplicantAccount + private KontoverbindungNational kontoverbindungAuftraggeber; + @Element( + description = {@ElementDescription(number = 2, segmentVersion = 7)}) + @ApplicantAccount + private KontoverbindungInternational kontoverbindungInternational; + @Element( + description = {@ElementDescription(number = 3, length = 1)}) + @jn + private String alleKonten; + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.O, length = -4)}) + @num + private Integer maximaleAnzahlAuftraege; + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -35)}) + @an + private String aufsetzpunkt; + + private HIRMS statusElement; + private List replyElements; + + public HKSAL(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungNational getKontoverbindungAuftraggeber() { + return kontoverbindungAuftraggeber; + } + + public void setKontoverbindungAuftraggeber(KontoverbindungNational kontoverbindungAuftraggeber) { + this.kontoverbindungAuftraggeber = kontoverbindungAuftraggeber; + } + + public KontoverbindungInternational getKontoverbindungInternational() { + return kontoverbindungInternational; + } + + public void setKontoverbindungInternational(KontoverbindungInternational kontoverbindungInternational) { + this.kontoverbindungInternational = kontoverbindungInternational; + } + + public String getAlleKonten() { + return alleKonten; + } + + public void setAlleKonten(String alleKonten) { + this.alleKonten = alleKonten; + } + + public Integer getMaximaleAnzahlAuftraege() { + return maximaleAnzahlAuftraege; + } + + public void setMaximaleAnzahlAuftraege(Integer maximaleAnzahlAuftraege) { + this.maximaleAnzahlAuftraege = maximaleAnzahlAuftraege; + } + + public String getAufsetzpunkt() { + return aufsetzpunkt; + } + + public void setAufsetzpunkt(String aufsetzpunkt) { + this.aufsetzpunkt = aufsetzpunkt; + } + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + String accountId = ""; + if (segmentkopf.getSegmentVersion() < 7) { + accountId = kontoverbindungAuftraggeber.getKontonummer(); + } else { + accountId = SegmentUtil.ibanToAccountNr(kontoverbindungInternational.getIban()); + } + + SaldoDataObject saldo = dataAccessFacade.getSaldo(accountId, dialog.getLegitimationsInfo()); + double saldoWert = saldo.getGebuchterSaldo(); + + HISAL hisal = new HISAL(new byte[0]); + Segmentkopf sk = Segmentkopf.Builder.newInstance().setSegmentKennung(HISAL.class).setSegmentVersion(segmentkopf.getSegmentVersion()).setBezugssegment(segmentkopf.getSegmentNummer()).build(); + hisal.setSegmentkopf(sk); + hisal.setKontoverbindungAuftraggeber(kontoverbindungAuftraggeber); + hisal.setKontoverbindungInternational(kontoverbindungInternational); + hisal.setKontoproduktBezeichnung(dataAccessFacade.getKontoproduktbezeichnung(accountId, dialog.getLegitimationsInfo())); //TODO: Fehlermeldung, wenn hier was schiefläuft! + hisal.setKontowaehrung(dataAccessFacade.getKontowaehrung(accountId)); + Saldo gebuchterSaldo = new Saldo(new byte[0]); + if (saldoWert < 0) { + gebuchterSaldo.setSollHabenKennzeichen("D"); + } else { + gebuchterSaldo.setSollHabenKennzeichen("C"); + } + Betrag betrag = new Betrag(new byte[0]); + Betrag betragPrebooked = new Betrag(new byte[0]); + Betrag betragVerfuegbarerBetrag = new Betrag(new byte[0]); + Betrag betragKreditlinie = new Betrag(new byte[0]); + betrag.setWaehrung(saldo.getWaehrung()); + betragPrebooked.setWaehrung(saldo.getWaehrung()); + betragVerfuegbarerBetrag.setWaehrung(saldo.getWaehrung()); + betragKreditlinie.setWaehrung(saldo.getWaehrung()); + betrag.setWert(String.format(Locale.GERMAN, "%.2f", Math.abs(saldoWert))); + gebuchterSaldo.setBetrag(betrag); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd"); + gebuchterSaldo.setDatum(simpleDateFormat.format(new Date())); + hisal.setGebuchterSaldo(gebuchterSaldo); + if (saldo.getVerfuegbarerBetrag() != null) { + betragVerfuegbarerBetrag.setWert(String.format(Locale.GERMAN, "%.2f", Math.abs(saldo.getVerfuegbarerBetrag()))); + hisal.setVerfuegbarerBetrag(betragVerfuegbarerBetrag); + } + if (saldo.getKreditlinie() != null) { + betragKreditlinie.setWert(String.format(Locale.GERMAN, "%.2f", Math.abs(saldo.getKreditlinie()))); + hisal.setKreditlinie(betragKreditlinie); + } + if (saldo.getVorgemerkterSaldo() != null) { + Saldo vorgemerkterSaldo = new Saldo(new byte[0]); + if (saldo.getVorgemerkterSaldo() < 0) { + vorgemerkterSaldo.setSollHabenKennzeichen("D"); + } else { + vorgemerkterSaldo.setSollHabenKennzeichen("C"); + } + betragPrebooked.setWert(String.format(Locale.GERMAN, "%.2f", Math.abs(saldo.getVorgemerkterSaldo()))); + vorgemerkterSaldo.setBetrag(betragPrebooked); + vorgemerkterSaldo.setDatum(simpleDateFormat.format(new Date())); + hisal.setSaldoDerVorgemerktenUmsaetze(vorgemerkterSaldo); + } + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("0020"); + statusElement.addRueckmeldung(rueckmeldung); + + replyElements = new LinkedList(); + replyElements.add(hisal); + hisal.getSegmentkopf().setSegmentNummer(0); + return StatusCode.OK; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSPA.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSPA.java new file mode 100644 index 0000000..4284957 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSPA.java @@ -0,0 +1,124 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.AccountDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Kik; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungNational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungZvInternational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * Name: SEPA-Kontoverbindung anfordern + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKSPA + * Bezugssegment: - + * Segmentversion: 1 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKSPA extends Segment implements IExecutableElement, IDependentElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + private List kontoverbindungenNational; + + private HIRMS statusElement; + private List replyElements; + + public HKSPA(byte[] message) { + super(message); + } + + @Override + public StatusCode execute(Dialog dialog) { + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + HISPA hispa = new HISPA(new byte[0]); + hispa.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentVersion(segmentkopf.getSegmentVersion()).setSegmentKennung(HISPA.class).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + if (kontoverbindungenNational != null) { + + for (KontoverbindungNational kontoverbindungNational : kontoverbindungenNational) { + if (kontoverbindungNational == null) { + continue; + } + KontoverbindungZvInternational ktz = new KontoverbindungZvInternational(new byte[0]); + ktz.setKontoverwendungSepa("J"); + ktz.setIban(SegmentUtil.accountToIban(kontoverbindungNational.getKontonummer(), kontoverbindungNational.getKreditinstitutskennung().getKreditinstitutscode())); + ktz.setBic(dataAccessFacade.getBic()); + ktz.setKonto_depot_nummer(kontoverbindungNational.getKontonummer()); + ktz.setKreditsinstitutskennung(kontoverbindungNational.getKreditinstitutskennung()); + hispa.addSepaKontoverbindung(ktz); + } + } else { + ArrayList accountData = dataAccessFacade.getAccountData(dialog.getLegitimationsInfo()); + if (accountData != null) { + for (AccountDataObject accountDataObject : accountData) { + if (!accountDataObject.getErlaubteGeschaeftsvorfaelle().contains("HKSPA:1")) { + continue; + } + KontoverbindungZvInternational ktz = new KontoverbindungZvInternational(new byte[0]); + ktz.setKontoverwendungSepa("J"); + ktz.setIban(SegmentUtil.accountToIban(accountDataObject.getKontonummer(), dialog.getLegitimationsInfo().getBankId())); + ktz.setBic(dataAccessFacade.getBic()); + ktz.setKonto_depot_nummer(accountDataObject.getKontonummer()); + Kik kik = new Kik(("280:" + dialog.getLegitimationsInfo().getBankId()).getBytes()); + ktz.setKreditsinstitutskennung(kik); + hispa.addSepaKontoverbindung(ktz); + } + } + } + replyElements = new LinkedList(); + replyElements.add(hispa); + return StatusCode.OK; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public List getKontoverbindungenNational() { + return kontoverbindungenNational; + } + + public void setKontoverbindungenNational(List kontoverbindungenNational) { + this.kontoverbindungenNational = kontoverbindungenNational; + } + + public void addKontoverbindungNational(KontoverbindungNational kontoverbindungNational) { + if (kontoverbindungenNational == null) { + kontoverbindungenNational = new LinkedList(); + } + kontoverbindungenNational.add(kontoverbindungNational); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSYN.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSYN.java new file mode 100644 index 0000000..f9d8afa --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSYN.java @@ -0,0 +1,64 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.client.ClientProductInfo; +import net.petafuel.fuelifints.model.client.LegitimationInfo; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; + +import java.util.LinkedList; +import java.util.List; + +/** + * Name: Synchronisierungsnachricht + * Typ: Nachricht + * Sender: Kunde + */ +public class HKSYN extends Segment implements IExecutableElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + @Element( + description = {@ElementDescription(number = 2, length = 1)}) + @code(restrictions = {"0", "1", "2"}) + private String synchronisierungsmodus; + private List replyElements; + + public HKSYN(byte[] message) { + super(message); + } + + @Override + public StatusCode execute(Dialog dialog) { + replyElements = new LinkedList(); + dialog.getClientProductInfo().hksynRequested(); + ClientProductInfo clientProductInfo = dialog.getClientProductInfo(); + LegitimationInfo legitimationInfo = dialog.getLegitimationsInfo(); + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(legitimationInfo.getBankId()); + String userSystemId = dataAccessFacade.getOrGenerateUserSystemId(dialog.getDialogId(), legitimationInfo, clientProductInfo); + clientProductInfo.setUserSystemId(userSystemId); + HISYN hisyn = new HISYN(new byte[0]); + Segmentkopf segmentkopf;// = new Segmentkopf(("HISYN:0:4:"+this.segmentkopf.getSegmentNummer()).getBytes()); + segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(HISYN.class).setSegmentVersion(4).setBezugssegment(this.segmentkopf.getSegmentNummer()).build(); + + hisyn.setSegmentkopf(segmentkopf); + hisyn.setKundensystemId(userSystemId); + replyElements.add(hisyn); + return StatusCode.OK; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKTAN.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKTAN.java new file mode 100644 index 0000000..e03c0e1 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKTAN.java @@ -0,0 +1,387 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.FinTSVersionSwitch; +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.AccountDataObject; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.client.TransactionInfo; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.*; +import net.petafuel.fuelifints.protocol.fints3.segments.parameter.HIBPA; +import net.petafuel.fuelifints.protocol.fints3.segments.parameter.HIUPA; +import net.petafuel.fuelifints.protocol.fints3.segments.parameter.HIUPD; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.*; +import net.petafuel.mt94x.Helper; + +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * Name: Zwei-Schritt-TAN-Einreichung + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKTAN + * Bezugssegment: - + * Segmentversion: 1 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED}) +public class HKTAN extends Segment implements IExecutableElement, IDependentElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @code(restrictions = {"1", "2", "3", "4"}) + private String tanProzess; + + @Element( + description = { + @ElementDescription(number = 3, status = ElementDescription.StatusCode.C, length = -6, segmentVersion = 5), + @ElementDescription(number = 3, status = ElementDescription.StatusCode.C, length = -6, segmentVersion = 6) + } + ) + @an + private String segmentKennung; + + @Element( + description = { + @ElementDescription(number = 4, status = ElementDescription.StatusCode.C, segmentVersion = 5), + @ElementDescription(number = 4, status = ElementDescription.StatusCode.C, segmentVersion = 6), + } + ) + private KontoverbindungInternational kontoverbindungInternationalAuftraggeber; + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.C, length = -256, segmentVersion = 1), + @ElementDescription(number = 3, status = ElementDescription.StatusCode.C, length = -256, segmentVersion = 2), + @ElementDescription(number = 3, status = ElementDescription.StatusCode.C, length = -256, segmentVersion = 3), + @ElementDescription(number = 3, status = ElementDescription.StatusCode.C, length = -256, segmentVersion = 4), + @ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -256, segmentVersion = 5), + @ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -256, segmentVersion = 6) + }) + @bin + private byte[] auftragsHashwert; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C, length = -35, segmentVersion = 1), + @ElementDescription(number = 4, status = ElementDescription.StatusCode.C, length = -35, segmentVersion = 2), + @ElementDescription(number = 4, status = ElementDescription.StatusCode.C, length = -35, segmentVersion = 3), + @ElementDescription(number = 4, status = ElementDescription.StatusCode.C, length = -35, segmentVersion = 4), + @ElementDescription(number = 6, status = ElementDescription.StatusCode.C, length = -35, segmentVersion = 5), + @ElementDescription(number = 6, status = ElementDescription.StatusCode.C, length = -35, segmentVersion = 6) + } + ) + @an + private String Auftragsreferenz; + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -20, segmentVersion = 1), + @ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -20, segmentVersion = 2), + @ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -20, segmentVersion = 3), + @ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -20, segmentVersion = 4), + @ElementDescription(number = 7, status = ElementDescription.StatusCode.C, length = -20, segmentVersion = 5)}) + @an + private String tanListennummer; + + @Element( + description = {@ElementDescription(number = 6, status = ElementDescription.StatusCode.C, length = 1, segmentVersion = 1), + @ElementDescription(number = 6, status = ElementDescription.StatusCode.C, length = 1, segmentVersion = 2), + @ElementDescription(number = 6, status = ElementDescription.StatusCode.C, length = 1, segmentVersion = 3), + @ElementDescription(number = 6, status = ElementDescription.StatusCode.C, length = 1, segmentVersion = 4), + @ElementDescription(number = 8, status = ElementDescription.StatusCode.C, length = 1, segmentVersion = 5), + @ElementDescription(number = 7, status = ElementDescription.StatusCode.C, length = 1, segmentVersion = 6) + } + ) + @jn + private String weitereTanFolgt; + + @Element( + description = {@ElementDescription(number = 7, status = ElementDescription.StatusCode.C, length = -99, segmentVersion = 1)}) + @an + private String tanZusatzinformationen; + + @Element( + description = {@ElementDescription(number = 7, status = ElementDescription.StatusCode.C, length = 1, segmentVersion = 2), + @ElementDescription(number = 7, status = ElementDescription.StatusCode.C, length = 1, segmentVersion = 3), + @ElementDescription(number = 7, status = ElementDescription.StatusCode.C, length = 1, segmentVersion = 4), + @ElementDescription(number = 9, status = ElementDescription.StatusCode.C, length = 1, segmentVersion = 5), + @ElementDescription(number = 8, status = ElementDescription.StatusCode.C, length = 1, segmentVersion = 6) + } + ) + @jn + private String auftragStornieren; + + @Element( + description = { + @ElementDescription(number = 8, status = ElementDescription.StatusCode.C, segmentVersion = 4), + @ElementDescription(number = 10, status = ElementDescription.StatusCode.C, segmentVersion = 5), + @ElementDescription(number = 9, status = ElementDescription.StatusCode.C, segmentVersion = 6) + } + ) + private KontoverbindungInternational smsAbbuchungskonto; + + @Element( + description = {@ElementDescription(number = 8, status = ElementDescription.StatusCode.C, length = -2, segmentVersion = 2), + @ElementDescription(number = 8, status = ElementDescription.StatusCode.C, length = -2, segmentVersion = 3), + @ElementDescription(number = 9, status = ElementDescription.StatusCode.C, length = -2, segmentVersion = 4), + @ElementDescription(number = 11, status = ElementDescription.StatusCode.C, length = -2, segmentVersion = 5), + @ElementDescription(number = 10, status = ElementDescription.StatusCode.C, length = -2, segmentVersion = 6) + } + ) + @num + private Integer challengeKlasse; + + @Element( + description = {@ElementDescription(number = 9, status = ElementDescription.StatusCode.C, segmentVersion = 2), + @ElementDescription(number = 9, status = ElementDescription.StatusCode.C, segmentVersion = 3), + @ElementDescription(number = 10, status = ElementDescription.StatusCode.C, segmentVersion = 4), + @ElementDescription(number = 12, status = ElementDescription.StatusCode.C, segmentVersion = 5), + @ElementDescription(number = 11, status = ElementDescription.StatusCode.C, segmentVersion = 6) + } + ) + private ParameterChallengeKlasse parameterChallengeKlasse; + + @Element( + description = { + @ElementDescription(number = 10, status = ElementDescription.StatusCode.C, length = -32, segmentVersion = 3), + @ElementDescription(number = 11, status = ElementDescription.StatusCode.C, length = -32, segmentVersion = 4), + @ElementDescription(number = 13, status = ElementDescription.StatusCode.C, length = -32, segmentVersion = 5), + @ElementDescription(number = 12, status = ElementDescription.StatusCode.C, length = -32, segmentVersion = 6) + } + ) + @an + private String bezeichnungDesTanMediums; + + @Element( + description = { + @ElementDescription(number = 13, status = ElementDescription.StatusCode.C, length = -32, segmentVersion = 6) + } + ) + @jn + private String antwortHddUc; + + public HKTAN(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public String getTanProzess() { + return tanProzess; + } + + public void setTanProzess(String tanProzess) { + this.tanProzess = tanProzess; + } + + public byte[] getAuftragsHashwert() { + return auftragsHashwert; + } + + public void setAuftragsHashwert(byte[] auftragsHashwert) { + this.auftragsHashwert = auftragsHashwert; + } + + public String getAuftragsreferenz() { + return Auftragsreferenz; + } + + public void setAuftragsreferenz(String auftragsreferenz) { + Auftragsreferenz = auftragsreferenz; + } + + public String getTanListennummer() { + return tanListennummer; + } + + public void setTanListennummer(String tanListennummer) { + this.tanListennummer = tanListennummer; + } + + public String getWeitereTanFolgt() { + return weitereTanFolgt; + } + + public void setWeitereTanFolgt(String weitereTanFolgt) { + this.weitereTanFolgt = weitereTanFolgt; + } + + public String getTanZusatzinformationen() { + return tanZusatzinformationen; + } + + public void setTanZusatzinformationen(String tanZusatzinformationen) { + this.tanZusatzinformationen = tanZusatzinformationen; + } + + private HIRMS statusElement; + private List replyElements; + + @Override + public StatusCode execute(Dialog dialog) { + + /* + * Eingabeprüfung: + * Prozessvariante == 1? + * Hashwertlänge != 0? + */ + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setBezugssegment(segmentkopf.getSegmentNummer()).setSegmentVersion(2).build()); + + if (!this.getTanProzess().equals("1") || + this.auftragsHashwert.length == 0) { + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("9980"); + statusElement.addRueckmeldung(rueckmeldung); + return StatusCode.ERROR; + } + if ((getAuftragsreferenz() != null && !getAuftragsreferenz().isEmpty()) && dialog.getTransactionInfo() == null) { + //Es wurde eine Auftragsreferenz übermittelt, jedoch existiert keine TransactionInfo dazu + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("9960"); + statusElement.addRueckmeldung(rueckmeldung); + return StatusCode.ERROR; + } + + /* + * Challenge von der DataAccessFacade holen + */ + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + String auftragsreferenz = dataAccessFacade.generateAuftragsreferenz(dialog.getDialogId(), dialog.getLegitimationsInfo(), dialog.getClientProductInfo(), this.getAuftragsHashwert()); + + replyElements = new LinkedList(); + + TransactionInfo transactionInfo = dialog.getTransactionInfo(); + + // Falls eine TransactionInfo vorliegt soll das referenzierte Segment ausführen werden + // Abschließend wird die TransactionInfo gelöschen, um Folgefehler zu vermeiden + if (transactionInfo != null && this.segmentKennung.equals("HKIDN")) { + + // statusElement zurücksetzen + statusElement = null; + + HIUPA hiupa = new HIUPA(dialog.getLegitimationsInfo().getUserId(), dataAccessFacade.getCurrentUpdVersion(dialog.getLegitimationsInfo()), null, segmentkopf.getSegmentNummer()); + replyElements.add(hiupa); + ArrayList dataObjects = dataAccessFacade.getAccountData(dialog.getLegitimationsInfo()); + for (AccountDataObject ado : dataObjects) { + ado.setBankleitzahl(dialog.getBankId()); + replyElements.add(new HIUPD(ado, segmentkopf.getSegmentNummer())); + } + + HITAN hitan = new HITAN(new byte[0]); + hitan.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HITAN.class).setBezugssegment(segmentkopf.getSegmentNummer()).setSegmentVersion(segmentkopf.getSegmentVersion()).build()); + hitan.setTanProzess("4"); + hitan.setAuftragsHashwert(auftragsHashwert); + hitan.setAuftragsreferenz(auftragsreferenz); + + replyElements.add(hitan); + replyElements.add(0, new HIBPA(dataAccessFacade.getCommonBankParameters(FinTSVersionSwitch.FinTSVersion.FINTS_VERSION_3_0), segmentkopf.getSegmentNummer())); + + dialog.setTransactionInfo(null); + return StatusCode.OK; + } + + if(dialog.getLegitimationsInfo().isStrongAuthenticated()) { + + HIUPA hiupa = new HIUPA(dialog.getLegitimationsInfo().getUserId(), dataAccessFacade.getCurrentUpdVersion(dialog.getLegitimationsInfo()), null, segmentkopf.getSegmentNummer()); + replyElements.add(hiupa); + ArrayList dataObjects = dataAccessFacade.getAccountData(dialog.getLegitimationsInfo()); + for (AccountDataObject ado : dataObjects) { + ado.setBankleitzahl(dialog.getBankId()); + replyElements.add(new HIUPD(ado, segmentkopf.getSegmentNummer())); + } + + HITAN hitan = new HITAN(new byte[0]); + hitan.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HITAN.class).setBezugssegment(segmentkopf.getSegmentNummer()).setSegmentVersion(segmentkopf.getSegmentVersion()).build()); + hitan.setTanProzess("1"); + hitan.setAuftragsHashwert(auftragsHashwert); + hitan.setAuftragsreferenz(auftragsreferenz); + + replyElements.add(hitan); + return StatusCode.OK; + } + + /* + * Wir nutzen zur Zeit nur Prozessvariante 1 bei der 2-Schritt-Taneinreichung: + * + * TAN-Prozess=1: + * + * Im ersten Schritt wird ein Auftrags-Hashwert zum Institut übermittelt, der zur Her- + * leitung der Challenge dient, die vom Institut zum Kundenprodukt gesendet wird. + * Im zweiten Schritt werden die Auftragsdaten inklusive TAN eingereicht und bestä- + * tigt. + */ + + StatusCode resultCode = StatusCode.OK; + ReturnDataObject returnDataObject = dataAccessFacade.getChallenge(dialog.getDialogId(), dialog.getLegitimationsInfo(), dialog.getClientProductInfo(), this.getAuftragsHashwert(), dialog.getLegitimationsInfo().getSicherheitsfunktion(), this.parameterChallengeKlasse!=null?this.parameterChallengeKlasse.getChallengeKlasseParameter():new ArrayList()); + String challenge = returnDataObject.getMessage(); + if (!returnDataObject.isSuccess()) { + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("9999"); + rueckmeldung.setRueckmeldungstext(challenge); + statusElement.addRueckmeldung(rueckmeldung); + return StatusCode.ERROR; + } + + if (transactionInfo == null){ + //es liegt keine TransactionInfo vor und es wird eine neue erstellt + transactionInfo = new TransactionInfo(); + transactionInfo.setTanProzess(tanProzess); + transactionInfo.setAuftragsHashwert(SegmentUtil.getBytes(auftragsHashwert)); + transactionInfo.setAuftragsreferenz(auftragsreferenz); + transactionInfo.setTanListennummer(tanListennummer); + transactionInfo.setChallenge(challenge); + if (parameterChallengeKlasse != null) //gibt es nur in Segmentversion 5 + transactionInfo.setParameterChallengeKlasse(parameterChallengeKlasse.getChallengeKlasseParameter()); + } + HITAN hitan = new HITAN(new byte[0]); + hitan.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HITAN.class).setBezugssegment(segmentkopf.getSegmentNummer()).setSegmentVersion(segmentkopf.getSegmentVersion()).build()); + hitan.setTanProzess(tanProzess); + hitan.setAuftragsHashwert(auftragsHashwert); + hitan.setAuftragsreferenz(auftragsreferenz); + hitan.setChallenge(challenge); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd:HHmmss"); + GregorianCalendar gregorianCalendar = new GregorianCalendar(); + Date date = new GregorianCalendar().getTime(); + //15 Minuten gültige TAN + gregorianCalendar.setTimeInMillis(date.getTime() + 1000 * 60 * 15); + transactionInfo.setGueltigkeitsDatum(gregorianCalendar.getTime()); + GueltigkeitsdatumUndUhrzeitChallenge gueltigkeitsdatumUndUhrzeitChallenge = new GueltigkeitsdatumUndUhrzeitChallenge(simpleDateFormat.format(gregorianCalendar.getTime()).getBytes()); + hitan.setGueltigkeitsdatumUndUhrzeitChallenge(gueltigkeitsdatumUndUhrzeitChallenge); + hitan.setTanListennummer(tanListennummer); + hitan.setTanZusatzinformationen(tanZusatzinformationen); + + + replyElements.add(hitan); + + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("0030"); + statusElement.addRueckmeldung(rueckmeldung); + if (resultCode == StatusCode.OK) { + dialog.setTransactionInfo(transactionInfo); + } + return resultCode; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKUEB.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKUEB.java new file mode 100644 index 0000000..f9c07f6 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKUEB.java @@ -0,0 +1,191 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Betrag; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungNational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Verwendungszweck; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dig; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dta; + +import java.util.List; + +/** + * Name: Einzelüberweisung + * Typ: Segment + * Segmentart: Geschäftsvorfall + * Kennung: HKUEB + * Bezugssegment: - + * Version: 5 + * Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKUEB extends Segment implements IExecutableElement, IDependentElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @ApplicantAccount + private KontoverbindungNational kontoverbindungAuftraggeber; + + @Element( + description = {@ElementDescription(number = 3)}) + private KontoverbindungNational kontoverbindungEmpfaenger; + + @Element( + description = {@ElementDescription(number = 4, length = -27)}) + @dta + private String nameEmpfaenger1; + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.O, length = -27)}) + @dta + private String nameEmpfaenger2; + + @Element( + description = {@ElementDescription(number = 6)}) + private Betrag betrag; + + @Element( + description = {@ElementDescription(number = 7, length = 2)}) + @dig + private String textschluessel; + + @Element( + description = {@ElementDescription(number = 8, status = ElementDescription.StatusCode.O, length = 3)}) + @dig + private String textschluesselErgaenzung; + + @Element( + description = {@ElementDescription(number = 9, status = ElementDescription.StatusCode.O)}) + private Verwendungszweck verwendungszweck; + + public HKUEB(byte[] message) { + super(message); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungNational getKontoverbindungAuftraggeber() { + return kontoverbindungAuftraggeber; + } + + public void setKontoverbindungAuftraggeber(KontoverbindungNational kontoverbindungAuftraggeber) { + this.kontoverbindungAuftraggeber = kontoverbindungAuftraggeber; + } + + public KontoverbindungNational getKontoverbindungEmpfaenger() { + return kontoverbindungEmpfaenger; + } + + public void setKontoverbindungEmpfaenger(KontoverbindungNational kontoverbindungEmpfaenger) { + this.kontoverbindungEmpfaenger = kontoverbindungEmpfaenger; + } + + public String getNameEmpfaenger1() { + return nameEmpfaenger1; + } + + public void setNameEmpfaenger1(String nameEmpfaenger1) { + this.nameEmpfaenger1 = nameEmpfaenger1; + } + + public String getNameEmpfaenger2() { + return nameEmpfaenger2; + } + + public void setNameEmpfaenger2(String nameEmpfaenger2) { + this.nameEmpfaenger2 = nameEmpfaenger2; + } + + public Betrag getBetrag() { + return betrag; + } + + public void setBetrag(Betrag betrag) { + this.betrag = betrag; + } + + public String getTextschluessel() { + return textschluessel; + } + + public void setTextschluessel(String textschluessel) { + this.textschluessel = textschluessel; + } + + public String getTextschluesselErgaenzung() { + return textschluesselErgaenzung; + } + + public void setTextschluesselErgaenzung(String textschluesselErgaenzung) { + this.textschluesselErgaenzung = textschluesselErgaenzung; + } + + public Verwendungszweck getVerwendungszweck() { + return verwendungszweck; + } + + public void setVerwendungszweck(Verwendungszweck verwendungszweck) { + this.verwendungszweck = verwendungszweck; + } + + private HIRMS statusElement; + private List replyElements; + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + + ReturnDataObject transactionSubmitted = dataAccessFacade.submitNewTransaction(dialog.getLegitimationsInfo(), getKontoverbindungAuftraggeber(), getKontoverbindungEmpfaenger(), getNameEmpfaenger1(), getNameEmpfaenger2(), getTextschluessel(), getTextschluesselErgaenzung(), getVerwendungszweck().getVerwendungszweckzeilen(),getBetrag(), "", ""); + StatusCode statusCode; + if (transactionSubmitted.isSuccess()) { + //die Überweisung wurde angenommen + statusCode = StatusCode.OK; + } else { + //die Überweisung wurde abgelehnt + statusCode = StatusCode.ERROR; + } + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode(transactionSubmitted.getReturnCode()); + rueckmeldung.setRueckmeldungstext(transactionSubmitted.getMessage()); + statusElement.addRueckmeldung(rueckmeldung); + //entferne TansactionInfo object vom Dialog + dialog.setTransactionInfo(null); + return statusCode; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKVMK.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKVMK.java new file mode 100644 index 0000000..f595776 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKVMK.java @@ -0,0 +1,105 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IDependentElement; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.annotations.ApplicantAccount; +import net.petafuel.fuelifints.protocol.fints3.annotations.Requires; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungNational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.jn; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +/** + * Name: Vormerkposten anfordern + Typ: Segment + Segmentart: Geschäftsvorfall + Kennung: HKVMK + Bezugssegment: - + Version: 1 + Sender: Kunde + */ +@Requires({Requires.Requirement.EXECUTION_ALLOWED, + Requires.Requirement.KUNDENSYSTEM_ID, + Requires.Requirement.USER_IDENTIFIED, + Requires.Requirement.TAN}) +public class HKVMK extends Segment implements IExecutableElement, IDependentElement { + + @Element( + description = {@ElementDescription(number = 1)} + ) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @ApplicantAccount + private KontoverbindungNational kontoverbindungAuftraggeber; + + @Element( + description = {@ElementDescription(number = 3, length = 1)}) + @jn + private String alleKonten; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C, length = -4)}) + @num + private Integer maximaleAnzahlAuftraege; + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -35)}) + @an + private String aufsetzpunkt; + + public HKVMK(byte[] message) { + super(message); + } + + + private HIRMS statusElement; + private List replyMessageElements; + + @Override + public StatusCode execute(Dialog dialog) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(segmentkopf.getSegmentNummer()).build()); + + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + + byte[] nichtGebuchteUmsaetze = dataAccessFacade.getNichtGebuchteUmsaetze(kontoverbindungAuftraggeber.getKontonummer(), null, null, dialog.getLegitimationsInfo()); + if ((nichtGebuchteUmsaetze == null || nichtGebuchteUmsaetze.length == 0)) { + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("3010"); + statusElement.addRueckmeldung(rueckmeldung); + return StatusCode.WARNING; + } + + HIVMK hivmk = new HIVMK(nichtGebuchteUmsaetze, getSegmentkopf().getSegmentNummer(), getSegmentkopf().getSegmentVersion()); + replyMessageElements = new LinkedList(); + replyMessageElements.add(hivmk); + + Rueckmeldung rueckmeldung = Rueckmeldung.getRueckmeldung("0020"); + statusElement.addRueckmeldung(rueckmeldung); + + return StatusCode.OK; + } + + @Override + public List getReplyMessageElements() { + return replyMessageElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKVVB.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKVVB.java new file mode 100644 index 0000000..35be84c --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HKVVB.java @@ -0,0 +1,194 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.FinTSVersionSwitch; +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.dataaccess.dataobjects.AccountDataObject; +import net.petafuel.fuelifints.dataaccess.dataobjects.BankMessageObject; +import net.petafuel.fuelifints.dataaccess.dataobjects.ParameterDataObject; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.client.ClientProductInfo; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Rueckmeldung; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.segments.parameter.*; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * Name: Verarbeitungsvorbereitung + * Typ: Segment + * Segmentart: Administration + * Kennung: HKVVB + * Sender: Kunde + */ + +public class HKVVB extends Segment implements IExecutableElement { + + private static final Logger LOG = LogManager.getLogger(HKVVB.class); + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, + length = -3)}) + @num + private Integer bpd_version; + + @Element( + description = {@ElementDescription(number = 3, + length = -3)}) + @num + private Integer upd_Version; + + @Element( + description = {@ElementDescription(number = 4, + length = -3)}) + @num + private Integer dialogsprache; + + @Element( + description = {@ElementDescription(number = 5, + length = -25)}) + @an + private String produktbezeichnung; + + @Element( + description = {@ElementDescription(number = 6, + length = -5)}) + @an + private String produktversion; + + private List replyElements; + private HIRMS statusElement; + + public HKVVB(byte[] message) { + super(message); + } + + @Override + public String toString() { + return "HKVVB{" + + "segmentkopf=" + segmentkopf + + ", bpd_version=" + bpd_version + + ", upd_Version=" + upd_Version + + ", dialogsprache=" + dialogsprache + + ", produktbezeichnung='" + produktbezeichnung + '\'' + + ", produktversion='" + produktversion + '\'' + + '}'; + } + + @Override + public StatusCode execute(Dialog dialog) { + StatusCode resultCode = StatusCode.OK; + replyElements = new LinkedList(); + ClientProductInfo clientProductInfo = dialog.getClientProductInfo(); + clientProductInfo.setCurrentClientBPDVersion(bpd_version); + clientProductInfo.setCurrentClientUPDVersion(upd_Version); + clientProductInfo.setDialogLanguage(dialogsprache); + clientProductInfo.setClientProductName(produktbezeichnung); + clientProductInfo.setClientProductVersion(produktversion); + + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getLegitimationsInfo().getBankId()); + + + LOG.debug("Getting Bankparameter...."); + if (!dataAccessFacade.isBpdVersionCurrent(bpd_version)) { + //BPD nicht aktuell, schicke komplette Parameter: + + /* + HIBPA - M + HIKOM - O + HISHV - O + HIKPV - O + Parameterdaten - O + */ + HIKOM hikom = null; + try { + hikom = new HIKOM(segmentkopf.getSegmentVersion(), segmentkopf.getSegmentNummer(), dataAccessFacade.getKommunikationsParameterData(dialog.getBankId())); + } catch (Exception ex) { + //optionales feld muss nicht implementiert werden + } + + ArrayList dataObjects = dataAccessFacade.getParameterData(FinTSVersionSwitch.FinTSVersion.FINTS_VERSION_3_0); + LOG.debug("Anzahl dataObjects: {}", dataObjects.size()); + + for (ParameterDataObject pdo : dataObjects) { + replyElements.add(0, new ParameterSegment(pdo, segmentkopf.getSegmentNummer())); + } + replyElements.add(0, new HISHV(segmentkopf.getSegmentNummer())); + replyElements.add(new HIPINS(dataAccessFacade.getPinParameter(), segmentkopf.getSegmentNummer())); + if (hikom != null) { + replyElements.add(0, hikom); + } + replyElements.add(0, new HIBPA(dataAccessFacade.getCommonBankParameters(FinTSVersionSwitch.FinTSVersion.FINTS_VERSION_3_0), segmentkopf.getSegmentNummer())); + } + LOG.debug("Getting Userparameter...."); + if (dialog.getLegitimationsInfo() != null && !dialog.getLegitimationsInfo().isAnonymousAccount()) { + if (dialog.getLegitimationsInfo().isStrongAuthenticated()) { + HIUPA hiupa = new HIUPA(dialog.getLegitimationsInfo().getUserId(), dataAccessFacade.getCurrentUpdVersion(dialog.getLegitimationsInfo()), null, segmentkopf.getSegmentNummer()); + replyElements.add(hiupa); + ArrayList dataObjects = dataAccessFacade.getAccountData(dialog.getLegitimationsInfo()); + LOG.debug("Found {} dataobjects!", dataObjects.size()); + for (AccountDataObject ado : dataObjects) { + ado.setBankleitzahl(dialog.getBankId()); + replyElements.add(new HIUPD(ado, segmentkopf.getSegmentNummer())); + } + } + + if (statusElement == null/* && !dialog.getLegitimationsInfo().isAnonymousAccount()*/ && (dialog.getLegitimationsInfo().getSecurityMethod() == SecurityMethod.PIN_1 || dialog.getLegitimationsInfo().getSecurityMethod() == SecurityMethod.PIN_2)) { + statusElement = new HIRMS(new byte[0]); + statusElement.setSegmentkopf(Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setBezugssegment(segmentkopf.getSegmentNummer()).setSegmentVersion(2).build()); + List zugelasseneTanVerfahren = dataAccessFacade.getZugelasseneTanVerfahren(dialog.getLegitimationsInfo()); + + Rueckmeldung r = new Rueckmeldung(new byte[0]); + r.setRueckmeldungscode("0020"); + r.setRueckmeldungstext("Dialoginitialisierung erfolgreich"); + + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode("3920"); + rueckmeldung.setRueckmeldungstext("Zugelassene TAN-Verfahren fuer den Benutzer"); + rueckmeldung.setRueckmeldungsparamter(zugelasseneTanVerfahren); + statusElement.addRueckmeldung(r); + statusElement.addRueckmeldung(rueckmeldung); + resultCode = StatusCode.WARNING; + } + //Server-Nachrichten mitliefern + List bankMessageObjects = dataAccessFacade.getBankMessages(dialog.getLegitimationsInfo(), dialog.getClientProductInfo()); + for (BankMessageObject messageObject : bankMessageObjects) { + if (messageObject != null) { + HIKIM hikim = new HIKIM(messageObject.getSubject(), messageObject.getMessage()); + replyElements.add(hikim); + } + } + } else { + LOG.debug("no legitimationinfo or anonymous request"); + } + LOG.debug("....HKVVB executing finished!"); + return resultCode; + } + + @Override + public List getReplyMessageElements() { + return replyElements; + } + + @Override + public IMessageElement getStatusElement() { + return statusElement; + } + + public String getProduktbezeichnung() { + return produktbezeichnung; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNHBK.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNHBK.java new file mode 100644 index 0000000..5a59d47 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNHBK.java @@ -0,0 +1,122 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Bezugsnachricht; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dig; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Name: Nachrichtenkopf + * Typ: Segment + * Segmentart: Administration + * Kennung: HNHBK + * Sender: Kunde/Kreditinstitut + */ +public class HNHBK extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, + length = 12)}) + @dig + private String nachrichtengroesse; + + @Element( + description = {@ElementDescription(number = 3, + length = -3)}) + @num + private Integer hbci_version; + @Element( + description = {@ElementDescription(number = 4, + length = 0)}) + @id + private String dialog_id; + @Element( + description = {@ElementDescription(number = 5, + length = -4)}) + @num + private Integer nachrichtennummer; + + @Element( + description = {@ElementDescription(number = 6, + status = ElementDescription.StatusCode.C/*, + length = 0*/)}) + private Bezugsnachricht bezugsnachricht; + + + // HNHBK Example: + // HNHBK:1:3+MESSAGESIZE0+220+0+1' + + + public HNHBK(byte[] message) { + super(message); + } + + @Override + public String toString() { + return "HNHBK{" + + "segmentkopf=" + segmentkopf + + ", nachrichtengroesse='" + nachrichtengroesse + '\'' + + ", hbci_version='" + hbci_version + '\'' + + ", dialog_id='" + dialog_id + '\'' + + ", nachrichtennummer='" + nachrichtennummer + '\'' + + ", bezugsnachricht='" + bezugsnachricht + '\'' + + '}'; + } + + public String getNachrichtengroesse() { + return nachrichtengroesse; + } + + public Integer getNachrichtennummer() { + return nachrichtennummer; + } + + public Bezugsnachricht getBezugsnachricht() { + return bezugsnachricht; + } + + public String getDialogId() { + return dialog_id; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public void setNachrichtengroesse(String nachrichtengroesse) { + this.nachrichtengroesse = nachrichtengroesse; + } + + public Integer getHbci_version() { + return hbci_version; + } + + public void setHbci_version(Integer hbci_version) { + this.hbci_version = hbci_version; + } + + public String getDialog_id() { + return dialog_id; + } + + public void setDialog_id(String dialog_id) { + this.dialog_id = dialog_id; + } + + public void setNachrichtennummer(Integer nachrichtennummer) { + this.nachrichtennummer = nachrichtennummer; + } + + public void setBezugsnachricht(Bezugsnachricht bezugsnachricht) { + this.bezugsnachricht = bezugsnachricht; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNHBS.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNHBS.java new file mode 100644 index 0000000..9f3aa29 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNHBS.java @@ -0,0 +1,52 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Name: Nachrichtenabschluss + * Typ: Segment + * Segmentart: Administration + * Kennung: HNHBS + * Sender: Kunde/Kreditinstitut + */ + +public class HNHBS extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @num + private Integer nachrichtennummer; + + public HNHBS(byte[] message) { + super(message); + } + + @Override + public String toString() { + return "HNHBS{" + + "segmentkopf=" + segmentkopf + + ", nachrichtennummer=" + nachrichtennummer + + '}'; + } + + public Integer getNachrichtennummer() { + return nachrichtennummer; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public void setNachrichtennummer(Integer nachrichtennummer) { + this.nachrichtennummer = nachrichtennummer; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNSHA.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNSHA.java new file mode 100644 index 0000000..85c2c17 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNSHA.java @@ -0,0 +1,99 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.dataaccess.DataAccessFacade; +import net.petafuel.fuelifints.dataaccess.DataAccessFacadeManager; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.BenutzerdefinierteSignatur; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; + +import java.util.List; + +/** + * Name: Signaturabschluss + * Typ: Segment + * Segmentart: Administration + * Kennung: HNSHA + * Bezugssegment: + * Segmentversion: + * Sender: Kunde/Kreditinstitut + */ +public class HNSHA extends Segment implements IExecutableElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, length = -14)}) + @an + private String sicherheitskontrollreferenz; + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.C, length = -512)}) + @bin + private byte[] validierungsresultat; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C)}) + private BenutzerdefinierteSignatur benutzerdefinierteSignatur; + + public HNSHA(byte[] message) { + super(message); + } + + public byte[] getValidierungsresultat() { + return validierungsresultat; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public String getSicherheitskontrollreferenz() { + return sicherheitskontrollreferenz; + } + + public void setSicherheitskontrollreferenz(String sicherheitskontrollreferenz) { + this.sicherheitskontrollreferenz = sicherheitskontrollreferenz; + } + + public void setValidierungsresultat(byte[] validierungsresultat) { + this.validierungsresultat = validierungsresultat; + } + + public BenutzerdefinierteSignatur getBenutzerdefinierteSignatur() { + return benutzerdefinierteSignatur; + } + + public void setBenutzerdefinierteSignatur(BenutzerdefinierteSignatur benutzerdefinierteSignatur) { + this.benutzerdefinierteSignatur = benutzerdefinierteSignatur; + } + + @Override + public StatusCode execute(Dialog dialog) { + DataAccessFacade dataAccessFacade = DataAccessFacadeManager.getAccessFacade(dialog.getBankId()); + if (dialog.getTransactionInfo() != null && dialog.getTransactionInfo().isTanUsed()) { + dataAccessFacade.devalueTan(dialog.getDialogId(), dialog.getLegitimationsInfo(), dialog.getClientProductInfo(), getBenutzerdefinierteSignatur().getTan()); + dialog.setTransactionInfo(null); + } + return StatusCode.OK; + } + + @Override + public List getReplyMessageElements() { + return null; + } + + @Override + public IMessageElement getStatusElement() { + return null; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNSHK.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNSHK.java new file mode 100644 index 0000000..f5c7798 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNSHK.java @@ -0,0 +1,228 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.IExecutableElement; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.model.client.LegitimationInfo; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.*; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +import java.util.List; + +/** + * Name: Signaturkopf + * Typ: Segment + * Segmentart: Administration + * Kennung: HNSHK + * Sender: Kunde/Kreditinstitut + */ +public class HNSHK extends Segment implements IExecutableElement { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + private Sicherheitsprofil sicherheitsprofil; + + @Element( + description = {@ElementDescription(number = 3, length = -3)}) +// @code(restrictions = {"1", "2", "4", "911", "900", "999"}) + @an + private String sicherheitsfunktion; + + @Element( + description = {@ElementDescription(number = 4, length = -14)}) + @an + private String sicherheitskontrollreferenz; + + @Element( + description = {@ElementDescription(number = 5, length = -3)}) + @code(restrictions = {"1"}) + private String sicherheitsapplikationskontrollbereich; + + @Element( + description = {@ElementDescription(number = 6, length = -4)}) + @code(restrictions = {"1", "3", "4"}) + private String sicherheitslieferantenrolle; + + @Element( + description = {@ElementDescription(number = 7)}) + private Sicherheitsidentifikationsdetails sicherheitsidentifikationsdetails; + + @Element( + description = {@ElementDescription(number = 8, length = -16)}) + @num + private Integer sicherheitsreferenznummer; + + @Element( + description = {@ElementDescription(number = 9)}) + private SicherheitsdatumUndUhrzeit sicherheitsdatumUndUhrzeit; + + @Element( + description = {@ElementDescription(number = 10)}) + private Hashalgorithmus hashalgorithmus; + + @Element( + description = {@ElementDescription(number = 11)}) + private Signaturalgorithmus signaturalgorithmus; + + @Element( + description = {@ElementDescription(number = 12)}) + private Schluesselname schluesselname; + + @Element( + description = {@ElementDescription(number = 13, status = ElementDescription.StatusCode.C)}) + private Zertifikat zertifikat; //Darf bei PIN/TAN nicht belegt werden + + public HNSHK(byte[] message) { + super(message); + } + + public Sicherheitsprofil getSicherheitsprofil() { + return sicherheitsprofil; + } + + public Sicherheitsidentifikationsdetails getSicherheitsidentifikationsdetails() { + return sicherheitsidentifikationsdetails; + } + + public Schluesselname getSchluesselname() { + return schluesselname; + } + + public Zertifikat getZertifikat() { + return zertifikat; + } + + public void setZertifikat(Zertifikat zertifikat) { + this.zertifikat = zertifikat; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public void setSicherheitsprofil(Sicherheitsprofil sicherheitsprofil) { + this.sicherheitsprofil = sicherheitsprofil; + } + + public String getSicherheitsfunktion() { + return sicherheitsfunktion; + } + + public void setSicherheitsfunktion(String sicherheitsfunktion) { + this.sicherheitsfunktion = sicherheitsfunktion; + } + + public String getSicherheitskontrollreferenz() { + return sicherheitskontrollreferenz; + } + + public void setSicherheitskontrollreferenz(String sicherheitskontrollreferenz) { + this.sicherheitskontrollreferenz = sicherheitskontrollreferenz; + } + + public String getSicherheitsapplikationskontrollbereich() { + return sicherheitsapplikationskontrollbereich; + } + + public void setSicherheitsapplikationskontrollbereich(String sicherheitsapplikationskontrollbereich) { + this.sicherheitsapplikationskontrollbereich = sicherheitsapplikationskontrollbereich; + } + + public String getSicherheitslieferantenrolle() { + return sicherheitslieferantenrolle; + } + + public void setSicherheitslieferantenrolle(String sicherheitslieferantenrolle) { + this.sicherheitslieferantenrolle = sicherheitslieferantenrolle; + } + + public void setSicherheitsidentifikationsdetails(Sicherheitsidentifikationsdetails sicherheitsidentifikationsdetails) { + this.sicherheitsidentifikationsdetails = sicherheitsidentifikationsdetails; + } + + public Integer getSicherheitsreferenznummer() { + return sicherheitsreferenznummer; + } + + public void setSicherheitsreferenznummer(Integer sicherheitsreferenznummer) { + this.sicherheitsreferenznummer = sicherheitsreferenznummer; + } + + public SicherheitsdatumUndUhrzeit getSicherheitsdatumUndUhrzeit() { + return sicherheitsdatumUndUhrzeit; + } + + public void setSicherheitsdatumUndUhrzeit(SicherheitsdatumUndUhrzeit sicherheitsdatumUndUhrzeit) { + this.sicherheitsdatumUndUhrzeit = sicherheitsdatumUndUhrzeit; + } + + public Hashalgorithmus getHashalgorithmus() { + return hashalgorithmus; + } + + public void setHashalgorithmus(Hashalgorithmus hashalgorithmus) { + this.hashalgorithmus = hashalgorithmus; + } + + public Signaturalgorithmus getSignaturalgorithmus() { + return signaturalgorithmus; + } + + public void setSignaturalgorithmus(Signaturalgorithmus signaturalgorithmus) { + this.signaturalgorithmus = signaturalgorithmus; + } + + public void setSchluesselname(Schluesselname schluesselname) { + this.schluesselname = schluesselname; + } + + @Override + public StatusCode execute(Dialog dialog) { + LegitimationInfo legitimationInfo = dialog.getLegitimationsInfo(); + legitimationInfo.setSecurityMethod(SecurityMethod.valueOf(sicherheitsprofil.getSicherheitsverfahren() + "_" + sicherheitsprofil.getSicherheitsverfahrensversion())); + legitimationInfo.setHashalgorithmus(getHashalgorithmus()); + legitimationInfo.setSicherheitsfunktion(getSicherheitsfunktion()); + legitimationInfo.setUserId(schluesselname.getBenutzerkennung()); + return StatusCode.OK; + } + + @Override + public List getReplyMessageElements() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public IMessageElement getStatusElement() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String toString() { + return "HNSHK{" + + "segmentkopf=" + segmentkopf + + ", sicherheitsprofil=" + sicherheitsprofil + + ", sicherheitsfunktion='" + sicherheitsfunktion + '\'' + + ", sicherheitskontrollreferenz='" + sicherheitskontrollreferenz + '\'' + + ", sicherheitsapplikationskontrollbereich='" + sicherheitsapplikationskontrollbereich + '\'' + + ", sicherheitslieferantenrolle='" + sicherheitslieferantenrolle + '\'' + + ", sicherheitsidentifikationsdetails=" + sicherheitsidentifikationsdetails + + ", sicherheitsreferenznummer=" + sicherheitsreferenznummer + + ", sicherheitsdatumUndUhrzeit=" + sicherheitsdatumUndUhrzeit + + ", hashalgorithmus=" + hashalgorithmus + + ", signaturalgorithmus=" + signaturalgorithmus + + ", schluesselname=" + schluesselname + + ", zertifikat=" + zertifikat + + '}'; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNVSD.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNVSD.java new file mode 100644 index 0000000..1bb23e0 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNVSD.java @@ -0,0 +1,44 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; + +/** + * Name: Verschlüsselte Daten + * Typ: Segment + * Segmentart: Administration + * Kennung: HNVSD + * Sender: Kunde/Kreditinstitut + */ +public class HNVSD extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, length = 0)}) + @bin + private byte[] verschluesselte_daten; + + public HNVSD(byte[] message) { + super(message); + } + + public byte[] getVerschluesselteDaten() { + return verschluesselte_daten; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + + public void setVerschluesselteDaten(byte[] verschluesselte_daten) { + this.verschluesselte_daten = verschluesselte_daten; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNVSK.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNVSK.java new file mode 100644 index 0000000..7ae02b2 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/HNVSK.java @@ -0,0 +1,141 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.*; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; + +/** + * Name: Verschlüsselungskopf + * Typ: Segment + * Segmentart: Administration + * Kennung: HNVSK + * Sender: Kunde/Kreditinstitut + */ +public class HNVSK extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + private Sicherheitsprofil sicherheitsprofil; + + @Element( + description = {@ElementDescription(number = 3, length = -3)}) + @code(restrictions = {"4", "998"}) //4 = RDH, 998 = PIN/TAN + private String sicherheitsfunktion; + + @Element( + description = {@ElementDescription(number = 4, length = -3)}) + @code(restrictions = {"1", "4"}) + private String sicherheitslieferant; + + @Element( + description = {@ElementDescription(number = 5)}) + private Sicherheitsidentifikationsdetails sicherheitsidentifikationsdetails; + + @Element( + description = {@ElementDescription(number = 6)}) + private SicherheitsdatumUndUhrzeit sicherheitsdatumUndUhrzeit; + + @Element( + description = {@ElementDescription(number = 7)}) + private Verschluesselungsalgorithmus verschluesselungsalgorithmus; + + @Element( + description = {@ElementDescription(number = 8)}) + private Schluesselname schluesselname; + + @Element( + description = {@ElementDescription(number = 9)}) + @code(restrictions = {"0", "1", "2", "3", "4", "5", "6", "7", "999"}) + private String komprimierungsfunktion; + + @Element( + description = {@ElementDescription(number = 10, status = ElementDescription.StatusCode.O)}) + private Zertifikat zertifikat; + + public HNVSK(byte[] message) { + super(message); + } + + public Sicherheitsprofil getSicherheitsprofil() { + return sicherheitsprofil; + } + + public Verschluesselungsalgorithmus getVerschluesselungsalgorithmus() { + return verschluesselungsalgorithmus; + } + + public Schluesselname getSchluesselname() { + return schluesselname; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public void setSicherheitsprofil(Sicherheitsprofil sicherheitsprofil) { + this.sicherheitsprofil = sicherheitsprofil; + } + + public String getSicherheitsfunktion() { + return sicherheitsfunktion; + } + + public void setSicherheitsfunktion(String sicherheitsfunktion) { + this.sicherheitsfunktion = sicherheitsfunktion; + } + + public String getSicherheitslieferant() { + return sicherheitslieferant; + } + + public void setSicherheitslieferant(String sicherheitslieferant) { + this.sicherheitslieferant = sicherheitslieferant; + } + + public Sicherheitsidentifikationsdetails getSicherheitsidentifikationsdetails() { + return sicherheitsidentifikationsdetails; + } + + public void setSicherheitsidentifikationsdetails(Sicherheitsidentifikationsdetails sicherheitsidentifikationsdetails) { + this.sicherheitsidentifikationsdetails = sicherheitsidentifikationsdetails; + } + + public SicherheitsdatumUndUhrzeit getSicherheitsdatumUndUhrzeit() { + return sicherheitsdatumUndUhrzeit; + } + + public void setSicherheitsdatumUndUhrzeit(SicherheitsdatumUndUhrzeit sicherheitsdatumUndUhrzeit) { + this.sicherheitsdatumUndUhrzeit = sicherheitsdatumUndUhrzeit; + } + + public void setVerschluesselungsalgorithmus(Verschluesselungsalgorithmus verschluesselungsalgorithmus) { + this.verschluesselungsalgorithmus = verschluesselungsalgorithmus; + } + + public void setSchluesselname(Schluesselname schluesselname) { + this.schluesselname = schluesselname; + } + + public String getKomprimierungsfunktion() { + return komprimierungsfunktion; + } + + public void setKomprimierungsfunktion(String komprimierungsfunktion) { + this.komprimierungsfunktion = komprimierungsfunktion; + } + + public Zertifikat getZertifikat() { + return zertifikat; + } + + public void setZertifikat(Zertifikat zertifikat) { + this.zertifikat = zertifikat; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/MessageElementParser.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/MessageElementParser.java new file mode 100644 index 0000000..75cbaa0 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/MessageElementParser.java @@ -0,0 +1,312 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.exceptions.ElementParseException; +import net.petafuel.fuelifints.exceptions.HBCISyntaxException; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.DatenElementGruppe; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.UnterstuetzteCamtMessages; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; +import net.petafuel.fuelifints.support.ByteSplit; +import net.petafuel.fuelifints.support.FieldComparator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class MessageElementParser { + + private static final Logger LOG = LogManager.getLogger(MessageElementParser.class); + + protected IMessageElement myParseElement; + + public MessageElementParser(IMessageElement parseElement) { + this.myParseElement = parseElement; + } + + private int getElementCount(Class c, int segmentVersion) { + List fields = getAllDeclaredFieldsFromAllSuperClasses(c); + int result = 0; + for (Field f : fields) { + if (AbstractElement.getMatchingElementDescription(f, segmentVersion) != null) { + result++; + } + } + return result; + } + + /** + * Belegt alle entsprechenden Variablen einer Klasse mit dem zuständigen Wert eines Segments. + * DatenElemente und DatenElementGruppen werden rekursiv auch gefüllt. + * + * @throws ElementParseException + */ + public void fillElements() throws ElementParseException { + int splitMode = (myParseElement instanceof Segment) ? ByteSplit.MODE_DEG : ByteSplit.MODE_DE; + int segmentVersion = -1; + if (splitMode == ByteSplit.MODE_DEG) { + segmentVersion = getSegmentVersion(); + } + try { + + List splittedMessage = null; + try { + splittedMessage = ByteSplit.split(myParseElement.getBytes(), splitMode); + } catch (HBCISyntaxException e) { + throw new ElementParseException(e); + } + if (splittedMessage == null) { + return; + } + List fields = getAllDeclaredFieldsFromAllSuperClasses(myParseElement.getClass()); + + List usedFields = new LinkedList(); + for (Field f : fields) { + f.setAccessible(true); + if (AbstractElement.getMatchingElementDescription(f, segmentVersion) != null) + usedFields.add(f); + } + /* + * Sortiere die Fields anhand der Element-Number + */ + Collections.sort(usedFields, new FieldComparator(segmentVersion)); + if (myParseElement instanceof DatenElementGruppe) { + splittedMessage = groupSplittedMessage(splittedMessage, usedFields, segmentVersion); + } + for (Field f : usedFields) { + if (AbstractElement.getMatchingElementDescription(f, segmentVersion) != null) { + if (splittedMessage.size() >= AbstractElement.getMatchingElementDescription(f, segmentVersion).number()) { + f.setAccessible(true); + if (f.getAnnotation(bin.class) != null) { + //binary + byte[] bytes = splittedMessage.get(AbstractElement.getMatchingElementDescription(f, segmentVersion).number() - 1); + f.set(myParseElement, bytes); + } else if (f.getAnnotation(num.class) != null) { + //int type: + String currentElement = null; + try { + currentElement = new String(splittedMessage.get(AbstractElement.getMatchingElementDescription(f, segmentVersion).number() - 1), "ISO-8859-1"); + } catch (UnsupportedEncodingException e) { + LOG.error("UnsupportedEncodingException", e); + } + try { + f.set(myParseElement, Integer.parseInt(currentElement)); + } catch (NumberFormatException e) { + //Feld bleibt in dem Fall leer + } + } else if (f.getType().equals(List.class)) { + //liste gefunden, diese elemente müssen ganz zum schluss stehen! + if (AbstractElement.getMatchingElementDescription(f, segmentVersion).number() != usedFields.size()) { + for (int i = AbstractElement.getMatchingElementDescription(f, segmentVersion).number(); i < splittedMessage.size(); i++) { + LOG.error("Could not fill List.."); + } + + } else { + LOG.debug(new String(splittedMessage.get(AbstractElement.getMatchingElementDescription(f, segmentVersion).number() - 1))); + if (splitMode == ByteSplit.MODE_DE) { + Type type = f.getGenericType(); + ParameterizedType parameterizedType = (ParameterizedType) type; + type = parameterizedType.getActualTypeArguments()[0]; + if (type.equals(String.class)) { + LinkedList linkedList = new LinkedList<>(); + for (int i = AbstractElement.getMatchingElementDescription(f, segmentVersion).number() - 1; i < splittedMessage.size(); i++) { + try { + linkedList.add(new String(splittedMessage.get(i), "ISO-8859-1")); + } catch (UnsupportedEncodingException e) { + //ignored + } + } + f.set(myParseElement, linkedList); + } else if (type.equals(byte[].class)) { + f.set(myParseElement, splittedMessage.subList(AbstractElement.getMatchingElementDescription(f, segmentVersion).number(), splittedMessage.size())); + } + } + } + } else if(f.getType().equals(UnterstuetzteCamtMessages.class)) { + byte[] content = splittedMessage.get(AbstractElement.getMatchingElementDescription(f, segmentVersion).number() - 1); + + UnterstuetzteCamtMessages unterstuetzteCamtMessages = new UnterstuetzteCamtMessages(content); + f.set(myParseElement, unterstuetzteCamtMessages); + + } else if (!f.getType().equals(String.class)) { + //new DEGs + Class[] constructorParams = new Class[]{byte[].class}; + java.lang.reflect.Constructor constructor = f.getType().getConstructor(constructorParams); + Object[] messageObject = new Object[]{splittedMessage.get(AbstractElement.getMatchingElementDescription(f, segmentVersion).number() - 1)}; + + f.set(myParseElement, constructor.newInstance(messageObject)); + + } else { + String currentElement = null; + try { + currentElement = new String(splittedMessage.get(AbstractElement.getMatchingElementDescription(f, segmentVersion).number() - 1), "ISO-8859-1"); + } catch (UnsupportedEncodingException e) { + LOG.error("UnsupportedEncodingException", e); + } + currentElement = removeEscapeSymbols(currentElement); + f.set(myParseElement, currentElement); + } + } /*else { + throw new ElementParseException(); + } */ + + } + } + } catch (IllegalAccessException e) { + LOG.error("Illegal access while filling data elements: ", e); + } catch (NoSuchMethodException e) { + LOG.error("String constructor not found while filling data elements: ", e); + } catch (InstantiationException e) { + LOG.error("Problem with instantiating data element group: ", e); + } catch (InvocationTargetException e) { + LOG.error(e.getMessage(), e); + } + } + + private String removeEscapeSymbols(String currentElement) { + currentElement = currentElement.replaceAll("\\?\\:", ":"); + currentElement = currentElement.replaceAll("\\?\\+", "+"); + currentElement = currentElement.replaceAll("\\?\\'", "'"); + currentElement = currentElement.replaceAll("\\?\\@", "@"); + currentElement = currentElement.replaceAll("\\?\\?", "?"); + return currentElement; + } + + public int getSegmentVersion() throws ElementParseException { + if (myParseElement instanceof Segment) { + byte[] data = myParseElement.getBytes(); + int segmentVersion = 0; + int dataElementCounter = 0; + for (int i = 0; i < data.length; i++) { + if (dataElementCounter > 1) { + if (data[i] == ':' || data[i] == '+') { + break; + } + segmentVersion = segmentVersion * 10 + (data[i] - 48); + } + if (data[i] == ':') { + dataElementCounter++; + } + } + if (segmentVersion > 999 || segmentVersion < 0) { + //parse error or other issue + throw new ElementParseException("could not get segmentversion."); + } + //LOG.debug("Found Segmentversion: "+segmentVersion); + return segmentVersion; + } + return 0; + } + + private List groupSplittedMessage(List splittedMessage, List usedFields, int segmentVersion) { + List elements = new LinkedList(); + int offset = 0; + for (Field f : usedFields) { + byte[] degBytes; + if (AbstractElement.getMatchingElementDescription(f, segmentVersion).status() == ElementDescription.StatusCode.C + || AbstractElement.getMatchingElementDescription(f, segmentVersion).status() == ElementDescription.StatusCode.O) { + if (offset >= splittedMessage.size()) { + continue; + } + } + if (List.class.isAssignableFrom(f.getType()) && usedFields.size() == 1) { + return splittedMessage; + } else + if (DatenElementGruppe.class.isAssignableFrom(f.getType())) { + List degElements = new ArrayList(); + int elementCount = getElementCount(f.getType(), segmentVersion); + for (int i = offset; i < offset + elementCount; i++) { + degElements.add(splittedMessage.get(i)); + } + degBytes = combineArraysInList(degElements); + offset += elementCount - 1; + } else { + degBytes = splittedMessage.get(offset); + } + elements.add(degBytes); + offset++; + } + return elements; + } + + + private byte[] combineArraysInList(List deg) { + byte[] result; + int resultSize = 0; + for (byte[] b : deg) { + resultSize += b.length + 1; + } + resultSize -= 1; + result = new byte[resultSize]; + int offset = 0; + for (int i = 0; i < deg.size(); i++) { + if (i < deg.size() - 1) { + System.arraycopy(deg.get(i), 0, result, offset, deg.get(i).length); + offset += deg.get(i).length; + result[offset++] = 0x3A; + } else { + System.arraycopy(deg.get(i), 0, result, offset, deg.get(i).length); + } + } + return result; + } + + /** + * Über diese Methode erhält man alle Fields der übergebenen Klasse und deren Superklasse zurück in einer Map zurück. + * + * @param clazz Klassenobjekt + * @return HashMap, in welcher alle enthaltenen Fields aufgelistet sind + */ + public Map getFieldsFromClassAndSuperClass(Class clazz) { + /** + * Hack: to get all fields from the class up to net.petafuel.mobile.objectbroker.commondataacesslayer.SerializableObject + * we have to go over all super classes and read the declared fields. + */ + Map fieldsFromClassAndSuper = new HashMap(); + Class superClass = clazz; + while (!AbstractElement.class.equals(superClass) && superClass != null) { + for (Field f : superClass.getDeclaredFields()) { + fieldsFromClassAndSuper.put(f.getName(), f); + } + superClass = superClass.getSuperclass(); + } + return fieldsFromClassAndSuper; + } + + /** + * Über diese Methoden werden alle deklarierten Fields geholt. + * Nicht nur die der ersten Superklasse, sondern die aller Superklassen bis hin zum + * net.petafuel.mobile.objectbroker.commondataacesslayer.SerializableObject + * + * @param clazz Klasse + * @return LinklList mit allen Fields + */ + public LinkedList getAllDeclaredFieldsFromAllSuperClasses(Class clazz) { + Field[] fields = clazz.getDeclaredFields(); + LinkedList fieldsFromClassAndSuper = new LinkedList(); + fieldsFromClassAndSuper.addAll(Arrays.asList(fields)); + /** + * Hack: to get all fields from the class up to net.petafuel.mobile.objectbroker.commondataacesslayer.SerializableObject + * we have to go over all super classes and read the declared fields. + */ + Class superClass = clazz.getSuperclass(); + while (!AbstractElement.class.equals(superClass) && superClass != null) { + fields = superClass.getDeclaredFields(); + fieldsFromClassAndSuper.addAll(Arrays.asList(fields)); + superClass = superClass.getSuperclass(); + } + return fieldsFromClassAndSuper; + } + +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/Segment.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/Segment.java new file mode 100644 index 0000000..ed2bc42 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/Segment.java @@ -0,0 +1,121 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.exceptions.ElementParseException; +import net.petafuel.fuelifints.exceptions.HBCIValidationException; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.DatenElementGruppe; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.util.Set; + +public abstract class Segment extends AbstractElement { + + private static final Logger LOG = LogManager.getLogger(Segment.class); + + protected byte[] bytes; + + protected MessageElementParser myParser = null; + + @Override + public void parseElement() throws ElementParseException { + //build Elements + if (bytes != null) { + this.myParser.fillElements(); + } + } + + public int getVersion() throws ElementParseException { + return this.myParser == null ? 0 : this.myParser.getSegmentVersion(); + } + + @Override + public byte[] getBytes() { + return bytes; + } + + @Override + public boolean validate() throws HBCIValidationException { + Validator v = Validation.buildDefaultValidatorFactory().getValidator(); + Set> constraintViolations = v.validate(this); + + for (ConstraintViolation violation : constraintViolations) { + LOG.error(violation.getPropertyPath() + " " + violation.getMessage() + " class: " + this.getClass().getSimpleName()); + throw new HBCIValidationException(violation.getMessage()); + } + + Field[] declaredFields = getClass().getDeclaredFields(); + for (Field f : declaredFields) { + f.setAccessible(true); + if (DatenElementGruppe.class.isAssignableFrom(f.getType())) { + try { + DatenElementGruppe deg = (DatenElementGruppe) f.get(this); + if (deg != null) { + deg.validate(); + } + } catch (IllegalAccessException e) { + LOG.error("Cannot access field {}", f.getName(), e); + } + } + } + LOG.debug(">> validation ok"); + return true; + } + + public Segment(byte[] message) { + bytes = message; + this.myParser = new MessageElementParser(this); + } + + public MessageElementParser getMyParser() { + return myParser; + } + + public void setMyParser(MessageElementParser myParser) { + this.myParser = myParser; + } + + @Override + protected char getSeparatorCharacter() { + return '+'; + } + + @Override + public String toString() { + try { + return "Segment{" + + "message='" + new String(bytes, "ISO-8859-1") + '\'' + + '}'; + } catch (UnsupportedEncodingException e) { + return "could not convert bytes to ISO-8859-1 (latin 1)"; + } + } + + public boolean checkDependencies() { + return true; + } + + public Segmentkopf getSegmentkopf() { + Field segmentkopfField = null; + for (Field f : getClass().getDeclaredFields()) { + f.setAccessible(true); + if (Segmentkopf.class.equals(f.getType())) { + segmentkopfField = f; + break; + } + } + if (segmentkopfField != null) { + try { + return (Segmentkopf) segmentkopfField.get(this); + } catch (IllegalAccessException e) { + LOG.error("IllegalAccessException", e); + } + } + return null; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/SegmentManager.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/SegmentManager.java new file mode 100644 index 0000000..d8ae618 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/SegmentManager.java @@ -0,0 +1,133 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.HBCIParseException; +import net.petafuel.fuelifints.exceptions.ElementParseException; +import net.petafuel.fuelifints.exceptions.HBCISyntaxException; +import net.petafuel.fuelifints.exceptions.HBCIValidationException; +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.SegmentNotSupportedException; +import net.petafuel.fuelifints.protocol.fints3.segments.acknowledgement.AckString; +import net.petafuel.fuelifints.support.ByteSplit; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +public class SegmentManager { + private static final Logger LOG = LogManager.getLogger(SegmentManager.class); + /** + * Parst den Auftrag und liefert die entsprechenden Segmente. + * Es wird zunächst der Auftrag in die einzelnen Segmente gesplitted, anschließend + * wird für jedes Segment eine Instanz erstellt und die Werte der enstprechenden + * Datenelemte gesetzt. + * + * @param requestBytes entschlüsselter Auftrag als byte[]. + * @return die entsprechenden Segmente die dieser Auftrag beinhaltet. + * @throws SegmentNotSupportedException + * @throws HBCIParseException + */ + public static ArrayList getSegments(byte[] requestBytes) throws SegmentNotSupportedException, HBCIParseException { + List segments = null; + try { + segments = ByteSplit.split(requestBytes, ByteSplit.MODE_SEGMENT); + } catch (HBCISyntaxException e) { + throw new HBCIParseException(e); + } + ArrayList returnElements = new ArrayList(); + for (byte[] segmentBytes : segments) { + Segment segment = getSegment(segmentBytes); + try { + segment.parseElement(); + segment.validate(); + if (segment != null) { + returnElements.add(segment); + } + } catch (HBCIValidationException e) { + throw new HBCIParseException(e); + } catch (ElementParseException e) { + throw new HBCIParseException(e); + } + } + if (returnElements != null) { + return returnElements; + } + throw new HBCIParseException(AckString.getAckString("9010")); + } + + private static String getSegmentName(byte[] segment) { + int index = 0; + for (byte b : segment) { + if (b == ':') { + break; + } + index++; + } + try { + return new String(segment, 0, index, "ISO-8859-1"); + } catch (UnsupportedEncodingException ex) { + //ignored ISO-8859-1 (Latin-1) is supported + } + return ""; + } + + private static Segment getSegment(byte[] segment) throws SegmentNotSupportedException { + String segmentName = getSegmentName(segment); + //try to find Segment Class: + try { + Class segmentClass = Class.forName("net.petafuel.fuelifints.protocol.fints3.segments." + segmentName.toUpperCase()); + + Class[] constructorParams = new Class[]{byte[].class}; + Constructor constructor = segmentClass.getConstructor(constructorParams); + Object[] messageObject = new Object[]{segment}; + return (Segment) constructor.newInstance(messageObject); + } catch (ClassNotFoundException e) { + throw new SegmentNotSupportedException(segmentName); + } catch (InvocationTargetException e) { + LOG.error("InvocationTargetException", e); + } catch (NoSuchMethodException e) { + LOG.error("NoSuchMethodException", e); + } catch (InstantiationException e) { + LOG.error("InstantiationException", e); + } catch (IllegalAccessException e) { + LOG.error("IllegalAccessException", e); + } + return null; + } + + /** + * Factory method, instantiate new Segment + * + * @param name + * @param message + * @return + */ + private static Segment getSegment(String name, String message) throws SegmentNotSupportedException { + try { + //try to find Segment Class: + Class segment = Class.forName("net.petafuel.fuelifints.protocol.fints3.segments." + name.toUpperCase()); + + //instantiate and return Segment: + Class[] constructorParams = new Class[]{String.class}; + java.lang.reflect.Constructor constructor = segment.getConstructor(constructorParams); + Object[] messageObject = new Object[]{message}; + return (Segment) constructor.newInstance(messageObject); + + } catch (ClassNotFoundException e) { + throw new SegmentNotSupportedException(name); + } catch (NoSuchMethodException e) { + LOG.info("Falscher Parameter, Konstruktor(String) zu Segment {} nicht gefunden", name); + } catch (InvocationTargetException e) { + throw new SegmentNotSupportedException(name); + } catch (InstantiationException e) { + throw new SegmentNotSupportedException(name); + } catch (IllegalAccessException e) { + throw new SegmentNotSupportedException(name); + } + + return null; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/SegmentUtil.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/SegmentUtil.java new file mode 100644 index 0000000..8957c03 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/SegmentUtil.java @@ -0,0 +1,138 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.UnsupportedEncodingException; + +/** + * Hilfsklasse um den Umgang mit binären DE umzugehen. + */ +public class SegmentUtil { + + static final Logger LOG = LogManager.getLogger(SegmentUtil.class); + + /** + * Liefert den Segment-Namen zu einem gegebenen byte[]. + * + * @param segment die bytes die das Segment repräsentieren. + * @return den Namen des Segments als String. + */ + public static String getSegmentName(byte[] segment) { + int index = 0; + for (byte b : segment) { + if (b == ':') { + break; + } + index++; + } + try { + return new String(segment, 0, index, "ISO-8859-1"); + } catch (UnsupportedEncodingException ex) { + //ignored ISO-8859-1 (Latin-1) is supported + } + return ""; + } + + /** + * Liefert die echten Binär-Daten eines binären Datenelements. + * Beispiel: @3@123 liefert 123 zurück. + * + * @param binaryFormat das binäre DE als byte[]. + * @return die reinen Binär-Daten. + */ + public static byte[] getBytes(byte[] binaryFormat) { + byte[] result; + int index = 1; + for (; index < binaryFormat.length; index++) { + if (binaryFormat[index] == 0x40) { + index++; + break; + } + } + result = new byte[binaryFormat.length - index]; + System.arraycopy(binaryFormat, index, result, 0, result.length); + return result; + } + + public static byte[] wrapBinary(byte[] bytes) { + if (bytes == null) { + LOG.debug("input bytes was null in wrap binary"); + return null; + } + byte[] result = null; + try { + result = new byte[bytes.length + 2 + Integer.toString(bytes.length).length()]; + result[0] = '@'; + int i = 1; + for (byte b : Integer.toString(bytes.length).getBytes("ISO-8859-1")) { + result[i++] = b; + } + result[i] = '@'; + System.arraycopy(bytes, 0, result, i + 1, bytes.length); + } catch (UnsupportedEncodingException es) { + //ISO is supported + } + LOG.debug("wrap binary {}", new String(result)); + return result; + } + + public static String ibanToAccountNr(String iban) { + /* + * DEpp bbbb bbbb kkkk kkkk kk + * + * Dabei bedeutet: + * + * DE Länderkennzeichen für Deutschland + * pp zweistellige Prüfsumme mit Prüfziffern + * bbbbbbbb die 8-stellige deutsche Bankleitzahl + * kkkkkkkkkk die 10-stellige Kontonummer + */ + if (iban != null) { + if (iban.length() == 22 && iban.startsWith("DE")) { + //gilt nur für deutsche ibans + String account = iban.substring(12); + while (account.startsWith("0")) + account = account.substring(1); //führende Nullen entfernen + return account; + } + } + return ""; + + } + + public static String accountToIban(String accountNr, String bankCode) { + /* + * DEpp bbbb bbbb kkkk kkkk kk + * + * Dabei bedeutet: + * + * DE Länderkennzeichen für Deutschland + * pp zweistellige Prüfsumme mit Prüfziffern + * bbbbbbbb die 8-stellige deutsche Bankleitzahl + * kkkkkkkkkk die 10-stellige Kontonummer + */ + + + if (accountNr.length() < 10) + accountNr = "0000000000".substring(accountNr.length()) + accountNr; //Kontonummer mit Nullen auf 10 Stellen auffüllen + + String bban = bankCode + accountNr; //Bankleitzahl + 10 stellige Kontonummer = BBAN + String countryIdentifier = "131400"; // (4+9) + (5+9) + 00 + String checksum = (bban + countryIdentifier); + + //Einfach eine 22 stellige Zahl mod 97 nehmen ist leider nicht genau genug + long checkDigit = 0; + for (int i = 0; i < checksum.length(); i++) { + int charValue = Character.getNumericValue(checksum.charAt(i)); + + checkDigit = (charValue > 9 ? checkDigit * 100 : checkDigit * 10) + charValue; + if (checkDigit > 999999999) { //Berechnungen mit zu langen Zahlen führen zu Fehlern + checkDigit = (checkDigit % 97); + } + } + checkDigit = (checkDigit % 97); //Prüfumme Modulo 97 + checkDigit = 98 - checkDigit; + return String.format("%s%02d%s", "DE", checkDigit, bban); + } +} \ No newline at end of file diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/acknowledgement/AckString.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/acknowledgement/AckString.java new file mode 100644 index 0000000..0a6ab03 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/acknowledgement/AckString.java @@ -0,0 +1,85 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.acknowledgement; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.io.IOException; + + +/** + * Gibt zu einem Acknowledgement Code die passende Meldung zurück. + * + * Dabei werden ACK Codes und Meldungen aus einer XML Datei ausgelesen + * + */ +public class AckString { + + private static final Logger LOG = LogManager.getLogger(AckString.class); + + public static String getAckString(String ackCode) { + + return getMessageFromXml(ackCode); + } + + public static String getMessageText(String ackCode) { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + try { + // File file = new File("src/test/resources/xml/acknowledgementcodes.xml"); + + + + Document doc = factory.newDocumentBuilder().parse(AckString.class.getResourceAsStream("/xml/acknowledgementcodes.xml")); + + NodeList codes = doc.getElementsByTagName("ack_code"); + for (int i = 0; i < codes.getLength(); i++) { + NamedNodeMap parameter = codes.item(i).getAttributes(); + + if (parameter.item(1).getNodeValue().equals(ackCode)) { + return parameter.item(2).getNodeValue(); + } + + } + } catch (ParserConfigurationException e) { + LOG.error("ParserConfiguration Fehler", e); + } catch (SAXException e) { + LOG.error("SAX Fehler", e); + } catch (IOException e) { + LOG.error("IO-Fehler", e); + } + return null; + } + + private static String getMessageFromXml(String code) { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + try { + File file = new File("src/test/resources/xml/acknowledgementcodes.xml"); + + Document doc = factory.newDocumentBuilder().parse(file); + + NodeList codes = doc.getElementsByTagName("ack_code"); + for (int i = 0; i < codes.getLength(); i++) { + NamedNodeMap parameter = codes.item(i).getAttributes(); + + if (parameter.item(1).getNodeValue().equals(code)) { + return code + "::" + parameter.item(2).getNodeValue(); + } + + } + } catch (ParserConfigurationException e) { + LOG.error("ParserConfiguration Fehler", e); + } catch (SAXException e) { + LOG.error("SAX Fehler", e); + } catch (IOException e) { + LOG.error("IO-Fehler", e); + } + return null; + } + +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/AngabenEmpfaengerkonten.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/AngabenEmpfaengerkonten.java new file mode 100644 index 0000000..a424f2d --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/AngabenEmpfaengerkonten.java @@ -0,0 +1,85 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Angaben zu Empfängerkonten + * Typ: DEG + * Version: 1 + */ +public class AngabenEmpfaengerkonten extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1)}) + @an + private KontoverbindungInternational kontoverbindungInternationalEmpfaenger; + + @Element( + description = {@ElementDescription(number = 2,status = ElementDescription.StatusCode.O)}) + @an + private String name1Empfaenger = ""; + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.O)}) + @an + private String name2Empfaenger = ""; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.O)}) + @num + private Integer kontoart; + + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.O)}) + @an + private String kontoproduktbezeichnung = ""; + + public AngabenEmpfaengerkonten(byte[] bytes) { + super(bytes); + } + + public KontoverbindungInternational getKontoverbindungInternationalEmpfaenger() { + return kontoverbindungInternationalEmpfaenger; + } + + public void setKontoverbindungInternationalEmpfaenger(KontoverbindungInternational kontoverbindungInternationalEmpfaenger) { + this.kontoverbindungInternationalEmpfaenger = kontoverbindungInternationalEmpfaenger; + } + + public String getName1Empfaenger() { + return name1Empfaenger; + } + + public void setName1Empfaenger(String name1Empfaenger) { + this.name1Empfaenger = name1Empfaenger; + } + + public String getName2Empfaenger() { + return name2Empfaenger; + } + + public void setName2Empfaenger(String name2Empfaenger) { + this.name2Empfaenger = name2Empfaenger; + } + + public Integer getKontoart() { + return kontoart; + } + + public void setKontoart(Integer kontoart) { + this.kontoart = kontoart; + } + + public String getKontoproduktbezeichnung() { + return kontoproduktbezeichnung; + } + + public void setKontoproduktbezeichnung(String kontoproduktbezeichnung) { + this.kontoproduktbezeichnung = kontoproduktbezeichnung; + } +} \ No newline at end of file diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/BenutzerdefinierteSignatur.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/BenutzerdefinierteSignatur.java new file mode 100644 index 0000000..89347be --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/BenutzerdefinierteSignatur.java @@ -0,0 +1,49 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; + +/** + * Benutzerdefinierte Signatur + * Bei nicht-schlüsselbasierten Sicherheitsverfahren kann der Benutzer hier + * Angaben zur Authentisierung machen. Ob das Feld verpflichtend ist, ist vom + * jeweiligen Sicherheitsverfahren abhängig. + * Format: s. Spezifikation „Sicherheitsverfahren PIN/TAN“ + * Typ: DEG + * Format: + * Länge: + * Version: 1 + */ +public class BenutzerdefinierteSignatur extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = -99)}) + @an + private String pin; + + @Element( + description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.O, length = -99)}) + @an + private String tan; + + public BenutzerdefinierteSignatur(byte[] degString) { + super(degString); + } + + public String getPin() { + return pin; + } + + public void setPin(String pin) { + this.pin = pin; + } + + public String getTan() { + return tan; + } + + public void setTan(String tan) { + this.tan = tan; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Betrag.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Betrag.java new file mode 100644 index 0000000..b1ed6db --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Betrag.java @@ -0,0 +1,42 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; + +/** + * Beispiel: 4567,89:EUR + */ +public class Betrag extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1)}) + @an + private String wert; + + @Element( + description = {@ElementDescription(number = 2)}) + @an + private String waehrung; + + + public Betrag(byte[] bytes) { + super(bytes); + } + + public String getWert() { + return wert; + } + + public void setWert(String wert) { + this.wert = wert; + } + + public String getWaehrung() { + return waehrung; + } + + public void setWaehrung(String waehrung) { + this.waehrung = waehrung; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/BetragSollHabenKennung.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/BetragSollHabenKennung.java new file mode 100644 index 0000000..98b2ffd --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/BetragSollHabenKennung.java @@ -0,0 +1,55 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; + +import java.lang.reflect.Field; +import java.util.List; + +public class BetragSollHabenKennung extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1)}) + @an + private String wert = ""; + + @Element( + description = {@ElementDescription(number = 2)}) + @an + private String waehrung = ""; + + @Element( + description = {@ElementDescription(number = 3,length = 1)}) + @code(restrictions = {"C","D",""}) + private String sollHabenKennung = ""; + + public BetragSollHabenKennung(byte[] bytes) { + super(bytes); + } + + public String getWert() { + return wert; + } + + public void setWert(String wert) { + this.wert = wert; + } + + public String getWaehrung() { + return waehrung; + } + + public void setWaehrung(String waehrung) { + this.waehrung = waehrung; + } + + public String getSollHabenKennung() { + return sollHabenKennung; + } + + public void setSollHabenKennung(String sollHabenKennung) { + this.sollHabenKennung = sollHabenKennung; + } +} \ No newline at end of file diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Bezugsnachricht.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Bezugsnachricht.java new file mode 100644 index 0000000..805c1f4 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Bezugsnachricht.java @@ -0,0 +1,43 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Bezugsnachricht + * Eindeutige Referenz für Kundennachrichten. Die eindeutige Referenzierung + * erfolgt anhand der Dialog-ID und der Nachrichtennummer der Kundennach- + * richt. Falls auf eine Dialoginitialisierungsnachricht des Kunden referenziert + * werden soll, ist nicht die vom Kunden übermittelte Dialog-ID (0), sondern die + * vom Kreditinstitut neu vergebene Dialog-ID einzustellen. + * Es darf nur auf Nachrichten des dialogführenden Benutzers referenziert + * werden. Eine explizite Angabe der Benutzerkennung als Referenzierungs- + * kriterium ist nicht erforderlich, da diese bereits im Signaturkopf spezifiziert + * wurde. + */ +public class Bezugsnachricht extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = 0)}) + @id + private String dialog_id; + + @Element( + description = {@ElementDescription(number = 2, length = -4)}) + @num + private Integer nachrichtenummern; + + public Bezugsnachricht(byte[] degString) { + super(degString); + } + + @Override + public String toString() { + return "Bezugsnachricht{" + + "dialog_id='" + dialog_id + '\'' + + ", nachrichtenummern=" + nachrichtenummern + + '}'; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/DatenElementGruppe.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/DatenElementGruppe.java new file mode 100644 index 0000000..ed345ee --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/DatenElementGruppe.java @@ -0,0 +1,93 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + + +import net.petafuel.fuelifints.exceptions.ElementParseException; +import net.petafuel.fuelifints.exceptions.HBCIValidationException; +import net.petafuel.fuelifints.protocol.fints3.segments.AbstractElement; +import net.petafuel.fuelifints.protocol.fints3.segments.MessageElementParser; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import java.lang.reflect.Field; +import java.util.Set; + +/** + * Repräsentiert eine abstrakte DEG. + * DEGs beinhalten Dateneletemente. + */ +public abstract class DatenElementGruppe extends AbstractElement { + + private static final Logger LOG = LogManager.getLogger(DatenElementGruppe.class); + + protected byte[] bytes; + + protected MessageElementParser messageElementParser; + + public DatenElementGruppe(byte[] bytes) { + this.bytes = bytes; + this.messageElementParser = new MessageElementParser(this); + try { + this.parseElement(); + } catch (ElementParseException e) { + LOG.error("ElementParseException", e); + } + } + + public MessageElementParser getMessageElementParser() { + return messageElementParser; + } + + public void setMessageElementParser(MessageElementParser messageElementParser) { + this.messageElementParser = messageElementParser; + } + + public byte[] getBytes() { + return bytes; + } + + + @Override + public void parseElement() throws ElementParseException { + //build Elements + if (bytes != null) { + this.messageElementParser.fillElements(); + } + } + + @Override + public boolean validate() throws HBCIValidationException { + Validator v = Validation.buildDefaultValidatorFactory().getValidator(); + Set> constraintViolations = v.validate(this); + + for (ConstraintViolation violation : constraintViolations) { + LOG.error(violation.getPropertyPath() + " " + violation.getMessage() + " class: " + this.getClass().getSimpleName()); + throw new HBCIValidationException(violation.getMessage()); + } + + Field[] fields = getClass().getDeclaredFields(); + for (Field f : fields) { + f.setAccessible(true); + if (DatenElementGruppe.class.isAssignableFrom(f.getType())) { + try { + DatenElementGruppe deg = (DatenElementGruppe) f.get(this); + if (deg != null) { + deg.validate(); + } + } catch (IllegalAccessException e) { + LOG.error("Cannot access field {}", f.getName(), e); + } + } + } + + LOG.debug(">> validation ok"); + return true; + } + + @Override + protected char getSeparatorCharacter() { + return ':'; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/ErlaubteGeschaeftsvorfaelle.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/ErlaubteGeschaeftsvorfaelle.java new file mode 100644 index 0000000..65e6ff9 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/ErlaubteGeschaeftsvorfaelle.java @@ -0,0 +1,86 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Erlaubte Geschäftsvorfälle + * Information darüber, ob der Kunde zur Ausführung des jeweiligen Ge- + * schäftsvorfalls zugelassen ist und wie viele Signaturen hierzu mindestens er- + * forderlich sind. Ferner können für jeden Geschäftsvorfall Einzelauftragslimite + * angegeben werden, sofern dies bankfachlich möglich ist. Die Reihenfolge + * der Geschäftsvorfälle ist unerheblich. + */ +public class ErlaubteGeschaeftsvorfaelle extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = -6)}) + @an + private String geschaeftsvorfall; + + @Element( + description = {@ElementDescription(number = 2, length = -2)}) + @num + private Integer anzahlBenoetigterSignaturen; + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.O, length = 1)}) + @code(restrictions = {"E", "T", "W", "M", "Z"}) + private String limitart; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C)}) + private Betrag betrag; + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -3)}) + @num + private Integer limitTage; + + public ErlaubteGeschaeftsvorfaelle(byte[] bytes) { + super(bytes); + } + + public String getGeschaeftsvorfall() { + return geschaeftsvorfall; + } + + public void setGeschaeftsvorfall(String geschaeftsvorfall) { + this.geschaeftsvorfall = geschaeftsvorfall; + } + + public Integer getAnzahlBenoetigterSignaturen() { + return anzahlBenoetigterSignaturen; + } + + public void setAnzahlBenoetigterSignaturen(Integer anzahlBenoetigterSignaturen) { + this.anzahlBenoetigterSignaturen = anzahlBenoetigterSignaturen; + } + + public String getLimitart() { + return limitart; + } + + public void setLimitart(String limitart) { + this.limitart = limitart; + } + + public Betrag getBetrag() { + return betrag; + } + + public void setBetrag(Betrag betrag) { + this.betrag = betrag; + } + + public Integer getLimitTage() { + return limitTage; + } + + public void setLimitTage(Integer limitTage) { + this.limitTage = limitTage; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/GebuchteCamtUmsaetze.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/GebuchteCamtUmsaetze.java new file mode 100644 index 0000000..9d41046 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/GebuchteCamtUmsaetze.java @@ -0,0 +1,33 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; + +import java.util.LinkedList; +import java.util.List; + +public class GebuchteCamtUmsaetze extends DatenElementGruppe { + + @Element(description = @ElementDescription(number = 1)) + private List camtUmsaetzeGebucht; + + public GebuchteCamtUmsaetze(byte[] bytes) { + super(bytes); + } + + public List getCamtUmsaetzeGebucht() { + return camtUmsaetzeGebucht; + } + + public void setCamtUmsaetzeGebucht(List camtUmsaetzeGebucht) { + this.camtUmsaetzeGebucht = camtUmsaetzeGebucht; + } + + public void addCamtUmsatz(byte[] camtUmsatz) { + if(camtUmsaetzeGebucht == null) { + camtUmsaetzeGebucht = new LinkedList<>(); + } + camtUmsaetzeGebucht.add(camtUmsatz); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/GeschaeftsvorfallspezifischePinTanInformation.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/GeschaeftsvorfallspezifischePinTanInformation.java new file mode 100644 index 0000000..f3f102b --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/GeschaeftsvorfallspezifischePinTanInformation.java @@ -0,0 +1,58 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.jn; + +/** + * Geschäftsvorfallspezifische PIN/TAN-Informationen + * Eine DEG dieses Typs enthält für genau einen Geschäftsvorfall PIN/TAN- + * relevante Informationen. Ist für einen Geschäftsvorfall eine zugehörige DEG + * hinterlegt, kann das Kundenprodukt diesen Geschäftsvorfall über das + * PIN/TAN-Verfahren absichern, andernfalls ist dies nicht erlaubt. + * Hierdurch wird nicht festgelegt, ob und wie oft ein Geschäftsvorfall zu signie- + * ren ist. Dies wird weiterhin über die BPD und UPD angegeben. + * Werden mehr Signaturen eingestellt als in BPD und UPD gefordert, so sind + * diese alle gemäß der Einstellungen im HIPINS-Segment zu bilden. + * Werden in BPD und UPD keine Signaturen gefordert, können diese selbst + * dann weggelassen werden, wenn für den betreffenden Geschäftsvorfall eine + * TAN erforderlich ist. + * Im Feld „Segmentkennung“ ist die Kennung des Auftragssegments des Ge- + * schäftsvorfalls anzugeben, auf den sich die PIN/TAN-Informationen bezie- + * hen. + */ +public class GeschaeftsvorfallspezifischePinTanInformation extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = -6)}) + @an + private String segmentkennung; + + @Element( + description = {@ElementDescription(number = 2, length = 1)}) + @jn + private String tanErforderlich; + + public GeschaeftsvorfallspezifischePinTanInformation(String segmentkennung, boolean tanErforderlich) { + super(new byte[0]); + this.segmentkennung = segmentkennung; + this.tanErforderlich = tanErforderlich ? "J" : "N"; + } + + public String getSegmentkennung() { + return segmentkennung; + } + + public void setSegmentkennung(String segmentkennung) { + this.segmentkennung = segmentkennung; + } + + public String getTanErforderlich() { + return tanErforderlich; + } + + public void setTanErforderlich(String tanErforderlich) { + this.tanErforderlich = tanErforderlich; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/GueltigkeitsdatumUndUhrzeitChallenge.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/GueltigkeitsdatumUndUhrzeitChallenge.java new file mode 100644 index 0000000..87d07a3 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/GueltigkeitsdatumUndUhrzeitChallenge.java @@ -0,0 +1,45 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.tim; + +/** + * Gültigkeitsdatum und –uhrzeit für Challenge + * Datum und Uhrzeit, bis zu welchem Zeitpunkt eine TAN auf Basis der ge- + * sendeten Challenge gültig ist. Nach Ablauf der Gültigkeitsdauer wird die ent- + * sprechende TAN entwertet. + */ +public class GueltigkeitsdatumUndUhrzeitChallenge extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1)}) + @dat + private String datum; + + @Element( + description = {@ElementDescription(number = 2)}) + @tim + private String uhrzeit; + + public GueltigkeitsdatumUndUhrzeitChallenge(byte[] bytes) { + super(bytes); + } + + public String getDatum() { + return datum; + } + + public void setDatum(String datum) { + this.datum = datum; + } + + public String getUhrzeit() { + return uhrzeit; + } + + public void setUhrzeit(String uhrzeit) { + this.uhrzeit = uhrzeit; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Hashalgorithmus.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Hashalgorithmus.java new file mode 100644 index 0000000..cb53490 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Hashalgorithmus.java @@ -0,0 +1,70 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; + +/** + * Hashalgorithmus + * Angaben zu einem kryptographischen Algorithmus, seinen Operations- + * modus, sowie dessen Einsatz. + */ +public class Hashalgorithmus extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = -3)}) + @code(restrictions = {"1"}) + private String hashalgorithmusverwendung; + + @Element( + description = {@ElementDescription(number = 2, length = -3)}) + @code(restrictions = {"1", "2", "3", "4", "5", "6", "999"}) + private String hashalgorithmus; + + @Element( + description = {@ElementDescription(number = 3, length = -3)}) + @code(restrictions = {"1"}) + private String hashalgorithmusparameterbezeichner; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.O, length = -512)}) + @bin + private byte[] hashalgorithmusparameterwert; + + public Hashalgorithmus(byte[] degString) { + super(degString); + } + + public String getHashalgorithmusverwendung() { + return hashalgorithmusverwendung; + } + + public void setHashalgorithmusverwendung(String hashalgorithmusverwendung) { + this.hashalgorithmusverwendung = hashalgorithmusverwendung; + } + + public String getHashalgorithmus() { + return hashalgorithmus; + } + + public void setHashalgorithmus(String hashalgorithmus) { + this.hashalgorithmus = hashalgorithmus; + } + + public String getHashalgorithmusparameterbezeichner() { + return hashalgorithmusparameterbezeichner; + } + + public void setHashalgorithmusparameterbezeichner(String hashalgorithmusparameterbezeichner) { + this.hashalgorithmusparameterbezeichner = hashalgorithmusparameterbezeichner; + } + + public byte[] getHashalgorithmusparameterwert() { + return hashalgorithmusparameterwert; + } + + public void setHashalgorithmusparameterwert(byte[] hashalgorithmusparameterwert) { + this.hashalgorithmusparameterwert = hashalgorithmusparameterwert; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Kik.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Kik.java new file mode 100644 index 0000000..465a49a --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Kik.java @@ -0,0 +1,54 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; + +/** + * Description: + *

+ *

280:BLZ + *

+ */ +public class Kik extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, + length = -3)}) + String laenderkennzeichen; + + @Element( + description = {@ElementDescription(number = 2, + status = ElementDescription.StatusCode.O, + length = -30)}) + @an + private String kreditinstitutscode; + + public Kik(byte[] degString) { + super(degString); + } + + @Override + public String toString() { + return "Kik{" + + "laenderkennzeichen='" + laenderkennzeichen + '\'' + + ", kreditinstitutscode='" + kreditinstitutscode + '\'' + + '}'; + } + + public String getKreditinstitutscode() { + return kreditinstitutscode; + } + + public String getLaenderkennzeichen() { + return laenderkennzeichen; + } + + public void setLaenderkennzeichen(String laenderkennzeichen) { + this.laenderkennzeichen = laenderkennzeichen; + } + + public void setKreditinstitutscode(String kreditinstitutscode) { + this.kreditinstitutscode = kreditinstitutscode; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KommunikationsParameter.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KommunikationsParameter.java new file mode 100644 index 0000000..85d8aa4 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KommunikationsParameter.java @@ -0,0 +1,122 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Kommunikationsparameter + * Die Kommunikationsparameter enthalten Informationen für den Aufbau der + * Transportverbindung. + */ +public class KommunikationsParameter extends DatenElementGruppe { + + /** + * Kommunikationsdienst + * Unterstütztes Kommunikationsverfahren (Protokollstack). + * Zur Zeit unterstützte Kommunikationsverfahren: + * 1: T-Online (mit FinTS V3.0 nicht mehr unterstützt) + * 2: TCP/IP (Protokollstack SLIP/PPP) + * 3: https (verwendet im Sicherheitsverfahren PIN/TAN) + */ + @Element(description = {@ElementDescription(number = 1, length = -2)}) + @num + private Integer kommunikationsdienst; + + /** + * Kommunikationsadresse + * Beim Zugang über T-Online ist die Gateway-Seite als numerischer Wert + * (ohne die Steuerzeichen * und #) einzustellen. + * Beim Zugang über TCP/IP ist die IP-Adresse als alphanumerischer Wert + * (z.B. ‘123.123.123.123’) einzustellen. + * Beim Zugang über https ist die Adresse des Servlets als alphanumerischer + * Wert (z.B. „https://www.xyz.de:7000/Servlet“) einzustellen. + */ + @Element(description = {@ElementDescription(number = 2, length = -512)}) + @an + private String kommunikationsadresse; + + /** + * Kommunikationsadressenzusatz + * Beim Zugang über T-Online ist der Regionalbereich einzustellen (‚00’ für ein + * bundesweites Angebot). Beim Zugang über TCP/IP und https wird das Feld + * nicht belegt. + */ + @Element(description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.C, length = -512)}) + @an + private String kommunikationsadressenzusatz; + + /** + * Filterfunktion + * Falls das Übertragungsverfahren eine Umwandlung der Nachricht in eine + * 7 Bit-Zeichendarstellung erfordert (z.B. Internet), so ist hier das anzuwen- + * dende Filterverfahren anzugeben. Die Nachricht ist stets komplett zu filtern, + * auch wenn eine Filterung nicht notwendig wäre, da bspw. keine binären Da- + * ten enthalten sind. Ein Kreditinstitut darf jeweils nur eine Filterfunktion unter- + * stützen. + * Codierung: + * MIM: MIME Base 64 + * UUE: Uuencode/Uudecode + */ + @Element(description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.C, length = 3)}) + @an + private String filterfunktion; + + /** + * Version der Filterfunktion + */ + @Element(description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -3)}) + private Integer versionDerFilterfunktion; + + public KommunikationsParameter(String zugangsAdresse) { + super(new byte[0]); + if (zugangsAdresse.toLowerCase().contains("https")) { + kommunikationsdienst = 3; + } else { + kommunikationsdienst = 2; + filterfunktion = "UUE"; + } + kommunikationsadresse = zugangsAdresse; + } + + public Integer getKommunikationsdienst() { + return kommunikationsdienst; + } + + public void setKommunikationsdienst(Integer kommunikationsdienst) { + this.kommunikationsdienst = kommunikationsdienst; + } + + public String getKommunikationsadresse() { + return kommunikationsadresse; + } + + public void setKommunikationsadresse(String kommunikationsadresse) { + this.kommunikationsadresse = kommunikationsadresse; + } + + public String getKommunikationsadressenzusatz() { + return kommunikationsadressenzusatz; + } + + public void setKommunikationsadressenzusatz(String kommunikationsadressenzusatz) { + this.kommunikationsadressenzusatz = kommunikationsadressenzusatz; + } + + public String getFilterfunktion() { + return filterfunktion; + } + + public void setFilterfunktion(String filterfunktion) { + this.filterfunktion = filterfunktion; + } + + public Integer getVersionDerFilterfunktion() { + return versionDerFilterfunktion; + } + + public void setVersionDerFilterfunktion(Integer versionDerFilterfunktion) { + this.versionDerFilterfunktion = versionDerFilterfunktion; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Kontolimit.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Kontolimit.java new file mode 100644 index 0000000..1a540d3 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Kontolimit.java @@ -0,0 +1,45 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Kontobezogenes Limit für Verfügungen am Konto. + * Die Angabe eines Kontolimits ist kreditinstitutsseitig optional, + * so dass für den Kunden ein Limit bestehen kann, auch wenn dieses nicht in die + * UPD eingestellt wurde. Ein kontobezogenes Limit darf nicht gleichzeitig mit + * geschäftsvorfallbezogenen Limiten angegeben werden. + */ +public class Kontolimit extends DatenElementGruppe { + + /** + * Codierung: + * E: Einzelauftragslimit + * T: Tageslimit + * W: Wochenlimit + * M: Monatslimit + * Z: Zeitlimit + */ + @Element( + description = {@ElementDescription(number = 1, length = 1)}) + @code(restrictions = {"E", "T", "W", "M", "Z"}) + private String limitart; + + @Element( + description = {@ElementDescription(number = 2)}) + private Betrag limitbetrag; + + /** + * C => Optional nur bei limitart == Z, sonst nicht + */ + @Element( + description = {@ElementDescription(number = 3, length = -3)}) + @num + private Betrag limit_tage; + + public Kontolimit(byte[] bytes) { + super(bytes); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KontoverbindungInternational.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KontoverbindungInternational.java new file mode 100644 index 0000000..1c7f506 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KontoverbindungInternational.java @@ -0,0 +1,89 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; + +/** + * Kontoverbindung international + * Die Kontoverbindung international dient zur Verwendung von IBAN und BIC sowie + * auch der nationalen Elemente Kreditinstitutskennung und Konto-/Depotnummer mit + * optionalem Unterkontomerkmal, strukturell angelehnt an das Aggregate „Account“ in + * ISO20022. + *

+ * Name: Kontoverbindung international + * Typ: Mehrfach verwendetes Element + * Kennung: kti + * Version: 1 + */ +public class KontoverbindungInternational extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, status = ElementDescription.StatusCode.O, length = -34)}) + @an + private String iban; + + @Element( + description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.O, length = -11)}) + @an + private String bic; + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.O, length = -30)}) + @id + private String konto_depot_nummer; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.O, length = -30)}) + @id + private String unterkontenmerkmal; + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.O)}) + private Kik kreditsinstitutskennung; + + public KontoverbindungInternational(byte[] bytes) { + super(bytes); + } + + public String getIban() { + return iban; + } + + public void setIban(String iban) { + this.iban = iban; + } + + public String getBic() { + return bic; + } + + public void setBic(String bic) { + this.bic = bic; + } + + public String getKonto_depot_nummer() { + return konto_depot_nummer; + } + + public void setKonto_depot_nummer(String konto_depot_nummer) { + this.konto_depot_nummer = konto_depot_nummer; + } + + public String getUnterkontenmerkmal() { + return unterkontenmerkmal; + } + + public void setUnterkontenmerkmal(String unterkontenmerkmal) { + this.unterkontenmerkmal = unterkontenmerkmal; + } + + public Kik getKreditsinstitutskennung() { + return kreditsinstitutskennung; + } + + public void setKreditsinstitutskennung(Kik kreditsinstitutskennung) { + this.kreditsinstitutskennung = kreditsinstitutskennung; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KontoverbindungNational.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KontoverbindungNational.java new file mode 100644 index 0000000..daaff82 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KontoverbindungNational.java @@ -0,0 +1,69 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; + +/** + * Deutsche Kontoverbindung, die im Rahmen der Abwicklung eines Auftrags benötigt wird. + * Typ: DEG + * Formatkennung: ktv + * Länge: # + * Version: 2 + *

+ * Beispiel: 1234567:EUR:280:10020030 + */ +public class KontoverbindungNational extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1)}) + @an + private String kontonummer; + + @Element( + description = {@ElementDescription(number = 2)}) + @an + private String unterkontomerkmal; + + /* + @Element(number = 3) + @an + private String sprache; + + @Element(number = 4) + @an + private String bankleitzahl; + */ + @Element( + description = {@ElementDescription(number = 3)}) + private Kik kreditinstitutskennung; + + + public KontoverbindungNational(byte[] bytes) { + super(bytes); + } + + public String getKontonummer() { + return kontonummer; + } + + public void setKontonummer(String kontonummer) { + this.kontonummer = kontonummer; + } + + public String getUnterkontomerkmal() { + return unterkontomerkmal; + } + + public void setUnterkontomerkmal(String unterkontomerkmal) { + this.unterkontomerkmal = unterkontomerkmal; + } + + public Kik getKreditinstitutskennung() { + return kreditinstitutskennung; + } + + public void setKreditinstitutskennung(Kik kreditinstitutskennung) { + this.kreditinstitutskennung = kreditinstitutskennung; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KontoverbindungZvInternational.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KontoverbindungZvInternational.java new file mode 100644 index 0000000..c737593 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KontoverbindungZvInternational.java @@ -0,0 +1,103 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.jn; + +/** + * Kontoverbindung ZV international + * Die Kontoverbindung ZV international dient zur Verwendung von IBAN und BIC im + * Bereich der SEPA-Zahlungsverkehrsinstrumente sowie – über BPD-Parameter + * steuerbar – auch der nationalen Elemente Kreditinstitutskennung und Konto- + * /Depotnummer mit optionalem Unterkontomerkmal. + *

+ * Name: Kontoverbindung ZV international + * Typ: Mehrfach verwendetes Element + * Kennung: ktz + * Version: 1 + */ +public class KontoverbindungZvInternational extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = 1)}) + @jn + private String kontoverwendungSepa; + + @Element( + description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.C, length = -34)}) + @an + private String iban; + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.C, length = -11)}) + @an + private String bic; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.M, length = -30)}) + @id + private String konto_depot_nummer; + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.C, length = -30)}) + @id + private String unterkontenmerkmal; + + @Element( + description = {@ElementDescription(number = 6, status = ElementDescription.StatusCode.M)}) + private Kik kreditsinstitutskennung; + + public KontoverbindungZvInternational(byte[] bytes) { + super(bytes); + } + + public String getKontoverwendungSepa() { + return kontoverwendungSepa; + } + + public void setKontoverwendungSepa(String kontoverwendungSepa) { + this.kontoverwendungSepa = kontoverwendungSepa; + } + + public String getIban() { + return iban; + } + + public void setIban(String iban) { + this.iban = iban; + } + + public String getBic() { + return bic; + } + + public void setBic(String bic) { + this.bic = bic; + } + + public String getKonto_depot_nummer() { + return konto_depot_nummer; + } + + public void setKonto_depot_nummer(String konto_depot_nummer) { + this.konto_depot_nummer = konto_depot_nummer; + } + + public String getUnterkontenmerkmal() { + return unterkontenmerkmal; + } + + public void setUnterkontenmerkmal(String unterkontenmerkmal) { + this.unterkontenmerkmal = unterkontenmerkmal; + } + + public Kik getKreditsinstitutskennung() { + return kreditsinstitutskennung; + } + + public void setKreditsinstitutskennung(Kik kreditsinstitutskennung) { + this.kreditsinstitutskennung = kreditsinstitutskennung; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KreditkartenUmsatz.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KreditkartenUmsatz.java new file mode 100644 index 0000000..635529a --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/KreditkartenUmsatz.java @@ -0,0 +1,215 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dig; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.jn; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Umsatz Kreditkartenkonto + * Buchungsposition einer Kreditkartenabrechnung bzw. -umsatzanzeige. + */ +public class KreditkartenUmsatz extends DatenElementGruppe{ + + @Element(description = {@ElementDescription(number = 1,length = -16)}) + @an + private String getaetigtVon; + + @Element(description = {@ElementDescription(number = 2)}) + @dat + private String belegDatum; + + @Element(description = {@ElementDescription(number = 3)}) + @dat + private String buchungsDatum; + + @Element(description = {@ElementDescription(number = 4,status = ElementDescription.StatusCode.O)}) + @dat + private String wertstellungsDatum; + + @Element(description = {@ElementDescription(number = 5,status = ElementDescription.StatusCode.O)}) + private BetragSollHabenKennung originalBetrag; + + @Element(description = {@ElementDescription(number = 6,status = ElementDescription.StatusCode.O)}) + @an + private String umrechnungskurs; + + @Element(description = {@ElementDescription(number = 7)}) + private BetragSollHabenKennung buchungsBetrag; + + @Element(description = {@ElementDescription(number = 8)}) + private Transaktionsbeschreibung transaktionsbeschreibung; + + @Element(description = {@ElementDescription(number = 9,status = ElementDescription.StatusCode.O)}) + @dig + private String laenderkennzeichen; + + @Element(description = {@ElementDescription(number = 10)}) + @jn + private String umsatzAbgerechnet; + + @Element(description = {@ElementDescription(number = 11,status = ElementDescription.StatusCode.O)}) + @id + private String buchungsreferenz; + + @Element(description = {@ElementDescription(number = 12,status = ElementDescription.StatusCode.O)}) + @an + private String gebuehrenschluessel; + + @Element(description = {@ElementDescription(number = 13,status = ElementDescription.StatusCode.O)}) + @num + private String haendlerart; + + @Element(description = {@ElementDescription(number = 14,status = ElementDescription.StatusCode.O)}) + @an + private String abrechnungskennzeichen; + + @Element(description = {@ElementDescription(number = 15,status = ElementDescription.StatusCode.O)}) + @an + private String gaaBarEntgeltBuchungsreferenz; + + @Element(description = {@ElementDescription(number = 16,status = ElementDescription.StatusCode.O)}) + @an + private String aeeBuchungsreferenz; + + public KreditkartenUmsatz(byte[] bytes) { + super(bytes); + } + + public String getGetaetigtVon() { + return getaetigtVon; + } + + public void setGetaetigtVon(String getaetigtVon) { + this.getaetigtVon = getaetigtVon; + } + + public String getBelegDatum() { + return belegDatum; + } + + public void setBelegDatum(String belegDatum) { + this.belegDatum = belegDatum; + } + + public String getBuchungsDatum() { + return buchungsDatum; + } + + public void setBuchungsDatum(String buchungsDatum) { + this.buchungsDatum = buchungsDatum; + } + + public String getWertstellungsDatum() { + return wertstellungsDatum; + } + + public void setWertstellungsDatum(String wertstellungsDatum) { + this.wertstellungsDatum = wertstellungsDatum; + } + + public BetragSollHabenKennung getOriginalBetrag() { + return originalBetrag; + } + + public void setOriginalBetrag(BetragSollHabenKennung originalBetrag) { + this.originalBetrag = originalBetrag; + } + + public String getUmrechnungskurs() { + return umrechnungskurs; + } + + public void setUmrechnungskurs(String umrechnungskurs) { + this.umrechnungskurs = umrechnungskurs; + } + + public BetragSollHabenKennung getBuchungsBetrag() { + return buchungsBetrag; + } + + public void setBuchungsBetrag(BetragSollHabenKennung buchungsBetrag) { + this.buchungsBetrag = buchungsBetrag; + } + + public Transaktionsbeschreibung getTransaktionsbeschreibung() { + return transaktionsbeschreibung; + } + + public void setTransaktionsbeschreibung(Transaktionsbeschreibung transaktionsbeschreibung) { + this.transaktionsbeschreibung = transaktionsbeschreibung; + } + + public String getLaenderkennzeichen() { + return laenderkennzeichen; + } + + public void setLaenderkennzeichen(String laenderkennzeichen) { + this.laenderkennzeichen = laenderkennzeichen; + } + + public String getUmsatzAbgerechnet() { + return umsatzAbgerechnet; + } + + public void setUmsatzAbgerechnet(String umsatzAbgerechnet) { + this.umsatzAbgerechnet = umsatzAbgerechnet; + } + + public String getBuchungsreferenz() { + return buchungsreferenz; + } + + public void setBuchungsreferenz(String buchungsreferenz) { + this.buchungsreferenz = buchungsreferenz; + } + + public String getGebuehrenschluessel() { + return gebuehrenschluessel; + } + + public void setGebuehrenschluessel(String gebuehrenschluessel) { + this.gebuehrenschluessel = gebuehrenschluessel; + } + + public String getHaendlerart() { + return haendlerart; + } + + public void setHaendlerart(String haendlerart) { + this.haendlerart = haendlerart; + } + + public String getAbrechnungskennzeichen() { + return abrechnungskennzeichen; + } + + public void setAbrechnungskennzeichen(String abrechnungskennzeichen) { + this.abrechnungskennzeichen = abrechnungskennzeichen; + } + + public String getGaaBarEntgeltBuchungsreferenz() { + return gaaBarEntgeltBuchungsreferenz; + } + + public void setGaaBarEntgeltBuchungsreferenz(String gaaBarEntgeltBuchungsreferenz) { + this.gaaBarEntgeltBuchungsreferenz = gaaBarEntgeltBuchungsreferenz; + } + + public String getAeeBuchungsreferenz() { + return aeeBuchungsreferenz; + } + + public void setAeeBuchungsreferenz(String aeeBuchungsreferenz) { + this.aeeBuchungsreferenz = aeeBuchungsreferenz; + } + + @Override + public byte[] getHbciEncoded() { + return super.getHbciEncoded(); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/OeffentlicherSchluessel.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/OeffentlicherSchluessel.java new file mode 100644 index 0000000..d41ac67 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/OeffentlicherSchluessel.java @@ -0,0 +1,123 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; + +/** + * Öffentlicher Schlüssel + * Information, die beim RAH-/RDH-Key-Management zum Transport des öf- + * fentlichen Schlüssels zwischen Kunde und Kreditinstitut bzw. umgekehrt + * dient. + */ +public class OeffentlicherSchluessel extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = -3)}) + @code(restrictions = {"5", "6"}) + private String verwendungszweckOeffentlicherSchluessel; + + @Element( + description = {@ElementDescription(number = 2, length = -3)}) + @code(restrictions = {"2", "16", "17", "18", "19", "999"}) + private String opertationsmodusKodiert; + + @Element( + description = {@ElementDescription(number = 3, length = -3)}) + @code(restrictions = {"10"}) + private String verfahrenBenutzer; + + @Element( + description = {@ElementDescription(number = 4, length = -512)}) + @bin + private byte[] modulus; + + @Element( + description = {@ElementDescription(number = 5, length = -3)}) + @code(restrictions = {"12"}) + private String bezeichnerModulus; + + @Element( + description = {@ElementDescription(number = 6, length = -512)}) + @bin + private byte[] exponent; + + @Element( + description = {@ElementDescription(number = 7, length = -3)}) + @code(restrictions = {"13"}) + private String bezeichnerExponent; + + public OeffentlicherSchluessel(byte[] degString) { + super(degString); + } + + public String getVerwendungszweckOeffentlicherSchluessel() { + return verwendungszweckOeffentlicherSchluessel; + } + + public void setVerwendungszweckOeffentlicherSchluessel(String verwendungszweckOeffentlicherSchluessel) { + this.verwendungszweckOeffentlicherSchluessel = verwendungszweckOeffentlicherSchluessel; + } + + public String getOpertationsmodusKodiert() { + return opertationsmodusKodiert; + } + + public void setOpertationsmodusKodiert(String opertationsmodusKodiert) { + this.opertationsmodusKodiert = opertationsmodusKodiert; + } + + public String getVerfahrenBenutzer() { + return verfahrenBenutzer; + } + + public void setVerfahrenBenutzer(String verfahrenBenutzer) { + this.verfahrenBenutzer = verfahrenBenutzer; + } + + public byte[] getModulus() { + return modulus; + } + + public void setModulus(byte[] modulus) { + this.modulus = modulus; + } + + public String getBezeichnerModulus() { + return bezeichnerModulus; + } + + public void setBezeichnerModulus(String bezeichnerModulus) { + this.bezeichnerModulus = bezeichnerModulus; + } + + public byte[] getExponent() { + return exponent; + } + + public void setExponent(byte[] exponent) { + this.exponent = exponent; + } + + public String getBezeichnerExponent() { + return bezeichnerExponent; + } + + public void setBezeichnerExponent(String bezeichnerExponent) { + this.bezeichnerExponent = bezeichnerExponent; + } + + @Override + public String toString() { + return "OeffentlicherSchluessel{" + + "verwendungszweckOeffentlicherSchluessel='" + verwendungszweckOeffentlicherSchluessel + '\'' + + ", opertationsmodusKodiert='" + opertationsmodusKodiert + '\'' + + ", verfahrenBenutzer='" + verfahrenBenutzer + '\'' + + ", modulus=" + modulus.length + + ", bezeichnerModulus='" + bezeichnerModulus + '\'' + + ", exponent=" + exponent.length + + ", bezeichnerExponent='" + bezeichnerExponent + '\'' + + '}'; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Parameter.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Parameter.java new file mode 100644 index 0000000..21db6e7 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Parameter.java @@ -0,0 +1,28 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; + +import java.util.List; + +/** + * Parameter für Parameter-Segmente. + */ +public class Parameter extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1)}) + private List parameter; + + public Parameter(byte[] bytes) { + super(bytes); + } + + public List getParameter() { + return parameter; + } + + public void setParameter(List parameter) { + this.parameter = parameter; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/ParameterChallengeKlasse.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/ParameterChallengeKlasse.java new file mode 100644 index 0000000..4d9cf28 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/ParameterChallengeKlasse.java @@ -0,0 +1,31 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; + +import java.util.List; + +/** + * Parameter Challenge-Klasse + * Auftragsspezifische Daten, die entsprechend der Challenge-Klasse für die + * Verarbeitung im Institut benötigt werden. + */ +public class ParameterChallengeKlasse extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, status = ElementDescription.StatusCode.O, length = -999)}) + private List challengeKlasseParameter; + + public ParameterChallengeKlasse(byte[] bytes) { + super(bytes); + } + + public List getChallengeKlasseParameter() { + return challengeKlasseParameter; + } + + public void setChallengeKlasseParameter(List challengeKlasseParameter) { + this.challengeKlasseParameter = challengeKlasseParameter; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/PinTanSpezifischeInformationen.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/PinTanSpezifischeInformationen.java new file mode 100644 index 0000000..356ac5e --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/PinTanSpezifischeInformationen.java @@ -0,0 +1,49 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +import java.util.List; + +/** + * Name: Parameter PIN/TAN-spezifische Informationen + * Typ: Datenelementgruppe + * Status: M + */ +public class PinTanSpezifischeInformationen extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, status = ElementDescription.StatusCode.O, length = -2)}) + @num + public Integer minPinLaenge; + + @Element( + description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.O, length = -2)}) + @num + public Integer maxPinLaenge; + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.O, length = -2)}) + @num + public Integer maxTanLaenge; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.O, length = -30)}) + @an + public String textBelegungBenutzerkennung; + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.O, length = -30)}) + @an + public String textBelegungKundenId; + + @Element( + description = {@ElementDescription(number = 6, status = ElementDescription.StatusCode.O)}) + public List geschaeftsvorfallspezifischePinTanInformationen; + + public PinTanSpezifischeInformationen() { + super(new byte[0]); + } +} \ No newline at end of file diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Rueckmeldung.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Rueckmeldung.java new file mode 100644 index 0000000..ebe0fd9 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Rueckmeldung.java @@ -0,0 +1,94 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.segments.acknowledgement.AckString; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dig; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; + +/** + * Rückmeldung + * Rückmeldung des Kreditsinstituts. + */ +public class Rueckmeldung extends DatenElementGruppe { + + private static final Logger LOG = LogManager.getLogger(Rueckmeldung.class); + + @Element( + description = {@ElementDescription(number = 1, length = 4)}) + @dig + private String rueckmeldungscode; + + @Element( + description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.C, length = -7)}) + @an + private String bezugsdatenelement; + + @Element( + description = {@ElementDescription(number = 3, length = -80)}) + @an + private String rueckmeldungstext; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.O, length = -35)}) + private List rueckmeldungsparamter; + + public static Rueckmeldung getRueckmeldung(String code) { + Rueckmeldung rueckmeldung = new Rueckmeldung(new byte[0]); + rueckmeldung.setRueckmeldungscode(code); + String rueckmeldungsText = AckString.getMessageText(code); + LOG.debug("RückmeldungsText from AckString: {}", rueckmeldungsText); + rueckmeldung.setRueckmeldungstext(rueckmeldungsText); + return rueckmeldung; + } + + public Rueckmeldung(byte[] bytes) { + super(bytes); + } + + public String getRueckmeldungscode() { + return rueckmeldungscode; + } + + public void setRueckmeldungscode(String rueckmeldungscode) { + this.rueckmeldungscode = rueckmeldungscode; + } + + public String getBezugsdatenelement() { + return bezugsdatenelement; + } + + public void setBezugsdatenelement(String bezugsdatenelement) { + this.bezugsdatenelement = bezugsdatenelement; + } + + public String getRueckmeldungstext() { + return rueckmeldungstext; + } + + public void setRueckmeldungstext(String rueckmeldungstext) { + this.rueckmeldungstext = rueckmeldungstext; + } + + public List getRueckmeldungsparamter() { + return rueckmeldungsparamter; + } + + public void setRueckmeldungsparamter(List rueckmeldungsparamter) { + this.rueckmeldungsparamter = rueckmeldungsparamter; + } + + @Override + public String toString() { + return "Rueckmeldung{" + + "rueckmeldungscode='" + rueckmeldungscode + '\'' + + ", bezugsdatenelement='" + bezugsdatenelement + '\'' + + ", rueckmeldungstext='" + rueckmeldungstext + '\'' + + ", rueckmeldungsparamter='" + rueckmeldungsparamter + '\'' + + '}'; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Saldo.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Saldo.java new file mode 100644 index 0000000..4cf3301 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Saldo.java @@ -0,0 +1,71 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.tim; + +/** + * Saldo + * Kontostand zum aktuellen oder zu einem vergangenen Zeitpunkt, welcher sich als + * Unterschiedsbetrag zwischen der Soll- und Haben-Seiten des Kontos bis dato + * ergibt. + */ +public class Saldo extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = 1)}) + @code(restrictions = {"C", "D"}) + private String sollHabenKennzeichen; + + @Element( + description = {@ElementDescription(number = 2)}) + private Betrag betrag; + + @Element( + description = {@ElementDescription(number = 3, length = 8)}) + @dat + private String datum; + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.O, length = 6)}) + @tim + private String uhrzeit; + + public Saldo(byte[] bytes) { + super(bytes); + } + + public String getSollHabenKennzeichen() { + return sollHabenKennzeichen; + } + + public void setSollHabenKennzeichen(String sollHabenKennzeichen) { + this.sollHabenKennzeichen = sollHabenKennzeichen; + } + + public Betrag getBetrag() { + return betrag; + } + + public void setBetrag(Betrag betrag) { + this.betrag = betrag; + } + + public String getDatum() { + return datum; + } + + public void setDatum(String datum) { + this.datum = datum; + } + + public String getUhrzeit() { + return uhrzeit; + } + + public void setUhrzeit(String uhrzeit) { + this.uhrzeit = uhrzeit; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Schluesselname.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Schluesselname.java new file mode 100644 index 0000000..c835fce --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Schluesselname.java @@ -0,0 +1,102 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Schlüsselname + * Verwendeter Schlüsselnamen beim RAH- und RDH-Verfahren respektive die + * Referenz auf den Chiffrierschlüssel beim DDV-Verfahren in strukturierter + * Form. Mit dieser Information kann die Referenz auf einen Schlüssel herge- + * stellt werden. + * Dabei enthält das DE „Benutzerkennung“ bei Schlüsseln des Kunden die + * Benutzerkennung, mit der der Kunde eindeutig identifiziert wird. Bei Schlüs- + * seln des Kreditinstituts ist dagegen eine beliebige Kennung einzustellen, die + * dazu dient, den Kreditinstitutsschlüssel eindeutig zu identifizieren. Diese + * Kennung darf weder einer anderen gültigen Benutzerkennung des Kreditin- + * stituts noch der Benutzerkennung für den anonymen Zugang entsprechen. + */ +public class Schluesselname extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = 0)}) + private Kik kreditsinstitutkennung; + + @Element( + description = {@ElementDescription(number = 2, length = 0)}) + @id + private String benutzerkennung; + + @Element( + description = {@ElementDescription(number = 3, length = 1)}) + @code(restrictions = {"D", "S", "V"}) + private String schluesselart; + + @Element( + description = {@ElementDescription(number = 4, length = -3)}) + @num + private Integer schluesselnummer; + + @Element( + description = {@ElementDescription(number = 5, length = -3)}) + @num + private Integer schluesselversion; + + public Schluesselname(byte[] degString) { + super(degString); + } + + public Kik getKreditsinstitutkennung() { + return kreditsinstitutkennung; + } + + public String getBenutzerkennung() { + return benutzerkennung; + } + + public Integer getSchluesselversion() { + return schluesselversion; + } + + public String getSchluesselart() { + return schluesselart; + } + + public Integer getSchluesselnummer() { + return schluesselnummer; + } + + public void setKreditsinstitutkennung(Kik kreditsinstitutkennung) { + this.kreditsinstitutkennung = kreditsinstitutkennung; + } + + public void setBenutzerkennung(String benutzerkennung) { + this.benutzerkennung = benutzerkennung; + } + + public void setSchluesselart(String schluesselart) { + this.schluesselart = schluesselart; + } + + public void setSchluesselnummer(Integer schluesselnummer) { + this.schluesselnummer = schluesselnummer; + } + + public void setSchluesselversion(Integer schluesselversion) { + this.schluesselversion = schluesselversion; + } + + @Override + public String toString() { + return "Schluesselname{" + + "kreditsinstitutkennung=" + kreditsinstitutkennung + + ", benutzerkennung='" + benutzerkennung + '\'' + + ", schluesselart='" + schluesselart + '\'' + + ", schluesselnummer=" + schluesselnummer + + ", schluesselversion=" + schluesselversion + + '}'; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Segmentkopf.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Segmentkopf.java new file mode 100644 index 0000000..fe5a258 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Segmentkopf.java @@ -0,0 +1,130 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.segments.Segment; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +public class Segmentkopf extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, + length = -6)}) + @an + String segmentKennung; + + @Element( + description = {@ElementDescription(number = 2, + length = -3)}) + @num + Integer segmentNummer; + + @Element( + description = {@ElementDescription(number = 3, + length = -3)}) + @num + Integer segmentVersion; + + @Element( + description = {@ElementDescription(number = 4, + status = ElementDescription.StatusCode.C, + length = -3)}) + @num + Integer bezugssegment; + + private Segmentkopf() { + super(new byte[0]); + } + + public static class Builder { + private String segmentKennung; + private Integer segmentNumber; + private Integer segmentVersion; + private Integer bezugssegment; + + public static Builder newInstance() { + return new Builder(); + } + + public Builder setSegmentKennung(String segmentKennung) { + this.segmentKennung = segmentKennung; + return this; + } + + public Builder setSegmentKennung(Class cls) { + this.segmentKennung = cls.getSimpleName(); + return this; + } + + public Builder setSegmentNumber(int segmentNumber) { + this.segmentNumber = segmentNumber; + return this; + } + + public Builder setSegmentVersion(int segmentVersion) { + this.segmentVersion = segmentVersion; + return this; + } + + public Builder setBezugssegment(int bezugssegment) { + this.bezugssegment = bezugssegment; + return this; + } + + public Segmentkopf build() { + Segmentkopf segmentkopf = new Segmentkopf(); + segmentkopf.setSegmentKennung(this.segmentKennung); + segmentkopf.setSegmentNummer(this.segmentNumber); + segmentkopf.setSegmentVersion(this.segmentVersion); + segmentkopf.setBezugssegment(this.bezugssegment); + return segmentkopf; + } + } + + public Segmentkopf(byte[] message) { + super(message); + } + + @Override + public String toString() { + return "Segmentkopf{" + + "segmentKennung='" + segmentKennung + '\'' + + ", segmentNummer='" + segmentNummer + '\'' + + ", segmentVersion='" + segmentVersion + '\'' + + ", bezugssegment='" + bezugssegment + '\'' + + '}'; + } + + public String getSegmentKennung() { + return segmentKennung; + } + + public void setSegmentKennung(String segmentKennung) { + this.segmentKennung = segmentKennung; + } + + public Integer getSegmentNummer() { + return segmentNummer; + } + + public void setSegmentNummer(Integer segmentNummer) { + this.segmentNummer = segmentNummer; + } + + public Integer getSegmentVersion() { + return segmentVersion; + } + + public void setSegmentVersion(Integer segmentVersion) { + this.segmentVersion = segmentVersion; + } + + public Integer getBezugssegment() { + return bezugssegment; + } + + public void setBezugssegment(Integer bezugssegment) { + this.bezugssegment = bezugssegment; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/SicherheitsdatumUndUhrzeit.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/SicherheitsdatumUndUhrzeit.java new file mode 100644 index 0000000..e93aa3e --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/SicherheitsdatumUndUhrzeit.java @@ -0,0 +1,35 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.tim; + +/** + * Sicherheitsdatum und -uhrzeit + * Zeitstempel, beispielsweise Datum und Uhrzeit des lokalen Rechners, an + * dem die elektronische Unterschrift geleistet wurde, sowie die Bedeutung des + * Zeitstempels. + */ +public class SicherheitsdatumUndUhrzeit extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = -3)}) + @code(restrictions = {"1", "6"}) + private String datumUndZeitbezeichner; + + @Element( + description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.O, length = 0)}) + @dat + private String datum; + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.C, length = 0)}) + @tim + private String uhrzeit; + + public SicherheitsdatumUndUhrzeit(byte[] degString) { + super(degString); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Sicherheitsidentifikationsdetails.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Sicherheitsidentifikationsdetails.java new file mode 100644 index 0000000..e04e995 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Sicherheitsidentifikationsdetails.java @@ -0,0 +1,36 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; + +/** + * Sicherheitsidentifikation, Details + * Identifikation der im Sicherheitsprozess involvierten Parteien. Dient zur + * Übermittlung der CID bei kartenbasierten Sicherheitsverfahren bzw. der + * Kundensystem-ID bei softwarebasierten Verfahren (z.B. Speicherung der + * Schlüssel in einer Schlüsseldatei). + */ +public class Sicherheitsidentifikationsdetails extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = -3)}) + @code(restrictions = {"1", "2"}) + private String sicherheitsparteibezeichner; + + @Element( + description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.C, length = -256)}) + @bin + private byte[] cid; + + @Element( + description = {@ElementDescription(number = 3, status = ElementDescription.StatusCode.C, length = 0)}) + @id + private String parteiidentifizierung; + + public Sicherheitsidentifikationsdetails(byte[] degString) { + super(degString); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Sicherheitsprofil.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Sicherheitsprofil.java new file mode 100644 index 0000000..5ccdec3 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Sicherheitsprofil.java @@ -0,0 +1,39 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Sicherheitsprofil + * Verfahren zur Absicherung der Transaktionen, das zwischen Kunde und + * Kreditinstitut vereinbar wurde. Das Sicherheitsprofil wird anhand der Kombi- + * nation der beiden Elemente „Sicherheitsverfahren“ und „Version“ bestimmt + * (z.B. RDH-3, DDV-1). Für das Sicherheitsverfahren PINTAN ist als Code der + * Wert PIN und als Version der Wert 1 einzustellen. + */ +public class Sicherheitsprofil extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = 3)}) + @code(restrictions = {"DDV", "RAH", "RDH", "PIN", "EMV"}) + private String sicherheitsverfahren; + + @Element( + description = {@ElementDescription(number = 2, length = -3)}) + @num + private Integer sicherheitsverfahrensversion; + + public Sicherheitsprofil(byte[] degString) { + super(degString); + } + + public String getSicherheitsverfahren() { + return sicherheitsverfahren; + } + + public Integer getSicherheitsverfahrensversion() { + return sicherheitsverfahrensversion; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Signaturalgorithmus.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Signaturalgorithmus.java new file mode 100644 index 0000000..bb06460 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Signaturalgorithmus.java @@ -0,0 +1,33 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; + +/** + * Signaturalgorithmus + * Angaben zum kryptographischen Algorithmus, zu seinem Operationsmodus, + * so wie zu dessen Einsatz, in diesem Fall für die Signaturbildung über DDV + * bzw. RAH / RDH. + */ +public class Signaturalgorithmus extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = -3)}) + @code(restrictions = {"6"}) + private String signaturalgorithmusverwendung; + + @Element( + description = {@ElementDescription(number = 2, length = -3)}) + @code(restrictions = {"1", "10"}) + private String signaturalgorithmus; + + @Element( + description = {@ElementDescription(number = 3, length = -3)}) + @code(restrictions = {"16", "17", "18", "19", "999"}) + private String operationsmodus; + + public Signaturalgorithmus(byte[] degString) { + super(degString); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Transaktionsbeschreibung.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Transaktionsbeschreibung.java new file mode 100644 index 0000000..19f7a88 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Transaktionsbeschreibung.java @@ -0,0 +1,44 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; + +import java.lang.reflect.Field; +import java.util.List; + +/** + * CTransaktionsbeschreibung. + */ +public class Transaktionsbeschreibung extends DatenElementGruppe { + + @Element(description = {@ElementDescription(number = 1,length = -25)}) + @an + private String transaktionsbeschreibungsgrundtext; + + + @Element(description = {@ElementDescription(number = 2,status = ElementDescription.StatusCode.O, length = -25)}) + @an + private String transaktionsbeschreibungszusatz; + + + public Transaktionsbeschreibung(byte[] bytes) { + super(bytes); + } + + public String getTransaktionsbeschreibungsgrundtext() { + return transaktionsbeschreibungsgrundtext; + } + + public void setTransaktionsbeschreibungsgrundtext(String transaktionsbeschreibungsgrundtext) { + this.transaktionsbeschreibungsgrundtext = transaktionsbeschreibungsgrundtext; + } + + public String getTransaktionsbeschreibungszusatz() { + return transaktionsbeschreibungszusatz; + } + + public void setTransaktionsbeschreibungszusatz(String transaktionsbeschreibungszusatz) { + this.transaktionsbeschreibungszusatz = transaktionsbeschreibungszusatz; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/UnterstuetzesSicherheitsverfahren.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/UnterstuetzesSicherheitsverfahren.java new file mode 100644 index 0000000..6527946 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/UnterstuetzesSicherheitsverfahren.java @@ -0,0 +1,45 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Unterstützte Sicherheitsverfahren + * Information über die kreditinstitutsseitig unterstützten Sicherheitsverfahren. + * Anhand der Kombination der beiden Elemente „Sicherheitsverfahren“ und + * „Version“ wird das Sicherheitsprofil (z.B. RAH-7) bestimmt. + */ +public class UnterstuetzesSicherheitsverfahren extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1)}) + @an + private String verfahren; + + @Element( + description = {@ElementDescription(number = 2)}) + @num + private Integer verfahrensVersion; + + public UnterstuetzesSicherheitsverfahren(byte[] bytes) { + super(bytes); + } + + public String getVerfahren() { + return verfahren; + } + + public void setVerfahren(String verfahren) { + this.verfahren = verfahren; + } + + public Integer getVerfahrensVersion() { + return verfahrensVersion; + } + + public void setVerfahrensVersion(Integer verfahrensVersion) { + this.verfahrensVersion = verfahrensVersion; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/UnterstuetzteCamtMessages.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/UnterstuetzteCamtMessages.java new file mode 100644 index 0000000..8f6d5b2 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/UnterstuetzteCamtMessages.java @@ -0,0 +1,40 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; + +import java.util.LinkedList; +import java.util.List; + +/** + * Unterstützte camt-messages + * Jedes DE enthält eine URN einer camt Schema-Definition. + * Wird verwendet, um die zur Verfügung stehenden camt Schema-Definitionen bekannt zu geben + * und gibt bei Abfragen an, welche camt Schemata durch das Kundenprodukt unterstützt werden. + * Diese müssen aus den vom Kredit- institut unterstützten in der BPD übermittelten camt + * Schema-Definitionen ausgewählt werden. + */ +public class UnterstuetzteCamtMessages extends DatenElementGruppe { + + @Element(description = {@ElementDescription(number = 1, length = -99)}) + private List camtDescriptor; + + public UnterstuetzteCamtMessages(byte[] bytes) { + super(bytes); + } + + public List getCamtDescriptor() { + return camtDescriptor; + } + + public void setCamtDescriptor(List camtDescriptor) { + this.camtDescriptor = camtDescriptor; + } + + public void addCamtDescriptor(String descriptorString) { + if(camtDescriptor == null) { + camtDescriptor = new LinkedList<>(); + } + camtDescriptor.add(descriptorString); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Verschluesselungsalgorithmus.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Verschluesselungsalgorithmus.java new file mode 100644 index 0000000..04d66e0 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Verschluesselungsalgorithmus.java @@ -0,0 +1,57 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; + +/** + * Verschlüsselungsalgorithmus + * Angaben zum kryptographischen Algorithmus, zu seinem Operationsmodus, + * so wie zu dessen Einsatz, in diesem Fall für die Nachrichtenverschlüsselung. + */ +public class Verschluesselungsalgorithmus extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = -3)}) + @code(restrictions = {"2"}) + private String verwedungVerschluesselungsalgorithmusKodiert; + + @Element( + description = {@ElementDescription(number = 2, length = -3)}) + @code(restrictions = {"2", "16", "17", "18", "19"}) + private String operationsmodusKodiert; + + @Element( + description = {@ElementDescription(number = 3, length = -3)}) + @code(restrictions = {"13", "14"}) + private String verschluesselungsalgorithmusKodiert; + + @Element( + description = {@ElementDescription(number = 4, length = -512)}) + @bin + private byte[] wertAlgorithmusparameterSchluessel; + + @Element( + description = {@ElementDescription(number = 5, length = -3)}) + @code(restrictions = {"5", "6"}) + private String bezeichnerAlgorithmusparameterSchluessel; + + @Element( + description = {@ElementDescription(number = 6, length = -3)}) + @code(restrictions = {"1"}) + private String bezeichnerAlgorithmusparameterIV; + + @Element( + description = {@ElementDescription(number = 7, status = ElementDescription.StatusCode.O, length = -512)}) + @bin + private byte[] wertAlgorithmusparameterIV; + + public Verschluesselungsalgorithmus(byte[] degString) { + super(degString); + } + + public byte[] getWertAlgorithmusparameterSchluessel() { + return wertAlgorithmusparameterSchluessel; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Verwendungszweck.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Verwendungszweck.java new file mode 100644 index 0000000..459ac19 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Verwendungszweck.java @@ -0,0 +1,32 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; + +import java.util.List; + +/** + * Verwendungszweck + * Angabe zum Verwendungszweck bei einem Überweisungsauftrag. + * Die maximale Anzahl der Verwendungszweckzeilen ergibt sich aus den + * BPD. Es ist der DTAUS0-Zeichensatz mit der entsprechenden Codierung zu + * verwenden. + */ +public class Verwendungszweck extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = -27)}) + private List verwendungszweckzeilen; + + public Verwendungszweck(byte[] bytes) { + super(bytes); + } + + public List getVerwendungszweckzeilen() { + return verwendungszweckzeilen; + } + + public void setVerwendungszweckzeilen(List verwendungszweckzeilen) { + this.verwendungszweckzeilen = verwendungszweckzeilen; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Zeitstempel.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Zeitstempel.java new file mode 100644 index 0000000..d15eb94 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Zeitstempel.java @@ -0,0 +1,43 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.dat; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.tim; + +/** + * Zeitstempel + * Diese Struktur enthält eine Zeitangabe, bestehend aus Datum und optional Uhrzeit. + */ +public class Zeitstempel extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = 8)}) + @dat + private String datum; + + @Element( + description = {@ElementDescription(number = 2, status = ElementDescription.StatusCode.O, length = 6)}) + @tim + private String uhrzeit; + + public Zeitstempel(byte[] bytes) { + super(bytes); + } + + public String getDatum() { + return datum; + } + + public void setDatum(String datum) { + this.datum = datum; + } + + public String getUhrzeit() { + return uhrzeit; + } + + public void setUhrzeit(String uhrzeit) { + this.uhrzeit = uhrzeit; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Zertifikat.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Zertifikat.java new file mode 100644 index 0000000..708c9ae --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/Zertifikat.java @@ -0,0 +1,41 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.bin; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; + +import java.util.Arrays; + +/** + * Zertifikat + * Zertifikat eines öffentlichen Schlüssels. + * Da Zertifikate Informationen beinhalten, die auch in den HBCI-Formaten ent- + * halten sind (z.B. Zertifikatsreferenz respektive Schlüsselnamen), können Da- + * ten redundant vorkommen. Diese müssen dann auf Konsistenz überprüft + * werden. Bei Unstimmigkeiten hat das Zertifikat Vorrang. + */ +public class Zertifikat extends DatenElementGruppe { + + @Element( + description = {@ElementDescription(number = 1, length = 1)}) + @code(restrictions = {"1", "2", "3"}) + private String zertifikatstyp; + + @Element( + description = {@ElementDescription(number = 2, length = -4096)}) + @bin + private byte[] zertifikatsinhalt; + + public Zertifikat(byte[] degString) { + super(degString); + } + + @Override + public String toString() { + return "Zertifikat{" + + "zertifikatstyp='" + zertifikatstyp + '\'' + + ", zertifikatsinhalt=" + Arrays.toString(zertifikatsinhalt) + + '}'; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIBPA.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIBPA.java new file mode 100644 index 0000000..3c0fa76 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIBPA.java @@ -0,0 +1,170 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.parameter; + +import net.petafuel.fuelifints.dataaccess.dataobjects.CommonBankParameterDataObject; +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.segments.Segment; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Kik; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Name: Bankparameter allgemein + * Typ: Segment + * Segmentart:Administration + * Kennung: HIBPA + * Bezugssegment: HKVVB + * Version: 3 + * Sender: Kreditinstitut + */ +public class HIBPA extends Segment { + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, length = -3)}) + @num + private Integer bpd_version; + + @Element( + description = {@ElementDescription(number = 3)}) + private Kik kreditInstitutskennung; + + @Element( + description = {@ElementDescription(number = 4, length = -60)}) + @an + private String kreditinstitutsbezeichnung; + + @Element( + description = {@ElementDescription(number = 5, length = -3)}) + @num + private Integer anzahl_geschaeftsvorfallarten; + + @Element( + description = {@ElementDescription(number = 6)}) + @an + private String unterstuetzte_sprachen; + + @Element( + description = {@ElementDescription(number = 7)}) + @an + private String unterstuetzte_hbci_versionen; + + @Element( + description = {@ElementDescription(number = 8, length = -4, status = ElementDescription.StatusCode.O)}) + @num + private Integer maximale_nachrichtengroesse; + + @Element( + description = {@ElementDescription(number = 9, length = -4, status = ElementDescription.StatusCode.O)}) + @num + private Integer minimaler_timeoutwert; + + @Element( + description = {@ElementDescription(number = 10, length = -4, status = ElementDescription.StatusCode.O)}) + @num + private Integer maximaler_timeoutwert; + + public HIBPA(CommonBankParameterDataObject paramData, int bezugsSegment) { + super(new byte[0]); + /* + this.segmentkopf = new Segmentkopf( + ("HIBPA:0:3:" + + bezugsSegment).getBytes() + ); + */ + this.segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(HIBPA.class).setSegmentVersion(3).setBezugssegment(bezugsSegment).build(); + + this.bpd_version = paramData.getBpd_version(); + this.kreditInstitutskennung = new Kik(("280:" + paramData.getBankleitzahl()).getBytes()); + this.kreditinstitutsbezeichnung = paramData.getKreditinstitutsbezeichnung(); + this.anzahl_geschaeftsvorfallarten = 0; //Wird in HKVVB.execute() gesetzt, abhängig von der Anzahl der unterstützten Geschäftsvorfallarten + this.unterstuetzte_sprachen = paramData.getUnterstuetzte_Sprachen(); + this.unterstuetzte_hbci_versionen = paramData.getUnterstuetzte_hbci_versionen(); + this.maximale_nachrichtengroesse = paramData.getMaximale_nachrichten_groesse(); + this.minimaler_timeoutwert = paramData.getMinimaler_timeout_wert(); + this.maximaler_timeoutwert = paramData.getMaximaler_timeout_wert(); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public Integer getBpd_version() { + return bpd_version; + } + + public void setBpd_version(Integer bpd_version) { + this.bpd_version = bpd_version; + } + + public Kik getKreditInstitutskennung() { + return kreditInstitutskennung; + } + + public void setKreditInstitutskennung(Kik kreditInstitutskennung) { + this.kreditInstitutskennung = kreditInstitutskennung; + } + + public String getKreditinstitutsbezeichnung() { + return kreditinstitutsbezeichnung; + } + + public void setKreditinstitutsbezeichnung(String kreditinstitutsbezeichnung) { + this.kreditinstitutsbezeichnung = kreditinstitutsbezeichnung; + } + + public Integer getAnzahl_geschaeftsvorfallarten() { + return anzahl_geschaeftsvorfallarten; + } + + public void setAnzahl_geschaeftsvorfallarten(Integer anzahl_geschaeftsvorfallarten) { + this.anzahl_geschaeftsvorfallarten = anzahl_geschaeftsvorfallarten; + } + + public String getUnterstuetzte_sprachen() { + return unterstuetzte_sprachen; + } + + public void setUnterstuetzte_sprachen(String unterstuetzte_sprachen) { + this.unterstuetzte_sprachen = unterstuetzte_sprachen; + } + + public String getUnterstuetzte_hbci_versionen() { + return unterstuetzte_hbci_versionen; + } + + public void setUnterstuetzte_hbci_versionen(String unterstuetzte_hbci_versionen) { + this.unterstuetzte_hbci_versionen = unterstuetzte_hbci_versionen; + } + + public Integer getMaximale_nachrichtengroesse() { + return maximale_nachrichtengroesse; + } + + public void setMaximale_nachrichtengroesse(Integer maximale_nachrichtengroesse) { + this.maximale_nachrichtengroesse = maximale_nachrichtengroesse; + } + + public Integer getMinimaler_timeoutwert() { + return minimaler_timeoutwert; + } + + public void setMinimaler_timeoutwert(Integer minimaler_timeoutwert) { + this.minimaler_timeoutwert = minimaler_timeoutwert; + } + + public Integer getMaximaler_timeoutwert() { + return maximaler_timeoutwert; + } + + public void setMaximaler_timeoutwert(Integer maximaler_timeoutwert) { + this.maximaler_timeoutwert = maximaler_timeoutwert; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIPINS.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIPINS.java new file mode 100644 index 0000000..88af289 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIPINS.java @@ -0,0 +1,158 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.parameter; + +import net.petafuel.fuelifints.dataaccess.dataobjects.PinParameterObject; +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.segments.Segment; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.GeschaeftsvorfallspezifischePinTanInformation; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.PinTanSpezifischeInformationen; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +import java.util.LinkedList; +import java.util.List; + +/** + * PIN/TAN-spezifische Informationen (HIPINS) + * Die für die Kennzeichnung des PIN/TAN-Verfahrens notwendige BPD-/UPD- + * Erweiterung wird in Form eines speziellen Parametersegmentes realisiert, welches + * sich auf keinen echten Geschäftsvorfall bezieht, sondern Daten zu allen unterstütz- + * ten Geschäftsvorfällen aufnehmen kann. + * Das Spezialsegment HIPINS wird verwendet, um in die BPD-Segmentfolge + * PIN/TAN-spezifische Daten einzufügen. Aufgrund seines Aufbaus analog zu einem + * Segmentparametersegment wird es von Kundenprodukten, die das PIN/TAN- + * Verfahren nicht unterstützen, ignoriert, da es sich auf einen ihnen unbekannten Ge- + * schäftsvorfall zu beziehen scheint. + * Die in HIPINS aufgeführten Geschäftsvorfälle dürfen vom Kunden in über PIN/TAN + * abgesicherte Nachrichten eingestellt werden, sofern sie in den BPD und UPD als + * generell erlaubt hinterlegt sind. Alle übrigen Geschäftsvorfälle können mit dem + * PIN/TAN-Verfahren nicht verwendet werden. + */ +public class HIPINS extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, length = -3)}) + @num + private Integer maximaleAnzahlAuftraege; + + @Element( + description = {@ElementDescription(number = 3, length = 1)}) + @num + private Integer anzahlSignaturenMindestens; + + @Element( + description = {@ElementDescription(number = 4, length = 1)}) + @code(restrictions = {"0", "1", "2", "3", "4"}) + protected String sicherheitsKlasse; + + @Element( + description = {@ElementDescription(number = 4)}) + private PinTanSpezifischeInformationen pinTanSpezifischeInformationen; + + public HIPINS(byte[] message) { + super(message); + } + + public HIPINS(PinParameterObject pinParameterObject, int bezugssegment) { + super(new byte[0]); + + this.segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(HIPINS.class).setSegmentVersion(1).setBezugssegment(bezugssegment).build(); + + this.maximaleAnzahlAuftraege = pinParameterObject.getMaximaleAnzahlAuftraege(); + this.anzahlSignaturenMindestens = pinParameterObject.getAnzahlSignaturenMindestens(); + this.sicherheitsKlasse = pinParameterObject.getSicherheitsklasse().toString(); + + this.pinTanSpezifischeInformationen = new PinTanSpezifischeInformationen(); + + this.pinTanSpezifischeInformationen.minPinLaenge = pinParameterObject.getMinPinLaenge(); + this.pinTanSpezifischeInformationen.maxPinLaenge = pinParameterObject.getMaxPinLaenge(); + this.pinTanSpezifischeInformationen.maxTanLaenge = pinParameterObject.getMaxTanLaenge(); + this.pinTanSpezifischeInformationen.textBelegungBenutzerkennung = pinParameterObject.getTextBelegungBenutzerkennung(); + this.pinTanSpezifischeInformationen.textBelegungKundenId = pinParameterObject.getTextBelegungKundenId(); + this.pinTanSpezifischeInformationen.geschaeftsvorfallspezifischePinTanInformationen = pinParameterObject.getGeschaeftsvorfallspezifischePinTanInformationen(); + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public Integer getMaximaleAnzahlAuftraege() { + return maximaleAnzahlAuftraege; + } + + public void setMaximaleAnzahlAuftraege(Integer maximaleAnzahlAuftraege) { + this.maximaleAnzahlAuftraege = maximaleAnzahlAuftraege; + } + + public Integer getAnzahlSignaturenMindestens() { + return anzahlSignaturenMindestens; + } + + public void setAnzahlSignaturenMindestens(Integer anzahlSignaturenMindestens) { + this.anzahlSignaturenMindestens = anzahlSignaturenMindestens; + } + + public Integer getMinPinLaenge() { + return pinTanSpezifischeInformationen.minPinLaenge; + } + + public void setMinPinLaenge(Integer minPinLaenge) { + this.pinTanSpezifischeInformationen.minPinLaenge = minPinLaenge; + } + + public Integer getMaxPinLaenge() { + return pinTanSpezifischeInformationen.maxPinLaenge; + } + + public void setMaxPinLaenge(Integer maxPinLaenge) { + this.pinTanSpezifischeInformationen.maxPinLaenge = maxPinLaenge; + } + + public Integer getMaxTanLaenge() { + return pinTanSpezifischeInformationen.maxTanLaenge; + } + + public void setMaxTanLaenge(Integer maxTanLaenge) { + this.pinTanSpezifischeInformationen.maxTanLaenge = maxTanLaenge; + } + + public String getTextBelegungBenutzerkennung() { + return pinTanSpezifischeInformationen.textBelegungBenutzerkennung; + } + + public void setTextBelegungBenutzerkennung(String textBelegungBenutzerkennung) { + this.pinTanSpezifischeInformationen.textBelegungBenutzerkennung = textBelegungBenutzerkennung; + } + + public String getTextBelegungKundenId() { + return pinTanSpezifischeInformationen.textBelegungKundenId; + } + + public void setTextBelegungKundenId(String textBelegungKundenId) { + this.pinTanSpezifischeInformationen.textBelegungKundenId = textBelegungKundenId; + } + + public List getGeschaeftsvorfallspezifischePinTanInformationen() { + return pinTanSpezifischeInformationen.geschaeftsvorfallspezifischePinTanInformationen; + } + + public void setGeschaeftsvorfallspezifischePinTanInformationen(List geschaeftsvorfallspezifischePinTanInformationen) { + this.pinTanSpezifischeInformationen.geschaeftsvorfallspezifischePinTanInformationen = geschaeftsvorfallspezifischePinTanInformationen; + } + + public void addGeschaeftsvorfallspezifischePinTanInformation(GeschaeftsvorfallspezifischePinTanInformation geschaeftsvorfallspezifischePinTanInformation) { + if (pinTanSpezifischeInformationen.geschaeftsvorfallspezifischePinTanInformationen == null) { + pinTanSpezifischeInformationen.geschaeftsvorfallspezifischePinTanInformationen = new LinkedList(); + } + pinTanSpezifischeInformationen.geschaeftsvorfallspezifischePinTanInformationen.add(geschaeftsvorfallspezifischePinTanInformation); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HISHV.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HISHV.java new file mode 100644 index 0000000..8a30fcc --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HISHV.java @@ -0,0 +1,80 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.parameter; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.segments.Segment; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.UnterstuetzesSicherheitsverfahren; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.jn; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * Name: Sicherheitsverfahren + * Typ: Segment + * Segmentart: Administration + * Version: 3 + */ +public class HISHV extends Segment { + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @jn + private String mischung_zulaessig; + + @Element( + description = {@ElementDescription(number = 3)}) + private List unterstuetzte_sicherheitsverfahren = new LinkedList<>(); + + public HISHV(int bezugsSegment) { + super(new byte[0]); + /* + this.segmentkopf = new Segmentkopf( + ("HISHV:0:3:" + + bezugsSegment).getBytes() + ); + */ + this.segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(HISHV.class).setSegmentVersion(3).setBezugssegment(bezugsSegment).build(); + + this.mischung_zulaessig = "N"; + + String hbciSecurityProtocols = System.getProperties().getProperty("hbci.security.protocols"); + List enabledSecurityProtocols = Arrays.asList(hbciSecurityProtocols.split(";")); + for (SecurityMethod method : SecurityMethod.values()) { + if (enabledSecurityProtocols.contains(method.toString())) { + UnterstuetzesSicherheitsverfahren unterstuetzeSicherheitsverfahren = new UnterstuetzesSicherheitsverfahren(method.getHbciDEG()); + unterstuetzte_sicherheitsverfahren.add(unterstuetzeSicherheitsverfahren); + } + } + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public String getMischung_zulaessig() { + return mischung_zulaessig; + } + + public void setMischung_zulaessig(String mischung_zulaessig) { + this.mischung_zulaessig = mischung_zulaessig; + } + + public List getUnterstuetzte_sicherheitsverfahren() { + return unterstuetzte_sicherheitsverfahren; + } + + public void setUnterstuetzte_sicherheitsverfahren(List unterstuetzte_sicherheitsverfahren) { + this.unterstuetzte_sicherheitsverfahren = unterstuetzte_sicherheitsverfahren; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIUPA.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIUPA.java new file mode 100644 index 0000000..3a132c0 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIUPA.java @@ -0,0 +1,121 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.parameter; + +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.segments.Segment; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; + +/** + * Name: Userparameter allgemein + * Typ: Segment + * Segmentart: Administration + * Kennung: HIUPA + * Bezugssegment: HKVVB + * Version: 4 + * Sender: Kreditinstitut + *

+ * FinTS 3.0: Formals, E.2 + * Beispiel: HIUPA:14:3:7+12345+4+0+Herr Meier' + */ +public class HIUPA extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + @id + private String benutzerkennung; + + @Element( + description = {@ElementDescription(number = 3, length = -3)}) + @num + private Integer upd_version; + + @Element( + description = {@ElementDescription(number = 3, length = 1)}) + @code(restrictions = {"0", "1"}) + private Integer upd_verwendung; + + @Element( + description = {@ElementDescription(number = 4, length = -35, status = ElementDescription.StatusCode.O)}) + @an + private String benutzername; + + @Element( + description = {@ElementDescription(number = 5, length = -2048, status = ElementDescription.StatusCode.O)}) + @an + private String erweiterung_allgemein; + + + public HIUPA(String userId, int upd_version, String userName, int bezugsSegment) { + super(new byte[0]); + + /* + this.segmentkopf = new Segmentkopf( + ("HIUPA:0:4:" + + bezugsSegment).getBytes() + ); + */ + this.segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(HIUPA.class).setSegmentVersion(4).setBezugssegment(bezugsSegment).build(); + + this.upd_verwendung = 0; //0: Die nicht aufgeführten Geschäftsvorfälle sind gesperrt (die aufgeführten Geschäftsvorfälle sind zugelassen). + //1: Bei den nicht aufgeführten Geschäftsvorfällen ist anhand der UPD keine Aussage darüber möglich, ob diese erlaubt oder gesperrt sind. Diese Prü- fung kann nur online vom Kreditinstitutssystem vorgenommen werden. + this.benutzerkennung = userId; + this.upd_version = upd_version; + this.benutzername = userName; + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public String getBenutzerkennung() { + return benutzerkennung; + } + + public void setBenutzerkennung(String benutzerkennung) { + this.benutzerkennung = benutzerkennung; + } + + public Integer getUpd_version() { + return upd_version; + } + + public void setUpd_version(Integer upd_version) { + this.upd_version = upd_version; + } + + public Integer getUpd_verwendung() { + return upd_verwendung; + } + + public void setUpd_verwendung(Integer upd_verwendung) { + this.upd_verwendung = upd_verwendung; + } + + public String getBenutzername() { + return benutzername; + } + + public void setBenutzername(String benutzername) { + this.benutzername = benutzername; + } + + public String getErweiterung_allgemein() { + return erweiterung_allgemein; + } + + public void setErweiterung_allgemein(String erweiterung_allgemein) { + this.erweiterung_allgemein = erweiterung_allgemein; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIUPD.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIUPD.java new file mode 100644 index 0000000..fb04c5d --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIUPD.java @@ -0,0 +1,224 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.parameter; + +import net.petafuel.fuelifints.dataaccess.dataobjects.AccountDataObject; +import net.petafuel.fuelifints.exceptions.HBCISyntaxException; +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.segments.Segment; +import net.petafuel.fuelifints.protocol.fints3.segments.SegmentUtil; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.ErlaubteGeschaeftsvorfaelle; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Kontolimit; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.KontoverbindungNational; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.cur; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.id; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; +import net.petafuel.fuelifints.support.ByteSplit; + +import java.util.LinkedList; +import java.util.List; + +/** + * Name: Kontoinformation + * Typ: Segment + * Segmentart: Administration + * Kennung: HIUPD + * Bezugssegment: HKVVB + * Version: 6 + * Sender: Kreditinstitut + */ +public class HIUPD extends Segment { + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2)}) + private KontoverbindungNational kontoverbindung; + + @Element( + description = {@ElementDescription(number = 3, length = -34, status = ElementDescription.StatusCode.O)}) + @an + private String iban; + + @Element( + description = {@ElementDescription(number = 4)}) + @id + private String kunden_id; + + @Element( + description = {@ElementDescription(number = 5, length = -2, status = ElementDescription.StatusCode.O)}) + @num + private String kontoart; + + @Element( + description = {@ElementDescription(number = 6, status = ElementDescription.StatusCode.O)}) + @cur + private String kontowaehrung = "EUR"; + + @Element( + description = {@ElementDescription(number = 7, length = -27)}) + @an + private String name_des_kontoinhabers_1; + + @Element( + description = {@ElementDescription(number = 8, length = -27, status = ElementDescription.StatusCode.O)}) + @an + private String name_des_kontoinhabers_2; + + @Element( + description = {@ElementDescription(number = 9, length = -30, status = ElementDescription.StatusCode.O)}) + @an + private String kontoproduktbezeichnung; + + @Element( + description = {@ElementDescription(number = 10, status = ElementDescription.StatusCode.O)}) + private Kontolimit kontolimit; + + @Element( + description = {@ElementDescription(number = 11, status = ElementDescription.StatusCode.O)}) + private List erlaubte_Geschaeftsvorfaelle; + + @Element( + description = {@ElementDescription(number = 12, length = -2048, status = ElementDescription.StatusCode.O)}) + @an + private String erweiterung_kontobezogen; + + public HIUPD(AccountDataObject accountDataObject, int bezugsSegment) { + super(new byte[0]); + + /* + this.segmentkopf = new Segmentkopf( + ("HIUPD:0:6:" + + bezugsSegment).getBytes() + ); + */ + + this.segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(HIUPD.class).setSegmentVersion(6).setBezugssegment(bezugsSegment).build(); + + this.kontoverbindung = new KontoverbindungNational((accountDataObject.getKontonummer() + "::280:" + accountDataObject.getBankleitzahl()).getBytes()); + this.kunden_id = accountDataObject.getKunden_id(); + + this.iban = SegmentUtil.accountToIban(accountDataObject.getKontonummer(), accountDataObject.getBankleitzahl()); + + String name = accountDataObject.getName(); + if (name.length() > 27) { + this.name_des_kontoinhabers_1 = name.substring(0, 27); + this.name_des_kontoinhabers_2 = name.substring(27); + } else + this.name_des_kontoinhabers_1 = name; + + this.kontoproduktbezeichnung = accountDataObject.getProduktbzeichnung(); + List splitted = null; + try { + splitted = ByteSplit.split(accountDataObject.getErlaubteGeschaeftsvorfaelle().getBytes(), ByteSplit.MODE_DEG); + } catch (HBCISyntaxException e) { + //ignored here + } + this.erlaubte_Geschaeftsvorfaelle = new LinkedList<>(); + if (splitted != null) { + for (byte[] split : splitted) { + ErlaubteGeschaeftsvorfaelle erlaubteGeschaeftsvorfaelle = new ErlaubteGeschaeftsvorfaelle(split); + erlaubte_Geschaeftsvorfaelle.add(erlaubteGeschaeftsvorfaelle); + } + } + } + + public Segmentkopf getSegmentkopf() { + return segmentkopf; + } + + public void setSegmentkopf(Segmentkopf segmentkopf) { + this.segmentkopf = segmentkopf; + } + + public KontoverbindungNational getKontoverbindung() { + return kontoverbindung; + } + + public void setKontoverbindung(KontoverbindungNational kontoverbindung) { + this.kontoverbindung = kontoverbindung; + } + + public String getIban() { + return iban; + } + + public void setIban(String iban) { + this.iban = iban; + } + + public String getKunden_id() { + return kunden_id; + } + + public void setKunden_id(String kunden_id) { + this.kunden_id = kunden_id; + } + + public String getKontoart() { + return kontoart; + } + + public void setKontoart(String kontoart) { + this.kontoart = kontoart; + } + + public String getKontowaehrung() { + return kontowaehrung; + } + + public void setKontowaehrung(String kontowaehrung) { + this.kontowaehrung = kontowaehrung; + } + + public String getName_des_kontoinhabers_1() { + return name_des_kontoinhabers_1; + } + + public void setName_des_kontoinhabers_1(String name_des_kontoinhabers_1) { + this.name_des_kontoinhabers_1 = name_des_kontoinhabers_1; + } + + public String getName_des_kontoinhabers_2() { + return name_des_kontoinhabers_2; + } + + public void setName_des_kontoinhabers_2(String name_des_kontoinhabers_2) { + this.name_des_kontoinhabers_2 = name_des_kontoinhabers_2; + } + + public String getKontoproduktbezeichnung() { + return kontoproduktbezeichnung; + } + + public void setKontoproduktbezeichnung(String kontoproduktbezeichnung) { + this.kontoproduktbezeichnung = kontoproduktbezeichnung; + } + + public List getErlaubte_Geschaeftsvorfaelle() { + return erlaubte_Geschaeftsvorfaelle; + } + + public void setErlaubte_Geschaeftsvorfaelle(List erlaubte_Geschaeftsvorfaelle) { + this.erlaubte_Geschaeftsvorfaelle = erlaubte_Geschaeftsvorfaelle; + } + + public String getErweiterung_kontobezogen() { + return erweiterung_kontobezogen; + } + + public void setErweiterung_kontobezogen(String erweiterung_kontobezogen) { + this.erweiterung_kontobezogen = erweiterung_kontobezogen; + } + + public Kontolimit getKontolimit() { + return kontolimit; + } + + public void setKontolimit(Kontolimit kontolimit) { + this.kontolimit = kontolimit; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/ParameterSegment.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/ParameterSegment.java new file mode 100644 index 0000000..d1aba7c --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/ParameterSegment.java @@ -0,0 +1,136 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.parameter; + +import net.petafuel.fuelifints.dataaccess.dataobjects.ParameterDataObject; +import net.petafuel.fuelifints.exceptions.ElementParseException; +import net.petafuel.fuelifints.exceptions.HBCISyntaxException; +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; +import net.petafuel.fuelifints.protocol.fints3.segments.Segment; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Parameter; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.code; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.num; +import net.petafuel.fuelifints.support.ByteSplit; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.UnsupportedEncodingException; +import java.util.LinkedList; +import java.util.List; + +/** + * Abstrakte Klasse für Bankparameter Segmente. + *

+ * Diese Parametersegmente sind konstant, d.h. sie sind für alle Kunden einer Bank gleich. + * Gleichzeitig können diese Segmente anonym angefragt werden, der Kunde muss sich nicht authentifizieren. + *

+ * (FinTS 3.0) Beispiele: + *

+ * HIBPA + * HIKOM + * HISHV + * HISALS + * HIKAZS + * (...) + */ +public class ParameterSegment extends Segment { + private static final Logger LOG = LogManager.getLogger(ParameterSegment.class); + + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf; + + @Element( + description = {@ElementDescription(number = 2, length = -3)}) + @num + protected Integer maximaleAnzahlAuftraege; + + @Element( + description = {@ElementDescription(number = 3, length = 1)}) + @num + protected Integer anzahlSignaturenMindestens; + + + @Element( + description = {@ElementDescription(number = 4, status = ElementDescription.StatusCode.O, length = 0)}) + @code(restrictions = {"0", "1", "2", "3", "4"}) + protected String sicherheitsKlasse; + + @Element( + description = {@ElementDescription(number = 5, status = ElementDescription.StatusCode.O)}) + protected Parameter parameter; + + public ParameterSegment(byte[] message) { + super(message); + } + + public ParameterSegment(ParameterDataObject paramData, int bezugsSegment) { + super(new byte[0]); + if (paramData != null) { + /* + this.segmentkopf = new Segmentkopf( + (paramData.getSegmentName() + ":0:" + + paramData.getSegmentVersion() + ":" + + bezugsSegment).getBytes() + ); + */ + + this.segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(paramData.getSegmentName()).setSegmentVersion(paramData.getSegmentVersion()).setBezugssegment(bezugsSegment).build(); + + this.maximaleAnzahlAuftraege = paramData.getMaximaleAnzahlAuftraege(); + this.anzahlSignaturenMindestens = paramData.getAnzahlSignaturenMindestens(); + this.sicherheitsKlasse = paramData.getSicherheitsKlasse(); + LOG.debug("parameterData {}", paramData.getParameter()); + List splitted = null; + try { + splitted = ByteSplit.split(paramData.getParameter().getBytes(), ByteSplit.MODE_DE); + } catch (HBCISyntaxException e) { + // ignored here + } + List params = new LinkedList<>(); + if (splitted != null) { + for (byte[] temp : splitted) { + try { + params.add(new String(temp, "ISO-8859-1")); + } catch (UnsupportedEncodingException e) { + //ISO-8859-1 is supported + } + } + parameter = new Parameter(new byte[0]); + parameter.setParameter(params); + } + } + } + + @Override + public void parseElement() throws ElementParseException { + //nichts zu tun, Parametersegmente werden nur vom Kreditinstitut geschickt und müssen nicht geparsed werden + throw new ElementParseException("Parametersegmente werden nur vom Kreditinstitut geschickt und können nicht geparsed werden"); + } + + + @Override + public byte[] getHbciEncoded() { + byte[] paramBytes = super.getHbciEncoded(); + + /* + * In alten HBCI 220 Segmenten wurde keine Sicherheitsklasse berücksichtigt. + * Weil insbesondere StarMoney aber trotz FinTS 300 Support noch 220 Segmente nutzt (HKSAL & HKKAZ), + * müssen die entsprechenden Parameter noch HBCI 220 kompatibel sein. + * + * Wenn also die Sicherheitsklasse in einem Parametersegment nicht gesetzt wurde: + * - muss es sich um ein HBCI 220 Segment handeln + * - muss sie komplett ignoriert werden, ein Datenelement dafür ist nicht vorgesehen + * + * Das doppelte ++ als Datenelementtrennzeichen wird durch ein einfaches + ersetzt: + * + * Aus + * ANZAHLSIGS+SICHERKLASSE(null)+PARAMETER + * wird + * ANZAHLSIGS+PARAMETER + */ + if (sicherheitsKlasse == null || sicherheitsKlasse.equals("")) + paramBytes = new String(paramBytes).replace("++", "+").getBytes(); + return paramBytes; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/AnValidator.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/AnValidator.java new file mode 100644 index 0000000..24b8f84 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/AnValidator.java @@ -0,0 +1,28 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +/** + * Description: FinTS-Basiszeichensatz ohne CR (0D) & LF (0A) + *

+ * Die FinTS-Basiszeichensätze sind Subsets des ISO 8859. Erlaubt sind nur druckba- re Zeichen des ISO 8859-Zeichensatzes, d.h. die Bereiche X’20’ bis X’7E’ und X’A1’ bis X’FF’ + *

+ */ +public class AnValidator implements ConstraintValidator { + @Override + public void initialize(an an) { + + } + + @Override + public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { + /* + * Falls ein O/C Feld auftritt ist der String leer, dies ist jedoch korrekt. + */ + if (s == null || s.isEmpty()) { + return true; + } + return s.matches("^[\u0020-\u007E\u00A1-\u00FF]+$"); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/BinValidator.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/BinValidator.java new file mode 100644 index 0000000..ddac0c1 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/BinValidator.java @@ -0,0 +1,25 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +/** + * Description: Binär + *

+ * Binäre Daten werden unverändert in den HBCI-Datensatz eingestellt. + * Eine Umwandlung in eine Zeichendarstellung er- folgt nicht. + * Es ist zu beachten, dass der HBCI-Basiszeichen- satz für binäre Daten keine Gültigkeit besitzt. + * Ferner gelten die speziellen Syntaxregeln für binäre Daten + *

+ */ +public class BinValidator implements ConstraintValidator { + @Override + public void initialize(bin bin) { + + } + + @Override + public boolean isValid(byte[] bytes, ConstraintValidatorContext constraintValidatorContext) { + return true; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/CodeValidator.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/CodeValidator.java new file mode 100644 index 0000000..3ce2931 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/CodeValidator.java @@ -0,0 +1,24 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class CodeValidator implements ConstraintValidator { + + private code code; + + @Override + public void initialize(code code) { + this.code = code; + } + + @Override + public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { + for (String restriction : code.restrictions()) { + if (s.equals(restriction)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/CurValidator.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/CurValidator.java new file mode 100644 index 0000000..f6f6465 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/CurValidator.java @@ -0,0 +1,19 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +/** + * Kennzeichen gemäß ISO 4217 (alphabetischer Code) in Großbuchstaben + */ +public class CurValidator implements ConstraintValidator { + @Override + public void initialize(cur cur) { + + } + + @Override + public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { + return true; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/DatValidator.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/DatValidator.java new file mode 100644 index 0000000..f1ccb40 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/DatValidator.java @@ -0,0 +1,30 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +/** + * Description: Datum + *

+ * Format: JJJJMMTT + * Erlaubt sind alle existenten Datumsangaben.

+ */ +public class DatValidator implements ConstraintValidator { + @Override + public void initialize(dat dat) { + + } + + @Override + public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { + /* + * Falls ein O/C Feld auftritt ist der String leer, dies ist jedoch korrekt. + */ + if (s == null || s.isEmpty()) { + return true; + } + if (s.length() != 8) + return false; + return s.matches("^[1-9]+[0-9]*$"); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/DigValidator.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/DigValidator.java new file mode 100644 index 0000000..b3632f5 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/DigValidator.java @@ -0,0 +1,21 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +/** + * Description: 0-9 + *

+ * Zulässig sind lediglich die Ziffern ‘0’ bis ‘9’. Führende Nullen sind zugelassen. + */ +public class DigValidator implements ConstraintValidator { + @Override + public void initialize(dig dig) { + + } + + @Override + public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { + return s==null || s.equals("") || s.matches("^[0-9]+$"); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/DtaValidator.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/DtaValidator.java new file mode 100644 index 0000000..71c4806 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/DtaValidator.java @@ -0,0 +1,25 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +/** + * Description: DTAUS-Zeichensatz + *

+ * Es gilt der DTAUS-Zeichensatz mit der entsprechenden Co- dierung. + */ +public class DtaValidator implements ConstraintValidator { + @Override + public void initialize(dta dta) { + + } + + @Override + public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { + //optionale Felder sind nicht zwangsweise belegt + if (s == null || s.isEmpty()) { + return true; + } + return s.matches("^[\u0020-\u007E\u00A1-\u00FF\r\n]+$"); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/IdValidator.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/IdValidator.java new file mode 100644 index 0000000..b860e15 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/IdValidator.java @@ -0,0 +1,25 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +/** + * Description: Identifikation + *

+ * dient der eindeutigen Kennzeichnung von Ob- jekten (z.B. Benutzerkennung, Kontonummer) + */ +public class IdValidator implements ConstraintValidator { + + @Override + public void initialize(id id) { + + } + + @Override + public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { + /* + * Falls ein O/C Feld auftritt ist der String leer, dies ist jedoch korrekt. + */ + return s == null || (s.matches("^[\u0020-\u007E\u00A1-\u00FF]+$") && s.length() < 31) || s.length() == 0; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/JnValidator.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/JnValidator.java new file mode 100644 index 0000000..e6b2f08 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/JnValidator.java @@ -0,0 +1,25 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +/** + * Description: Ja/Nein + *

+ * Format: J bzw. N (in Großbuchstaben) Hat das DE den Status „Kann“, so gilt bei Auslassung der Standardwert „N“. + */ +public class JnValidator implements ConstraintValidator { + @Override + public void initialize(jn jn) { + + } + + @Override + public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { + //optionale oder coditional felder müssen möglicherweise dieses feld nicht setzen + if (s == null || s.equals("")) { + return true; + } + return (s.equals("J") || s.equals("N")); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/NumValidator.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/NumValidator.java new file mode 100644 index 0000000..27e7f22 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/NumValidator.java @@ -0,0 +1,21 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +/** + * Description: 0-9 + *

+ * Zulässig sind lediglich die Ziffern ‘0’ bis ‘9’. Führende Nullen sind nicht zugelassen. + */ +public class NumValidator implements ConstraintValidator { + @Override + public void initialize(num num) { + + } + + @Override + public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) { + return true; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/TimValidator.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/TimValidator.java new file mode 100644 index 0000000..9f82b19 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/TimValidator.java @@ -0,0 +1,29 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.text.ParseException; +import java.text.SimpleDateFormat; + +public class TimValidator implements ConstraintValidator { + + private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HHmmss"); + + @Override + public void initialize(tim tim) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { + try { + simpleDateFormat.parse(s); + } catch (NumberFormatException e) { + //Falls das Parsen fehl schlägt dann ist die Zeitangabe falsch + return false; + } catch (ParseException e) { + return false; + } + return true; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/TxtValidator.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/TxtValidator.java new file mode 100644 index 0000000..65fb005 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/TxtValidator.java @@ -0,0 +1,21 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +/** + * Description: FinTS-Basiszeichensatz mit CR (0D) & LF (0A) + *

+ * Die FinTS-Basiszeichensätze sind Subsets des ISO 8859. Erlaubt sind nur druckba- re Zeichen des ISO 8859-Zeichensatzes, d.h. die Bereiche X’20’ bis X’7E’ und X’A1’ bis X’FF’ + */ +public class TxtValidator implements ConstraintValidator { + @Override + public void initialize(txt txt) { + + } + + @Override + public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { + return s.matches("^[\u0020-\u007E\u00A1-\u00FF\r\n]+$"); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/an.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/an.java new file mode 100644 index 0000000..b360440 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/an.java @@ -0,0 +1,26 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; + +/** + * Description: FinTS-Basiszeichensatz ohne CR (0D) & LF (0A) + *

+ * Die FinTS-Basiszeichensätze sind Subsets des ISO 8859. Erlaubt sind nur druckba- re Zeichen des ISO 8859-Zeichensatzes, d.h. die Bereiche X’20’ bis X’7E’ und X’A1’ bis X’FF’ + */ +@Constraint(validatedBy = AnValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface an { + String message() default "entspricht nicht dem FinTS-Basiszeichensatz"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/bin.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/bin.java new file mode 100644 index 0000000..5efeda8 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/bin.java @@ -0,0 +1,25 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +/** + * Description: Binär + *

+ * Binäre Daten werden unverändert in den HBCI-Datensatz eingestellt. + * Eine Umwandlung in eine Zeichendarstellung er- folgt nicht. + * Es ist zu beachten, dass der HBCI-Basiszeichen- satz für binäre Daten keine Gültigkeit besitzt. + * Ferner gelten die speziellen Syntaxregeln für binäre Daten + */ +@Constraint(validatedBy = BinValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface bin { + String message() default "enthält keine gültigen Binärdaten"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/code.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/code.java new file mode 100644 index 0000000..57b2e67 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/code.java @@ -0,0 +1,20 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +@Constraint(validatedBy = CodeValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface code { + + String message() default "enthält keinen gültigen code"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + String[] restrictions(); +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/cur.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/cur.java new file mode 100644 index 0000000..8e7ac6c --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/cur.java @@ -0,0 +1,20 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +/** + * Kennzeichen gemäß ISO 4217 (alphabetischer Code) in Großbuchstaben + */ +@Constraint(validatedBy = CurValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface cur { + String message() default "ist keine gültige Währung"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/dat.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/dat.java new file mode 100644 index 0000000..2cbf11d --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/dat.java @@ -0,0 +1,23 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +/** + * Description: Datum + *

+ * Format: JJJJMMTT + * Erlaubt sind alle existenten Datumsangaben.

+ */ +@Constraint(validatedBy = DatValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface dat { + String message() default "entspricht nicht dem FinTS-Basiszeichensatz"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/dig.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/dig.java new file mode 100644 index 0000000..e932b28 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/dig.java @@ -0,0 +1,22 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +/** + * Description: + *

+ * Zulässig sind lediglich die Ziffern ‘0’ bis ‘9’. Führende Nullen sind zugelassen. + */ +@Constraint(validatedBy = DigValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface dig { + String message() default "ist keine gültige Zahl"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/dta.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/dta.java new file mode 100644 index 0000000..5c7f303 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/dta.java @@ -0,0 +1,24 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +/** + * Description: + *

+ * Description: DTAUS-Zeichensatz + *

+ * Es gilt der DTAUS-Zeichensatz mit der entsprechenden Co- dierung. + */ +@Constraint(validatedBy = DtaValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface dta { + String message() default "entspricht dem DTAUS Zeichensatz"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/id.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/id.java new file mode 100644 index 0000000..2f1fc13 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/id.java @@ -0,0 +1,22 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +/** + * Description: Identifikation + *

+ * dient der eindeutigen Kennzeichnung von Ob- jekten (z.B. Benutzerkennung, Kontonummer) + */ +@Constraint(validatedBy = IdValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface id { + String message() default "ist keine gültige Identifikation"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/jn.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/jn.java new file mode 100644 index 0000000..916e316 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/jn.java @@ -0,0 +1,22 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +/** + * Description: Ja/Nein + *

+ * Format: J bzw. N (in Großbuchstaben) Hat das DE den Status „Kann“, so gilt bei Auslassung der Standardwert „N“. + */ +@Constraint(validatedBy = JnValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface jn { + String message() default "ist weder J noch N"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/num.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/num.java new file mode 100644 index 0000000..91b3ac0 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/num.java @@ -0,0 +1,22 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +/** + * Description: + *

+ * Zulässig sind lediglich die Ziffern ‘0’ bis ‘9’. Führende Nullen sind nicht zugelassen. + */ +@Constraint(validatedBy = NumValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface num { + String message() default "ist keine gültige Zahl"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/tim.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/tim.java new file mode 100644 index 0000000..afdde54 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/tim.java @@ -0,0 +1,18 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +@Constraint(validatedBy = TimValidator.class) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface tim { + + String message() default "entspricht nicht dem FinTS-Basiszeichensatz"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/txt.java b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/txt.java new file mode 100644 index 0000000..3a98a28 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/txt.java @@ -0,0 +1,24 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import net.petafuel.fuelifints.protocol.fints3.validator.validators.AnValidator; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.*; + +/** + * Description: FinTS-Basiszeichensatz mit CR (0D) & LF (0A) + *

+ * Die FinTS-Basiszeichensätze sind Subsets des ISO 8859. Erlaubt sind nur druckba- re Zeichen des ISO 8859-Zeichensatzes, d.h. die Bereiche X’20’ bis X’7E’ und X’A1’ bis X’FF’ + */ +@Constraint(validatedBy = TxtValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface txt { + String message() default "entspricht nicht dem vollständigen FinTS-Basiszeichensatz"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/net/petafuel/fuelifints/protocol/fints4/FinTS4Controller.java b/src/main/java/net/petafuel/fuelifints/protocol/fints4/FinTS4Controller.java new file mode 100644 index 0000000..ec9ac54 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/protocol/fints4/FinTS4Controller.java @@ -0,0 +1,4 @@ +package net.petafuel.fuelifints.protocol.fints4; + +public class FinTS4Controller { +} diff --git a/src/main/java/net/petafuel/fuelifints/support/ByteSplit.java b/src/main/java/net/petafuel/fuelifints/support/ByteSplit.java new file mode 100644 index 0000000..d8d8053 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/support/ByteSplit.java @@ -0,0 +1,194 @@ +package net.petafuel.fuelifints.support; + +import net.petafuel.fuelifints.exceptions.HBCISyntaxException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +/** + * Trennt byte[] anhand von vorgegebenen Trennzeichen auf. + */ +public class ByteSplit { + + private static final Logger LOG = LogManager.getLogger(ByteSplit.class); + + //escape character (?) as hex value + private static final byte ESCAPE = 0x3F; + //binary character (@) as hex value + private static final byte BINARY = 0x40; + //segment end character (') as hex value + private static final byte SEGMENT_END = 0x27; + //DEG end character (+) as hex value + private static final byte DEG_END = 0x2B; + //DEG end character (:) as hex value + private static final byte DE_END = 0x3A; + //Default storage for a segment, will grow automatically + public static final int SEGMENT_INIT_SIZE = 32; + + + public static final int MODE_SEGMENT = 0; + public static final int MODE_DEG = 1; + public static final int MODE_DE = 2; + + + public static List split(byte[] input, int mode) throws HBCISyntaxException { + try { + if (input.length <= 0) { + return null; + } + List segments = new ArrayList(); + + int index = 0; + + byte currentByte = input[index++]; + + //this will be doubled + byte[] currentSegment = new byte[SEGMENT_INIT_SIZE]; + int segmentIndex = 0; + + while (index <= input.length) { + //every character here will be stored in the current segment + currentSegment = addToSegment(currentSegment, segmentIndex++, currentByte); + + switch (currentByte) { + case ESCAPE: + /** + * When the escape char is parsed first time this and the next character can be stored in the segment. + */ + currentSegment = addToSegment(currentSegment, segmentIndex++, input[index]); + index++; + break; + case BINARY: + /** + * Find the next binary character and store as much bytes as there are needed. + */ + int startIndex = index; + int length = 0; + int endIndex = -1; + for (int i = startIndex; i < input.length; i++) { + currentSegment = addToSegment(currentSegment, segmentIndex++, input[i]); + if (input[i] != BINARY) { + //48 is the numeric value of 0 + length = length * 10 + (input[i] - 48); + } else { + endIndex = i; + break; + } + } + //LOG.info("Length: "+length); + + if (endIndex == -1) { + LOG.error("expected @ in binary block but was not found."); + throw new RuntimeException("Input String incorrect"); + } + + currentSegment = addToSegment(currentSegment, segmentIndex, input, endIndex + 1, length); + segmentIndex += length; + index = endIndex + 1 + length; + if (index < input.length) + currentByte = input[index]; + index++; + + continue; + case SEGMENT_END: + byte[] cuttedSegment = new byte[segmentIndex - 1]; + System.arraycopy(currentSegment, 0, cuttedSegment, 0, cuttedSegment.length); + + segments.add(cuttedSegment); + + currentSegment = new byte[SEGMENT_INIT_SIZE]; + segmentIndex = 0; + break; + case DEG_END: + if (mode == MODE_DEG) { + byte[] cuttedDEG = new byte[segmentIndex - 1]; + System.arraycopy(currentSegment, 0, cuttedDEG, 0, cuttedDEG.length); + + segments.add(cuttedDEG); + + currentSegment = new byte[SEGMENT_INIT_SIZE]; + segmentIndex = 0; + } + break; + case DE_END: + if (mode == MODE_DE) { + byte[] cuttedDE = new byte[segmentIndex - 1]; + System.arraycopy(currentSegment, 0, cuttedDE, 0, cuttedDE.length); + + segments.add(cuttedDE); + + currentSegment = new byte[SEGMENT_INIT_SIZE]; + segmentIndex = 0; + } + break; + } + + if (index < input.length) { + currentByte = input[index]; + } + index++; + } + if (mode == MODE_DEG || mode == MODE_DE) { + byte[] cuttedDEG = new byte[segmentIndex]; + System.arraycopy(currentSegment, 0, cuttedDEG, 0, cuttedDEG.length); + + segments.add(cuttedDEG); + + //currentSegment = new byte[SEGMENT_INIT_SIZE]; + //segmentIndex = 0; + } + return segments; + } catch (Exception ex) { + throw new HBCISyntaxException(ex); + } + } + + + /** + * Fast Array doubling. + * + * @param input the input array. + * @return byte array with double size. + */ + private static byte[] doubleSizeAndCopy(byte[] input) { + byte[] result = new byte[input.length << 2]; + System.arraycopy(input, 0, result, 0, input.length); + return result; + } + + /** + * Add a new byte to the current segment. + * + * @param segment the segment. + * @param index index in the byte array. + * @param character the character to be added as byte. + * @return the segment as byte array. + */ + private static byte[] addToSegment(byte[] segment, int index, byte character) { + if (index >= segment.length) { + segment = doubleSizeAndCopy(segment); + } + segment[index] = character; + return segment; + } + + /** + * Add a new bytes to the current segment. + * + * @param segment the current segment. + * @param offset the offset in segment byte array. + * @param characters the characters to be added as byte array. + * @param start start offset in characters array. + * @param length the number of bytes which should be copied. + * @return the segments byte array. + */ + private static byte[] addToSegment(byte[] segment, int offset, byte[] characters, int start, int length) { + while (offset + length >= segment.length) { + segment = doubleSizeAndCopy(segment); + } + System.arraycopy(characters, start, segment, offset, length); + return segment; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/support/CkontoLocal.java b/src/main/java/net/petafuel/fuelifints/support/CkontoLocal.java new file mode 100644 index 0000000..75f6395 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/support/CkontoLocal.java @@ -0,0 +1,179 @@ +package net.petafuel.fuelifints.support; + +import net.petafuel.fuelifints.dataaccess.dataobjects.ReturnDataObject; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +public class CkontoLocal { + public static final String ckontoStatus = "status"; + public static final String ckontoCode = "code"; + public static final String ckontoIban = "ibn"; + public static final String ckontoBic = "bic"; + public static final String UTF_8 = "UTF-8"; + public static final String returnCode = "9930"; + private static final Logger LOG = LogManager.getLogger(CkontoLocal.class); + private final static String paramsIbanStatus = "[path] sepa -allinfo ibn=[iban]&bic=[bic]&key=[key]"; + private final static String paramsKtoStatus = "[path] -allinfo knr=[kto]&blz=[blz]&key=[key]"; + private final List codes; + private String key = ""; + private String path = ""; + private boolean active = false; + public CkontoLocal(String key, String path, String codes) { + this.key = key; + this.path = path; + this.codes = Arrays.asList(codes.split(",")); + if (!key.equals("") && !path.equals("")) { + File file = new File(path); + if (file.exists()) { + if (file.canExecute()) { + this.active = true; + LOG.info("Status: Aktiv"); + } else { + LOG.error("Status: execute Flag missing: {}", path); + } + } + } else { + LOG.warn("Status: nicht eingerichtet"); + this.active = false; + } + } + + /** + * Gibt zurück ob Ckonto richtig eingerichtet ist + * @return true wenn alles okay + */ + public boolean isActive() { + return active; + } + + public ReturnDataObject checkIzv(String kontonummer, String bankleitzahl) { + kontonummer = kontonummer.replace(" ", ""); + bankleitzahl = bankleitzahl.replace(" ", ""); + try { + HashMap ktoparams = getKontoInfo(kontonummer, bankleitzahl); + if (checkCode(ktoparams)) { + return new ReturnDataObject(true, ""); + } else { + return new ReturnDataObject(false, ktoparams.get(ckontoStatus)); + } + } catch (URISyntaxException e) { + LOG.error("Exception", e); + return null; + } + } + public ReturnDataObject checkSepa(String iban, String bic, Boolean deSonderCheck) { + bic = bic.toUpperCase().replace(" ", ""); + iban = iban.toUpperCase().replace(" ", ""); + if (deSonderCheck && iban.substring(0, 2).equals("DE")) { + String blz = StringUtils.mid(iban, 4, 8); + String kto = StringUtils.mid(iban, 12, 10); + HashMap ktoparams = null; + try { + ktoparams = getKontoInfo(kto, blz); + if (checkCode(ktoparams)) { + if (!iban.equals(ktoparams.get(ckontoIban))) { + return new ReturnDataObject(false, "IBAN-Pruefsumme stimmt nicht: " + iban + " richtig waere " + ktoparams.get(ckontoIban), returnCode); + } else if (!bic.equals(ktoparams.get(ckontoBic))) { + String cBic = ktoparams.get(ckontoBic); + if (bic.length() == 8 && cBic.length() == 11) { + String cBic8 = StringUtils.mid(cBic, 0, 8); + if (cBic.endsWith("XXX") && cBic8.equals(bic)) { + return new ReturnDataObject(true, ""); + } + } + return new ReturnDataObject(false, "BIC stimmt nicht: " + bic + " richtig waere " + ktoparams.get(ckontoBic), returnCode); + } else { + return new ReturnDataObject(true, ""); + } + } else return new ReturnDataObject(false, ktoparams.get(ckontoStatus), returnCode); + } catch (URISyntaxException e) { + LOG.error("Exception", e); + return null; + } + } else { + HashMap ibanparams = null; + try { + ibanparams = getIbanInfo(iban, bic); + if (checkCode(ibanparams)) { + return new ReturnDataObject(true, "", returnCode); + } else { + return new ReturnDataObject(false, ibanparams.get(ckontoStatus), returnCode); + } + } catch (URISyntaxException e) { + LOG.error("Exception", e); + return null; + } + } + } + private boolean checkCode(HashMap params) { + return codes.contains(params.get(ckontoCode)); + } + private HashMap getIbanInfo(String iban, String bic) throws URISyntaxException { + return parseOutput(paramsIbanStatus.replace("[iban]", iban).replace("[bic]", bic)); + } + private HashMap getKontoInfo(String kontonummer, String bankleitzahl) throws URISyntaxException { + return parseOutput(paramsKtoStatus.replace("[kto]", kontonummer).replace("[blz]", bankleitzahl)); + } + private HashMap parseOutput(String command) throws URISyntaxException { + if (path != null && !path.equals("")) { + command = command.replace("[path]", path); + } + if (key != null && !key.equals("")) { + command = command.replace("[key]", key); + } + LOG.debug(command); + Exec exec = new Exec(command.split(" ")); + String url = exec.run(); + LOG.debug(url); + HashMap paramsMap = new HashMap<>(); + + // separate key value pairs + String[] items = url.split("&"); + + for (String item: items) { + // separate key and value + String[] pair = item.split("="); + + if(pair.length == 1) { + paramsMap.put(pair[0], ""); // set empty string as default value + } else if(pair.length == 2) { + paramsMap.put(pair[0], pair[1]); + } else { + LOG.error("Invaild response from cKonto"); + } + } + + String code = paramsMap.get(ckontoCode); + if (code != null) { + paramsMap.put(ckontoStatus, codeStaties.get(code)); + } + LOG.info(paramsMap); + return paramsMap; + } + + final public static HashMap codeStaties = new HashMap() { + { + put("0", "Die IBAN ist ungültig, sie kann von diesem Institut nicht vergeben werden"); + put("1", "Die IBAN ist gültig, sie kann von diesem Institut vergeben werden"); + put("2", "BIC: Fehler bei der Eingabe"); + put("3", "IBAN: Fehler bei der Eingabe oder Kleinbuchstaben verwendet"); + put("4", "IBAN: Format ungültig"); + put("5", "IBAN / BIC: Genereller Eingabefehler des Übergabeparameters - enthält evtl. Leerzeichen"); + put("6", "Fehler im Format des Übergabeparameters"); + put("7", "IBAN / BIC: Die Bankleitzahl wurde in der Datenbank nicht gefunden (existiert nicht)"); + put("8", "IBAN / BIC: Die von der Bank verwendete Prüfmethode ist im Demonstrations-Modus nicht verfügbar"); + put("9", "IBAN / BIC: Die Kontonummer kann nicht geprüft werden, da die Bank entweder keine Prüfziffern verwendet oder es sich um eine spezielle Kontonummer handelt"); + put("10", "IBAN / BIC: Prüfung der Bankverbindung nicht möglich"); + put("20", "IBAN: IBAN ist ungültig"); + put("21", "BIC: BIC ist ungültig"); + put("22", "IBAN / BIC: IBAN und BIC sind ungültig"); + } + }; +} diff --git a/src/main/java/net/petafuel/fuelifints/support/Exec.java b/src/main/java/net/petafuel/fuelifints/support/Exec.java new file mode 100644 index 0000000..76139b8 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/support/Exec.java @@ -0,0 +1,52 @@ +package net.petafuel.fuelifints.support; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.Arrays; + +public class Exec { + private static final Logger LOG = LogManager.getLogger(Exec.class); + private final String[] cmd; + private String errorReturn = ""; + + public Exec(String[] cmd) { + this.cmd = cmd; + } + public String run() { + StringBuilder out = new StringBuilder(); + StringBuilder error = new StringBuilder(); + try { + LOG.debug(Arrays.toString(this.cmd)); + String line; + Process p = Runtime.getRuntime().exec(this.cmd); + BufferedReader bri = new BufferedReader + (new InputStreamReader(p.getInputStream())); + BufferedReader bre = new BufferedReader + (new InputStreamReader(p.getErrorStream())); + while ((line = bri.readLine()) != null) { + out.append(line); + } + bri.close(); + while ((line = bre.readLine()) != null) { + error.append(line); + } + bre.close(); + p.waitFor(); + LOG.debug("Done."); + } catch (Exception err) { + LOG.error("Exception", err); + } + this.errorReturn = error.toString(); + if (!error.toString().equals("")) { + LOG.warn(error.toString()); + } + return out.toString(); + } + + public String getErrorReturn() { + return errorReturn; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/support/FieldComparator.java b/src/main/java/net/petafuel/fuelifints/support/FieldComparator.java new file mode 100644 index 0000000..1b58ea5 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/support/FieldComparator.java @@ -0,0 +1,25 @@ +package net.petafuel.fuelifints.support; + +import net.petafuel.fuelifints.protocol.fints3.segments.AbstractElement; +import net.petafuel.fuelifints.protocol.fints3.segments.Element; +import net.petafuel.fuelifints.protocol.fints3.segments.ElementDescription; + +import java.lang.reflect.Field; +import java.util.Comparator; + +public class FieldComparator implements Comparator { + + int segmentVersion = 0; + + public FieldComparator(int segmentVersion) { + this.segmentVersion = segmentVersion; + } + + @Override + public int compare(Field o1, Field o2) { + if (o1.getAnnotation(Element.class) == null || o2.getAnnotation(Element.class) == null) { + throw new ClassCastException("couldn't find necessary annotation"); + } + return Integer.compare(AbstractElement.getMatchingElementDescription(o1, segmentVersion).number(), AbstractElement.getMatchingElementDescription(o2, segmentVersion).number()); + } +} diff --git a/src/main/java/net/petafuel/fuelifints/support/LogHelper.java b/src/main/java/net/petafuel/fuelifints/support/LogHelper.java new file mode 100644 index 0000000..e0773eb --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/support/LogHelper.java @@ -0,0 +1,21 @@ +package net.petafuel.fuelifints.support; + +public class LogHelper { + + public static String removeSignatureInformation(String toLog) { + + try { + if (toLog.contains("HNSHA")) { + String hnsha = toLog.substring(toLog.indexOf("HNSHA")); + hnsha = hnsha.substring(0, hnsha.indexOf("'")); + String pinTan = hnsha.substring(hnsha.lastIndexOf("+") + 1); + toLog = toLog.replace(pinTan, "*****"); + } + return toLog; + } + catch (Exception ignored) { + + } + return toLog; + } +} \ No newline at end of file diff --git a/src/main/java/net/petafuel/fuelifints/support/Payments.java b/src/main/java/net/petafuel/fuelifints/support/Payments.java new file mode 100644 index 0000000..999a30d --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/support/Payments.java @@ -0,0 +1,310 @@ +package net.petafuel.fuelifints.support; + +import net.petafuel.jsepa.model.CCTInitiation; +import net.petafuel.jsepa.model.CategoryPurpose; +import net.petafuel.jsepa.model.CreditTransferTransactionInformation; +import net.petafuel.jsepa.model.Debitor; +import net.petafuel.jsepa.model.DebitorAccount; +import net.petafuel.jsepa.model.DebitorAgent; +import net.petafuel.jsepa.model.Document; +import net.petafuel.jsepa.model.GroupHeader; +import net.petafuel.jsepa.model.PAIN00100203Document; +import net.petafuel.jsepa.model.PAIN00100303Document; +import net.petafuel.jsepa.model.PaymentInstructionInformation; +import net.petafuel.jsepa.model.PaymentTypeInformation; + +import java.math.BigInteger; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Vector; + +public class Payments { + public static SimpleDateFormat creationFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + public static SimpleDateFormat executionFormat = new SimpleDateFormat("yyyy-MM-dd"); + + public enum versions { + pain00100203("sepade.pain.001.002.03.xsd", 0), + pain00100303("sepade.pain.001.003.03.xsd", 1); + + private String descriptor; + private int wert = 0; + + versions(String descriptor, int wert) { + this.descriptor = descriptor; + this.wert = wert; + } + + public String getDescriptor() { + return descriptor; + } + + public int getWert() { + return wert; + } + } + + public static versions getBestVersion(List unterstuetzteSepaVersionen) { + versions max = null; + versions maxFound = null; + for (versions version : versions.values()) { + if (unterstuetzteSepaVersionen != null && unterstuetzteSepaVersionen.size() > 0) { + for (String unterstuetzt : unterstuetzteSepaVersionen) { + if (unterstuetzt.contains(version.getDescriptor()) && (maxFound == null || (maxFound.getWert() < version.getWert()))) { + maxFound = version; + } + } + } + if (max == null || max.getWert() < version.getWert()) { + max = version; + } + } + return (maxFound == null) ? max : maxFound; + + } + + public static Document paymentsToSepa(LinkedList paymentEntries, String auftragsId, String MessageId, versions version) { + Document d = null; + switch (version) { + case pain00100203: + d = new PAIN00100203Document(); + break; + case pain00100303: + d = new PAIN00100303Document(); + break; + } + Vector pmtInfos = new Vector<>(); + PaymentInstructionInformation payment = new PaymentInstructionInformation(); + GroupHeader groupHeader = new GroupHeader(); + + + payment.setChargeBearer("SLEV"); + List creditTransferTransactionInformationVector = new ArrayList<>(); + boolean isDebitorSet = false; + Double sum = 0d; + for (PaymentEntry paymentEntry : paymentEntries) { + if (!isDebitorSet) { + Debitor debitor = new Debitor(); + debitor.setName(paymentEntry.getAbsenderName()); + payment.setDebitor(debitor); + DebitorAccount debitorAccount = new DebitorAccount(); + debitorAccount.setIban(paymentEntry.getAbsenderIban()); + payment.setDebitorAccount(debitorAccount); + DebitorAgent debitorAgent = new DebitorAgent(); + debitorAgent.setBic(paymentEntry.getAbsenderBIC()); + payment.setDebitorAgent(debitorAgent); + payment.setRequestedExecutionDate(executionFormat.format(paymentEntry.getAusfuehrungsdatum())); + groupHeader.setInitiatingPartyName(paymentEntry.getAbsenderName()); + groupHeader.setCreationTime((creationFormat.format(paymentEntry.getAnlagedatum()))); + isDebitorSet = true; + } + CreditTransferTransactionInformation cTTI = new CreditTransferTransactionInformation(); + cTTI.setAmount(paymentEntry.getAmount()); + cTTI.setCreditorIBAN(paymentEntry.getEmpfaengerIBAN()); + cTTI.setCreditorAgent(paymentEntry.getEmpfaengerBIC()); + cTTI.setCreditorName(paymentEntry.getEmpfaengerName()); + cTTI.setVwz(paymentEntry.getVerwendungszweck()); + cTTI.setEndToEndID("NOTPROVIDED"); + creditTransferTransactionInformationVector.add(cTTI); + sum += paymentEntry.getAmount(); + } + groupHeader.setNoOfTransactions(paymentEntries.size()); + groupHeader.setControlSum(sum); + groupHeader.setMessageId(MessageId.replace("_", "-")); + payment.setCreditTransferTransactionInformationVector(creditTransferTransactionInformationVector); + payment.setPaymentMethod("TRF"); + payment.setPmtInfId(auftragsId); + + payment.setCtrlSum(sum); + payment.setNoTxns(paymentEntries.size()); + PaymentTypeInformation paymentTypeInformation = new PaymentTypeInformation(); + CategoryPurpose categoryPurpose = new CategoryPurpose(); + paymentTypeInformation.setCategoryPurpose(categoryPurpose); +// paymentTypeInformation.setSequenceType("SEPA"); + payment.setPti(paymentTypeInformation); + pmtInfos.add(payment); + + CCTInitiation ccInitation = new CCTInitiation(); + ccInitation.setPmtInfos(pmtInfos); + ccInitation.setGrpHeader(groupHeader); + d.setCctInitiation(ccInitation); + return d; + } + + public static class PaymentEntry { + private String empfaengerName = ""; + private String empfaengerIBAN = ""; + private String empfaengerBIC = ""; + private Double amount = 0d; + private String absenderName = ""; + private String absenderIban = ""; + private String absenderBIC = ""; + private String verwendungszweck = ""; + private Date ausfuehrungsdatum = null; + private Date anlagedatum = null; + + public static PaymentEntry fromDauerautrag(ResultSet resultSet, String bic, String blz) throws SQLException { + PaymentEntry pE = new PaymentEntry(); + pE.empfaengerName = resultSet.getString("empfaenger"); + pE.empfaengerIBAN = resultSet.getString("iban"); + pE.empfaengerBIC = resultSet.getString("bic"); + pE.amount = resultSet.getDouble("betrag"); + pE.absenderName = resultSet.getString("auftraggeber"); + pE.absenderIban = convertKnrBlzToIBAN(resultSet.getString("auftraggeberkonto"), blz); + pE.absenderBIC = bic; + pE.verwendungszweck = resultSet.getString("verwendungszweck"); + pE.ausfuehrungsdatum = resultSet.getDate("ausfuehrungsdatum"); + pE.anlagedatum = resultSet.getDate("datum"); + return pE; + } + + public String getEmpfaengerName() { + return empfaengerName; + } + + public String getEmpfaengerIBAN() { + return empfaengerIBAN; + } + + public String getEmpfaengerBIC() { + return empfaengerBIC; + } + + public Double getAmount() { + return amount; + } + + public String getAbsenderName() { + return absenderName; + } + + public String getAbsenderIban() { + return absenderIban; + } + + public String getAbsenderBIC() { + return absenderBIC; + } + + public String getVerwendungszweck() { + return verwendungszweck; + } + + public Date getAusfuehrungsdatum() { + return ausfuehrungsdatum; + } + + public Date getAnlagedatum() { + return anlagedatum; + } + } + + public static class Result { + private boolean success = false; + private LinkedList ids = new LinkedList<>(); + private Throwable error = null; + + public Result(boolean success) { + this.success = success; + } + + public Result readPrepared(PreparedStatement pS) { + try { + ResultSet rs = pS.getGeneratedKeys(); + while (rs.next()) { + ids.add(rs.getString(1)); + } + } catch (SQLException ignored) { + } + return this; + } + + public boolean isSuccess() { + return success; + } + + public LinkedList getIds() { + return ids; + } + + public Result add(String id) { + this.ids.add(id); + return this; + } + + public Result setError(Throwable e) { + this.error = e; + return this; + } + + public Throwable getError() { + return error; + } + } + + public static class AuftragsId { + public static String separator = "-"; + public static String markSammler = "S"; + public static String markEinzel = "E"; + private final String auftragsid; + + public AuftragsId(String id) { + this.auftragsid = id; + } + + @Override + public String toString() { + return auftragsid; + } + + public static AuftragsId generate(boolean isSammler, String userid, String idSingle, String idSammler) { + return new Payments.AuftragsId(((isSammler) ? markSammler : markEinzel) + separator + userid + separator + (isSammler ? idSammler : idSingle)); + } + + @Override + public boolean equals(Object o) { + return o instanceof AuftragsId && o.toString().equals(auftragsid); + } + } + + /** + * @param knr Kontonummer (max 10 Stellen) + * @param blz Bankleitzahl (max 8 Stellen) + * @return Neue Kontonummer + */ + public static String convertKnrBlzToIBAN(String knr, String blz) { + // zehnstellige Kontonummer + int anz = 10 - knr.length(); + for (int i = 0; i < anz; i++) { + knr = "0" + knr; + } + // Pruefziffer + // die 1314 steht fuer DE und die 00 fuer die fehlenden Prueffziffern + String checkIBAN = blz + knr + "131400"; + // String in eine Zahl konvertieren + BigInteger checkIBANSum = new BigInteger(checkIBAN); + // Modulo rechnen + BigInteger faktor = new BigInteger("97"); + long div = checkIBANSum.remainder(faktor).longValue(); + // Differenz zu 98 + long pZiffer = 98 - div; + // IBAN Regeln einhalten (22 Stellen) + return "DE" + (pZiffer < 10 ? "0" : "") + pZiffer + blz + knr; + } + + /** + * Anscheinend darf der Verwendungszweck im Sepa-Format fehler [0..1] sein, darum bekommen wir bei getVwz() ein null + * Allerdings brauchen wir ein String. + * + * @param item String|null + * @return String + */ + public static String derfNullSein(String item) { + return (item != null) ? item : ""; + } +} diff --git a/src/main/java/net/petafuel/fuelifints/support/SegmentComparator.java b/src/main/java/net/petafuel/fuelifints/support/SegmentComparator.java new file mode 100644 index 0000000..496cb61 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/support/SegmentComparator.java @@ -0,0 +1,68 @@ +package net.petafuel.fuelifints.support; + +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.segments.HIKIM; +import net.petafuel.fuelifints.protocol.fints3.segments.HIRMG; +import net.petafuel.fuelifints.protocol.fints3.segments.HIRMS; +import net.petafuel.fuelifints.protocol.fints3.segments.Segment; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; + +import java.lang.reflect.Field; +import java.util.Comparator; + +public class SegmentComparator implements Comparator { + + @Override + public int compare(IMessageElement o1, IMessageElement o2) { + if (o1 instanceof Segment && o2 instanceof Segment) { + //HIRMG muss immer an den Anfang und kommt nur einmalig vor, deshalb genügt diese Unterscheidung + if (o1 instanceof HIRMG) { + return -1; + } else if (o2 instanceof HIRMG) { + return 1; + } else if (o1 instanceof HIRMS && !(o2 instanceof HIRMS)) { + return -1; + } else if (!(o1 instanceof HIRMS) && o2 instanceof HIRMS) { + return 1; + } else if(o1 instanceof HIKIM) { + return 1; + } else if(o2 instanceof HIKIM) { + return -1; + } else { + Field[] o1Fields = o1.getClass().getDeclaredFields(); + Field[] o2Fields = o2.getClass().getDeclaredFields(); + Field o1SegmentkopfField = null; + Field o2SegmentkopfField = null; + for (Field f : o1Fields) { + f.setAccessible(true); + if (f.getType().equals(Segmentkopf.class)) { + o1SegmentkopfField = f; + } + } + for (Field f : o2Fields) { + f.setAccessible(true); + if (f.getType().equals(Segmentkopf.class)) { + o2SegmentkopfField = f; + } + } + if (o1SegmentkopfField == null || o2SegmentkopfField == null) { + throw new ClassCastException("Es konnte kein Segmentkopf gefunden werden."); + } + try { + Segmentkopf o1Segmentkopf = (Segmentkopf) o1SegmentkopfField.get(o1); + Segmentkopf o2Segmentkopf = (Segmentkopf) o2SegmentkopfField.get(o2); + if (o1Segmentkopf.getBezugssegment() != null && o1Segmentkopf.getBezugssegment() != 0 && o2Segmentkopf.getBezugssegment() != null && o2Segmentkopf.getBezugssegment() != 0) { + return o1Segmentkopf.getBezugssegment() - o2Segmentkopf.getBezugssegment(); + } + return 1; + } catch (IllegalAccessException|NullPointerException e) { + //acessible true + return 0; + } + + } + } else { + throw new ClassCastException("Es können lediglich Segmente miteinander verglichen werden."); + } + } +} diff --git a/src/main/java/net/petafuel/fuelifints/support/StringList.java b/src/main/java/net/petafuel/fuelifints/support/StringList.java new file mode 100644 index 0000000..54ec2b1 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/support/StringList.java @@ -0,0 +1,64 @@ +package net.petafuel.fuelifints.support; + +import java.util.Iterator; + +/** + * Description: Stringliste ohne generics, die daher fixer arbeitet als die "original" Java Variante + */ +public class StringList implements Iterable { + + int size = 0; + private String[] array = new String[16]; + + public String get(int i){ + return array[i]; + } + + public void add(String b){ + array[size] = b; + size++; + if(size >= array.length){ + String[] temp = new String[array.length*2]; + System.arraycopy(array,0,temp,0,array.length); + array = temp; + temp = null; + } + + } + + private int size() { + return size; + } + + public String[] getArray(){ + String[] temp = new String[size()]; + System.arraycopy(array,0,temp,0,size()); + return temp; + } + + public Iterator iterator() { + return new StringListIterator(this); //To change body of implemented methods use File | Settings | File Templates. + } + + private class StringListIterator implements Iterator { + private final StringList stringList; + private int position = 0; + + public StringListIterator(StringList stringList) { + this.stringList = stringList; + } + + public boolean hasNext() { + return position < stringList.size(); + } + + public String next() { + return stringList.get(position++); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/src/main/java/net/petafuel/fuelifints/support/StringSplit.java b/src/main/java/net/petafuel/fuelifints/support/StringSplit.java new file mode 100644 index 0000000..a018403 --- /dev/null +++ b/src/main/java/net/petafuel/fuelifints/support/StringSplit.java @@ -0,0 +1,36 @@ +package net.petafuel.fuelifints.support; + +public class StringSplit { + + private static char escapeChar = '?'; + + /** + * Funktioniert im Prinzip wie String.split, aber + * trennt einen String bei einem Trennzeichen, + * wenn vor dem Trennzeichen nicht der EscapeCharacter steht + * + * @param splitString String, der geteilt werden soll + * @param trennzeichen char, bei dem geteilt werden soll + * @return Stringarray mit den Substrings + */ + public static String[] split(String splitString, char trennzeichen) { + StringList stringList = new StringList(); + if (splitString!=null) { + char[] splitArray = splitString.toCharArray(); + int start = 0; + int i; + for (i = 0; i + + + %d %-5p [%t] [] %c - %m%n + log/ + fuelifints.log + .%d{yyyy-MM-dd} + + + + + + + + + + + + + + diff --git a/src/main/resources/xml/acknowledgementcodes.xml b/src/main/resources/xml/acknowledgementcodes.xml new file mode 100644 index 0000000..6ff1ceb --- /dev/null +++ b/src/main/resources/xml/acknowledgementcodes.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/xml/fints3.0/hibpa.xml b/src/main/resources/xml/fints3.0/hibpa.xml new file mode 100644 index 0000000..c45d328 --- /dev/null +++ b/src/main/resources/xml/fints3.0/hibpa.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/xml/fints3.0/hikim.xml b/src/main/resources/xml/fints3.0/hikim.xml new file mode 100644 index 0000000..25257eb --- /dev/null +++ b/src/main/resources/xml/fints3.0/hikim.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + diff --git a/src/main/resources/xml/fints3.0/hikpv.xml b/src/main/resources/xml/fints3.0/hikpv.xml new file mode 100644 index 0000000..ab2e571 --- /dev/null +++ b/src/main/resources/xml/fints3.0/hikpv.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/xml/fints3.0/hissp.xml b/src/main/resources/xml/fints3.0/hissp.xml new file mode 100644 index 0000000..54d8268 --- /dev/null +++ b/src/main/resources/xml/fints3.0/hissp.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/xml/fints3.0/hiupd.xml b/src/main/resources/xml/fints3.0/hiupd.xml new file mode 100644 index 0000000..a5cca3f --- /dev/null +++ b/src/main/resources/xml/fints3.0/hiupd.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/xml/fints3.0/hkend.xml b/src/main/resources/xml/fints3.0/hkend.xml new file mode 100644 index 0000000..c7a97d6 --- /dev/null +++ b/src/main/resources/xml/fints3.0/hkend.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + diff --git a/src/main/resources/xml/fints3.0/hkidn.xml b/src/main/resources/xml/fints3.0/hkidn.xml new file mode 100644 index 0000000..ea28abd --- /dev/null +++ b/src/main/resources/xml/fints3.0/hkidn.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/xml/fints3.0/hkisa.xml b/src/main/resources/xml/fints3.0/hkisa.xml new file mode 100644 index 0000000..59c74ce --- /dev/null +++ b/src/main/resources/xml/fints3.0/hkisa.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/xml/fints3.0/hklif.xml b/src/main/resources/xml/fints3.0/hklif.xml new file mode 100644 index 0000000..a956fda --- /dev/null +++ b/src/main/resources/xml/fints3.0/hklif.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/src/main/resources/xml/fints3.0/hnhbk.xml b/src/main/resources/xml/fints3.0/hnhbk.xml new file mode 100644 index 0000000..30e7f34 --- /dev/null +++ b/src/main/resources/xml/fints3.0/hnhbk.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/xml/xsd/acknowledgement/acknowledgementcodes.xsd b/src/main/resources/xml/xsd/acknowledgement/acknowledgementcodes.xsd new file mode 100644 index 0000000..45c27e9 --- /dev/null +++ b/src/main/resources/xml/xsd/acknowledgement/acknowledgementcodes.xsd @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/xml/xsd/fints3.0/segment.xsd b/src/main/resources/xml/xsd/fints3.0/segment.xsd new file mode 100644 index 0000000..9641883 --- /dev/null +++ b/src/main/resources/xml/xsd/fints3.0/segment.xsd @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/net/petafuel/fuelifints/FinTS3ControllerTest.java b/src/test/java/net/petafuel/fuelifints/FinTS3ControllerTest.java new file mode 100644 index 0000000..90706a2 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/FinTS3ControllerTest.java @@ -0,0 +1,147 @@ +package net.petafuel.fuelifints; + +import junit.framework.Assert; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.model.Message; +import net.petafuel.fuelifints.protocol.FinTSPayload; +import net.petafuel.fuelifints.protocol.IFinTSDecryptor; +import net.petafuel.fuelifints.protocol.IFinTSExecutor; +import net.petafuel.fuelifints.protocol.IFinTSInjector; +import net.petafuel.fuelifints.protocol.IFinTSParser; +import net.petafuel.fuelifints.protocol.IFinTSReplyThread; +import net.petafuel.fuelifints.protocol.fints3.FinTS3Controller; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import static junit.framework.TestCase.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyObject; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class FinTS3ControllerTest { + + private static final Logger LOG = LogManager.getLogger(FinTS3ControllerTest.class); + + final static Object o = new Object(); + + + @Before + public void before() { + //Remove existing FinTS3Controller instance: + FinTS3Controller.removeInstance(); + + //Setup mocked classes + final Message mockedMessage = mock(Message.class); + when(mockedMessage.getMessageId()).thenReturn("0"); //First message in a dialog has to have message id 0 + when(mockedMessage.getTaskId()).thenReturn(1234); + + FinTSServer mockedServer = mock(FinTSServer.class); + when(mockedServer.respond(eq(1234), any(byte[].class), any(boolean.class))).thenAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + LOG.info("mockedServer.respond called!"); + if (invocationOnMock.getArguments() != null && invocationOnMock.getArguments().length == 2) + LOG.info(new String((byte[]) invocationOnMock.getArguments()[1])); + synchronized (o) { + o.notifyAll(); + } + return true; + } + }); + + ExecutorManager mockedManager = mock(ExecutorManager.class); + + //Step 1: Decrypt Message + when(mockedManager.addToDecryptionQueue((Dialog) anyObject(), (FinTSPayload) anyObject(), (IFinTSDecryptor) anyObject())).then(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + LOG.info("mockedManager.addToDecryptionQueue called!"); + Object[] args = invocationOnMock.getArguments(); + Assert.assertTrue(args.length > 0); + Assert.assertTrue(args[1] instanceof FinTSPayload); + Assert.assertEquals(1234, ((FinTSPayload) args[1]).getTaskId()); + FinTS3Controller.getInstance().finishedDecryption((FinTSPayload) args[1], (Dialog) args[0]); + return true; + } + }); + + //Step 2: Parse & verifySyntax + when(mockedManager.addToParseQueue((Dialog) anyObject(), (FinTSPayload) anyObject(), (IFinTSParser) anyObject())).then(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + LOG.info("mockedManager.addToParseQueue called!"); + Object[] args = invocationOnMock.getArguments(); + Assert.assertTrue(args.length > 0); + Assert.assertTrue(args[1] instanceof FinTSPayload); + Assert.assertEquals(1234, ((FinTSPayload) args[1]).getTaskId()); + FinTS3Controller.getInstance().finishedParsing(((FinTSPayload) args[1]).getTaskId(), (Dialog) args[0]); + return true; + } + }); + + //Step 3: CheckInjection & injection + when(mockedManager.addToInjectionQueue((Dialog) anyObject(), (IFinTSInjector) anyObject())).then(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + LOG.info("mockedManager.addToInjectionQueue called!"); + Object[] args = invocationOnMock.getArguments(); + Assert.assertTrue(args.length > 0); + Assert.assertTrue(args[0] instanceof Dialog); + FinTS3Controller.getInstance().finishedInjecting(((Dialog) args[0]).getTaskId()); + return true; + } + }); + + //Step 4: Execute + when(mockedManager.addToExecuteQueue((Dialog) anyObject(), (IFinTSExecutor) anyObject())).then(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + LOG.info("mockedManager.addToExecuteQueue called!"); + Object[] args = invocationOnMock.getArguments(); + assertTrue(args.length > 0); + Assert.assertTrue(args[0] instanceof Dialog); + FinTS3Controller.getInstance().finishedExecuting(((Dialog) args[0]).getCurrentMessage()); + return true; + } + }); + + //Step 5: Reply + when(mockedManager.addToReplyQueue((Dialog) anyObject(), any(FinTSServer.class), any(IFinTSReplyThread.class))).then(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + LOG.info("mockedManager.addToReplyQueue called!"); + Object[] args = invocationOnMock.getArguments(); + Assert.assertTrue(args.length > 0); + Assert.assertTrue(args[0] instanceof Dialog); + return true; + } + }); + IFinTSController controller = FinTS3Controller.getInstance(); + controller.setExecutor(mockedManager); + controller.setFinTSServer(mockedServer); + } + + @Test + @Ignore + public void testEndToEnd() throws Exception { + /* + new Thread() { + public void run() { + byte[] request = "HNHBK:1:3+000000000120+300+0+1'HKIDN:2:2+280:20041144+9999999999+0+0'HKVVB:3:2+0+0+0+123 Banking Android+0.3'HNHBS:4:1+1".getBytes(); + FinTSPayload payload = new FinTSPayload(request, 1234); + FinTS3Controller.getInstance().newRequest(payload, FinTSCommunicationHandler.CommunicationChannel.SSL); + } + }.start(); //I don't know why newRequest has to be started in a new thread, but otherwise notification for o comes before lock...*/ + /* + synchronized (o) { + o.wait(); //wait for tests to complete (i.e. wait for mockedServer to receive response) + } */ + } +} diff --git a/src/test/java/net/petafuel/fuelifints/communication/BlockingSSLRequestListenerThreadTest.java b/src/test/java/net/petafuel/fuelifints/communication/BlockingSSLRequestListenerThreadTest.java new file mode 100644 index 0000000..1e6a85f --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/communication/BlockingSSLRequestListenerThreadTest.java @@ -0,0 +1,65 @@ +package net.petafuel.fuelifints.communication; + +import org.junit.Test; + +public class BlockingSSLRequestListenerThreadTest { + FinTSCommunicationHandler currentRequest; + + @Test + public void testRun() throws Exception { + /* + Properties systemProps = System.getProperties(); + systemProps.put("javax.net.ssl.trustStore", "src/test/resources/keystore/truststore"); + systemProps.put("javax.net.ssl.trustStorePassword", "123456"); + System.setProperties(systemProps); + + FinTSServer server = mock(FinTSServer.class); + BlockingSSLRequestListenerThread thread = new BlockingSSLRequestListenerThread(8080, server); + IFinTSController controller = mock(FinTS3Controller.class); + when(server.addRequest((FinTSCommunicationHandler) anyObject())).then(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + Object[] args = invocationOnMock.getArguments(); + if (args.length > 0 && args[0] instanceof FinTSCommunicationHandler) + currentRequest = (FinTSCommunicationHandler) args[0]; + return true; + } + }); + + when(controller.newRequest((FinTSPayload) anyObject(), eq(FinTSCommunicationHandler.CommunicationChannel.SSL))).thenAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + if (currentRequest != null) { + currentRequest.sendResponse("TestAnswer".getBytes()); + } + currentRequest = null; + return true; + } + }); + FinTSVersionSwitch.controller = controller; + thread.start(); + + Thread.sleep(2000); //wait for server to start + + + SSLSocketFactory sslsocketFactory = new SSLSocketFactory(null, null); + sslsocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + + Scheme https = new Scheme("https", sslsocketFactory, 8080); + DefaultHttpClient httpClient = new DefaultHttpClient(); + + httpClient.getConnectionManager().getSchemeRegistry().register(https); + + HttpPost httpPost = new HttpPost("https://localhost:8080"); + StringEntity entity = new StringEntity("HNHBK:1:3+000000000120+300+0+1'HKIDN:2:2+280:20041144+9999999999+0+0'HKVVB:3:2+0+0+0+123 Banking Android+0.3'HNHBS:4:1+1"); + + httpPost.setEntity(entity); + + HttpResponse response = httpClient.execute(httpPost); + Thread.sleep(1000); + String responseString = EntityUtils.toString(response.getEntity()); + + Assert.assertTrue(responseString.equals("TestAnswer")); + */ + } +} diff --git a/src/test/java/net/petafuel/fuelifints/communication/BlockingSocketListenerThreadTest.java b/src/test/java/net/petafuel/fuelifints/communication/BlockingSocketListenerThreadTest.java new file mode 100644 index 0000000..039f1cd --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/communication/BlockingSocketListenerThreadTest.java @@ -0,0 +1,70 @@ +package net.petafuel.fuelifints.communication; + +import junit.framework.Assert; +import net.petafuel.fuelifints.FinTSServer; +import net.petafuel.fuelifints.FinTSVersionSwitch; +import net.petafuel.fuelifints.IFinTSController; +import net.petafuel.fuelifints.protocol.FinTSPayload; +import net.petafuel.fuelifints.protocol.fints3.FinTS3Controller; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import javax.net.SocketFactory; +import java.io.BufferedOutputStream; +import java.net.Socket; + +import static org.mockito.Mockito.*; + +public class BlockingSocketListenerThreadTest { + + FinTSCommunicationHandler currentRequest; + + //@todo fix Test + public void testRun() throws Exception { + FinTSServer server = mock(FinTSServer.class); + BlockingSocketListenerThread thread = new BlockingSocketListenerThread(3000, server); + IFinTSController controller = mock(FinTS3Controller.class); + when(server.addRequest((FinTSCommunicationHandler) anyObject())).then(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + Object[] args = invocationOnMock.getArguments(); + if (args.length > 0 && args[0] instanceof FinTSCommunicationHandler) + currentRequest = (FinTSCommunicationHandler) args[0]; + return true; + } + }); + + when(controller.newRequest((FinTSPayload) anyObject(), eq(FinTSCommunicationHandler.CommunicationChannel.SSL))).thenAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + if (currentRequest != null) { + currentRequest.sendResponse("TestAnswer".getBytes(), false); + } + currentRequest = null; + return true; + } + }); + FinTSVersionSwitch.controller = controller; + thread.start(); + + Thread.sleep(2000); //wait for server to start + + String request = "HNHBK:1:3+000000000120+300+0+1'HKIDN:2:2+280:20041144+9999999999+0+0'HKVVB:3:2+0+0+0+123 Banking Android+0.3'HNHBS:4:1+1"; + SocketFactory socketFactory = SocketFactory.getDefault(); + Socket s = socketFactory.createSocket("localhost", 3000); + s.setSoTimeout(1000); + BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream()); + + bos.write(request.getBytes()); + bos.flush(); + Thread.sleep(1000); + while (s.getInputStream().available() > 0) { + int numBytes = s.getInputStream().available(); + byte[] bytes = new byte[numBytes]; + s.getInputStream().read(bytes); + Assert.assertTrue(new String(bytes).equals("TestAnswer")); + } + s.close(); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/communication/NIOSSLRequestListenerThreadTest.java b/src/test/java/net/petafuel/fuelifints/communication/NIOSSLRequestListenerThreadTest.java new file mode 100644 index 0000000..c701383 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/communication/NIOSSLRequestListenerThreadTest.java @@ -0,0 +1,105 @@ +package net.petafuel.fuelifints.communication; + +import junit.framework.Assert; +import net.petafuel.fuelifints.FinTSServer; +import net.petafuel.fuelifints.FinTSVersionSwitch; +import net.petafuel.fuelifints.IFinTSController; +import net.petafuel.fuelifints.protocol.FinTSPayload; +import net.petafuel.fuelifints.protocol.fints3.FinTS3Controller; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.SchemePortResolver; +import org.apache.http.conn.UnsupportedSchemeException; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.util.EntityUtils; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import java.util.Properties; + +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class NIOSSLRequestListenerThreadTest { + FinTSCommunicationHandler currentRequest; + + //@todo fix Test + public void testRun() throws Exception { + Properties systemProps = System.getProperties(); + systemProps.put("javax.net.ssl.trustStore", "src/test/resources/keystore/truststore"); + systemProps.put("javax.net.ssl.trustStorePassword", "123456"); + System.setProperties(systemProps); + + FinTSServer server = mock(FinTSServer.class); + NIOSSLRequestListenerThread thread = new NIOSSLRequestListenerThread(8081, server); + IFinTSController controller = mock(FinTS3Controller.class); + when(server.addRequest((FinTSCommunicationHandler) anyObject())).then(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + Object[] args = invocationOnMock.getArguments(); + if (args.length > 0 && args[0] instanceof FinTSCommunicationHandler) + currentRequest = (FinTSCommunicationHandler) args[0]; + return true; + } + }); + + when(controller.newRequest((FinTSPayload) anyObject(), eq(FinTSCommunicationHandler.CommunicationChannel.SSL))).thenAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + if (currentRequest != null) { + currentRequest.sendResponse("TestAnswer".getBytes(), false); + } + currentRequest = null; + return true; + } + }); + FinTSVersionSwitch.controller = controller; + thread.start(); + + Thread.sleep(2000); //wait for server to start + + SSLContext sslContext = new SSLContextBuilder().build(); + + + SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, new HostnameVerifier() { + @Override + public boolean verify(String s, SSLSession sslSession) { + return true; + } + }); + + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); + httpClientBuilder.setSSLSocketFactory(sslSocketFactory); + httpClientBuilder.setSchemePortResolver(new SchemePortResolver() { + @Override + public int resolve(HttpHost httpHost) throws UnsupportedSchemeException { + return 8081; + } + }); + + CloseableHttpClient httpClient = httpClientBuilder.build(); + + HttpPost httpPost = new HttpPost("https://localhost:8081"); + StringEntity entity = new StringEntity("HNHBK:1:3+000000000120+300+0+1'HKIDN:2:2+280:20041144+9999999999+0+0'HKVVB:3:2+0+0+0+123 Banking Android+0.3'HNHBS:4:1+1"); + + httpPost.setEntity(entity); + + HttpResponse response = httpClient.execute(httpPost); + Thread.sleep(1000); + String responseString = EntityUtils.toString(response.getEntity()); + + Assert.assertTrue(responseString.equals("TestAnswer")); + + } +} diff --git a/src/test/java/net/petafuel/fuelifints/communication/NIOSocketListenerThreadTest.java b/src/test/java/net/petafuel/fuelifints/communication/NIOSocketListenerThreadTest.java new file mode 100644 index 0000000..04fc045 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/communication/NIOSocketListenerThreadTest.java @@ -0,0 +1,73 @@ +package net.petafuel.fuelifints.communication; + +import junit.framework.Assert; +import net.petafuel.fuelifints.FinTSServer; +import net.petafuel.fuelifints.FinTSVersionSwitch; +import net.petafuel.fuelifints.IFinTSController; +import net.petafuel.fuelifints.protocol.FinTSPayload; +import net.petafuel.fuelifints.protocol.fints3.FinTS3Controller; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import javax.net.SocketFactory; +import java.io.BufferedOutputStream; +import java.net.Socket; + +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class NIOSocketListenerThreadTest { + + FinTSCommunicationHandler currentRequest; + + //@todo: fix Test + public void testRun() throws Exception { + FinTSServer server = mock(FinTSServer.class); + NIOSocketListenerThread thread = new NIOSocketListenerThread(3001, server); + IFinTSController controller = mock(FinTS3Controller.class); + when(server.addRequest((FinTSCommunicationHandler) anyObject())).then(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + Object[] args = invocationOnMock.getArguments(); + if (args.length > 0 && args[0] instanceof FinTSCommunicationHandler) + currentRequest = (FinTSCommunicationHandler) args[0]; + return true; + } + }); + + when(controller.newRequest((FinTSPayload) anyObject(), eq(FinTSCommunicationHandler.CommunicationChannel.SSL))).thenAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + if (currentRequest != null) { + currentRequest.sendResponse("TestAnswer".getBytes(), false); + } + currentRequest = null; + return true; + } + }); + FinTSVersionSwitch.controller = controller; + thread.start(); + + Thread.sleep(2000); //wait for server to start + + String request = "HNHBK:1:3+000000000120+300+0+1'HKIDN:2:2+280:20041144+9999999999+0+0'HKVVB:3:2+0+0+0+123 Banking Android+0.3'HNHBS:4:1+1"; + SocketFactory socketFactory = SocketFactory.getDefault(); + Socket s = socketFactory.createSocket("localhost", 3001); + s.setSoTimeout(1000); + BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream()); + + bos.write(request.getBytes()); + bos.flush(); + Thread.sleep(1000); + while (s.getInputStream().available() > 0) { + int numBytes = s.getInputStream().available(); + byte[] bytes = new byte[numBytes]; + s.getInputStream().read(bytes); + Assert.assertTrue(new String(bytes).equals("TestAnswer")); + } + s.close(); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/cryptography/CryptographyFactoryTest.java b/src/test/java/net/petafuel/fuelifints/cryptography/CryptographyFactoryTest.java new file mode 100644 index 0000000..17de1ff --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/cryptography/CryptographyFactoryTest.java @@ -0,0 +1,24 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import org.junit.Test; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; + +public class CryptographyFactoryTest { + + @Test + public void testGetPinTanDecryptor() throws Exception { + Cryptography cryptography = CryptographyFactory.getCryptography(SecurityMethod.PIN_2); + assertNotNull(cryptography); + assertTrue(cryptography instanceof PinTanCryptography); + } + + @Test + public void testGetRDH10Decryptor() { + Cryptography cryptography = CryptographyFactory.getCryptography(SecurityMethod.RDH_10); + assertNotNull(cryptography); + assertTrue(cryptography instanceof RDH10Cryptography); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/cryptography/KeyManagerTest.java b/src/test/java/net/petafuel/fuelifints/cryptography/KeyManagerTest.java new file mode 100644 index 0000000..78d9565 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/cryptography/KeyManagerTest.java @@ -0,0 +1,59 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import org.junit.Test; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; + +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; + +public class KeyManagerTest { + @Test + public void testGetInstance() throws Exception { + KeyManager keyManager = KeyManager.getInstance("DEBUG"); + } + + @Test + public void testGetPrivateKey() throws Exception { + KeyManager keyManager = KeyManager.getInstance("DEBUG"); + PrivateKey cipherPrivateKey = keyManager.getPrivateKey(SecurityMethod.RDH_10, "V"); + PrivateKey signaturePrivateKey = keyManager.getPrivateKey(SecurityMethod.RDH_10, "S"); + assertNotNull(cipherPrivateKey); + assertNotNull(signaturePrivateKey); + assertNotEquals(cipherPrivateKey, signaturePrivateKey); + } + + @Test + public void testGetPublicKey() throws Exception { + KeyManager keyManager = KeyManager.getInstance("DEBUG"); + PublicKey cipherPublicKey = keyManager.getPublicKey(SecurityMethod.RDH_10, "V"); + PublicKey signaturePublicKey = keyManager.getPublicKey(SecurityMethod.RDH_10, "S"); + assertNotNull(cipherPublicKey); + assertNotNull(signaturePublicKey); + assertNotEquals(cipherPublicKey, signaturePublicKey); + } + + //@Todo Fix this Test + public void testGetUserKey() throws NoSuchAlgorithmException { + KeyManager keyManager = KeyManager.getInstance("DEBUG"); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); + int bits = 4096; + keyPairGenerator.initialize(bits, secureRandom); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + PublicKey publicKey = keyPair.getPublic(); + + keyManager.addUserPublicKey("aUserId", SecurityMethod.RDH_10, "S", 1, 10, publicKey); + PublicKey userPublicKey = keyManager.getUserPublicKey("aUserId", null, SecurityMethod.RDH_10, "S", 1, 10); + + assertEquals(publicKey, userPublicKey); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/cryptography/PinTanDecryptorTest.java b/src/test/java/net/petafuel/fuelifints/cryptography/PinTanDecryptorTest.java new file mode 100644 index 0000000..c88c35e --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/cryptography/PinTanDecryptorTest.java @@ -0,0 +1,17 @@ +package net.petafuel.fuelifints.cryptography; + +import org.junit.Test; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; + +public class PinTanDecryptorTest { + @Test + public void testDecrypt() throws Exception { + byte[] encrypted = "Hello World!".getBytes(); + PinTanCryptography pinTanDecryptor = new PinTanCryptography(); + byte[] decrypted = pinTanDecryptor.decrypt(encrypted,new byte[0],"DEBUG"); + assertNotNull(decrypted); + assertEquals(encrypted,decrypted); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/cryptography/RAH10CryptographyTest.java b/src/test/java/net/petafuel/fuelifints/cryptography/RAH10CryptographyTest.java new file mode 100644 index 0000000..77a6744 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/cryptography/RAH10CryptographyTest.java @@ -0,0 +1,64 @@ +package net.petafuel.fuelifints.cryptography; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.Test; + +import java.security.Security; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class RAH10CryptographyTest { + static { + Security.addProvider(new BouncyCastleProvider()); + } + + @Test + public void testDecrypt() throws Exception { + /* + RAH10Cryptography rah10Cryptography = new RAH10Cryptography(); + byte[] data = "Hello World!".getBytes("ISO-8859-1"); + + + KeyManager keyManager = KeyManager.getInstance("DEBUG"); + PublicKey bankPublicKey = keyManager.getPublicKey(SecurityMethod.RAH_10, "V"); + + byte[] symmetricKey = rah10Cryptography.generateKey(); + + byte[] encrypted = rah10Cryptography.encryptMessage(data, symmetricKey, "DEBUG"); + + + Cipher rsaCipher = Cipher.getInstance("RSA/NONE/NOPADDING"); + rsaCipher.init(Cipher.ENCRYPT_MODE, bankPublicKey); + + byte[] encryptedKey = rsaCipher.doFinal(symmetricKey); + + byte[] decrypted = rah10Cryptography.decrypt(encrypted, encryptedKey, "DEBUG"); + + assertNotNull(decrypted); + assertArrayEquals(data, decrypted); + */ + } + + @Test + public void testEncryptMessage() throws Exception { + /* + RAH10Cryptography rah10Cryptography = new RAH10Cryptography(); + byte[] generatedKey = rah10Cryptography.generateKey(); + assertNotNull(generatedKey); + assertTrue(generatedKey.length > 0); + byte[] message = "Hello World!".getBytes("ISO-8859-1"); + byte[] encrypted = rah10Cryptography.encryptMessage(message, generatedKey, "DEBUG"); + assertNotNull(encrypted); + assertTrue(encrypted.length > 0); + */ + } + + @Test + public void testGenerateKey() throws Exception { + RAH10Cryptography rah10Cryptography = new RAH10Cryptography(); + byte[] generatedKey = rah10Cryptography.generateKey(); + assertNotNull(generatedKey); + assertTrue(generatedKey.length > 0); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/cryptography/RDH10DecryptorTest.java b/src/test/java/net/petafuel/fuelifints/cryptography/RDH10DecryptorTest.java new file mode 100644 index 0000000..feeed7a --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/cryptography/RDH10DecryptorTest.java @@ -0,0 +1,56 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.Test; + +import javax.crypto.Cipher; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertNotNull; + +public class RDH10DecryptorTest { + + private static final Logger LOG = LogManager.getLogger(RDH10DecryptorTest.class); + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + @Test + public void testDecrypt() throws Exception { + + byte[] data = new byte[256]; + SecureRandom secureRandom = new SecureRandom(); + secureRandom.nextBytes(data); + + KeyManager keyManager = KeyManager.getInstance("DEBUG"); + PublicKey bankPublicKey = keyManager.getPublicKey(SecurityMethod.RDH_10, "V"); + + TwoKeyTripleDESCryptography twoKeyTripleDESCryptography = new TwoKeyTripleDESCryptography(); + byte[] tripleDesKey = twoKeyTripleDESCryptography.generatePassword(); + + LOG.info("TripleDESKeyLength: " + tripleDesKey.length); + + Cipher twoKeyTripleDesCipher = twoKeyTripleDESCryptography.createCipher(tripleDesKey, Cipher.ENCRYPT_MODE); + byte[] encryptedMessage = twoKeyTripleDesCipher.doFinal(data); + + Cipher rsaCipher = Cipher.getInstance("RSA/NONE/NOPADDING"); + rsaCipher.init(Cipher.ENCRYPT_MODE, bankPublicKey); + + byte[] encryptedKey = rsaCipher.doFinal(tripleDesKey); + + LOG.info("EncryptedKeyBytesLength: " + encryptedKey.length); + + RDH10Cryptography rdh10Decryptor = new RDH10Cryptography(); + byte[] decryptedData = rdh10Decryptor.decrypt(encryptedMessage, encryptedKey, "DEBUG"); + + assertNotNull(decryptedData); + assertArrayEquals(data, decryptedData); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/cryptography/RDH10SignatureHelperTest.java b/src/test/java/net/petafuel/fuelifints/cryptography/RDH10SignatureHelperTest.java new file mode 100644 index 0000000..dbfe970 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/cryptography/RDH10SignatureHelperTest.java @@ -0,0 +1,91 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.dataaccess.DummyAccessFacade; +import net.petafuel.fuelifints.model.Dialog; +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import net.petafuel.fuelifints.protocol.fints3.segments.HNSHA; +import net.petafuel.fuelifints.protocol.fints3.segments.HNSHK; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.engines.RSAEngine; +import org.bouncycastle.crypto.params.RSAKeyParameters; +import org.bouncycastle.crypto.signers.PSSSigner; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.MessageDigest; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.interfaces.RSAPrivateKey; + +import static org.junit.Assert.assertTrue; + +public class RDH10SignatureHelperTest { + + + DummyAccessFacade dummyAccessFacade = new DummyAccessFacade(null); + + @Before + public void before() { + dummyAccessFacade.clearUserKeys(); + } + + @After + public void after() { + dummyAccessFacade.clearUserKeys(); + } + + @Test + public void testValidateSignature() throws Exception { + KeyManager keyManager = KeyManager.getInstance("DEBUG"); + byte[] hnshkBytes = "HNSHK:2:4+RDH:10+2+123456+1+1+1::2+3234+1:20020701:111144+1:999:1+6:10:16+280:DEBUG:54321:S:0:0".getBytes("ISO-8859-1"); + byte[] toSign = ("HNSHK:2:4+RDH:10+2+123456+1+1+1::2+3234+1:20020701:111144+1:999:1+6:10:16+280:DEBUG:54321:S:0:0'" + + "HKIDN:3:2+280:10020030+54321+2+1'" + + "HKVVB:4:2+2+3+1+123 Banking Android+1.0'").getBytes("ISO-8859-1"); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); + int bits = 4096; + keyPairGenerator.initialize(bits, secureRandom); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + PrivateKey userSigningPrivateKey = keyPair.getPrivate(); + PublicKey userSigningPublicKey = keyPair.getPublic(); + + RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) userSigningPrivateKey; + + keyManager.addUserPublicKey("54321", SecurityMethod.RDH_10, "S", 0, 0, userSigningPublicKey); + + RDH10SignatureHelper rdh10SignatureHelper = new RDH10SignatureHelper(); + + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(toSign); + + PSSSigner signer = new PSSSigner(new RSAEngine(), new SHA256Digest(), hash.length); + signer.init(true, new RSAKeyParameters(true, rsaPrivateKey.getModulus(), rsaPrivateKey.getPrivateExponent())); + + signer.update(hash, 0, hash.length); + + HNSHK hnshk = new HNSHK(hnshkBytes); + hnshk.parseElement(); + byte[] signatureByes = signer.generateSignature(); + + byte[] hnshaBegin = ("HNSHA:5:2+654321+@" + signatureByes.length + "@").getBytes("ISO-8859-1"); + byte[] hnshaEnd = "".getBytes("ISO-8859-1"); + int hnshaLength = hnshaBegin.length + signatureByes.length + hnshaEnd.length; + byte[] hnshaBytes = new byte[hnshaLength]; + + int offset = 0; + System.arraycopy(hnshaBegin, 0, hnshaBytes, offset, hnshaBegin.length); + offset += hnshaBegin.length; + System.arraycopy(signatureByes, 0, hnshaBytes, offset, signatureByes.length); + offset += signatureByes.length; + System.arraycopy(hnshaEnd, 0, hnshaBytes, offset, hnshaEnd.length); + HNSHA hnsha = new HNSHA(hnshaBytes); + hnsha.parseElement(); + + assertTrue(!(rdh10SignatureHelper.validateSignature(toSign, hnshk, hnsha, new Dialog()).isSuccess())); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/cryptography/RDH9CryptographyTest.java b/src/test/java/net/petafuel/fuelifints/cryptography/RDH9CryptographyTest.java new file mode 100644 index 0000000..7a2f3e9 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/cryptography/RDH9CryptographyTest.java @@ -0,0 +1,71 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.Test; + +import javax.crypto.Cipher; +import java.security.PublicKey; +import java.security.Security; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class RDH9CryptographyTest { + + private static final Logger LOG = LogManager.getLogger(RDH9CryptographyTest.class); + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + @Test + public void testDecrypt() throws Exception { + byte[] data = "Hello World!".getBytes("ISO-8859-1"); + + KeyManager keyManager = KeyManager.getInstance("DEBUG"); + PublicKey bankPublicKey = keyManager.getPublicKey(SecurityMethod.RDH_9, "V"); + + TwoKeyTripleDESCryptography twoKeyTripleDESCryptography = new TwoKeyTripleDESCryptography(); + byte[] tripleDesKey = twoKeyTripleDESCryptography.generatePassword(); + + LOG.info("TripleDESKeyLength: " + tripleDesKey.length); + + Cipher twoKeyTripleDesCipher = twoKeyTripleDESCryptography.createCipher(tripleDesKey, Cipher.ENCRYPT_MODE); + byte[] encryptedMessage = twoKeyTripleDesCipher.doFinal(data); + + Cipher rsaCipher = Cipher.getInstance("RSA/NONE/PKCS1Padding"); + rsaCipher.init(Cipher.ENCRYPT_MODE, bankPublicKey); + + byte[] encryptedKey = rsaCipher.doFinal(tripleDesKey); + + LOG.info("EncryptedKeyBytesLength: " + encryptedKey.length); + + RDH9Cryptography rdh9Decryptor = new RDH9Cryptography(); + byte[] decryptedData = rdh9Decryptor.decrypt(encryptedMessage, encryptedKey, "DEBUG"); + + assertNotNull(decryptedData); + assertArrayEquals(data, decryptedData); + } + + @Test + public void testEncryptMessage() throws Exception { + byte[] data = "hello world!".getBytes(); + RDH9Cryptography rdh9Cryptography = new RDH9Cryptography(); + byte[] encryptionKey = rdh9Cryptography.generateKey(); + byte[] encrypted = rdh9Cryptography.encryptMessage(data, encryptionKey, "DEBUG"); + assertNotNull(encrypted); + assertTrue(encrypted.length > 0); + } + + @Test + public void testGenerateKey() throws Exception { + RDH9Cryptography rdh9Cryptography = new RDH9Cryptography(); + byte[] encryptionKey = rdh9Cryptography.generateKey(); + assertNotNull(encryptionKey); + assertTrue(encryptionKey.length > 0); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/cryptography/SignatureHelperFactoryTest.java b/src/test/java/net/petafuel/fuelifints/cryptography/SignatureHelperFactoryTest.java new file mode 100644 index 0000000..010af13 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/cryptography/SignatureHelperFactoryTest.java @@ -0,0 +1,16 @@ +package net.petafuel.fuelifints.cryptography; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import org.junit.Test; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; + +public class SignatureHelperFactoryTest { + @Test + public void testGetPinTanSignatureHelper() throws Exception { + SignatureHelper signatureHelper = SignatureHelperFactory.getSignatureHelper(SecurityMethod.PIN_2); + assertNotNull(signatureHelper); + assertTrue(signatureHelper instanceof PinTanSignatureHelper); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/cryptography/aesencryption/AESUtilTest.java b/src/test/java/net/petafuel/fuelifints/cryptography/aesencryption/AESUtilTest.java new file mode 100644 index 0000000..871b4a4 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/cryptography/aesencryption/AESUtilTest.java @@ -0,0 +1,25 @@ +package net.petafuel.fuelifints.cryptography.aesencryption; + +import org.junit.Test; + +import java.util.Base64; + +import static org.junit.Assert.assertEquals; + +public class AESUtilTest { + @Test + public void aesEncrypt() throws Exception { + byte[] base64test = ("123456".getBytes()); + + assertEquals(AESUtil.aesEncrypt(base64test), "vskYPyQODcBF9n9sPFGGrw=="); + } + + @Test + public void aesDecrypt() throws Exception { + String base64test = "123456"; + assertEquals(new String(AESUtil.aesDecrypt("vskYPyQODcBF9n9sPFGGrw==")), base64test); + } + + + +} \ No newline at end of file diff --git a/src/test/java/net/petafuel/fuelifints/dataaccess/DummyAccessFacadeTest.java b/src/test/java/net/petafuel/fuelifints/dataaccess/DummyAccessFacadeTest.java new file mode 100644 index 0000000..3e8f6e7 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/dataaccess/DummyAccessFacadeTest.java @@ -0,0 +1,82 @@ +package net.petafuel.fuelifints.dataaccess; + +import net.petafuel.fuelifints.protocol.fints3.model.SecurityMethod; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; + +import static junit.framework.Assert.assertFalse; +import static org.junit.Assert.*; + +public class DummyAccessFacadeTest { + + private DummyAccessFacade dummyAccessFacade; + + @Before + public void setup() { + dummyAccessFacade = new DummyAccessFacade(null); + } + + @After + public void tearDown() { + dummyAccessFacade.clearDB(); + dummyAccessFacade.close(); + } + + @AfterClass + public static void afterClass() { + + File file = new File("dummyfacade-test.db"); + file.delete(); + file.deleteOnExit(); + } + + @Test + public void testJdbcSqliteConnection() { + DummyAccessFacade dummyAccessFacade = new DummyAccessFacade(null); + } + + @Test + public void testHasKeyPair() { + assertFalse(dummyAccessFacade.hasKeyPair(SecurityMethod.RDH_10, "V")); + } + + @Test + public void testInsertKeyPair() { + dummyAccessFacade.updateKeyPair(SecurityMethod.RDH_10, "privatekey", "publickey", "V", null, null); + assertTrue(dummyAccessFacade.hasKeyPair(SecurityMethod.RDH_10, "V")); + } + + @Test + public void testGetKeys() { + dummyAccessFacade.updateKeyPair(SecurityMethod.RDH_10, "privatekey", "publickey", "V", null, null); + String privateKey = dummyAccessFacade.getPrivateKey(SecurityMethod.RDH_10, "V"); + String publicKey = dummyAccessFacade.getPublicKey(SecurityMethod.RDH_10, "V"); + assertNotNull(privateKey); + assertNotNull(publicKey); + } + + @Test + public void testGetKeysNotInserted() { + String privateKey = dummyAccessFacade.getPrivateKey(SecurityMethod.RDH_10, "S"); + String publicKey = dummyAccessFacade.getPublicKey(SecurityMethod.RDH_10, "S"); + assertNull(privateKey); + assertNull(publicKey); + } + + @Test + public void testUpdateKeyPair() { + dummyAccessFacade.updateKeyPair(SecurityMethod.RDH_10, "privatekey2", "publickey2", "V", null, null); + assertTrue(dummyAccessFacade.hasKeyPair(SecurityMethod.RDH_10, "V")); + } + + @Test + public void testAddUserKey() { + dummyAccessFacade.addUserPublicKey("AUserId", null, SecurityMethod.RDH_10, "uPubKey", "V", 1, 10, null, null); + String chipherUserPublicKey = dummyAccessFacade.getUserPublicKey("AUserId", null, SecurityMethod.RDH_10, "V", 1, 10); + assertNotNull(chipherUserPublicKey); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/model/client/ClientProductInfoTest.java b/src/test/java/net/petafuel/fuelifints/model/client/ClientProductInfoTest.java new file mode 100644 index 0000000..f3d58f6 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/model/client/ClientProductInfoTest.java @@ -0,0 +1,59 @@ +package net.petafuel.fuelifints.model.client; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.util.Enumeration; +import java.util.Properties; + +public class ClientProductInfoTest { + + private ClientProductInfo clientProductInfo; + + @Before + public void setup() { + this.clientProductInfo = new ClientProductInfo(); + + Properties properties = new Properties(); + try { + BufferedInputStream stream = new BufferedInputStream(new FileInputStream("src/test/resources/test.properties")); + properties.load(stream); + stream.close(); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + + @SuppressWarnings("unchecked") + Enumeration enums = (Enumeration) properties.propertyNames(); + while (enums.hasMoreElements()) { + String key = enums.nextElement(); + String value = properties.getProperty(key); + System.setProperty(key, value); + } + } + + @Test + public void goodConfig() { + Assert.assertEquals("true", System.getProperty("productinfo.csv.check")); + Assert.assertNotEquals("", System.getProperty("productinfo.csv.filepath")); + Assert.assertNotEquals(null, System.getProperty("productinfo.csv.filepath")); + } + + @Test + public void validateClientProductInfo() { + + // Produktbezeichung fehlt + Assert.assertFalse(clientProductInfo.validateClientProductInfo()); + + // Produktbezeichung falsch + clientProductInfo.setClientProductName("falsche Produktbezeichung"); + Assert.assertFalse(clientProductInfo.validateClientProductInfo()); + + // Produktbezeichung richtig + clientProductInfo.setClientProductName("Q519D9BWIB7GINUI"); + Assert.assertTrue(clientProductInfo.validateClientProductInfo()); + } +} \ No newline at end of file diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/FinTS3ParserTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/FinTS3ParserTest.java new file mode 100644 index 0000000..1a99361 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/FinTS3ParserTest.java @@ -0,0 +1,24 @@ +package net.petafuel.fuelifints.protocol.fints3; + +import org.junit.Test; + +public class FinTS3ParserTest { + @Test + public void testParseAndValidateRequest() throws Exception { + /* + String testMessage = "HNHBK:1:3+000000000121+300+0+1'HKIDN:2:2+280:20041144+9999999999+0+0'HKVVB:3:2+0+0+0+123 Banking Android+0.3'HNHBS:4:1+1'"; + FinTS3Parser parser = new FinTS3Parser(); + parser.parseAndValidateRequest(0, testMessage.getBytes()); + */ + } + + @Test + public void testGetBankPublicKeys() { + String testMessage = "HNHBK:1:3+000000000121+300+0+1'" + + "HKIDN:2:2+280:20041144+9999999999+0+0'" + + "HKVVB:3:2+0+0+0+123 Banking Android+0.3'" + + "HKISA:4:3+2+124+RDH:10+280:10020030:9999999999:V:1:1'" + + "HKISA:5:3+2+124+RDH:10+280:10020030:9999999999:S:1:1'" + + "HNHBS:6:1+1'"; + } +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/AbstractElementTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/AbstractElementTest.java new file mode 100644 index 0000000..e38b768 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/AbstractElementTest.java @@ -0,0 +1,28 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Segmentkopf; +import net.petafuel.fuelifints.protocol.fints3.validator.validators.an; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class AbstractElementTest { + @Test + public void testGetHbciEncoded() throws Exception { + Segment dummy = new Segment(new byte[0]) { + @Element( + description = {@ElementDescription(number = 1)}) + private Segmentkopf segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung("Dummy").setSegmentVersion(1).setSegmentNumber(1).build(); + + @Element( + description = {@ElementDescription(number = 2, length = -32)}) + @an + private String text = "Hans@Maulwurf.de:hello world+some question?some sad smiley:'("; + }; + + byte[] hbciEncoded = dummy.getHbciEncoded(); + assertNotNull(hbciEncoded); + assertEquals("Dummy:1:1+Hans?@Maulwurf.de?:hello world?+some question??some sad smiley?:?'(", new String(hbciEncoded)); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HIRMSTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HIRMSTest.java new file mode 100644 index 0000000..ebdc859 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HIRMSTest.java @@ -0,0 +1,27 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import org.junit.Test; + +public class HIRMSTest { + + + @Test + public void testGetHbciEncoded() throws Exception { + /* + HIRMS hirms = new HIRMS(new byte[0]); + Segmentkopf segmentkopf;// = new Segmentkopf("HIRMS:0:2:3".getBytes("ISO-8859-1")); + segmentkopf = Segmentkopf.Builder.newInstance().setSegmentKennung(HIRMS.class).setSegmentVersion(2).setBezugssegment(3).build(); + hirms.setSegmentkopf(segmentkopf); + List rueckmeldungen = new LinkedList(); + Rueckmeldung rueckmeldung = new Rueckmeldung("3010::Auftrag Teilweise ausgeführt.".getBytes("ISO-8859-1")); + rueckmeldungen.add(rueckmeldung); + rueckmeldung = new Rueckmeldung("0010::Auftrag ausgeführt.".getBytes("ISO-8859-1")); + rueckmeldungen.add(rueckmeldung); + rueckmeldung = new Rueckmeldung("9910::Auftrag inhaltlich ungültig.".getBytes("ISO-8859-1")); + rueckmeldungen.add(rueckmeldung); + hirms.setRueckmeldung(rueckmeldungen); + byte[] hbciEncoded = hirms.getHbciEncoded(); + LOG.info(new String(hbciEncoded, "ISO-8859-1")); + */ + } +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HKIDNTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HKIDNTest.java new file mode 100644 index 0000000..5144854 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HKIDNTest.java @@ -0,0 +1,21 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.exceptions.ElementParseException; +import org.junit.Test; + +public class HKIDNTest { + + @Test + public void testHKIDN() { + + try { + HKIDN test = new HKIDN("HKIDN:2:2+280:20041144+9999999999+0+0".getBytes()); + test.parseElement(); + System.out.println(test.toString()); + } catch (ElementParseException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + + } + +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSALTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSALTest.java new file mode 100644 index 0000000..a297708 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HKSALTest.java @@ -0,0 +1,20 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import org.junit.Test; + +public class HKSALTest { + @Test + public void testHKSAL6() throws Exception { + HKSAL hksal = new HKSAL("HKSAL:3:6+1234567::280:10020030+N".getBytes("ISO-8859-1")); + hksal.parseElement(); + } + + /* Eventuell mal ein HKSAL version 7 suchen + @Test + public void testHKSAL7() throws Exception { + HKSAL hksal = new HKSAL("HKSAL:3:6+1234567::280:10020030+N".getBytes("ISO-8859-1")); + hksal.parseElement(); + System.out.println(new String(hksal.getHbciEncoded(),"ISO-8859-1")); + } + */ +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HKVVBTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HKVVBTest.java new file mode 100644 index 0000000..2a385b3 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HKVVBTest.java @@ -0,0 +1,20 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.exceptions.ElementParseException; +import org.junit.Test; + +public class HKVVBTest { + + @Test + public void testHKVVB() { + + try { + HKVVB test = new HKVVB("HKVVB:3:2+0+0+0+123 Banking Android+0.3".getBytes()); + test.parseElement(); + System.out.println(test.toString()); + } catch (ElementParseException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + + } +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HNHBKTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HNHBKTest.java new file mode 100644 index 0000000..de47446 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/HNHBKTest.java @@ -0,0 +1,21 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import junit.framework.Assert; +import net.petafuel.fuelifints.exceptions.ElementParseException; +import org.junit.Test; + +public class HNHBKTest { + + @Test + public void testHNHBK() { + + try { + HNHBK test = new HNHBK("HNHBK:1:3+000000000120+300+0+1".getBytes()); + test.parseElement(); + System.out.println(test.toString()); + } catch (ElementParseException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + + } +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/MessageElementParserTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/MessageElementParserTest.java new file mode 100644 index 0000000..b33c7e6 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/MessageElementParserTest.java @@ -0,0 +1,40 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import net.petafuel.fuelifints.exceptions.ElementParseException; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Kik; +import net.petafuel.fuelifints.protocol.fints3.segments.deg.Sicherheitsprofil; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class MessageElementParserTest { + + @Test + public void testFillElements() throws Exception { + HNVSK hnvsk = new HNVSK("HNVSK:998:3+PIN:1+998+1+1::2+1:20020610:102044+2:2:13:@8@12345678:5:1+280:10020030:12345:V:0:0+0".getBytes("ISO-8859-1")); + hnvsk.parseElement(); + Sicherheitsprofil sicherheitsprofil = hnvsk.getSicherheitsprofil(); + assertNotNull(sicherheitsprofil); + String sicherheitsverfahren = sicherheitsprofil.getSicherheitsverfahren(); + Integer sicherheitsverfahrenversion = sicherheitsprofil.getSicherheitsverfahrensversion(); + assertNotNull(sicherheitsverfahren); + assertNotNull(sicherheitsverfahrenversion); + assertEquals("PIN",sicherheitsverfahren); + assertEquals(new Integer(1),sicherheitsverfahrenversion); + } + + @Test + public void testFillElements2() throws ElementParseException { + HKISA hkisa = new HKISA("HKISA:5:3+2+124+RDH:10+280:12345678:999:V:999:999".getBytes()); + hkisa.parseElement(); + System.out.println(hkisa); + } + + @Test + public void testKikParsing() throws ElementParseException { + Kik kik = new Kik("280:12345678".getBytes()); + kik.parseElement(); + System.out.println(kik); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/SegmentTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/SegmentTest.java new file mode 100644 index 0000000..6c60414 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/SegmentTest.java @@ -0,0 +1,19 @@ +package net.petafuel.fuelifints.protocol.fints3.segments; + +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertNotNull; + +public class SegmentTest { + @Test + public void testGetHbciEncoded() throws Exception { + byte[] expected = "HNHBK:1:3+000000000120+300+0+1".getBytes("ISO-8859-1"); + Segment hnhbk = new HNHBK(expected); + hnhbk.parseElement(); + byte[] hbciEncoded = hnhbk.getHbciEncoded(); + assertNotNull(hbciEncoded); + assertArrayEquals(expected,hbciEncoded); + + } +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/DatenElementGruppeTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/DatenElementGruppeTest.java new file mode 100644 index 0000000..5032a8c --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/deg/DatenElementGruppeTest.java @@ -0,0 +1,23 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.deg; + +import org.junit.Test; + +import java.io.UnsupportedEncodingException; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class DatenElementGruppeTest { + + @Test + public void testGetHbciEncoded() throws UnsupportedEncodingException { + Segmentkopf segmentkopf = new Segmentkopf(new byte[0]); + segmentkopf.segmentKennung = "HNHBK"; + segmentkopf.segmentNummer = 1; + segmentkopf.segmentVersion = 3; + byte[] hbciEncoded = segmentkopf.getHbciEncoded(); + assertNotNull(hbciEncoded); + assertArrayEquals("HNHBK:1:3".getBytes(), hbciEncoded); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIBPATest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIBPATest.java new file mode 100644 index 0000000..7710ee1 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIBPATest.java @@ -0,0 +1,19 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.parameter; + +import net.petafuel.fuelifints.FinTSVersionSwitch; +import net.petafuel.fuelifints.dataaccess.DummyAccessFacade; +import net.petafuel.fuelifints.dataaccess.dataobjects.CommonBankParameterDataObject; +import org.junit.Test; + +public class HIBPATest { + + @Test + public void testHIBPA() { + DummyAccessFacade daf = new DummyAccessFacade(null); + + CommonBankParameterDataObject cbpdo = daf.getCommonBankParameters(FinTSVersionSwitch.FinTSVersion.FINTS_VERSION_3_0); + HIBPA hibpa = new HIBPA(cbpdo,3); + } + + +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HISHVTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HISHVTest.java new file mode 100644 index 0000000..9c6caec --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HISHVTest.java @@ -0,0 +1,15 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.parameter; + +import org.junit.Test; + +public class HISHVTest { + @Test + public void testHISHV() { + /* + HISHV hishv = new HISHV(3); + String hishvString = new String(hishv.getHbciEncoded()); + for (SecurityMethod method : SecurityMethod.values()) + assertTrue(hishvString.contains(new String(method.getHbciDEG()))); + */ + } +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIUPDTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIUPDTest.java new file mode 100644 index 0000000..af0e95a --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/HIUPDTest.java @@ -0,0 +1,20 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.parameter; + +import org.junit.Test; + +public class HIUPDTest { + + @Test + public void testHIUPD() { + /* + DummyAccessFacade daf = new DummyAccessFacade(); + LegitimationInfo legitimationInfo = new LegitimationInfo(); + legitimationInfo.setUserId("100"); + ArrayList dataObjects = daf.getAccountData(legitimationInfo); + + for (AccountDataObject data : dataObjects) { + HIUPD hiupd = new HIUPD(data, 3); + } + */ + } +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/ParameterSegmentTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/ParameterSegmentTest.java new file mode 100644 index 0000000..80f81df --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/segments/parameter/ParameterSegmentTest.java @@ -0,0 +1,23 @@ +package net.petafuel.fuelifints.protocol.fints3.segments.parameter; + +import net.petafuel.fuelifints.FinTSVersionSwitch; +import net.petafuel.fuelifints.dataaccess.DummyAccessFacade; +import net.petafuel.fuelifints.dataaccess.dataobjects.ParameterDataObject; +import org.junit.Test; + +import java.util.ArrayList; + +public class ParameterSegmentTest { + + @Test + public void testParameterSegment() { + + DummyAccessFacade daf = new DummyAccessFacade(null); + ArrayList dataObjects = daf.getParameterData(FinTSVersionSwitch.FinTSVersion.FINTS_VERSION_3_0); + + for (ParameterDataObject paramData : dataObjects) { + ParameterSegment segment = new ParameterSegment(paramData, 0); + } + } + +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/AnValidatorTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/AnValidatorTest.java new file mode 100644 index 0000000..69d2103 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/AnValidatorTest.java @@ -0,0 +1,33 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import junit.framework.Assert; +import org.junit.Test; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import java.util.Set; + +public class AnValidatorTest { + + class TestValue { + @an + String bla; + } + + @Test + public void testIsValid() throws Exception { + TestValue tv = new TestValue(); + tv.bla = "0909"; + + Validator v = Validation.buildDefaultValidatorFactory().getValidator(); + Set> constraintViolations = v.validate(tv); + + Assert.assertTrue(constraintViolations.size() == 0); + + tv.bla = ":"; + constraintViolations = v.validate(tv); + + Assert.assertTrue(constraintViolations.size() == 0); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/DigValidatorTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/DigValidatorTest.java new file mode 100644 index 0000000..058b0cc --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/DigValidatorTest.java @@ -0,0 +1,32 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import junit.framework.Assert; +import org.junit.Test; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import java.util.Set; + +public class DigValidatorTest { + class TestValue { + @dig + String bla; + } + + @Test + public void testIsValid() throws Exception { + TestValue tv = new TestValue(); + tv.bla = "0909"; + + Validator v = Validation.buildDefaultValidatorFactory().getValidator(); + Set> constraintViolations = v.validate(tv); + + Assert.assertTrue(constraintViolations.size() == 0); + + tv.bla = ":"; + constraintViolations = v.validate(tv); + + Assert.assertTrue(constraintViolations.size() > 0); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/NumValidatorTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/NumValidatorTest.java new file mode 100644 index 0000000..4a99c49 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/NumValidatorTest.java @@ -0,0 +1,32 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import junit.framework.Assert; +import org.junit.Test; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import java.util.Set; + +public class NumValidatorTest { + class TestValue { + @num + Integer bla; + } + + @Test + public void testIsValid() throws Exception { + TestValue tv = new TestValue(); + tv.bla = 909; + + Validator v = Validation.buildDefaultValidatorFactory().getValidator(); + Set> constraintViolations = v.validate(tv); + + //Assert.assertTrue(constraintViolations.size() > 0); //0 zu Beginn ist nicht erlaubt! + + //tv.bla = 1919; + //constraintViolations = v.validate(tv); + + Assert.assertTrue(constraintViolations.size() == 0); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/TxtValidatorTest.java b/src/test/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/TxtValidatorTest.java new file mode 100644 index 0000000..e9d45f3 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/protocol/fints3/validator/validators/TxtValidatorTest.java @@ -0,0 +1,32 @@ +package net.petafuel.fuelifints.protocol.fints3.validator.validators; + +import junit.framework.Assert; +import org.junit.Test; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import java.util.Set; + +public class TxtValidatorTest { + class TestValue { + @txt + String bla; + } + + @Test + public void testIsValid() throws Exception { + TestValue tv = new TestValue(); + tv.bla = "0909"; + + Validator v = Validation.buildDefaultValidatorFactory().getValidator(); + Set> constraintViolations = v.validate(tv); + + Assert.assertTrue(constraintViolations.size() == 0); + + tv.bla = ":"; + constraintViolations = v.validate(tv); + + Assert.assertTrue(constraintViolations.size() == 0); + } +} diff --git a/src/test/java/net/petafuel/fuelifints/support/FieldComparatorTest.java b/src/test/java/net/petafuel/fuelifints/support/FieldComparatorTest.java new file mode 100644 index 0000000..54212d8 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/support/FieldComparatorTest.java @@ -0,0 +1,26 @@ +package net.petafuel.fuelifints.support; + +import org.junit.Test; + +public class FieldComparatorTest { + + @Test + public void testCompare() throws Exception { + /* + Field[] fields = HNVSK.class.getDeclaredFields(); + List usedFields = new ArrayList(); + for(Field f: fields) { + if(AbstractElement.getMatchingElementDescription(f,0) != null) { + usedFields.add(f); + } + } + Collections.sort(usedFields, new FieldComparator(0)); + + for(int i = 0; i < usedFields.size(); i++) { + if(usedFields.get(i).getAnnotation(ElementDescription.class).number()-1 != i) { + fail(); + } + } + */ + } +} diff --git a/src/test/java/net/petafuel/fuelifints/support/SegmentComparatorTest.java b/src/test/java/net/petafuel/fuelifints/support/SegmentComparatorTest.java new file mode 100644 index 0000000..71fecb2 --- /dev/null +++ b/src/test/java/net/petafuel/fuelifints/support/SegmentComparatorTest.java @@ -0,0 +1,31 @@ +package net.petafuel.fuelifints.support; + +import net.petafuel.fuelifints.model.IMessageElement; +import net.petafuel.fuelifints.protocol.fints3.segments.HIRMS; +import net.petafuel.fuelifints.protocol.fints3.segments.HISYN; +import org.junit.Test; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; + +public class SegmentComparatorTest { + @Test + public void testCompare() throws Exception { + HIRMS hirms = new HIRMS("HIRMS:4:2:5+0010::Auftrag entgegengenommen".getBytes()); + HIRMS hirms2 = new HIRMS("HIRMS:4:2:3+0010::Auftrag entgegengenommen".getBytes()); + HISYN hisyn = new HISYN("HISYN:10:3:8+2".getBytes()); + hirms.parseElement(); + hirms2.parseElement(); + hisyn.parseElement(); + List unsortedList = new LinkedList(); + unsortedList.add(hisyn); + unsortedList.add(hirms); + unsortedList.add(hirms2); + Collections.sort(unsortedList,new SegmentComparator()); + assertEquals(3,unsortedList.size()); + assertEquals(hirms2,unsortedList.get(0)); + } +} diff --git a/src/test/resources/log4j2-test.xml b/src/test/resources/log4j2-test.xml new file mode 100644 index 0000000..4355530 --- /dev/null +++ b/src/test/resources/log4j2-test.xml @@ -0,0 +1,26 @@ + + + + %d %-5p [%t] [] %c - %m%n + log/ + fuelifints.log + .%d{yyyy-MM-dd} + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/test.properties b/src/test/resources/test.properties new file mode 100644 index 0000000..ea871e4 --- /dev/null +++ b/src/test/resources/test.properties @@ -0,0 +1,2 @@ +productinfo.csv.filepath = src/test/resources/Test-Lizenzdatei.csv +productinfo.csv.check = true \ No newline at end of file diff --git a/src/test/resources/xml/acknowledgementcodes.xml b/src/test/resources/xml/acknowledgementcodes.xml new file mode 100644 index 0000000..9cc4bc1 --- /dev/null +++ b/src/test/resources/xml/acknowledgementcodes.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +