跨平台rsa签名与验签
2017-01-09 20:46
405 查看
本文主要讨论 java 和python之前的跨平台rsa签名、验签,当然,其他语言也可以参考一下,比如php等等
在开发中我们经常遇到这一类问题,java平台和python平台进行通信时,比如我们服务器是python后台而对方的服务器是java后台,而如果要使用对方提供的接口就必须要rsa签名与验签,而这两个平台的rsa密钥的格式并不相同,这个时候,应该怎么办?
第一种情况,java生成密钥,签名, 使用python验签
java生成密钥的代码如下:
这样生成的密钥是这种形式(以公钥为例):
“30819f30......203010001”
这是16进制的密钥
这种密钥是无法在python平台直接使用的,在python平台,大家见惯的密钥应该是“MIGfMA0GCSqGSIb3DpQb1mjeGLy6gw+AfOKZ1dpNbMUyZml+p3stTS......”这种形式的。
那么python平台应该怎么用公钥验签呢?
主要思路是获取这个公钥的参数,如 n, e ,再在python平台上用 n,e 生成一个公钥, 如果python平台需要使用java生成的私钥,思路也是一样的,唯一不同的就是需要4个参数了,n,e,q,p
这种思路的好处是可以任意跨平台,不论这个平台或者这个库使用的是什么格式的密钥,都可以搞定,因为这些参数是rsa算法的基础,任意格式的密钥都是基于这些参数的。
首先, 可以在
这一行打断点,获取到rsaPublic 这个类的成员中就可以看到
modulus=cd6c01988......6187,
publicExponent=10001
这两个值其实就是 n 和 e(这两个数都是16进制的)
如果密钥的生成过程无法再重复,比如java的签名是某个其他第三方接口给你的,则需要根据
“30819f......0001”
进行反推。
根据以上代码,就可以使用16进制密钥get到 PublicKey
类型的一个实例,也就可以获取到rsa公钥的n , e,获取rsa私钥的参数的方法与这个是一样的。
接着就可以在python平台上开始验签了,使用 python的Crypto库, 以下代码中的n 就是PublicKey 这个类的成员变量 modulus的值,
e就是publicExponent, 一般java、python平台默认生成的rsa密钥用的e都是65537,即16进制的10001
打印结果:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNbAGYhY2W/44KP/dbBYL3HA/D
qJSpmVyfsNwrr1PyIjD7WurKropwSomzEktQp5ZWXNSeMfvPLNgdNqwnTJwH96Xz
pWQVQ2B/n4jE/OHXSQNUiA5FXZWb9OMb0hjbpKFXELzHCqztBtdPSl90CEnbV9Rn
88lvxVvWbes4ENlhhwIDAQAB
-----END PUBLIC KEY-----
这个就是在python平台上我们经常使用的格式了, 至于具体如何去验签,这里不详谈了。
至于,如何使用RSA.construct生成私钥,见
http://pydoc.net/Python/pycrypto/2.5/Crypto.PublicKey.RSA/
第二种情况,python生成密钥,签名, java验签
这种时候生成的密钥是这种格式的,即
“MIGfMA0GCSq......bV9Rn88lvxVvWbes4ENlhhwIDAQAB”
生成方法可以参考命令:
java平台是无法直接使用这种格式的密钥的,这个时候,验签如何进行?
第一种方法的思路是一样的,就是获取这个python密钥的n,e,然后再在java中生成对应的16进制密钥。
对应的方法十分比较简单,在python部分:
在java部分:
后面再按照前面第一种情况中生成密钥的代码,
即可得到16进制密钥
或者,还有另一种方式可以将python的密钥转化为java平台能使用的格式
可在java平台进行如下转化:
通过getKey()获得的结果
“30819f300......3010001”
这就是 rsa的16进制密钥,即与第一种情况中的java代码里genRSAKeyPair()生成的密钥是同一类型,再之后就可以进行java平台的验签了。另外,获取私钥的方法与获取公钥的方法是一样的。
在开发中我们经常遇到这一类问题,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平台,大家见惯的密钥应该是“MIGfMA0GCSqGSIb3DpQb1mjeGLy6gw+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生成私钥,见
http://pydoc.net/Python/pycrypto/2.5/Crypto.PublicKey.RSA/
第二种情况,python生成密钥,签名, java验签
这种时候生成的密钥是这种格式的,即
“MIGfMA0GCSq......bV9Rn88lvxVvWbes4ENlhhwIDAQAB”
生成方法可以参考命令:
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平台的验签了。另外,获取私钥的方法与获取公钥的方法是一样的。
相关文章推荐
- RSA非对称加密算法,加密和签名
- iOS RSA的网络安全模型、iOS签名机制总结(登录、token安全、签名)
- Java使用RSA加密解密签名及校验
- 关于RSA、公钥、私钥、加密、签名的那些概念
- Java使用RSA加密解密签名及校验
- C#自定义RSA加密解密及RSA签名和验证封装类
- RSA之php私钥签名与android、ios公钥加密
- 基于Crypto++/Cryptopp的rsa密钥生成,rsa加密、解密,rsa签名、验签12
- Java使用RSA加密解密签名及校验
- RSA数字签名机制
- [Android Pro] Android签名与认证详细分析之二(CERT.RSA剖析)
- python实现aes加密解密,RSA签名和验签,RSA加密解密,并调用接口
- Python下RSA加密/解密, 签名/
- 工作中常常使用的几种加密以及签名的方式:RSA加密
- Node-RSA 验证签名
- ASP.NET(C#)-JAVA对接RSA加密签名-第三章
- 使用OpenSSL做RSA签名验证 支付宝移动快捷支付 的服务器异步通知
- 支付宝支付中一键生成RSA密钥工具生成签名及验签功能,运行不了的解决办法
- C#_RSA的加解密与签名验证