Initial drop of Fueli FinTS SCA version.

This commit is contained in:
pF-max 2021-02-22 14:01:54 +01:00
parent a6a7b9befb
commit 42b4047bac
290 changed files with 27417 additions and 12 deletions

6
.gitignore vendored
View file

@ -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/

13
LICENSE
View file

@ -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.

View file

@ -0,0 +1 @@
aes_key=youraeskey

View file

@ -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

View file

@ -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

View file

@ -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<Runnable>(15));
private ThreadPoolExecutor parseQueue = new ThreadPoolExecutor(2, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(15));
private ThreadPoolExecutor injectionQueue = new ThreadPoolExecutor(2, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(15));
private ThreadPoolExecutor executeQueue = new ThreadPoolExecutor(2, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(15));
private ThreadPoolExecutor encryptionQueue = new ThreadPoolExecutor(2, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(15));
private ThreadPoolExecutor replyQueue = new ThreadPoolExecutor(2, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(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);
}
}

View file

@ -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<FinTSCommunicationHandler> runningRequests = new ArrayList<FinTSCommunicationHandler>();
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<String> enums = (Enumeration<String>) 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<FinTSCommunicationHandler> 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());
}
}
}
}

View file

@ -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'";
}
}

View file

@ -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);
}
}

View file

@ -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();
}

View file

@ -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());
}
}
}
}
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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;
}
}

View file

@ -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 <a href="http://hc.apache.org/httpcomponents-core-ga/httpcore-nio/examples/org/apache/http/examples/nio/NHttpServer.java">Apache HttpCore NIO Example</a>
*/
@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<DefaultNHttpServerConnection> 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<HttpRequest> {
HttpResponse response;
HttpAsyncExchange exchange;
public NioSSLHandler() {
super(CommunicationChannel.SSL);
}
@Override
public HttpAsyncRequestConsumer<HttpRequest> 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));
}
}
}

View file

@ -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<NioSocketWorker> runningWorkers = new ArrayList<NioSocketWorker>();
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);
}
}
}
}

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -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<String, KeyManager> 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());
}
}

View file

@ -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};
}
}

View file

@ -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
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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<byte[]> 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];
}
}

View file

@ -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();
}
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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<finalKey.length; i++) {
finalKey[i] = getOddParity(startKey[i%startKey.length]);
}
return finalKey.clone();
}
/**
* Generiert eine ungerade Parität ¼+r das übergebene byte
* Dabei wird das LSB verworfen und durch ein Paritätsbit ersetzt
*
* @param b byte, dem eine ungerade parität gegeben werden soll
* @return byte mit ungerader parität
*/
private byte getOddParity(byte b) {
b = (byte) ((b >>> 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:
//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;
}
}

View file

@ -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;
}
}

File diff suppressed because it is too large Load diff

View file

@ -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
* <p/>
* 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.
* <p/>
* 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<ParameterDataObject> getParameterData(FinTSVersionSwitch.FinTSVersion fintsVersion);
/**
* Liefert Bankparameterdaten als Rückmeldung auf die Dialoginitilisierung
* des Kunden zurück.
* <p/>
* 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
* <p/>
* FinTS 3.0: Formals, E.1 (HIUPD)
*
* @param legitimationInfo Legitimationsinfo zum Benutzer
* @return AccountDataObject-Liste für alle passenden Konten
*/
ArrayList<AccountDataObject> 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<? extends IMessageElement> 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<? extends IMessageElement> 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<String> 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<String> 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<String> 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<BankMessageObject> 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<DirectDebitDataObject> 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<DirectDebitDataObject> 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<Document> 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<camt.052>
*/
List<byte[]> 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<CreditCardRevenueDataObject> 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<RecipientAccountDataObject> getEmpfaengerkontenbestand(LegitimationInfo legitimationsInfo, KontoverbindungInternational kontoverbindungInternational);
}

View file

@ -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<String, DataAccessFacade> 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
* <BLZ>.<FACADEIMPL>.properties
* Sollte keine passende Configdatei gefunden werden, liefert diese Funktion null zurück
* <p/>
* 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;
}
}

View file

@ -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<Connection, Boolean> connections = new Hashtable<>();
public synchronized Connection getConnection() throws SQLException {
Connection con;
Enumeration<Connection> 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;
}
}

View file

@ -0,0 +1,120 @@
package net.petafuel.fuelifints.dataaccess.dataobjects;
/**
* Enthält Daten zu den Konten eines Kunden
* <p/>
* 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 + '\'' +
'}';
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,4 @@
package net.petafuel.fuelifints.dataaccess.dataobjects;
public interface IDataObject {
}

View file

@ -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<String> kommunikationszugaenge;
public String getBankId() {
return bankId;
}
public void setBankId(String bankId) {
this.bankId = bankId;
}
public List<String> getKommunikationszugangsAdressen() {
return kommunikationszugaenge;
}
public void setKommunikationszugaenge(List<String> kommunikationszugaenge) {
this.kommunikationszugaenge = kommunikationszugaenge;
}
public void addKommunikationszugang(String zugangsAdresse) {
if (kommunikationszugaenge == null) {
kommunikationszugaenge = new LinkedList<>();
}
kommunikationszugaenge.add(zugangsAdresse);
}
}

View file

@ -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
* <p/>
* 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 + '\'' +
'}';
}
}

View file

@ -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<GlobalParameter> bankParameters = null;
/**
* Liste aller Geschäftsvorfälle (auch nicht aktive)
*/
private static HashMap<FINTS30, String> geschaeftsvorfaelle = new HashMap<FINTS30, String>()
{{
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<FINTS30, Boolean> pinRequired = null;
private String permissions = "";
/**
* Liste der aktiven Geschäftsvorfälle
* @return FINTS30-Liste
*/
public static List<FINTS30> 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<GlobalParameter> 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<FINTS30, String> 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<FINTS30, Boolean> 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;
}
}
}

