/* $Id: AbstractRDHSWPassport.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.math.BigInteger;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import org.kapott.cryptalgs.RSAPrivateCrtKey2;
import org.kapott.hbci.exceptions.HBCI_Exception;
import org.kapott.hbci.manager.HBCIKey;
import org.kapott.hbci.manager.HBCIUtils;
import org.kapott.hbci.manager.HBCIUtilsInternal;
public abstract class AbstractRDHSWPassport
extends AbstractRDHPassport
{
protected HBCIKey[][] keys;
protected AbstractRDHSWPassport(Object init)
{
super(init);
keys=new HBCIKey[3][];
for (int i=0;i<3;i++) {
keys[i]=new HBCIKey[2];
for (int j=0;j<2;j++) {
keys[i][j]=null;
}
}
}
public boolean hasInstSigKey()
{
return getInstSigKey()!=null;
}
public boolean hasInstEncKey()
{
return getInstEncKey()!=null;
}
public boolean hasMySigKey()
{
return getMyPublicSigKey()!=null;
}
public boolean hasMyEncKey()
{
return getMyPublicEncKey()!=null;
}
public HBCIKey getKey(int i,int j)
{
return keys[i][j];
}
public void setInstSigKey(HBCIKey key)
{
setKey(0,0,key);
}
public void setInstEncKey(HBCIKey key)
{
setKey(0,1,key);
}
public void setMySigKey(HBCIKey key)
{
setKey(1,0,key);
setKey(1,1,key);
}
public void setMyEncKey(HBCIKey key)
{
setKey(2,0,key);
setKey(2,1,key);
}
public void setMyDigKey(HBCIKey key)
{
// TODO
}
public void setMyPublicSigKey(HBCIKey key)
{
setKey(1,0,key);
}
public void setMyPrivateSigKey(HBCIKey key)
{
setKey(1,1,key);
}
public void setMyPublicEncKey(HBCIKey key)
{
setKey(2,0,key);
}
public void setMyPrivateEncKey(HBCIKey key)
{
setKey(2,1,key);
}
public void setMyPublicDigKey(HBCIKey key)
{
// TODO
}
public void setMyPrivateDigKey(HBCIKey key)
{
// TODO
}
public HBCIKey getMyPublicSigKey()
{
return getKey(1,0);
}
public HBCIKey getMyPrivateSigKey()
{
return getKey(1,1);
}
public HBCIKey getMyPublicEncKey()
{
return getKey(2,0);
}
public HBCIKey getMyPrivateEncKey()
{
return getKey(2,1);
}
public HBCIKey getMyPublicDigKey()
{
// TODO
return null;
}
public HBCIKey getMyPrivateDigKey()
{
// TODO
return null;
}
public HBCIKey getInstSigKey()
{
return getKey(0,0);
}
public String getInstSigKeyName()
{
return getInstSigKey()!=null?getInstSigKey().userid:null;
}
public String getInstSigKeyNum()
{
return getInstSigKey()!=null?getInstSigKey().num:null;
}
public String getInstSigKeyVersion()
{
return getInstSigKey()!=null?getInstSigKey().version:null;
}
public HBCIKey getInstEncKey()
{
return getKey(0,1);
}
public String getInstEncKeyName()
{
return getInstEncKey()!=null?getInstEncKey().userid:null;
}
public String getInstEncKeyNum()
{
return getInstEncKey()!=null?getInstEncKey().num:null;
}
public String getInstEncKeyVersion()
{
return getInstEncKey()!=null?getInstEncKey().version:null;
}
public String getMySigKeyName()
{
return getMyPublicSigKey()!=null?getMyPublicSigKey().userid:null;
}
public String getMySigKeyNum()
{
return getMyPublicSigKey()!=null?getMyPublicSigKey().num:null;
}
public String getMySigKeyVersion()
{
return getMyPublicSigKey()!=null?getMyPublicSigKey().version:null;
}
public String getMyEncKeyName()
{
return getMyPublicEncKey()!=null?getMyPublicEncKey().userid:null;
}
public String getMyEncKeyNum()
{
return getMyPublicEncKey()!=null?getMyPublicEncKey().num:null;
}
public String getMyEncKeyVersion()
{
return getMyPublicEncKey()!=null?getMyPublicEncKey().version:null;
}
public final void setKey(int i,int j,HBCIKey key)
{
// System.out.println("passportRDH: setting key "+i+","+j+" to "+(key==null?"null":key.country+":"+key.blz+":"+key.cid+":"+key.num+":"+key.version));
keys[i][j]=key;
}
public byte[] sign(byte[] data)
{
/* data is the result from the hash() method. In most cases, this is simply
* the hbci message to be signed, because the signature algorithms used here
* (iso-9796-1, iso-9796-2, pkcs#1-pss) INCLUDE the hash-step, so it must
* not be done manually before.
* the only exception for this is is RDH-10 where an extra round of hashing
* must be done before using PKCS#1-PSS */
try {
Signature sig=getSignatureInstance();
sig.initSign((PrivateKey)(getMyPrivateSigKey().key));
sig.update(data);
byte[] result=sig.sign();
result=checkForCryptDataSize(result, getCryptDataSize(getMyPublicSigKey().key));
return result;
} catch (Exception ex) {
throw new HBCI_Exception("*** signing of message failed",ex);
}
}
public boolean verify(byte[] data,byte[] sig)
{
/* data is the result from the hash() method. In most cases, this is simply
* the hbci message to be signed, because the signature algorithms used here
* (iso-9796-1, iso-9796-2, pkcs#1-pss) INCLUDE the hash-step, so it must
* not be done manually before.
* the only exception for this is is RDH-10 where an extra round of hashing
* must be done before using PKCS#1-PSS */
try {
Signature sign=getSignatureInstance();
sign.initVerify((PublicKey)(getInstSigKey().key));
sign.update(data);
return sign.verify(sig);
} catch (Exception ex) {
throw new HBCI_Exception("*** verification of message signature failed",ex);
}
}
private byte[] encryptMessage(byte[] plainMsg,SecretKey msgkey)
{
try {
Cipher cipher=Cipher.getInstance("DESede/CBC/NoPadding");
byte[] iv=new byte[8];
Arrays.fill(iv,(byte)(0));
IvParameterSpec spec=new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE,msgkey,spec);
return cipher.doFinal(plainMsg);
} catch (Exception ex) {
throw new HBCI_Exception("*** can not encrypt message",ex);
}
}
private byte[] encryptKey(SecretKey msgkey)
{
try {
// schluessel als byte-array abspeichern
SecretKeyFactory factory=SecretKeyFactory.getInstance("DESede");
DESedeKeySpec spec=(DESedeKeySpec)(factory.getKeySpec(msgkey,DESedeKeySpec.class));
byte[] plainKey=spec.getKey(); // plainKey ist der DESede-Key
// abh�ngig von der L�nge des inst-enc-keys
int cryptDataSize=getCryptDataSize(getInstEncKey().key);
byte[] plainText=new byte[cryptDataSize];
Arrays.fill(plainText,(byte)(0));
System.arraycopy(plainKey,0,plainText,plainText.length-16,16);
BigInteger m=new BigInteger(+1,plainText);
Key k=getInstEncKey().key;
BigInteger c=m.modPow(((RSAPublicKey)(k)).getPublicExponent(),
((RSAPublicKey)(k)).getModulus());
byte[] result=c.toByteArray();
result=checkForCryptDataSize(result, cryptDataSize);
return result;
} catch (Exception ex) {
throw new HBCI_Exception("*** can not encrypt message key",ex);
}
}
public byte[][] encrypt(byte[] plainMsg)
{
try {
SecretKey msgkey=createMsgKey();
byte[] cryptMsg=encryptMessage(plainMsg,msgkey);
byte[] cryptKey=encryptKey(msgkey);
byte[][] ret=new byte[2][];
ret[0]=cryptKey;
ret[1]=cryptMsg;
return ret;
} catch (Exception ex) {
throw new HBCI_Exception("*** error while encrypting",ex);
}
}
public byte[] decrypt(byte[] cryptedKey,byte[] cryptedMsg)
{
try {
// key entschluesseln
Key k=getMyPrivateEncKey().key;
byte[] plainKey;
if (k instanceof RSAPrivateKey) {
HBCIUtils.log("decrypting message key with (n,d)-algorithm",HBCIUtils.LOG_DEBUG);
BigInteger exponent=((RSAPrivateKey)(k)).getPrivateExponent();
BigInteger modulus=((RSAPrivateKey)(k)).getModulus();
BigInteger c=new BigInteger(+1,cryptedKey);
plainKey=c.modPow(exponent,modulus).toByteArray();
} else {
HBCIUtils.log("decrypting message key with (p,q,dP,dQ,qInv)-algorithm",HBCIUtils.LOG_DEBUG);
BigInteger p=((RSAPrivateCrtKey2)k).getP();
BigInteger q=((RSAPrivateCrtKey2)k).getQ();
BigInteger dP=((RSAPrivateCrtKey2)k).getdP();
BigInteger dQ=((RSAPrivateCrtKey2)k).getdQ();
BigInteger qInv=((RSAPrivateCrtKey2)k).getQInv();
BigInteger c=new BigInteger(+1,cryptedKey);
BigInteger m1=c.modPow(dP,p);
BigInteger m2=c.modPow(dQ,q);
BigInteger h=m1.subtract(m2).multiply(qInv).mod(p);
plainKey=m2.add(q.multiply(h)).toByteArray();
}
byte[] realPlainKey=new byte[24];
System.arraycopy(plainKey,plainKey.length-16,realPlainKey,0,16);
System.arraycopy(plainKey,plainKey.length-16,realPlainKey,16,8);
DESedeKeySpec spec=new DESedeKeySpec(realPlainKey);
SecretKeyFactory fac=SecretKeyFactory.getInstance("DESede");
SecretKey key=fac.generateSecret(spec);
// nachricht entschluesseln
Cipher cipher=Cipher.getInstance("DESede/CBC/NoPadding");
byte[] ivarray=new byte[8];
Arrays.fill(ivarray,(byte)(0));
IvParameterSpec iv=new IvParameterSpec(ivarray);
cipher.init(Cipher.DECRYPT_MODE,key,iv);
return cipher.doFinal(cryptedMsg);
} catch (Exception ex) {
throw new HBCI_Exception("*** error while decrypting message",ex);
}
}
private int getKeySizeByProfile()
{
int ret=-1;
int profile=Integer.parseInt(getProfileVersion());
switch (profile) {
case 1:
ret=768;
break;
case 2:
ret=2048;
break;
case 10:
HBCIKey k=getInstSigKey();
if (k==null) {
k=getInstEncKey();
}
if (k!=null) {
RSAPublicKey pkey=(RSAPublicKey)k.key;
ret = pkey.getModulus().bitLength();
} else {
ret=4096;
}
break;
default:
throw new HBCI_Exception("*** dont know which keysize to use for profile rdh-"+profile);
}
HBCIUtils.log("using keysize "+ret+" bits for newly generated keys",HBCIUtils.LOG_DEBUG);
return ret;
}
public HBCIKey[][] generateNewUserKeys()
{
HBCIKey[] newSigKey=null;
HBCIKey[] newEncKey=null;
try {
HBCIUtils.log("generating new user keys",HBCIUtils.LOG_INFO);
String blz=getBLZ();
String country=getCountry();
String userid=getUserId();
String profileVersion=getProfileVersion();
newSigKey=new HBCIKey[2];
newEncKey=new HBCIKey[2];
String num=hasMySigKey()?getMyPublicSigKey().num:profileVersion;
String version=hasMySigKey()?getMyPublicSigKey().version:"0";
version=Integer.toString(Integer.parseInt(version)+1);
int keySize=getKeySizeByProfile();
// TODO: auch dig key neu generieren?
for (int i=0;i<2;i++) {
KeyPairGenerator keygen=KeyPairGenerator.getInstance("RSA");
// die schl�ssell�nge ist vom sicherheitsprofil abh�ngig
keygen.initialize(keySize);
KeyPair pair=keygen.generateKeyPair();
if (i==0) {
newSigKey[0]=new HBCIKey(country,blz,userid,num,version,pair.getPublic());
newSigKey[1]=new HBCIKey(country,blz,userid,num,version,pair.getPrivate());
} else {
newEncKey[0]=new HBCIKey(country,blz,userid,num,version,pair.getPublic());
newEncKey[1]=new HBCIKey(country,blz,userid,num,version,pair.getPrivate());
}
}
} catch (Exception ex) {
throw new HBCI_Exception(HBCIUtilsInternal.getLocMsg("EXCMSG_GENKEYS_ERR"),ex);
}
HBCIKey[][] ret=new HBCIKey[3][];
ret[0]=newSigKey;
ret[1]=newEncKey;
// TODO: dig keys
ret[2]=null;
return ret;
}
}