All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kapott.hbci.security.Sig Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version

/*  $Id: Sig.java,v 1.2 2012/03/27 21:33:13 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.security;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Random;

import org.kapott.hbci.comm.Comm;
import org.kapott.hbci.exceptions.HBCI_Exception;
import org.kapott.hbci.manager.HBCIUtils;
import org.kapott.hbci.manager.HBCIUtilsInternal;
import org.kapott.hbci.manager.IHandlerData;
import org.kapott.hbci.manager.MsgGen;
import org.kapott.hbci.passport.HBCIPassportInternal;
import org.kapott.hbci.passport.HBCIPassportList;
import org.kapott.hbci.protocol.MSG;
import org.kapott.hbci.protocol.MultipleSEGs;
import org.kapott.hbci.protocol.MultipleSyntaxElements;
import org.kapott.hbci.protocol.SEG;
import org.kapott.hbci.protocol.SyntaxElement;
import org.kapott.hbci.protocol.factory.SEGFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

public final class Sig
{
    public final static String SECFUNC_HBCI_SIG_RDH="1";
    public final static String SECFUNC_HBCI_SIG_DDV="2";
    
    public final static String SECFUNC_FINTS_SIG_DIG="1";
    public final static String SECFUNC_FINTS_SIG_SIG="2";
    
    public final static String SECFUNC_SIG_PT_1STEP="999";
    public final static String SECFUNC_SIG_PT_2STEP_MIN="900";
    public final static String SECFUNC_SIG_PT_2STEP_MAX="997";

    public final static String HASHALG_SHA1="1";
    public final static String HASHALG_SHA256="3";
    public final static String HASHALG_SHA384="4";
    public final static String HASHALG_SHA512="5";
    public final static String HASHALG_SHA256_SHA256="6";
    public final static String HASHALG_RIPEMD160="999";
    
    public final static String SIGALG_DES="1";
    public final static String SIGALG_RSA="10";
    
    public final static String SIGMODE_ISO9796_1="16";
    public final static String SIGMODE_ISO9796_2="17";
    public final static String SIGMODE_PKCS1="18";
    public final static String SIGMODE_PSS="19";
    public final static String SIGMODE_RETAIL_MAC="999";

    private IHandlerData     handlerdata;
    private MSG              msg;
    private HBCIPassportList passports;

    private String u_secfunc;
    private String u_cid;
    private String u_role;
    private String u_range;
    private String u_keyblz;
    private String u_keycountry;
    private String u_keyuserid;
    private String u_keynum;
    private String u_keyversion;
    private String u_sysid;
    private String u_sigid;
    private String u_sigalg;
    private String u_sigmode;
    private String u_hashalg;
    private String sigstring;
    
    private void initData(IHandlerData handlerdata, MSG msg, HBCIPassportList passports)
    {
        this.msg = msg;
        this.handlerdata = handlerdata;
        this.passports = passports;
    }

    public Sig(IHandlerData handlerdata, MSG msg, HBCIPassportList passports) 
    {
        initData(handlerdata,msg,passports);
    }

    public void init(IHandlerData handlerdata, MSG msg, HBCIPassportList passports)
    {
        initData(handlerdata,msg,passports);
    }

    // sighead-segment mit werten aus den lokalen variablen füllen
    private void fillSigHead(SEG sighead)
    {
        String sigheadName = sighead.getPath();
        String seccheckref = Integer.toString(Math.abs(new Random().nextInt()));

        Date d=new Date();

        sighead.propagateValue(sigheadName + ".secfunc",u_secfunc,
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        sighead.propagateValue(sigheadName + ".seccheckref", seccheckref,
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        /* TODO: enable this later (when other range types are supported)
             sighead.propagateValue(sigheadName+".range",range,false); */
        sighead.propagateValue(sigheadName + ".role", u_role,
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        sighead.propagateValue(sigheadName+".SecIdnDetails.func",(msg.getName().endsWith("Res")?"2":"1"),
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        if (u_cid.length()!=0) {
            // DDV
            sighead.propagateValue(sigheadName + ".SecIdnDetails.cid", "B"+u_cid,
                    SyntaxElement.DONT_TRY_TO_CREATE,
                    SyntaxElement.DONT_ALLOW_OVERWRITE);
        } else {
            // RDH und PinTan
            sighead.propagateValue(sigheadName + ".SecIdnDetails.sysid", u_sysid,
                    SyntaxElement.DONT_TRY_TO_CREATE,
                    SyntaxElement.DONT_ALLOW_OVERWRITE);
        }
        sighead.propagateValue(sigheadName + ".SecTimestamp.date", HBCIUtils.date2StringISO(d),
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        sighead.propagateValue(sigheadName + ".SecTimestamp.time", HBCIUtils.time2StringISO(d),
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        
        sighead.propagateValue(sigheadName + ".secref", u_sigid,
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        
        sighead.propagateValue(sigheadName + ".HashAlg.alg",u_hashalg,
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        sighead.propagateValue(sigheadName + ".SigAlg.alg", u_sigalg,
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        sighead.propagateValue(sigheadName + ".SigAlg.mode", u_sigmode,
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        
        sighead.propagateValue(sigheadName + ".KeyName.KIK.country", u_keycountry,
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        sighead.propagateValue(sigheadName + ".KeyName.KIK.blz", u_keyblz,
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        sighead.propagateValue(sigheadName + ".KeyName.userid", u_keyuserid,
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        sighead.propagateValue(sigheadName + ".KeyName.keynum", u_keynum,
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);
        sighead.propagateValue(sigheadName + ".KeyName.keyversion", u_keyversion,
                SyntaxElement.DONT_TRY_TO_CREATE,
                SyntaxElement.DONT_ALLOW_OVERWRITE);

        sighead.propagateValue(sigheadName + ".SecProfile.method", passports.getMainPassport().getProfileMethod(),
            SyntaxElement.DONT_TRY_TO_CREATE,
            SyntaxElement.DONT_ALLOW_OVERWRITE);
        sighead.propagateValue(sigheadName + ".SecProfile.version", passports.getMainPassport().getProfileVersion(),
            SyntaxElement.DONT_TRY_TO_CREATE,
            SyntaxElement.DONT_ALLOW_OVERWRITE);
    }

    // sigtail-segment mit werten aus den lokalen variablen füllen
    private void fillSigTail(SEG sighead, SEG sigtail)
    {
        String sigtailName = sigtail.getPath();

        sigtail.propagateValue(sigtailName + ".seccheckref",
                               sighead.getValueOfDE(sighead.getPath() + ".seccheckref"),
                               SyntaxElement.DONT_TRY_TO_CREATE,
                               SyntaxElement.DONT_ALLOW_OVERWRITE);
    }

    /* daten zusammensammeln, die signiert werden müssen; idx gibt dabei an,
     * die wievielte signatur erzeugt werden soll - wird benötigt, um festzustellen,
     * welche sighead- und sigtail-segmente in die signatur eingehen */
    private String collectHashData(int idx)
    {
        int          numOfPassports=passports.size();
        StringBuffer ret=new StringBuffer(1024);

        List msgelementslist = msg.getChildContainers();
        List sigheads = ((MultipleSEGs)(msgelementslist.get(1))).getElements();
        List sigtails = ((MultipleSEGs)(msgelementslist.get(msgelementslist.size() - 2))).getElements();

        // alle benötigten sighead-segmente zusammensuchen
        for (int i=numOfPassports-1-idx; i<(u_range.equals("1")?(numOfPassports-idx):numOfPassports);i++) {
            ret.append(((SEG)(sigheads.get(i))).toString(0));
        }

        // alle nutzdaten hinzufügen
        for (int i=2; i msgelements=msg.getChildContainers();
                        List sigheads=((MultipleSEGs)(msgelements.get(1))).getElements();
                        List sigtails=((MultipleSEGs)(msgelements.get(msgelements.size()-2))).getElements();

                        // insert sighead segment in msg
                        if ((numOfPassports-1-idx) msgelements=msg.getChildContainers();
                        List sigheads=((MultipleSEGs)(msgelements.get(1))).getElements();
                        List sigtails=((MultipleSEGs)(msgelements.get(msgelements.size()-2))).getElements();

                        SEG sighead=(SEG)sigheads.get(numOfPassports-1-idx);
                        SEG sigtail=(SEG)sigtails.get(idx);
                        
                        fillSigHead(sighead);
                        fillSigTail(sighead,sigtail);
                    }
                     
                    msg.enumerateSegs(0,SyntaxElement.ALLOW_OVERWRITE);
                    msg.validate();
                    msg.enumerateSegs(1,SyntaxElement.ALLOW_OVERWRITE);

                    // calculate signatures for each segment
                    for (int idx=0;idx                 msgelements=msg.getChildContainers();
                        List                 sigtails=((MultipleSEGs)(msgelements.get(msgelements.size()-2))).getElements();
                        SEG                  sigtail=(SEG)sigtails.get(idx);

                        /* first calculate hash-result, then sign the hashresult. In
                         * most cases, the hash() step will be executed by the signature
                         * algorithm, so the hash() call returns the message as-is.
                         * Currently the only exception is PKCS#1-10, where an extra
                         * round of hashing must be executed before applying the 
                         * signature process */
                        String hashdata=collectHashData(idx);
                        byte[] hashresult=passport.hash(hashdata.getBytes(Comm.ENCODING));
                        byte[] signature=passport.sign(hashresult);

                        if (passport.needUserSig()) {
                            String pintan=new String(signature,Comm.ENCODING);
                            int pos=pintan.indexOf("|");
                            
                            if (pos!=-1) {
                                // wenn überhaupt eine signatur existiert
                                // (wird für server benötigt)
                                String pin=pintan.substring(0,pos);
                                msg.propagateValue(sigtail.getPath()+".UserSig.pin",pin,
                                        SyntaxElement.DONT_TRY_TO_CREATE,
                                        SyntaxElement.DONT_ALLOW_OVERWRITE);
                                
                                if (pos values=new Hashtable();
            msg.extractValues(values);
            
            String pin=values.get(msg.getName()+".SigTail.UserSig.pin");
            String tan=values.get(msg.getName()+".SigTail.UserSig.tan");
            
            sigstring=((pin!=null)?pin:"")+"|"+((tan!=null)?tan:"");
        } else {
            sigstring = msg.getValueOfDE(msg.getName() + ".SigTail.sig");
        }

        String checkref=msg.getValueOfDE(msg.getName()+".SigHead.seccheckref");
        String checkref2=msg.getValueOfDE(msg.getName()+".SigTail.seccheckref");

        if (checkref==null || !checkref.equals(checkref2)) {
            String errmsg=HBCIUtilsInternal.getLocMsg("EXCMSG_SIGREFFAIL");
            if (!HBCIUtilsInternal.ignoreError(null,"client.errors.ignoreSignErrors",errmsg))
                throw new HBCI_Exception(errmsg);
        }
        
        // TODO: dieser test ist erst mal deaktiviert. grund: beim pin/tan-zwei-
        // schritt-verfahren ist die passport.getSigFunction()==922 (z.B.). 
        // wenn jedoch zeitgleich HITAN über eine bankensignatur abgesichert
        // wird, steht in der antwort secfunc=1 (RDH) drin. 
        /*
        if (!u_secfunc.equals(mainPassport.getSigFunction())) {
            String errmsg=HBCIUtilsInternal.getLocMsg("EXCMSG_SIGTYPEFAIL",new String[] {u_secfunc,mainPassport.getSigFunction()});
            if (!HBCIUtilsInternal.ignoreError(null,"client.errors.ignoreSignErrors",errmsg))
                throw new HBCI_Exception(errmsg);
        }
        */
        
        // TODO: hier auch die DEG SecProfile lesen und überprüfen
        
        // TODO: diese checks werden vorerst abgeschaltet, damit die pin-tan sigs
        // ohne probleme funktionieren
        /*
        if (!u_sigalg.equals(passport.getSigAlg()))
            throw new HBCI_Exception(HBCIUtils.getLocMsg("EXCMSG_SIGALGFAIL",new String[] {u_sigalg,passport.getSigAlg()}));
        if (!u_sigmode.equals(passport.getSigMode()))
            throw new HBCI_Exception(HBCIUtils.getLocMsg("EXCMSG_SIGMODEFAIL",new String[] {u_sigmode,passport.getSigMode()}));
        if (!u_hashalg.equals(passport.getHashAlg()))
            throw new HBCI_Exception(HBCIUtils.getLocMsg("EXCMSG_SIGHASHFAIL",new String[] {u_hashalg,passport.getHashAlg()}));
        */
    }

    private boolean hasSig()
    {
        boolean ret = true;
        MultipleSyntaxElements seglist = (msg.getChildContainers().get(1));

        if (seglist instanceof MultipleSEGs) {
            SEG sighead = null;
            try {
                /* TODO: multiple signatures not supported until now */
                sighead = (SEG)(seglist.getElements().get(0));
            } catch (IndexOutOfBoundsException e) {
                ret = false;
            }

            if (ret) {
                String sigheadCode = "HNSHK";
            	MsgGen gen=handlerdata.getMsgGen();

                if (!sighead.getCode(gen).equals(sigheadCode))
                    ret = false;
            }
        }
        else ret = false;

        return ret;
    }

    public boolean verify()
    {
        HBCIPassportInternal mainPassport=passports.getMainPassport();
        boolean              ret=false;

        if (mainPassport.hasInstSigKey()) {
            String msgName = msg.getName();
            MsgGen gen=handlerdata.getMsgGen();
            Node msgNode = msg.getSyntaxDef(msgName, gen.getSyntax());
            String dontsignAttr = ((Element)msgNode).getAttribute("dontsign");

            if (dontsignAttr.length()==0) {
                if (hasSig()) {
                    readSigHead();
                    try {
                        /* first calculate hash-result, then verify the hashresult. In
                         * most cases, the hash() step will be executed by the signature
                         * algorithm, so the hash() call returns the message as-is.
                         * Currently the only exception is PKCS#1-10, where an extra
                         * round of hashing must be executed before applying the 
                         * signature process */
                        String hashdata=collectHashData(0,0);
                        byte[] hashresult=mainPassport.hash(hashdata.getBytes(Comm.ENCODING));
                        ret=mainPassport.verify(hashresult, sigstring.getBytes(Comm.ENCODING));
                    } catch (Exception e) {
                        ret=false;
                    }
                } else {
                    HBCIUtils.log("message has no signature",HBCIUtils.LOG_WARN);
                    /* das ist nur für den fall, dass das institut prinzipiell nicht signiert
                       (also für den client-code);
                       die verify()-funktion für den server-code überprüft selbstständig, ob
                       tatsächlich eine benötigte signatur vorhanden ist (verlässt sich also nicht
                       auf dieses TRUE, was beim fehlen einer signatur zurückgegeben wird */
                    ret=true;
                }
            } else {
                HBCIUtils.log("message does not need a signature",HBCIUtils.LOG_DEBUG);
                ret=true;
            }
        } else {
            HBCIUtils.log("can not check signature - no signature key available",HBCIUtils.LOG_WARN);
            ret=true;
        }

        return ret;
    }

    public void setParam(String key, String value)
    {
        try {
            Field f=this.getClass().getDeclaredField("u_"+key);
            HBCIUtils.log("setting "+key+" to "+value,HBCIUtils.LOG_DEBUG);
            f.set(this,value);
        } catch (Exception ex) {
            throw new HBCI_Exception("*** error while setting sig parameter",ex);
        }
    }
    
    public void destroy()
    {
        handlerdata=null;
        msg=null;
        passports=null;
        sigstring=null;
        u_cid=null;
        u_hashalg=null;
        u_keyblz=null;
        u_keycountry=null;
        u_keynum=null;
        u_keyuserid=null;
        u_keyversion=null;
        u_range=null;
        u_role=null;
        u_secfunc=null;
        u_sigalg=null;
        u_sigid=null;
        u_sigmode=null;
        u_sysid=null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy