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

org.kapott.cryptalgs.ISO9796p1 Maven / Gradle / Ivy

Go to download

HBCI4j - Home Banking Computer Interface for Java - Clone from https://github.com/hbci4j/hbci4java

There is a newer version: 3.5.46
Show newest version
/*  $Id: ISO9796p1.java,v 1.1 2011/05/04 22:37:58 willuhn Exp $

    This file is part of CryptAlgs4Java
    Copyright (C) 2001-2010  Stefan Palme

    CryptAlgs4Java 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.

    CryptAlgs4Java 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.cryptalgs;

import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import java.util.logging.Logger;

public class ISO9796p1
    extends SignatureSpi {
    private RSAPublicKey pubKey;
    private PrivateKey privKey;
    private MessageDigest dig;
    private SignatureParamSpec param;

    public static byte[] prepareForSig(byte[] buffer, BigInteger bModulus)
        throws SignatureException {
        /* padding; 'cause my buffer is already byte-aligned, there
           are no padding bits to be prepended */
        byte[] mp = new byte[buffer.length];
        System.arraycopy(buffer, 0, mp, 0, buffer.length);

        int k = bModulus.bitLength() - 1;
        int z = mp.length;
        int r = 1;

        if (!((z << 4) <= (k + 3))) {
            throw new SignatureException("16*z is greater than k");
        }

        // extension (concatenate MP multiple times)
        /*double t2=((double)(k))/8;
        if ((k&0x07) != 0)
            t2 += 1.0;
        int t = ((int)(t2)) >> 1;*/
        int t = (k - 1) >> 4;
        if (((k - 1) & 0x0F) != 0)
            t++;
        byte[] me = getMEfromMP(mp, t);

        /* creating redundancy by interleaving the extended message bytes
           with redundancy bytes */
        byte[] mr = getMRfromME(me, t, z, r);

        // get intermediate integer
        byte[] ir = getIRfromMR(mr, k);

        // calculate signature
        byte[] rr = ir; // getRRfromIR(ir, k, modulus, privExponent);

        return rr;
    }

    private static byte[] getISfromSig(byte[] sig, byte[] exp, byte[] mod) {
        return (new BigInteger(+1, sig)).modPow(new BigInteger(+1, exp), new BigInteger(+1, mod)).toByteArray();
    }

    private static byte[] getIRfromIS(byte[] is, byte[] exp, byte[] mod, int[] ks)
        throws SignatureException {
        BigInteger is_b = new BigInteger(+1, is);
        BigInteger mod_b = new BigInteger(+1, mod);
        BigInteger exp_b = new BigInteger(+1, exp);

        BigInteger ret = null;

        if (is_b.mod(new BigInteger("16")).equals(new BigInteger("6")))
            ret = is_b;
        else if (mod_b.subtract(is_b).mod(new BigInteger("16")).equals(new BigInteger("6")))
            ret = mod_b.subtract(is_b);

        if (ret == null && exp_b.mod(new BigInteger("2")).compareTo(new BigInteger("0")) == 0)
            if (is_b.mod(new BigInteger("8")).equals(new BigInteger("3")))
                ret = is_b.multiply(new BigInteger("2"));
            else if (mod_b.subtract(is_b).mod(new BigInteger("8")).equals(new BigInteger("3")))
                ret = mod_b.subtract(is_b).multiply(new BigInteger("2"));

        if (ret == null) {
            throw new SignatureException("can not convert IS to IR");
        }

        byte[] reta = ret.toByteArray();
        int k = reta.length << 3;

        for (int idx = 0, pos = 0; ; ) {
            if ((reta[idx] & (1 << (7 - pos))) != 0)
                break;

            k--;

            if (++pos == 8) {
                pos = 0;
                idx++;
            }
        }
        ks[0] = k;

        if ((reta[reta.length - 1] & 0x0F) != 0x06)
            throw new SignatureException("last nibble is not 0x06");

        return reta;
    }

    private static byte Perm(int b) {
        return (new byte[]{
            0x0E, 0x03, 0x05, 0x08,
            0x09, 0x04, 0x02, 0x0F,
            0x00, 0x0D, 0x0B, 0x06,
            0x07, 0x0A, 0x0C, 0x01})[b];
    }

    private static byte Perm1(int b) {
        return (new byte[]{
            0x08, 0x0F, 0x06, 0x01,
            0x05, 0x02, 0x0B, 0x0C,
            0x03, 0x04, 0x0D, 0x0A,
            0x0E, 0x09, 0x00, 0x07})[b];
    }

    private static byte[] getMRfromIR(byte[] ir, int k, int[] ts) {
        /*
        double t2 = ((double)(k)) / 8;
        if ((k & 0x07) != 0)
            t2 += 1.0;
        int t = ((int)(t2)) >> 1;*/

        int t = (k - 1) >> 4;
        if (((k - 1) & 0x0F) != 0)
            t++;
        ts[0] = t;

        byte[] mr = new byte[2 * t];

        int bitsum = 0;
        for (int i = 0; i < 2 * t; i++) {
            mr[2 * t - 1 - i] = ir[2 * t - 1 - i];
            bitsum += 8;

            if (bitsum >= k)
                mr[2 * t - 1 - i] &= (1 << (7 - (bitsum - k))) - 1;
        }

        mr[2 * t - 1] = (byte) ((Perm1((mr[2 * t - 2] >> 4) & 0x0F) << 4) | ((mr[2 * t - 1] >> 4) & 0x0F));

        return mr;
    }

    private static byte S(int x) {
        return (byte) ((Perm((x >> 4) & 0x0F) << 4) | Perm(x & 0x0F));
    }

    private static byte[] getMPfromMR(byte[] mr, int t, int[] zs, int[] rs)
        throws SignatureException {
        int i;
        for (i = 0; i < t; i++) {
            byte sum = (byte) (S(mr[2 * t - 1 - (2 * i)]) ^ mr[2 * t - 1 - (2 * i + 1)]);

            if (sum != 0) {
                zs[0] = i + 1;
                rs[0] = sum & 0x0F;
                break;
            }
        }

        if (i == t) {
            throw new SignatureException("all sums are 0");
        }

        if (rs[0] < 1 || rs[0] > 8)
            throw new SignatureException("r is not in range 1..8");

        int z = zs[0];
        byte[] mp = new byte[z];
        for (i = 0; i < z; i++) {
            mp[z - 1 - i] = mr[2 * t - 1 - (2 * i)];
        }

        return mp;
    }

    private static byte[] getMEfromMP(byte[] mp, int t) {
        byte[] ret = new byte[t];
        int sum = 0;

        while (sum < t) {
            if (sum + mp.length <= t) {
                System.arraycopy(mp, 0, ret, ret.length - sum - mp.length, mp.length);
                sum += mp.length;
            } else {
                int diff = t - sum;
                System.arraycopy(mp, mp.length - diff, ret, 0, diff);
                sum += diff;
            }
        }

        return ret;
    }

    private static byte[] getMRfromME(byte[] me, int t, int z, int r) {
        byte[] mr = new byte[2 * t];

        for (int i = 0; i < t; i++) {
            mr[2 * t - 1 - (2 * i)] = me[t - 1 - i];
            mr[2 * t - 1 - (2 * i + 1)] = S(me[t - 1 - i]);
        }

        mr[(t - z) << 1] ^= r;

        return mr;
    }

    private static byte[] getIRfromMR(byte[] mr, int k) {
        int len = mr.length;
        byte[] ir = new byte[len];
        System.arraycopy(mr, 0, ir, 0, len);

        ir[0] &= (1 << (7 - ((len << 3) - k))) - 1;
        ir[0] |= (1 << (7 - ((len << 3) - k)));
        ir[len - 1] = (byte) (((ir[len - 1] & 0x0F) << 4) | 0x06);
        return ir;
    }

    private static byte[] getSigFromIS(byte[] is, byte[] modulus) {
        BigInteger bIS = new BigInteger(+1, is);
        BigInteger bModulus = new BigInteger(+1, modulus);
        BigInteger bIS2 = bModulus.subtract(bIS);
        BigInteger bSig = null;

        if (bIS.compareTo(bIS2) < 0)
            bSig = bIS;
        else
            bSig = bIS2;

        return bSig.toByteArray();
    }

    // ---------------------------------------------------------------------------------

    protected Logger getLogger() {
        return Logger.getLogger(this.getClass().getName());
    }

    @Override
    @Deprecated
    protected Object engineGetParameter(String parameter) {
        return null;
    }

    @Override
    protected void engineInitSign(PrivateKey privateKey) {
        try {
            this.dig = MessageDigest.getInstance(this.param.getHashAlg(), this.param.getProvider());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (NoSuchProviderException e) {
            throw new RuntimeException(e);
        }
        this.privKey = privateKey;
    }

    @Override
    protected void engineInitVerify(PublicKey publicKey) {
        try {
            this.dig = MessageDigest.getInstance(this.param.getHashAlg(), this.param.getProvider());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (NoSuchProviderException e) {
            throw new RuntimeException(e);
        }
        this.pubKey = (RSAPublicKey) publicKey;
    }

    @Override
    @Deprecated
    protected void engineSetParameter(String param1, Object value) {
        // do nothing
    }

    @Override
    protected void engineSetParameter(AlgorithmParameterSpec param1)
        throws InvalidAlgorithmParameterException {
        if (param1 instanceof SignatureParamSpec)
            this.param = (SignatureParamSpec) (param1);
        else {
            throw new InvalidAlgorithmParameterException();
        }
    }

    @Override
    protected byte[] engineSign()
        throws SignatureException {
        BigInteger bModulus;
        if (this.privKey instanceof RSAPrivateKey) {
            bModulus = ((RSAPrivateKey) this.privKey).getModulus();
        } else {
            RSAPrivateCrtKey2 key2 = (RSAPrivateCrtKey2) this.privKey;
            bModulus = key2.getP().multiply(key2.getQ());
        }
        byte[] modulus = bModulus.toByteArray();

        byte[] buffer = this.dig.digest();
        byte[] rr = prepareForSig(buffer, bModulus);

        byte[] is;
        if (this.privKey instanceof RSAPrivateKey) {
            getLogger().fine("signing with (n,d)-algorithm");
            BigInteger bPrivExponent = ((RSAPrivateKey) this.privKey).getPrivateExponent();
            BigInteger bIS = (new BigInteger(+1, rr)).modPow(bPrivExponent, bModulus);
            is = bIS.toByteArray();
        } else {
            getLogger().fine("signing with (p,q,dP,dQ,qInv)-algorithm");
            RSAPrivateCrtKey2 key2 = (RSAPrivateCrtKey2) this.privKey;
            BigInteger p = key2.getP();
            BigInteger q = key2.getQ();
            BigInteger dP = key2.getdP();
            BigInteger dQ = key2.getdQ();
            BigInteger qInv = key2.getQInv();

            BigInteger encData = new BigInteger(+1, rr);
            BigInteger m1 = encData.modPow(dP, p);
            BigInteger m2 = encData.modPow(dQ, q);
            BigInteger h = m1.subtract(m2).multiply(qInv).mod(p);
            is = m2.add(q.multiply(h)).toByteArray();
        }

        // adjust value
        byte[] sig = getSigFromIS(is, modulus);

        return sig;
    }

    @Override
    protected int engineSign(byte[] output, int offset, int len)
        throws SignatureException {
        byte[] sig = engineSign();

        if (offset + len > output.length)
            throw new SignatureException("output result too large for buffer");
        System.arraycopy(sig, 0, output, offset, sig.length);
        return sig.length;
    }

    @Override
    protected void engineUpdate(byte b) {
        this.dig.update(b);
    }

    @Override
    protected void engineUpdate(byte[] b, int offset, int length) {
        for (int i = 0; i < length; i++) {
            engineUpdate(b[offset + i]);
        }
    }

    /*
    private int jacobi(BigInteger a,BigInteger n)
    {
        int j=1;

        while (a.compareTo(new BigInteger("0"))!=0) {
            while (a.mod(new BigInteger("2")).compareTo(new BigInteger("0"))==0) {
                a=a.divide(new BigInteger("2"));
                
                BigInteger mod8=n.mod(new BigInteger("8"));
                if (mod8.compareTo(new BigInteger("3"))==0 ||
                    mod8.compareTo(new BigInteger("5"))==0) {
                    j=-j;
                }
            }

            BigInteger temp=a;
            a=n;
            n=temp;

            if (a.mod(new BigInteger("4")).compareTo(new BigInteger("3"))==0 &&
                n.mod(new BigInteger("4")).compareTo(new BigInteger("3"))==0) {
                j=-j;
            }

            a=a.mod(n);
        }

        int ret;
        
        if (n.compareTo(new BigInteger("1"))==0)
            ret=j;
        else
            ret=0;
        
        return ret;
    }*/

    /*private byte[] getRRfromIR(byte[] ir, int k, byte[] modulus, byte[] privExponent)
    {
        byte[] ir2 = new byte[ir.length];
        System.arraycopy(ir, 0, ir2, 0, ir.length);
        ir2[0] &= ((1 << (8 - ((ir.length << 3) - k))) - 1);

        BigInteger bIR = new BigInteger(+1, ir2);
        BigInteger bModulus = new BigInteger(+1, modulus);
        BigInteger bRR = null;

        bRR = bIR;

        return bRR.toByteArray();
    }*/

    @Override
    protected boolean engineVerify(byte[] sig)
        throws SignatureException {
        BigInteger bExponent = this.pubKey.getPublicExponent();
        byte[] exponent = bExponent.toByteArray();
        BigInteger bModulus = this.pubKey.getModulus();
        byte[] modulus = bModulus.toByteArray();

        byte[] is = getISfromSig(sig, exponent, modulus);

        int[] ks = new int[1];
        byte[] ir = getIRfromIS(is, exponent, modulus, ks);
        int k = ks[0];

        int[] ts = new int[1];
        byte[] mr = getMRfromIR(ir, k, ts);
        int t = ts[0];

        int[] zs = new int[1];
        int[] rs = new int[1];
        byte[] mp = getMPfromMR(mr, t, zs, rs);
        int z = zs[0];
        int r = rs[0];

        int datalen = (z << 3) + 1 - r;
        int databytes = (datalen >> 3);
        if ((datalen & 0x07) != 0) {
            databytes++;
        }
        byte[] recHash = new byte[databytes];
        System.arraycopy(mp, mp.length - databytes, recHash, 0, databytes);
        if ((datalen & 0x07) != 0) {
            recHash[0] &= (1 << (datalen & 0x07)) - 1;
        }
        BigInteger hash = new BigInteger(+1, recHash);
        BigInteger hash2 = new BigInteger(+1, this.dig.digest());

        byte[] me2 = getMEfromMP(mp, t);
        byte[] mr2 = getMRfromME(me2, t, z, r);

        mr[0] &= (1 << (7 - ((mr.length << 3) - k))) - 1;
        mr2[0] &= (1 << (7 - ((mr2.length << 3) - k))) - 1;

        return hash.equals(hash2) && Arrays.equals(mr, mr2);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy