org.kapott.cryptalgs.ISO9796p2 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hbci4j-adorsys Show documentation
Show all versions of hbci4j-adorsys Show documentation
HBCI4j - Home Banking Computer Interface for Java - Clone from https://github.com/hbci4j/hbci4java
/* $Id: ISO9796p2.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 ISO9796p2
extends SignatureSpi {
private RSAPublicKey pubKey;
private PrivateKey privKey;
private MessageDigest dig;
private SignatureParamSpec param;
private static byte[] getSigFromISig(BigInteger iSig, BigInteger modulus) {
// Es wird der kleinere Wert von sig und n-sig verwendet
BigInteger iSig2 = modulus.subtract(iSig);
BigInteger sig = null;
if (iSig.compareTo(iSig2) < 0)
sig = iSig;
else
sig = iSig2;
return sig.toByteArray();
}
private static BigInteger getJfromSig(BigInteger sig, BigInteger exp, BigInteger mod) {
return sig.modPow(exp, mod);
}
// entweder x oder n-x zurueckgeben
private static BigInteger adjustJ(BigInteger J, BigInteger modulus) {
byte[] ba = J.toByteArray();
int last = ba[ba.length - 1];
if ((last & 0x0F) == 0x0C) {
return J;
}
BigInteger twelve = new BigInteger("12");
byte[] modulus2 = modulus.subtract(twelve).toByteArray();
int last2 = modulus2[modulus2.length - 1];
if ((last & 0x0F) == (last2 & 0x0F)) {
return modulus.subtract(J);
}
return null;
}
protected Logger getLogger() {
return Logger.getLogger(this.getClass().getName());
}
@Override
@Deprecated
protected Object engineGetParameter(String parameter) {
return null;
}
@Override
protected void engineInitSign(PrivateKey privateKey) {
String provider = this.param.getProvider();
try {
if (provider != null) {
this.dig = MessageDigest.getInstance(this.param.getHashAlg(), provider);
} else {
this.dig = MessageDigest.getInstance(this.param.getHashAlg());
}
} 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 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]);
}
}
// -----------------------------------------------------------------------
@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 byte[] engineSign() {
BigInteger bModulus;
if (this.privKey instanceof RSAPrivateKey) {
bModulus = ((RSAPrivateKey) this.privKey).getModulus();
} else {
RSAPrivateCrtKey2 key2 = (RSAPrivateCrtKey2) this.privKey;
bModulus = key2.getP().multiply(key2.getQ());
}
// Hash-Wert holen
byte[] H = this.dig.digest();
// System.out.println("H = " + ba2st(H));
// ein paar "Eckdaten"
int k = bModulus.bitLength();
int t = 1;
int T = 0xBC;
int Lh = H.length << 3;
// System.out.println("k (len of modulus) = "+ k +" bits");
// System.out.println("t (octets in trailer field) = "+t);
// System.out.println("T (trailer field) = 0xBC");
// System.out.println("Lh (length of hash) = " + Lh);
// System.out.println("c (capacity of signature) = k - Lh - 8t - 4 = "+(k-Lh-8*t-4)+" bits");
// System.out.println("M = M1 = M2 = ''");
// der padded String besteht aus ein paar Indikator-Bits, dem Hashwert,
// eigentlich noch dem recoverable Teil der Message und Padding-Bits
int padded_size = k >> 3;
if ((k & 0x07) != 0) {
padded_size++;
}
// System.out.println("length of padded string will be "+padded_size+" bytes");
byte[] paddedM = new byte[padded_size];
// Ende-Zeichen 0xBC anfuegen
paddedM[padded_size - 1] = (byte) T;
// davor kommt der Hashwert
System.arraycopy(H, 0, paddedM, padded_size - 1 - H.length, H.length);
// davor kommen 8 bytes "message"
// nichts zu tun, wir schreiben einfach 0-bytes rein
// davor kommt der Indikator, dass die Padding-Bytes hier zu Ende sind
// laut spez werden 0-nibbled durch 0xb ersetzt, hier aber scheinbar nicht
// paddedM[padded_size-1-H.length-8-1] = (byte)0xBA;
paddedM[padded_size - 1 - H.length - 8 - 1] = (byte) 0x01;
// davor kommen padding-bits und -bytes
int nof_zero_bits = k - Lh - 8 * 8 - 8 * t - 4;
// System.out.println("number of zero bits for padding: "+nof_zero_bits);
// es werden schon 5 bits im ersten byte und nochmal 7 bits im letzten
// byte fuers padding gebraucht, also:
int nof_zero_bytes = (nof_zero_bits - 5 - 7) >> 3;
// System.out.println("number of zero bytes: "+((nof_zero_bits-5-7)/8.0));
byte[] zero_bytes = new byte[nof_zero_bytes];
// laut spez werden 0-nibbled durch 0xb ersetzt, hier aber scheinbar nicht
// Arrays.fill(zero_bytes,(byte)0xBB);
Arrays.fill(zero_bytes, (byte) 0x00);
System.arraycopy(zero_bytes, 0, paddedM, padded_size - 1 - H.length - 8 - 1 - nof_zero_bytes, nof_zero_bytes);
// das erste Byte besteht aus den Bits 01, einem Indikator-Bit fuer
// partielles Recovery und ein paar Padding-Bits
// laut spez werden 0-nibbled durch 0xb ersetzt, hier aber scheinbar nicht
// paddedM[padded_size-1-H.length-8-1-nof_zero_bytes-1] = (byte)0x6B;
paddedM[padded_size - 1 - H.length - 8 - 1 - nof_zero_bytes - 1] = (byte) 0x60;
// System.out.println("padded string = "+ba2st(paddedM));
// F ist eigentlich paddedM ohne das highest nibble...
byte[] F = paddedM;
// hier jetzt die mathematische Operation mit der Integer-Interpretation
// von F durchfuehren
BigInteger iSig;
if (this.privKey instanceof RSAPrivateKey) {
getLogger().fine("signing with (n,d)-algorithm");
BigInteger bPrivExponent = ((RSAPrivateKey) this.privKey).getPrivateExponent();
iSig = (new BigInteger(+1, F)).modPow(bPrivExponent, bModulus);
} 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, F);
BigInteger m1 = encData.modPow(dP, p);
BigInteger m2 = encData.modPow(dQ, q);
BigInteger h = m1.subtract(m2).multiply(qInv).mod(p);
iSig = m2.add(q.multiply(h));
}
// adjust value
return getSigFromISig(iSig, bModulus);
}
@Override
protected boolean engineVerify(byte[] sig) {
// System.out.println("verifying signature "+ba2st(sig));
BigInteger S = new BigInteger(+1, sig);
BigInteger exponent = this.pubKey.getPublicExponent();
BigInteger modulus = this.pubKey.getModulus();
// es wird sig^exp mod modulus gerechnet
BigInteger J_ = getJfromSig(S, exponent, modulus);
// hier wird als ergebnis entweder x oder n-x verwendet, entsprechend
// dem letzten Schritt bei der erzeugung der signatur
BigInteger I_ = adjustJ(J_, modulus);
if (I_ == null) {
getLogger().severe("neither x nor n-x are valid signatures");
return false;
}
// message representative F* ist die Bitfolgen-Interpretation von I*
byte[] F_ = I_.toByteArray();
// System.out.println("decrypted signature is "+ba2st(F_));
int last = F_[F_.length - 1] & 0xFF;
if (last != 0xBC) {
getLogger().severe("last nibble is not 0xBC");
}
byte[] hash = this.dig.digest();
// System.out.println("real hash of message is "+ba2st(hash));
byte[] hash_ = new byte[hash.length];
System.arraycopy(F_, F_.length - 1 - hash.length, hash_, 0, hash.length);
// System.out.println("recovered hash of message is "+ba2st(hash_));
return Arrays.equals(hash, hash_);
}
}