跨平台rsa签名与验签

这篇博客探讨了如何在Java和Python之间实现跨平台的RSA签名验证过程,适用于不同语言之间的互操作,例如PHP。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


本文主要讨论 java 和python之前的跨平台rsa签名、验签,当然,其他语言也可以参考一下,比如php等等


在开发中我们经常遇到这一类问题,java平台和python平台进行通信时,比如我们服务器是python后台而对方的服务器是java后台,而如果要使用对方提供的接口就必须要rsa签名与验签,而这两个平台的rsa密钥的格式并不相同,这个时候,应该怎么办?

第一种情况,java生成密钥,签名, 使用python验签

java生成密钥的代码如下:
 /**
     * 将字节数组转换为16进制字符串的形式.
     */
    public static final String bytesToHexStr(byte[] bcd) {
        StringBuffer s = new StringBuffer(bcd.length * 2);
        for (int i = 0; i < bcd.length; i++) {
            s.append(HEX_LOOKUP_STRING[(bcd[i] >>> 4) & 0x0f]);
            s.append(HEX_LOOKUP_STRING[bcd[i] & 0x0f]);
        }
        return s.toString();
    }
/**
     * 本方法用于产生1024位RSA公私钥对。
     * @return 私钥、公钥
     */
    private static String[] genRSAKeyPair() throws Exception {
        KeyPairGenerator rsaKeyGen = null;
        KeyPair rsaKeyPair = null;
        System.out.println("Generating a pair of RSA key ... ");
        rsaKeyGen = KeyPairGenerator.getInstance("RSA");
        SecureRandom random = new SecureRandom();
        random.setSeed(("" + System.currentTimeMillis() * Math.random() * Math.random()).getBytes());
        rsaKeyGen.initialize(1024, random);
        rsaKeyPair = rsaKeyGen.genKeyPair();
        PublicKey rsaPublic = rsaKeyPair.getPublic();
        PrivateKey rsaPrivate = rsaKeyPair.getPrivate();
        String privateAndPublic[] = new String[2];
        privateAndPublic[0] = bytesToHexStr(rsaPrivate.getEncoded());
        privateAndPublic[1] = bytesToHexStr(rsaPublic.getEncoded());
        System.out.println("私钥:" + privateAndPublic[0]);
        System.out.println("公钥:" + privateAndPublic[1]);
        System.out.println("1024-bit RSA key GENERATED.");
        return privateAndPublic;
    }

这样生成的密钥是这种形式(以公钥为例):
30819f30...... 203010001
这是16进制的密钥

这种密钥是无法在python平台直接使用的,在python平台,大家见惯的密钥应该是“ MIGfMA0GCSqGSIb3D pQb1mjeGLy6gw+AfOKZ1dpNbMUyZml+p3stTS...... ”这种形式的。
 那么python平台应该怎么用公钥验签呢?
     主要思路是获取这个公钥的参数,如 n, e ,再在python平台上用 n,e 生成一个公钥, 如果python平台需要使用java生成的私钥,思路也是一样的,唯一不同的就是需要4个参数了,n,e,q,p
    这种思路的好处是可以任意跨平台,不论这个平台或者这个库使用的是什么格式的密钥,都可以搞定,因为这些参数是rsa算法的基础,任意格式的密钥都是基于这些参数的。

首先, 可以在
PublicKey rsaPublic = rsaKeyPair.getPublic();

这一行打断点,获取到 rsaPublic  这个类的成员中就可以看到
modulus=cd6c01988......6187,
publicExponent=10001
这两个值其实就是 n 和 e(这两个数都是16进制的)

如果密钥的生成过程无法再重复,比如java的签名是某个其他第三方接口给你的,则需要根据
30819f......0001
进行反推。

 /**
     * 将16进制字符串还原为字节数组.
     */
    public static final byte[] hexStrToBytes(String s) {
        byte[] bytes;
        bytes = new byte[s.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) Integer.parseInt(s.substring(2 * i, 2 * i + 2), 16);
        }
        return bytes;
    }
    /**
     * 得到公钥对象
     *
     * @param key 密钥字符串(经过16进制编码)
     * @throws Exception
     */
    public static PublicKey getPublicKey(String key) throws Exception {
        byte[] keyBytes = hexStrToBytes(key.trim());
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(keySpec);
    }