View file

@ -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<GeschaeftsvorfallspezifischePinTanInformation> tanInfo = new LinkedList<GeschaeftsvorfallspezifischePinTanInformation>();
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<GeschaeftsvorfallspezifischePinTanInformation> 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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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
* <p/>
* Damit der TanProzess funktionieren kann, müssen folgende Parameter gesetzt werden:
* <p/>
* * 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;
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,4 @@
package net.petafuel.fuelifints.exceptions;
public class DeprecatedVersionHKTAN extends Exception {
}

View file

@ -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);
}
}

View file

@ -0,0 +1,7 @@
package net.petafuel.fuelifints.exceptions;
public class HBCISyntaxException extends Exception {
public HBCISyntaxException(Exception ex) {
super(ex);
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,4 @@
package net.petafuel.fuelifints.exceptions;
public class UnregisteredClientProduct extends Exception {
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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<Message> messages = new ArrayList<Message>();
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;
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,4 @@
package net.petafuel.fuelifints.model;
public interface IDependentElement {
}

View file

@ -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<IMessageElement> getReplyMessageElements();
public IMessageElement getStatusElement();
}

View file

@ -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;
}

View file

@ -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<IMessageElement> messageElements;
private SecurityMethod securityMethod;
private String userId;
private String bankId;
private int segmentCount;
private List<IMessageElement> replyElements = new ArrayList<>();
public Message(int taskId, List<IMessageElement> 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<IMessageElement> 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<IMessageElement> getReplyElements() {
return replyElements;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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<String> 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<String> getParameterChallengeKlasse() {
return parameterChallengeKlasse;
}
public void setParameterChallengeKlasse(List<String> parameterChallengeKlasse) {
this.parameterChallengeKlasse = parameterChallengeKlasse;
}
public boolean isTanUsed() {
return tanUsed;
}
public void setTanUsed(boolean tanUsed) {
this.tanUsed = tanUsed;
}
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -0,0 +1,9 @@
package net.petafuel.fuelifints.protocol;
public class SegmentNotSupportedException extends Exception {
public SegmentNotSupportedException(String segmentName) {
super(segmentName + " not supported");
}
}

View file

@ -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<Dialog> runningDialogs = new ArrayList<Dialog>();
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<Dialog> 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<byte[]> 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<IMessageElement>(), 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<IMessageElement> messageElements = new LinkedList<IMessageElement>();
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;
}
}

View file

@ -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
* <p/>
* 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<byte[]> 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;
}
}

View file

@ -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<IMessageElement> elements = new LinkedList<IMessageElement>();
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<IMessageElement> elements = new LinkedList<IMessageElement>();
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);
}
}

View file

@ -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<IMessageElement> messageElements = msg.getMessageElements();
//AntwortSegmente
List<IMessageElement> 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<IMessageElement> 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<IMessageElement> 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<IMessageElement> 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<IMessageElement> 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);
}
}

View file

@ -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;
}
}

View file

@ -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
* <p/>
* <p/>
* 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<IMessageElement> 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<byte[]> signedSegments = new LinkedList<byte[]>();
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<byte[]> 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;
}
}

View file

@ -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;
}
}

View file

@ -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 {
}

View file

@ -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();
}

View file

@ -0,0 +1,4 @@
package net.petafuel.fuelifints.protocol.fints3.annotations;
public @interface Inject {
}

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -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<Field> degs = new LinkedList<Field>();
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<Field> degs, int segmentVersion) throws IllegalAccessException {
if (stopSkippingUnusedElements) {
return;
}
List<Field> toRemove = new LinkedList<Field>();
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);
}
}

View file

@ -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<KreditkartenUmsatz> 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<KreditkartenUmsatz> getUmsaetze() {
return umsaetze;
}
public void setUmsaetze(List<KreditkartenUmsatz> umsaetze) {
this.umsaetze = umsaetze;
}
}

View file

@ -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<IMessageElement> 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<CreditCardRevenueDataObject> 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<KreditkartenUmsatz> 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<IMessageElement> getReplyMessageElements() {
return replyElements;
}
@Override
public IMessageElement getStatusElement() {
return statusElement;
}
}

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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<byte[]> 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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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> 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<AngabenEmpfaengerkonten> getAngabenEmpfaengerkonten() {
return angabenEmpfaengerkonten;
}
public void setAngabenEmpfaengerkonten(List<AngabenEmpfaengerkonten> angabenEmpfaengerkonten) {
this.angabenEmpfaengerkonten = angabenEmpfaengerkonten;
}
}

View file

@ -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 +
'}';
}
}

Some files were not shown because too many files have changed in this diff Show more