/* $Id: AbstractRDHPassport.java,v 1.1 2011/05/04 22:37:43 willuhn Exp $
This file is part of HBCI4Java
Copyright (C) 2001-2008 Stefan Palme
HBCI4Java is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
HBCI4Java is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.kapott.hbci.passport;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import org.kapott.cryptalgs.SignatureParamSpec;
import org.kapott.hbci.comm.Comm;
import org.kapott.hbci.exceptions.HBCI_Exception;
import org.kapott.hbci.manager.HBCIUtils;
import org.kapott.hbci.security.Crypt;
import org.kapott.hbci.security.Sig;
public abstract class AbstractRDHPassport
extends AbstractHBCIPassport
{
protected AbstractRDHPassport(Object init)
{
super(init);
}
public String getPassportTypeName()
{
return "RDH";
}
public boolean isSupported()
{
boolean ret=false;
if (getBPD()!=null) {
String[][] methods=getSuppSecMethods();
String passportVersion=getProfileVersion();
boolean noPassportVersion=(passportVersion.length()==0);
for (int i=0;i<methods.length;i++) {
String method=methods[i][0];
String version=methods[i][1];
if (method.equals("RDH") &&
(noPassportVersion || passportVersion.equals(version)))
{
// ein RDH-Passport gilt dann als "unterst�tzt", wenn in den
// BPD der SecMech "RDH" auftaucht, und dabei die RDH-Profilnummer
// aus den BPD mit der gew�nschten RDH-Profilnummer des
// Passports �bereinstimmt.
// Es reicht auch nur die �bereinstimmung auf "RDH", wenn
// das Passport seine Profil-Nummer noch nicht wei� ("").
ret=true;
break;
}
}
} else {
ret=true;
}
return ret;
}
public Comm getCommInstance()
{
return Comm.getInstance("Standard",this);
}
public String getSysStatus()
{
return "1";
}
public boolean needInstKeys()
{
return true;
}
public boolean needUserKeys()
{
return true;
}
public boolean needUserSig()
{
return false;
}
public String getProfileMethod()
{
return "RDH";
}
public String getCryptKeyType()
{
return Crypt.ENC_KEYTYPE_RSA;
}
public String getSigFunction()
{
String ret;
// TODO: hier auch h�here hbci-versionen ber�cksichtigen
if (getHBCIVersion().equals("300")) {
// TODO: bei verwendung der digisig hier anderen wert zur�ckgeben
ret=Sig.SECFUNC_FINTS_SIG_SIG;
} else {
ret=Sig.SECFUNC_HBCI_SIG_RDH;
}
HBCIUtils.log("using sig function "+ret,HBCIUtils.LOG_DEBUG2);
return ret;
}
public String getSigAlg()
{
return Sig.SIGALG_RSA;
}
public String getSigMode()
{
int profile=Integer.parseInt(getProfileVersion());
String ret=null;
switch (profile) {
case 1:
ret=Sig.SIGMODE_ISO9796_1;
break;
case 2:
ret=Sig.SIGMODE_ISO9796_2;
break;
case 10:
ret=Sig.SIGMODE_PSS;
break;
default:
// TODO das sp�ter vom security profile abh�ngig machen
// RDH3: ISO9796-(1|2)
// RDH4: PKCS1
// RDH5: PKCS1
throw new HBCI_Exception("*** dont know which sigmode to use for profile rdh-"+profile);
}
HBCIUtils.log("using sig mode "+ret,HBCIUtils.LOG_DEBUG2);
return ret;
}
public String getCryptFunction()
{
return Crypt.SECFUNC_ENC_3DES;
}
public String getCryptAlg()
{
return Crypt.ENCALG_2K3DES;
}
public String getCryptMode()
{
int profile=Integer.parseInt(getProfileVersion());
String ret=null;
switch (profile) {
case 1:
ret=Crypt.ENCMODE_CBC;
break;
case 2:
ret=Crypt.ENCMODE_CBC;
break;
case 10:
ret=Crypt.ENCMODE_CBC;
break;
default:
// TODO das sp�ter vom security profile abh�ngig machen
// RDH3: PKCS1
// RDH4: PKCS1
// RDH5: PKCS1
throw new HBCI_Exception("*** dont know which cryptmode to use for profile rdh-"+profile);
}
HBCIUtils.log("using crypt mode "+ret,HBCIUtils.LOG_DEBUG2);
return ret;
}
protected int getCryptDataSize(Key key)
{
int profile=Integer.parseInt(getProfileVersion());
int ret=-1;
switch (profile) {
case 1:
case 2:
case 10:
int bits=((RSAPublicKey)key).getModulus().bitLength();
int bytes=bits>>3;
if ((bits&0x07)!=0) {
bytes++;
}
ret=bytes;
break;
default:
// TODO das sp�ter vom security profile abh�ngig machen
throw new HBCI_Exception("*** dont know which crypt data size to use for profile rdh-"+profile);
}
HBCIUtils.log("using crypt data size "+ret,HBCIUtils.LOG_DEBUG2);
return ret;
}
public String getHashAlg()
{
int profile=Integer.parseInt(getProfileVersion());
String ret=null;
switch (profile) {
case 1:
ret=Sig.HASHALG_RIPEMD160;
break;
case 2:
ret=Sig.HASHALG_RIPEMD160;
break;
case 10:
ret=Sig.HASHALG_SHA256_SHA256;
break;
default:
// TODO das sp�ter vom security profile abh�ngig machen
// RDH3: RIPE/SHA
// RDH4: SHA
// RDH5: SHA
throw new HBCI_Exception("*** dont know which hashalg to use for profile rdh-"+profile);
}
HBCIUtils.log("using hash alg "+ret,HBCIUtils.LOG_DEBUG2);
return ret;
}
public SignatureParamSpec getSignatureParamSpec()
{
int profile=Integer.parseInt(getProfileVersion());
String hashalg=null;
String hashprovider=null;
switch (profile) {
case 1:
hashalg="RIPEMD160";
hashprovider="CryptAlgs4Java";
break;
case 2:
hashalg="RIPEMD160";
hashprovider="CryptAlgs4Java";
break;
case 10:
hashalg="SHA-256";
// hashprovider=null;
break;
default:
// TODO das sp�ter vom security profile abh�ngig machen
throw new HBCI_Exception("*** dont know which hash instance to use for profile rdh-"+profile);
}
HBCIUtils.log("using hash instance "+hashalg+"/"+hashprovider,HBCIUtils.LOG_DEBUG2);
return new SignatureParamSpec(hashalg, hashprovider);
}
protected Signature getSignatureInstance()
{
int profile=Integer.parseInt(getProfileVersion());
String sigalg=null;
String sigprovider=null;
switch (profile) {
case 1:
sigalg="ISO9796p1";
sigprovider="CryptAlgs4Java";
break;
case 2:
sigalg="ISO9796p2";
sigprovider="CryptAlgs4Java";
break;
case 10:
sigalg="PKCS1_PSS";
sigprovider="CryptAlgs4Java";
break;
default:
// TODO das sp�ter vom security profile abh�ngig machen
throw new HBCI_Exception("*** dont know which signature instance to use for profile rdh-"+profile);
}
HBCIUtils.log("using sig instance "+sigalg+"/"+sigprovider,HBCIUtils.LOG_DEBUG2);
try {
Signature sig=Signature.getInstance(sigalg, sigprovider);
sig.setParameter(getSignatureParamSpec());
return sig;
} catch (Exception ex) {
throw new HBCI_Exception("*** signing of message failed",ex);
}
}
protected SecretKey createMsgKey()
{
try {
KeyGenerator generator=KeyGenerator.getInstance("DESede");
SecretKey key=generator.generateKey();
SecretKeyFactory factory=SecretKeyFactory.getInstance("DESede");
DESedeKeySpec spec=(DESedeKeySpec)(factory.getKeySpec(key,DESedeKeySpec.class));
byte[] bytes=spec.getKey();
System.arraycopy(bytes,0,bytes,16,8);
spec=new DESedeKeySpec(bytes);
key=factory.generateSecret(spec);
return key;
} catch (Exception ex) {
throw new HBCI_Exception("*** can not create message key",ex);
}
}
public byte[] hash(byte[] data)
{
/* data is the plaintext message to be signed. In most cases, we do NOT
* have to execute an explicit hashing here, because most signature algorithms
* already INCLUDE their own hashing step. The only exception is RDH-10,
* where we have to sign the HASH value, not the HBCI message itself, so
* in this case we have to execute one round of hashing here */
byte[] result=data;
if (getHashAlg().equals(Sig.HASHALG_SHA256_SHA256)) {
// only if we need the double-round-hash thing
MessageDigest dig;
try {
dig = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
result=dig.digest(data);
}
return result;
}
}