/**
     * 得到私钥对象
     *
     * @param key 密钥字符串(经过16进制编码)
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String key) throws Exception {
        byte[] keyBytes = hexStrToBytes(key.trim());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(keySpec);
    }

根据以上代码,就可以使用16进制密钥get到  PublicKey   类型的一个实例,也就可以获取到 rsa公钥的n , e,获取rsa私钥的参数的方法与这个是一样的。
接着就可以在python平台上开始验签了,使用  python的Crypto库, 以下代码中的n 就是 PublicKey   这个类的成员变量    modulus的值, e就是 publicExponent, 一般java、python平台默认生成的rsa密钥用的e都是65537,即16进制的10001

key = Crypto.PublicKey.RSA.construct((long(n,16),long('10001',16)))
public_key = key.publickey().exportKey() 
print(public_key)

打印结果:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNbAGYhY2W/44KP/dbBYL3HA/D
qJSpmVyfsNwrr1PyIjD7WurKropwSomzEktQp5ZWXNSeMfvPLNgdNqwnTJwH96Xz
pWQVQ2B/n4jE/OHXSQNUiA5FXZWb9OMb0hjbpKFXELzHCqztBtdPSl90CEnbV9Rn
88lvxVvWbes4ENlhhwIDAQAB
-----END PUBLIC KEY-----

这个就是在python平台上我们经常使用的格式了, 至于具体如何去验签,这里不详谈了。

至于,如何使用RSA.construct生成私钥,见

第二种情况,python生成密钥,签名, java验签

这种时候生成的密钥是这种格式的,即
MIGfMA0GCSq...... bV9Rn 88lvxVvWbes4ENlhhwIDAQAB

生成方法可以参考命令:
openssl genrsa -out rsa_private_key.pem 1024
openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
openssl pkcs8 –nocrypt -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem 

java平台是无法直接使用这种格式的密钥的,这个时候,验签如何进行?

第一种方法的思路是一样的,就是获取这个python密钥的n,e,然后再在java中生成对应的16进制密钥。
对应的方法十分比较简单,在python部分:
>>> import rsa
>>> (pubkey, privkey) = rsa.newkeys(1024, poolsize=8)
>>> pubkey.n
20945457913487......24L
>>> pubkey.e
65537

在java部分:
 /**   
     * @param modulus 
     *            模 
     * @param exponent 
     *            指数 
     * @return 
     */  
    public static RSAPublicKey getPublicKey(String modulus, String exponent) {  
        try {  
            BigInteger b1 = new BigInteger(modulus);  
            BigInteger b2 = new BigInteger(exponent);  
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");  
            RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);  
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);  
        } catch (Exception e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  

后面再按照前面第一种情况中生成密钥的代码, 
bytesToHexStr(rsaPublic.getEncoded());
即可得到16进制密钥

或者,还有另一种方式可以将python的密钥转化为java平台能使用的格式

可在java平台进行如下转化:

import android.util.Base64;
public static final String PUB_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNbAGYhY2W/44KP/dbBYL3HA/DqJSpmVyfsNwrr1PyIjD7WurKropwSomzEktQp5ZWXNSeMfvPLNgdNqwnTJwH96XzpWQVQ2B/n4jE/OHXSQNUiA5FXZWb9OMb0hjbpKFXELzHCqztBtdPSl90CEnbV9Rn88lvxVvWbes4ENlhhwIDAQAB";
public static String getKey(){
    return byte2Hex(Base64.decode(PUB_KEY , Base64.DEFAULT));
}
public static String byte2Hex(byte[] digest) {  
    StringBuilder sb = new StringBuilder();  
    int len = digest.length;  
    String out = null;  
    for (int i = 0; i < len; i++) {   
        out = Integer.toHexString(0xFF & digest[i]);//原始方法  
        if (out.length() == 1) {  
            sb.append("0");//如果为1位 前面补个0  
        }  
        sb.append(out);  
    }  
    return sb.toString();  
} 

通过 getKey ( ) 获得的结果
30819f300......3010001
这就是 rsa的16进制密钥,即 与第一种情况中的java代码里 genRSAKeyPair () 生成的密钥是同一类型, 再之后就可以进行java平台的验签了。另外, 获取私钥的方法与获取公钥的方法是一样的。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值