rsa java签名和验证

z8dt9xmd  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(421)

我正在尝试编写自己的rsa库,用于签名、验证、加密和解密。下面是使用biginteger的代码,它支持从biginteger到字节数组(八进制字符串)的转换,以及根据pkcs#1规范从biginteger到字节数组(八进制字符串)的转换

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;
    }

}

当我使用这个类进行签名和验证时,一切都正常,但是如果我使用javaapi进行签名并使用这个rsa类进行验证,验证就会失败。

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);
    }

你知道为什么使用JavaAPI签名时验证失败吗?

c7rzv4ha

c7rzv4ha1#

NONEwithRSA 最初用pkcs#1 v1.5填充数据(更准确地说是rsassa-pkcs1-v1#u 5),这在您的定制实现中不会发生。因此,在 RSA.verify(m, c, key) , m 不是填充,而是 c 对应于填充数据的签名。
因此,为了成功地验证 c 通过

boolean verify_java = RSA.verify(m, c, key);
``` `m` 必须替换为

BigInteger mPadded = RSA.OS2IP(dataPadded)

哪里 `dataPadded` 是一个 `byte[]` 根据密钥大小为128字节,包含以下内容(参见rfc8017):

00 01 ff ... ff 00 66 6f 6f 6f

您可以导出此值,例如:

byte[] dataPadded = pad(data, 1024/8);
...
private static byte[] pad(byte[] data, int length) {
byte[] dataPadded = new byte[length];
for (int i = 0; i < dataPadded.length; i++)
dataPadded[i] = (byte)255;
System.arraycopy(data, 0, dataPadded, dataPadded.length - data.length, data.length);
dataPadded[0] = 0;
dataPadded[1] = 1;
dataPadded[dataPadded.length - data.length - 1] = 0;
return dataPadded;
}

相关问题