Package org.kapott.hbci.passport.rdhXfile

Source Code of org.kapott.hbci.passport.rdhXfile.RDHXFile

/*  $Id: RDHXFile.java,v 1.1 2011/05/04 22:37:48 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.rdhXfile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.kapott.cryptalgs.PBKDF2;
import org.kapott.hbci.exceptions.HBCI_Exception;
import org.kapott.hbci.exceptions.InvalidPassphraseException;
import org.kapott.hbci.manager.HBCIKey;
import org.kapott.hbci.manager.HBCIUtils;
import org.kapott.hbci.passport.rdhXfile.HBCIAccount.UserKeys;

public class RDHXFile
{
    private List<TLV>   fields;
    private byte[] passphrase;
   
    public RDHXFile(byte[] passphrase)
    {
        this.fields=new ArrayList<TLV>();
        this.passphrase=passphrase;
    }
   
    public RDHXFile(byte[] data, byte[] passphrase)
    {
        this(passphrase);
       
        // alle felder extrahieren und zu filecontent-content hinzuf�gen
        int posi=0;
        int len=data.length;
        while (posi<len) {
            TLV  tlv=new TLV(data,posi);
            long tag=tlv.getTag();
           
            if (tag==0x4e56L) {
                //   diskhead (tag=0x564e, len=26|28) (1..1)
                HBCIUtils.log("found diskhead field",HBCIUtils.LOG_INTERN);
                tlv=new FileHeader(tlv);
               
            } else if (tag==0x564bL) {
                //   bankdata (tag=0x4b56, len=..) (0..n)
                HBCIUtils.log("found hbciaccount field",HBCIUtils.LOG_INTERN);
                tlv=new HBCIAccount(tlv);
               
            } else if (tag==0x53d6L) {
                //   bankkeys (tag=0xd653, len=..) (0..n)
                HBCIUtils.log("found bankkeys field",HBCIUtils.LOG_INTERN);
                tlv=new BankKeys(tlv);
               
            } else if (tag==0x4452L) {
                //   datum (tag=0x5244, len=14) (1..1)
                HBCIUtils.log("found date field",HBCIUtils.LOG_INTERN);
                tlv=new DateField(tlv);
               
            } else if (tag==0x4d44L) {
                //   mac (tag=0x444d, len=20) (1..1)
                HBCIUtils.log("found mac field",HBCIUtils.LOG_INTERN);
                tlv=new MACField(tlv);
               
            } else {
                throw new HBCI_Exception("*** invalid field tag found: 0x"+Long.toString(tlv.getTag(),16));
            }
           
            addField(tlv);
            posi+=4+tlv.getLength();
        }
       
        if (getField(FileHeader.class)==null) {
            // ohne header-field geht gar nichts
            throw new HBCI_Exception("*** RDH-2/10-file does not contain a header field - aborting");
        }
       
        MACField macfield=(MACField)getField(MACField.class);
        if (macfield!=null) {
            byte[]  storedMac=macfield.getMac();
            byte[]  calculatedMac=calculateMAC();
            boolean macOK=Arrays.equals(storedMac,calculatedMac);
            HBCIUtils.log("MAC field ok: "+macOK,HBCIUtils.LOG_DEBUG);
            if (!macOK) {
                throw new InvalidPassphraseException();
            }
        } else {
            HBCIUtils.log("RDH-2/10-file does not contain a MAC field - ignoring this for now",HBCIUtils.LOG_ERR);
        }

        // decrypt private user keys
        try {
          // calculate decryption key
                FileHeader       fileHeader=(FileHeader)getField(FileHeader.class);
                String           algname=(fileHeader.getProfileVersion()==2)?"HmacSHA1":"HmacSHA256";
          byte[]           derivedKey=deriveKey(24, algname);
          SecretKeyFactory keyfac=SecretKeyFactory.getInstance("DESede");
          DESedeKeySpec    desKeyspec=new DESedeKeySpec(derivedKey);
          SecretKey        key=keyfac.generateSecret(desKeyspec);

          // loop through all userkeys to decrypt them
          TLV[] accounts=getFields(HBCIAccount.class);
          for (int i=0;i<accounts.length;i++) {
            HBCIAccount account=(HBCIAccount)accounts[i];
            List<UserKeys>        userkeys=account.getUserKeys();

            for (Iterator<UserKeys> j=userkeys.iterator();j.hasNext();) {
              HBCIAccount.UserKeys userkey= j.next();
              userkey.decrypt(key);
              HBCIUtils.log(userkey.toString(),HBCIUtils.LOG_INTERN);
            }
          }
        } catch (Exception e) {
          throw new HBCI_Exception(e);
        }
    }
   
    public byte[] getPassphrase()
    {
      return this.passphrase;
    }
   
    public void setPassphrase(byte[] passphrase)
    {
      this.passphrase = passphrase;
    }
   
    public void addField(TLV field)
    {
        this.fields.add(field);
    }
   
    public TLV getField(Class cl)
    {
        TLV ret=null;
       
        for (Iterator<TLV> i=fields.iterator();i.hasNext();) {
            TLV tlv= i.next();
            if (tlv.getClass().equals(cl)) {
                ret=tlv;
                break;
            }
        }
       
        return ret;
    }

    public TLV[] getFields(Class cl)
    {
        List<TLV> ret=new ArrayList<TLV>();
       
        for (Iterator<TLV> i=fields.iterator();i.hasNext();) {
            TLV tlv= i.next();
            if (tlv.getClass().equals(cl)) {
                ret.add(tlv);
            }
        }
       
        return ret.toArray(new TLV[ret.size()]);
    }

    public byte[] getFileData(int profileVersion)
    {
        // update DateField
        DateField dateField=(DateField)getField(DateField.class);
        if (dateField==null) {
            dateField=new DateField();
            addField(dateField);
        }
        dateField.setDate(new Date());
       
        // check for existance of required fields
        // HeaderField
        FileHeader headerField=(FileHeader)getField(FileHeader.class);
        if (headerField==null) {
            headerField=new FileHeader();
            addField(headerField);
           
            headerField.setNofIterations(10000);
            headerField.setRandomSalt();
            headerField.setVersion(1);
        }
        headerField.setProfileVersion(profileVersion);
       
        // MACField
        MACField macField=(MACField)getField(MACField.class);
        if (macField==null) {
            macField=new MACField();
            addField(macField);
        }
       
        // encrypt private user keys
        try {
                // calculate encryption key
                FileHeader       fileHeader=(FileHeader)getField(FileHeader.class);
                String           algname=(fileHeader.getProfileVersion()==2)?"HmacSHA1":"HmacSHA256";
                byte[]           derivedKey=deriveKey(24, algname);
                SecretKeyFactory keyfac=SecretKeyFactory.getInstance("DESede");
                DESedeKeySpec    desKeyspec=new DESedeKeySpec(derivedKey);
                SecretKey        key=keyfac.generateSecret(desKeyspec);

                // loop through all userkeys to decrypt them
                TLV[] accounts=getFields(HBCIAccount.class);
                for (int i=0;i<accounts.length;i++) {
                        HBCIAccount account=(HBCIAccount)accounts[i];
                        List<UserKeys>        userkeys=account.getUserKeys();

                        for (Iterator<UserKeys> j=userkeys.iterator();j.hasNext();) {
                                HBCIAccount.UserKeys userkey= j.next();
                                userkey.encrypt(key);
                        }
                }
        } catch (Exception e) {
                throw new HBCI_Exception(e);
        }

        // reorder fields to standard order
        List<TLV> newFields=new ArrayList<TLV>();
        Class[] order=new Class[] {FileHeader.class, HBCIAccount.class,
                                   BankKeys.class, DateField.class,
                                   MACField.class};
        for (int i=0; i<order.length; i++) {
            Class c=order[i];
            TLV[] fields=getFields(c);
            newFields.addAll(Arrays.asList(fields));
        }
        this.fields = newFields;

        // update all rawdata fields
        for (Iterator<TLV> i=this.fields.iterator();i.hasNext();) {
            TLV tlv= i.next();
            tlv.updateData();
        }

        // update mac field
        byte[] mac=calculateMAC();
        MACField macfield=(MACField)getField(MACField.class);
        macfield.setMac(mac);
       
        // collect filedata
        try {
            ByteArrayOutputStream os=new ByteArrayOutputStream();
           
            for (Iterator<TLV> i=this.fields.iterator();i.hasNext();) {
                TLV tlv= i.next();
                os.write(tlv.getRawData());
            }
           
            byte[] ret=os.toByteArray();
            os.close();
            return ret;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
   
    public HBCIKey getBankSigKey(HBCIAccount account)
    {
        return getBankKey(account,"S");
    }
   
    public void setBankSigKey(HBCIAccount account, HBCIKey key)
    {
        setBankKey(account,"S",key);
    }
   
    public HBCIKey getBankEncKey(HBCIAccount account)
    {
        return getBankKey(account,"V");
    }
   
    public void setBankEncKey(HBCIAccount account, HBCIKey key)
    {
        setBankKey(account,"V",key);
    }
   
    private HBCIKey getBankKey(HBCIAccount account,String keytype)
    {
        HBCIKey ret=null;
        String  blz=account.getBLZ();
        String  country=account.getCountry();
       
        TLV[] keyfields=getFields(BankKeys.class);
        for (int i=0;i<keyfields.length;i++) {
            BankKeys bankkeys=(BankKeys)keyfields[i];
           
            if (bankkeys.getCountry().equals(country) &&
                    bankkeys.getBLZ().equals(blz) &&
                    bankkeys.getKeyType().equals(keytype))
            {
                ret=bankkeys.getHBCIKey();
                break;
            }
        }
       
        return ret;
    }
   
    private void setBankKey(HBCIAccount account,String keytype,HBCIKey key)
    {
        if (key!=null) {
            String  blz=account.getBLZ();
            String  country=account.getCountry();

            TLV[]   keyfields=getFields(BankKeys.class);
            boolean found=false;
            for (int i=0;i<keyfields.length;i++) {
                BankKeys bankkeys=(BankKeys)keyfields[i];

                if (bankkeys.getCountry().equals(country) &&
                        bankkeys.getBLZ().equals(blz) &&
                        bankkeys.getKeyType().equals(keytype))
                {
                    bankkeys.setKey(keytype,key);
                    account.setKeyStatus((byte)(account.getKeyStatus()|0x04));
                    found=true;
                    break;
                }
            }

            if (!found) {
                BankKeys bankkeys=new BankKeys();
                addField(bankkeys);

                bankkeys.setCountry(account.getCountry());
                bankkeys.setBLZ(account.getBLZ());
                bankkeys.setKey(keytype,key);
                account.setKeyStatus((byte)(account.getKeyStatus()|0x04));
            }
        }
    }
   
    private byte[] getHashData()
    {
        try {
            ByteArrayOutputStream os=new ByteArrayOutputStream();
           
            for (Iterator<TLV> i=this.fields.iterator();i.hasNext();) {
                TLV tlv= i.next();
                if (!tlv.getClass().equals(MACField.class)) {
                    tlv.updateData();
                    os.write(tlv.getRawData());
                }
            }
           
            byte[] ret=os.toByteArray();
            os.close();
            return ret;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
   
    private byte[] deriveKey(int dkLen, String algname)
    {
      // key ableiten
        FileHeader diskhead=(FileHeader)getField(FileHeader.class);
        HBCIUtils.log(
            "calculating key with alg "+algname+" and length "+dkLen,
            HBCIUtils.LOG_DEBUG);
       
        byte[] derivedKey=PBKDF2.deriveKey(diskhead.getSalt(),
                                           diskhead.getNofIterations(),
                                           getPassphrase(),
                                           dkLen, algname);
        return derivedKey;
    }
   
    private byte[] calculateMAC()
    {
        try {
            FileHeader header=(FileHeader)getField(FileHeader.class);
            int        pversion=header.getProfileVersion();
           
            int keysize=(pversion==2)?20:32;
            HBCIUtils.log("using "+keysize+"-byte-key for MAC calculation", HBCIUtils.LOG_DEBUG);

            // TODO: HmacSHA256 is available only in Java-6++
            String algname=(pversion==2)?"HmacSHA1":"HmacSHA256";
            HBCIUtils.log("MAC algorithm is "+algname, HBCIUtils.LOG_DEBUG);
           
            byte[] derivedKey=deriveKey(keysize, algname);
           
            SecretKeySpec keyspec=new SecretKeySpec(derivedKey, algname);
            Mac           mac=Mac.getInstance(algname);
            mac.init(keyspec);
           
            byte[] hashdata=getHashData();
            byte[] calculatedMac=mac.doFinal(hashdata);

            return calculatedMac;
        } catch (Exception e) {
            throw new HBCI_Exception(e);
        }
    }
}
TOP

Related Classes of org.kapott.hbci.passport.rdhXfile.RDHXFile

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.