这周一个项目客户提供了一份对接文档要求用RSA数字签名,客户提供的是java的demo,但是自己不想用java来做,想用python来实现,就自己研究了下python下RSA签名。
先是研究了java-demo的流程和逻辑(因为是公司项目客户提供的,不便对外公开),基本的流程是这样的:
1)先根据私钥KEY字符串,获取私钥对象(PrivateKey)
私钥KEY----------就是一个长字符串
比如下面这个是我自己用openssl生成的(MIIEpQIBAAKCAQEA9JPHRFaWOhi7PXZz/Kmun/ldJrd YQy6diU2eFijVooL8NDG 4dsOojUpT9FhAUedXcDZrYN3BstFcaGRJQouyXZsbJjyFL8WNLLGFIwdcv1pTGve cLOEdU1Rl E4wCznYflxcUN3ngZQo7cN/la2poM1k/epR8UsWiH8wQj v7FHMrt4 XMhVSVAqslfhs6yqJQuQwu3YiaspZVySAywaFFveLCcedKPrlZYoN9Q1Qg7hyXZ hqIPIyXBHFU721GqlDJ8K6lCXxjNK7xFWwo1QzLAwdfLA0ZJ1K00 4nv9E1JUcbX emhDJHvyZ9O/ExRqdykkE6CAJD3tZkjhQwERnwIDAQABAoIBABQVxrl/ tpOiaHk hmXrcWHF0raJPyGtL rf53c oDtNHunp20tU ACuKPRbF4JJZUz7t2SedTnjm5JX WFoYWftFdGX sXKutp4hSE8Lqqd01B7ohN3wgCI59UdpwYVWqrHLEFvi1IHvttOb BtUqPdmy0MWUAxzXvmqwe9dGa9VGCp55HezZo0nDkwg8s7h/CKndxmGCvFX/O5iV RaJgUOkQ9iRp8v1M3ri/XBvt1BmAUzFvXs3RNmF3/fKsvOCINFIGnZt zyW7s7XA RBgOluorDLdMusvxy9qrGspc3U8L6Uf7xsHKwvaAVD/RLP03FopNkg8HPphikbQA naoWzKECgYEA/giQ3qSJfNH/0TELayYVzEq3MYOEc/tiI1YrUUXLTJ7rQ3yZSjt7 HMIgjPzO6Ek4iVdVX0QnNNvD9To40blr5KykeIoAOyta7hEAvZS4uMue1YXkW8i2 YpQJ9BzNho0SpHyB13/7TT9l9kyNSqemOVrDfvo24d4S7YafhBiNXOkCgYEA9nh4 GCmV6 XR7lwbMrp3JLE/xpyiGK8P27COT22pT9Q3in8BSgjMa vmJnGase6oQr6 xcd5Odqg5YmCs T2MLoJg0sSBfCJxASRZPDnpz2MjTordm09PkauOCSsoq1IhH5E xkDw97zIm1m1h87W8Zd E500E687vRBanEzFxUcCgYEAwdwNmiqcVWn4vE6eNSW6 Ss3V4W5JPS1g5jCTHBGUJKO TCEg5hpgSEEJEC65Q2DlSUIhf3MGLHttno3Q4JOM 99ScKvS9WmoqmTTWiNae5T9WtgHYlAam0LHCqsz0NzMfP/FYMPmU8I1qJTykMo2f 93MBb7xI97M ZI9w2iDWeEkCgYEAu5G1ZjiPfv3XYDNE1taZoU5k80taTGbvokqV LuDDZgDIdzp2XCpOllqAhN7KPKshYbusWuXSYO/8MJM0z9j4bt61rKBt5 1FutJL IAmiEglqNHRHbUn3KLS7k2h9pRPAs2wwxLvZZn/aHzfnSaJku9kxjpW9cxmRmfGf M4HNpLMCgYEAppL5J9jja2DrkRXoS9d4MvwaTlF/HsY2QRVRmlcvZddKXECcJ/fI wmP7V/NNegLBsMukdrPez28bK1ci0iigO6dCjxnUZF1KDPzLwgsNG2Z9i54nsmOT 7xwfITqzE3gA2NSRNVY7oo8pwfGU1kEeySrMUxrRZ3EaerTS0JSIdSg=)
注意java提供的私钥KEY字符串是不包括开头-----BEGIN RSA PRIVATE KEY-----和结尾-----END RSA PRIVATE KEY-----的。
因为openssl默认产生的PEM格式的是包括开头-----BEGIN RSA PRIVATE KEY-----和结尾-----END RSA PRIVATE KEY-----的
根据提供的私钥KEY字符,demo中得到一个私钥对象(PrivateKey)。
下面是java得到私钥对象的代码:
代码语言:javascript复制public static PrivateKey getPrivateKey(String key) {
try {
byte[] keyBytes = (Base64.decodeBase64(key));
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
} catch (Exception e) {
e.printStackTrace();
return null;
}
2)根据得到的私钥对象(PrivateKey),得到给定字符串的签名
代码语言:javascript复制public String sign(byte[] data, PrivateKey priKey) {
try {
Signature signature = Signature.getInstance("MD5WithRSA");
signature.initSign(priKey);
signature.update(data);
return Base64.encodeBase64URLSafeString(signature.sign());
} catch (Exception e) {
return null;
}
}
上面是java中的RSA的基本的流程,这里还有个验签的步骤,这里就不说了。
下面讲一下我在python中的使用,网上找了很多,也吸取了很多有用的东西,一开始用M2Crypto来做,这个库可以用pip install M2Crypto获取,但是这个库是依赖openssl的,先得安装openssl,我下得得官网得openssl-0.9.8-stable-SNAP-20150609.tar.gz,然后还得安装Perl,具体教程可以参考(http://www.cnblogs.com/ZhouL3777/archive/2012/10/21/2732890.html),我M2Crypto完全安装成功后,后面可能是我使用的问题,签名出来的字符串跟java签名出来的不一样,这个地方以后有空再深入研究下。后面讲下重点,使用pycrypto库。
使用pycrypto来做RSA签名:
1.因为pyCrypto库不依赖openssl库,所以直接pip install pycrypto 就可以安装成功(我的python2.7.9,pycrypto版本是2.6.1)
2.签名函数:
代码语言:javascript复制'''
RSA签名
'''
def sign(signdata):
'''
@param signdata: 需要签名的字符串
'''
h=MD5.new(signdata)
signer = pk.new(RSA.importKey(p))
signn=signer.sign(h)
# signn=base64.b64encode(signn,["-","_"])
signn=base64.urlsafe_b64encode(signn)
return signn
关键介绍下函数中p,这个是私钥字符串,但是开头-----BEGIN RSA PRIVATE KEY-----和结尾-----END RSA PRIVATE KEY-----得加上。
我用客户提供的私钥字符串(不含开头-----BEGIN RSA PRIVATE KEY-----和结尾-----END RSA PRIVATE KEY-----的),然后p是加上开头-----BEGIN RSA PRIVATE KEY-----和结尾-----END RSA PRIVATE KEY-----的值。
代码语言:javascript复制# signn=base64.b64encode(signn,["-","_"])
signn=base64.urlsafe_b64encode(signn)
这两个其实是一样的效果,把“ ”转成“-”,把“/”转成“_”而已。
这样签名出来的跟java签名出来的东西一样了, 但是最后多了一个“=”。 这个我不知道为什么会多了一个“=”。 如果哪位高手知道,解释一下。
写的有点凌乱啊,望看的见谅。