/* $Id: PKCS1_15.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.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidParameterException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureSpi;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
public class PKCS1_15
extends SignatureSpi
{
private RSAPublicKey pubKey;
private PrivateKey privKey;
private SignatureParamSpec param;
private ByteArrayOutputStream plainmsg;
// ----- some interface stuff ---------------------------------------------
@Override
@Deprecated
protected void engineSetParameter(String param1, Object value)
throws InvalidParameterException
{
// do nothing
}
@Override
protected void engineSetParameter(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException
{
if (params instanceof SignatureParamSpec)
this.param=(SignatureParamSpec)(params);
else {
throw new InvalidAlgorithmParameterException();
}
}
@Override
@Deprecated
protected Object engineGetParameter(String param1)
throws InvalidParameterException
{
return null;
}
public static MessageDigest getMessageDigest(SignatureParamSpec spec)
{
MessageDigest result;
try {
String provider=spec.getProvider();
if (provider!=null) {
result=MessageDigest.getInstance(spec.getHashAlg(),provider);
} else {
result=MessageDigest.getInstance(spec.getHashAlg());
}
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (NoSuchProviderException e) {
throw new RuntimeException(e);
}
return result;
}
@Override
protected void engineInitSign(PrivateKey privateKey)
{
this.privKey=privateKey;
this.plainmsg=new ByteArrayOutputStream();
}
@Override
protected void engineInitVerify(PublicKey publicKey)
{
this.pubKey=(RSAPublicKey)publicKey;
this.plainmsg=new ByteArrayOutputStream();
}
@Override
protected void engineUpdate(byte b)
{
this.plainmsg.write(b);
}
@Override
protected void engineUpdate(byte[] b, int off, int len)
{
for (int i=0;i<len;i++) {
engineUpdate(b[off+i]);
}
}
@Override
protected byte[] engineSign()
{
return sign(this.param, this.privKey, this.plainmsg.toByteArray());
}
@Override
protected boolean engineVerify(byte[] sigBytes)
{
return verify(this.param, this.pubKey, this.plainmsg.toByteArray(), sigBytes);
}
// --------------------------------------------------------------------
private static byte[] i2osp(BigInteger x, int len)
{
byte[] bytes=x.toByteArray();
if (bytes.length>len) {
// created output len does not fit into outLen
// maybe this are only leading zeroes, so we will check this
for (int i=0; i<(bytes.length-len); i++) {
if (bytes[i]!=0) {
throw new RuntimeException("value too large");
}
}
// ok, now remove leading zeroes
byte[] out=new byte[len];
System.arraycopy(bytes, bytes.length-len, out, 0, len);
bytes = out;
} else if (bytes.length<len) {
// created output is too small, so create leading zeroes
byte[] out=new byte[len];
System.arraycopy(bytes, 0, out, len-bytes.length, bytes.length);
bytes = out;
}
return bytes;
}
private static BigInteger os2ip(byte[] bytes)
{
return new BigInteger(+1, bytes);
}
private static BigInteger sp1(PrivateKey key, BigInteger m)
{
BigInteger result;
if (key instanceof RSAPrivateKey) {
BigInteger d=((RSAPrivateKey)key).getPrivateExponent();
BigInteger n=((RSAPrivateKey)key).getModulus();
result = m.modPow(d,n);
} else {
RSAPrivateCrtKey2 key2=(RSAPrivateCrtKey2)key;
BigInteger p=key2.getP();
BigInteger q=key2.getQ();
BigInteger dP=key2.getdP();
BigInteger dQ=key2.getdQ();
BigInteger qInv=key2.getQInv();
BigInteger s1 = m.modPow(dP,p);
BigInteger s2 = m.modPow(dQ,q);
BigInteger h = s1.subtract(s2).multiply(qInv).mod(p);
result = s2.add(q.multiply(h));
}
return result;
}
private static BigInteger vp1(RSAPublicKey key, BigInteger s)
{
BigInteger e=key.getPublicExponent();
BigInteger n=key.getModulus();
BigInteger m=s.modPow(e,n);
return m;
}
private static byte[] sign(SignatureParamSpec spec, PrivateKey privKey, byte[] msg)
{
BigInteger bModulus;
if (privKey instanceof RSAPrivateKey) {
bModulus=((RSAPrivateKey)privKey).getModulus();
} else {
bModulus=((RSAPrivateCrtKey2)privKey).getP().multiply(((RSAPrivateCrtKey2)privKey).getQ());
}
int modBits = bModulus.bitLength();
int k = modBits>>3;
if ((modBits&7)!=0) {
k++;
}
byte[] EM=emsa_encode(spec, msg, k);
BigInteger m=os2ip(EM);
BigInteger s=sp1(privKey, m);
byte[] S=i2osp(s, k);
return S;
}
private static boolean verify(SignatureParamSpec spec, PublicKey pubKey, byte[] msg, byte[] signature)
{
// TODO: check if signature.len == pubKey.getModulus().len
int modBits=((RSAPublicKey)pubKey).getModulus().bitLength();
int k=modBits>>3;
if ((modBits&0x07)!=0) {
k++;
}
// System.out.println("modBits: "+modBits+", k="+k);
// System.out.println("signature: "+Utils.bytes2String(signature));
BigInteger s=os2ip(signature);
BigInteger m=vp1((RSAPublicKey)pubKey, s);
byte[] EM2=i2osp(m, k);
// System.out.println("decoded EM2: "+Utils.bytes2String(EM2));
byte[] EM=emsa_encode(spec, msg, k);
// System.out.println("encoded EM: "+Utils.bytes2String(EM));
return Arrays.equals(EM, EM2);
}
private static byte[] hash(SignatureParamSpec spec, byte[] msg)
{
MessageDigest dig=getMessageDigest(spec);
return dig.digest(msg);
}
private static byte[] getHashAlgOID(SignatureParamSpec spec)
{
byte[] result;
if (spec.getHashAlg().equals("SHA-1")) {
result=new byte[] {(byte)0x2b, (byte)0x0e, (byte)0x03, (byte)0x02, (byte)0x1a};
} else if (spec.getHashAlg().equals("SHA-256")) {
result=new byte[] {
(byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01,
(byte)0x65, (byte)0x03, (byte)0x04, (byte)0x02,
(byte)0x01};
} else {
throw new IllegalArgumentException("dont know OID for "+spec.getHashAlg());
}
return result;
}
public static byte[] createDigestInfo(SignatureParamSpec spec, byte[] hash)
{
// write digest info
ByteArrayOutputStream digestInfoS=new ByteArrayOutputStream();
byte[] digestInfo_hashAlg=getHashAlgOID(spec);
try {
// sequence: outer wrapper
digestInfoS.write(0x30);
digestInfoS.write(8+digestInfo_hashAlg.length+hash.length);
// sequence hash info
digestInfoS.write(0x30);
digestInfoS.write(4+digestInfo_hashAlg.length);
// oid hashalg
digestInfoS.write(0x06);
digestInfoS.write(digestInfo_hashAlg.length);
digestInfoS.write(digestInfo_hashAlg);
// zero (no hashalg params)
digestInfoS.write(0x05);
digestInfoS.write(0x00);
// hash value
digestInfoS.write(0x04);
digestInfoS.write(hash.length);
digestInfoS.write(hash);
} catch (IOException e1) {
throw new RuntimeException(e1);
}
byte[] digestInfo=digestInfoS.toByteArray();
return digestInfo;
}
private static byte[] emsa_encode(SignatureParamSpec spec, byte[] msg, int emLen)
{
byte[] H=hash(spec, msg);
byte[] T=createDigestInfo(spec, H);
int tLen=T.length;
byte[] PS=new byte[emLen-tLen-3];
Arrays.fill(PS, (byte)0xFF);
ByteArrayOutputStream EMs=new ByteArrayOutputStream();
try {
EMs.write(0x00);
EMs.write(0x01);
EMs.write(PS);
EMs.write(0x00);
EMs.write(T);
} catch (IOException e) {
throw new RuntimeException(e);
}
byte[] EM=EMs.toByteArray();
return EM;
}
}