I am trying to write my own RSA library that signs, verifies, encrypts and decrypts. Below is the code, that uses BigInteger and that supports conversion from BigInteger to byte array (octet string) and vice versa according to PKCS#1 specs
class RSA {
public static RSAKeyPair generateKeyPair(int size) {
Random rnd = new SecureRandom();
BigInteger p = new BigInteger(size / 2, 100, rnd);
BigInteger q = new BigInteger(size / 2, 100, rnd);
BigInteger n = p.multiply(q);
BigInteger phi = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
BigInteger e;
do {
e = new BigInteger(phi.bitLength(), rnd);
} while (e.compareTo(BigInteger.ONE) <= 0 || e.compareTo(phi) >= 0 || !e.gcd(phi).equals(BigInteger.ONE));
BigInteger d = e.modInverse(phi);
return new RSAKeyPair(new RSAPublicKey(n, e), new RSAPrivateKey(n, d), n);
}
public static BigInteger encrypt(BigInteger m, RSAPublicKey key) {
return m.modPow(key.getPublicExponent(), key.getModulus());
}
public static BigInteger decrypt(BigInteger c, RSAPrivateKey key) {
return c.modPow(key.getPrivateExponent(), key.getModulus());
}
public static BigInteger sign(BigInteger m, RSAPrivateKey key) {
return m.modPow(key.getPrivateExponent(), key.getModulus());
}
public static boolean verify(BigInteger m, BigInteger s, RSAPublicKey key) {
return s.modPow(key.getPublicExponent(), key.getModulus()).equals(m);
}
public static BigInteger OS2IP(byte[]X){
BigInteger out = new BigInteger("0");
BigInteger twofiftysix = new BigInteger("256");
for(int i = 1; i <= X.length; i++){
out = out.add((BigInteger.valueOf(0xFF & X[i - 1])).multiply(twofiftysix.pow(X.length-i)));
}
//x = x(xLen–1)^256xLen–1 + x(xLen–2)^256xLen–2 + … + x(1)^256 + x0
return out;
}
public static byte[] I2OSP(BigInteger X, int XLen){
BigInteger twofiftysix = new BigInteger("256");
byte[] out = new byte[XLen];
BigInteger[] cur;
if(X.compareTo(twofiftysix.pow(XLen)) >= 0){
return new String("integer too large").getBytes();
}
for(int i = 1; i <= XLen; i++){
cur = X.divideAndRemainder(twofiftysix.pow(XLen-i));
//X = cur[1];
out[i - 1] = cur[0].byteValue();
}
//basically the inverse of the above
//Cur is an array of two bigints, with cur[0]=X/256^(XLen-i) and cur[1]=X/256^[XLen-i]
return out;
}
}
class RSAKeyPair {
private RSAPublicKey pub;
private RSAPrivateKey priv;
private BigInteger n;
public RSAKeyPair(RSAPublicKey pub, RSAPrivateKey priv, BigInteger n) {
this.pub = pub;
this.priv = priv;
this.n = n;
}
public RSAPublicKey getPublicKey() {
return pub;
}
public RSAPrivateKey getPrivateKey() {
return priv;
}
public BigInteger getModulus() {
return n;
}
}
class RSAPublicKey {
private BigInteger n;
private BigInteger e;
public RSAPublicKey(BigInteger n, BigInteger e) {
this.n = n;
this.e = e;
}
public BigInteger getModulus() {
return n;
}
public BigInteger getPublicExponent() {
return e;
}
}
class RSAPrivateKey {
private BigInteger n;
private BigInteger d;
public RSAPrivateKey(BigInteger n, BigInteger d) {
this.n = n;
this.d = d;
}
public BigInteger getModulus() {
return n;
}
public BigInteger getPrivateExponent() {
return d;
}
}
When I sign and verify using this class everything works fine, however if I sign using the Java api and verify using this RSA class the verification fails.
public static void main(String[] args) throws SignatureException, InvalidKeyException, NoSuchAlgorithmException {
byte[] data = "fooo".getBytes();
System.out.println("data is:" + Arrays.toString(data));
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(1024);
KeyPair pair = gen.generateKeyPair();
Signature sig = Signature.getInstance("NONEwithRSA");
sig.initSign(pair.getPrivate());
sig.update(data);
byte[] sign = sig.sign();
System.out.println("signature is: " +Arrays.toString(sign) + "\n" + "length" + sign.length);
java.security.interfaces.RSAPublicKey pub = (java.security.interfaces.RSAPublicKey) pair.getPublic();
java.security.interfaces.RSAPrivateKey pri = (java.security.interfaces.RSAPrivateKey) pair.getPrivate();
BigInteger m = RSA.OS2IP(data);
BigInteger c = RSA.OS2IP(sign);
RSAPublicKey key = new RSAPublicKey(pub.getModulus(), pub.getPublicExponent()); // not the java.security.interfaces.RSAPublicKey rather the RSAPublicKey from class see above
RSAPrivateKey pkey = new RSAPrivateKey(pri.getModulus(), pri.getPrivateExponent());
BigInteger signing = RSA.sign(m, pkey);
boolean verify = RSA.verify(m, signing, key);
boolean verify_java = RSA.verify(m, c, key);
System.out.println("signing and verfiying usinf RSA class:" +verify);
System.out.println("signing using java api and verify using RSA class:"+verify_java);
}
Any idea why the verifcation fails when using the java api to sign?