org.kapott.hbci.smartcardio.ChipTanCardService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hbci4j-core Show documentation
Show all versions of hbci4j-core Show documentation
HBCI4j - Home Banking Computer Interface for Java
/**********************************************************************
*
* This file is part of HBCI4Java.
* Copyright (c) 2018 Olaf Willuhn
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
**********************************************************************/
package org.kapott.hbci.smartcardio;
import java.io.ByteArrayOutputStream;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import org.kapott.hbci.exceptions.HBCI_Exception;
import org.kapott.hbci.manager.HBCIUtils;
/**
* Implementierung des Kartenservice fuer das Verfahren chipTAN-USB.
*/
public class ChipTanCardService extends SmartCardService
{
/**
* Sendet das HHDuc an den Kartenleser und liefert die eingegebene TAN zurueck.
* @param hhduc das HHDuc.
* @return die TAN. Oder NULL, wenn sie nicht zur Uebertragung ausgewaehlt wurde.
*/
public String getTan(String hhduc)
{
try
{
Integer feature = this.getFeatures().get(Feature.FEATURE_MCT_READER_DIRECT);
if (feature != null)
return this.getTanScardControl(hhduc);
else
return this.getTanScardTransmit(hhduc);
}
catch (HBCI_Exception e)
{
throw e;
}
catch (Exception e2)
{
throw new HBCI_Exception(e2);
}
}
/**
* Sendet das HHDuc per ScardControl an den Kartenleser und liefert die eingegebene TAN zurueck.
* @param hhduc das HHDuc.
* @return die TAN. Oder NULL, wenn sie nicht zur Uebertragung ausgewaehlt wurde.
* @throws Exception
*/
public String getTanScardControl(String hhduc) throws Exception
{
String tan = null;
//////////////////////////////////////////////////////////////////////////
// SECODER TRANSMIT HHDuc (Secoder 3G)
{
// Flickercode als Byte-Array rendern
byte[] hhdBytes = this.toBytes(hhduc);
HBCIUtils.log("sending HHDuc to chipcard using ScardControl, length: " + hhdBytes.length,HBCIUtils.LOG_DEBUG);
int len = 4 + 1 + 1 + 2 + hhdBytes.length; // Laenge von Activation ID, POF, Control Byte, HHD-Laenge, Payload
ByteArrayOutputStream cmd = new ByteArrayOutputStream();
cmd.write(new byte[]{(byte)0x20,(byte)0x76,(byte)0x00,(byte)0x00}); // Header
cmd.write(new byte[]{0x00,0x00,(byte) len}); // Li Lc Lc
cmd.write(new byte[]{0x00,0x00,0x00,0x00}); // Activation ID
cmd.write(0x01); // POF
cmd.write(0x00); // Control Byte
cmd.write(new byte[]{0x00,(byte) hhdBytes.length}); // HHD Laenge - kann eh nie laenger als 255 Zeichen sein - von daher reicht ein Byte aus
cmd.write(hhdBytes); // Payload
cmd.write(new byte[]{0x00,0x00}); // Le Le
byte[] request = cmd.toByteArray();
// "transmitControlCommand" verwendet intern "ScardControl"
// "getBasicChannel().transmit()" verwendet intern "ScardTransmit"
byte[] response = this.getCard().transmitControlCommand(this.getFeatures().get(Feature.FEATURE_MCT_READER_DIRECT),request);
ResponseAPDU apdu = new ResponseAPDU(response);
this.check(apdu,new byte[]{(byte)0x90,(byte)0x91});
byte[] data = apdu.getData();
// Es ist durchaus moeglich, dass der User bei der Frage "TAN uebertragen" abgebrochen hat.
// Dann haben wir auch keine TAN, die automatisch eingegeben werden kann.
// Checken, wie lang die TAN ist
if (data != null && data.length > 1)
{
int tanLength = data[0];
HBCIUtils.log("received TAN",HBCIUtils.LOG_DEBUG);
if (tanLength > 0)
{
byte[] tanBytes = new byte[tanLength];
System.arraycopy(data,1,tanBytes,0,tanLength);
tan = this.parseTAN(tanBytes);
HBCIUtils.log("TAN length: " + tan.length() + " chars",HBCIUtils.LOG_DEBUG);
}
}
}
//
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// SECODER FINALIZE TRANSACTION (SECODER 3G)
// Transaktion finalisieren
try
{
HBCIUtils.log("finalizing chipcard transaction",HBCIUtils.LOG_DEBUG);
ByteArrayOutputStream cmd = new ByteArrayOutputStream();
cmd.write(new byte[]{0x20,0x77,0x00,0x00}); // Header
cmd.write(new byte[]{0x00,0x00,0x06}); // Li Lc Lc
cmd.write(new byte[]{0x00,0x00,0x00,0x00}); // Activation ID
cmd.write(0x00); // POF
cmd.write(0x00); // Control Byte
cmd.write(new byte[]{0x00,0x00}); // Le Le
byte[] response = this.getCard().transmitControlCommand(this.getFeatures().get(Feature.FEATURE_MCT_READER_DIRECT),cmd.toByteArray());
ResponseAPDU apdu = new ResponseAPDU(response);
// Wir tolerieren hier auch 0x6D. Das liefert der Kartenleser,
// wenn er das Kommando nicht unterstuetzt. Die TAN haben wir
// ja bereits. Wenn das finalize danach nicht unterstuetzt wird,
// lassen wir das durchgehen.
this.check(apdu,new byte[]{(byte)0x90,(byte)0x91, (byte) 0x6D});
}
catch (Exception e)
{
// Den Fehler tolerieren wir
HBCIUtils.log("error while finalizing transaction, continue nevertheless since we already have a TAN - stacktrace for debugging purpose",HBCIUtils.LOG_DEBUG);
HBCIUtils.log(e,HBCIUtils.LOG_DEBUG);
}
HBCIUtils.log("returning TAN",HBCIUtils.LOG_DEBUG);
return tan;
//
//////////////////////////////////////////////////////////////////////////
}
/**
* Sendet das HHDuc per ScardTransmit an den Kartenleser und liefert die eingegebene TAN zurueck.
* @param hhduc das HHDuc.
* @return die TAN. Oder NULL, wenn sie nicht zur Uebertragung ausgewaehlt wurde.
* @throws Exception
*/
public String getTanScardTransmit(String hhduc) throws Exception
{
String tan = null;
//////////////////////////////////////////////////////////////////////////
// SECODER TRANSMIT HHDuc (Secoder 3G)
{
// Flickercode als Byte-Array rendern
byte[] hhdBytes = this.toBytes(hhduc);
HBCIUtils.log("sending HHDuc to chipcard using ScardTransmit, length: " + hhdBytes.length,HBCIUtils.LOG_DEBUG);
int len = 4 + 1 + 1 + 2 + hhdBytes.length; // Laenge von Activation ID, POF, Control Byte, HHD-Laenge, Payload
ByteArrayOutputStream cmd = new ByteArrayOutputStream();
cmd.write(new byte[]{(byte)0xFF,(byte)0x91,(byte)0x06,(byte)0x00}); // Header
cmd.write(new byte[]{0x00,0x00,(byte) len}); // Li Lc Lc
cmd.write(new byte[]{0x00,0x00,0x00,0x00}); // Activation ID
cmd.write(0x01); // POF
cmd.write(0x00); // Control Byte
cmd.write(new byte[]{0x00,(byte) hhdBytes.length}); // HHD Laenge - kann eh nie laenger als 255 Zeichen sein - von daher reicht ein Byte aus
cmd.write(hhdBytes); // Payload
cmd.write(new byte[]{0x00,0x00}); // Le Le
byte[] request = cmd.toByteArray();
CommandAPDU apdu = new CommandAPDU(request);
byte[] data = this.receive(apdu,new byte[]{(byte)0x90,(byte)0x91});
// Es ist durchaus moeglich, dass der User bei der Frage "TAN uebertragen" abgebrochen hat.
// Dann haben wir auch keine TAN, die automatisch eingegeben werden kann.
// Checken, wie lang die TAN ist
if (data != null && data.length > 1)
{
int tanLength = data[0];
HBCIUtils.log("received TAN",HBCIUtils.LOG_DEBUG);
if (tanLength > 0)
{
byte[] tanBytes = new byte[tanLength];
System.arraycopy(data,1,tanBytes,0,tanLength);
tan = this.parseTAN(tanBytes);
HBCIUtils.log("TAN length: " + tan.length() + " chars",HBCIUtils.LOG_DEBUG);
}
}
}
//
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// SECODER FINALIZE TRANSACTION (SECODER 3G)
// Transaktion finalisieren
try
{
HBCIUtils.log("finalizing chipcard transaction",HBCIUtils.LOG_DEBUG);
ByteArrayOutputStream cmd = new ByteArrayOutputStream();
cmd.write(new byte[]{(byte)0xFF,(byte)0x91,0x07,0x00}); // Header
cmd.write(new byte[]{0x00,0x00,0x06}); // Li Lc Lc
cmd.write(new byte[]{0x00,0x00,0x00,0x00}); // Activation ID
cmd.write(0x00); // POF
cmd.write(0x00); // Control Byte
cmd.write(new byte[]{0x00,0x00}); // Le Le
byte[] request = cmd.toByteArray();
// Wir tolerieren hier auch 0x6D. Das liefert der Kartenleser,
// wenn er das Kommando nicht unterstuetzt. Die TAN haben wir
// ja bereits. Wenn das finalize danach nicht unterstuetzt wird,
// lassen wir das durchgehen.
CommandAPDU apdu = new CommandAPDU(request);
this.receive(apdu,new byte[]{(byte)0x90,(byte)0x91,(byte)0x6D,(byte)0x01});
}
catch (Exception e)
{
// Den Fehler tolerieren wir
HBCIUtils.log("error while finalizing transaction, continue nevertheless since we already have a TAN - stacktrace for debugging purpose",HBCIUtils.LOG_DEBUG);
HBCIUtils.log(e,HBCIUtils.LOG_DEBUG);
}
HBCIUtils.log("returning TAN",HBCIUtils.LOG_DEBUG);
return tan;
//
//////////////////////////////////////////////////////////////////////////
}
/**
* Konvertiert die BCD-codierte TAN in einen String.
* @param bytes die Bytes.
* @return der String.
*/
public String parseTAN(byte[] bytes)
{
StringBuilder sb = new StringBuilder();
for(int i=0;i>4); // mit 0xf0 UNDen, um das linke Halbbyte zu erhalten und dann 4 Bit nach rechts verschieben
byte b2 = (byte)(bytes[i] & 0x0f); // mit 0x0f UNDen, um das rechte Halbbyte zu erhalten
sb.append(b1);
// Wenn die TAN eine ungerade Anzahl Stellen hat, wird das letzte Halbbyte
// mit "F" belegt.
if (i+1 == bytes.length && b2 == 0x0f)
break;
sb.append(b2);
}
return sb.toString();
}
}