Mar
22
和某厂通信需要按其要求用rsa加密某段数据,该厂给了个Example.java,和一个X509 Certificate。
之前是把java编译好,然后在python里用system来调用它,有点丑。
最近需要复用这段代码,希望代码干净点,所以在Python里重新实现一遍。
# 1. 将 x509cer 转成 PEM 格式
输出PEM编码的公钥,类似:MIGfMA0GCSqGSIb3DQ...(中间省略)...rAvxiOfQIDAQAB
# 2. 在Python中加密
由于该厂在Java代码中使用 "RSA/NONE/PKCS1Padding",和PKCS1_v1_5的默认padding一致,因此不需要特殊处理。
注:
如果希望使用 NoPadding,看起来Python的Crypto没有直接提供支持,可能需要用PKCS1_OAEP并自己填充ASCII 0(但不确定,好像OAEP也包含了某种padding,encrypt的文档里说"长度需要 - 2 - 2倍hashcode长度"),几年前用PHP的时候踩过坑,详见
https://www.felix021.com/blog/read.php?2169
RSA: Java bouncy castle 与 PHP openssl_public_encrypt 兼容的那点事儿
# 3. 解密
反向处理就好,先base64 decode,分段解密再拼起来
注:decrypt方法的文档里说,"sentinel" 应当是一个无意义的字符串,并且尽量应当和典型的明文长度相似(这里偷懒了)。因为PKCS1_v1_5没有完善的完整性校验,某个构造的输入可能可以被正确解码,虽然看起来是一串没有意义的随机文字。加上sentinel以后,当解密出错的时候,会返回指定的 sentinel 继续后续处理流程,从而可以躲避选择密文攻击的检测。
关于这个选择密文攻击的细节可参考这篇文章:SSL.TLS协议安全系列:SSL的Padding Oracle攻击
# 4. 其他
可以用这个命令来生成RSA的公私钥对
RSA.importKey(key) 方法的 key 也可以直接使用 PEM 文件的内容,也可以用 base64 decode 以后的 binary data。
转载请注明出自 ,如是转载文则注明原出处,谢谢:)
RSS订阅地址: https://www.felix021.com/blog/feed.php 。
之前是把java编译好,然后在python里用system来调用它,有点丑。
最近需要复用这段代码,希望代码干净点,所以在Python里重新实现一遍。
# 1. 将 x509cer 转成 PEM 格式
import java.io.*;
import java.util.Base64;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class Test2
{
public static PublicKey getPublicKeyByX509Cer(String cerFilePath) throws Exception
{
InputStream x509Is = new FileInputStream(cerFilePath);
CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)certificatefactory.generateCertificate(x509Is);
x509Is.close();
return cert.getPublicKey();
}
public static void main(String[] args) throws Exception {
PublicKey pubKey = getPublicKeyByX509Cer("public.cer");
System.out.println(Base64.getEncoder().encodeToString(pubKey.getEncoded()));
}
}
import java.util.Base64;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class Test2
{
public static PublicKey getPublicKeyByX509Cer(String cerFilePath) throws Exception
{
InputStream x509Is = new FileInputStream(cerFilePath);
CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)certificatefactory.generateCertificate(x509Is);
x509Is.close();
return cert.getPublicKey();
}
public static void main(String[] args) throws Exception {
PublicKey pubKey = getPublicKeyByX509Cer("public.cer");
System.out.println(Base64.getEncoder().encodeToString(pubKey.getEncoded()));
}
}
输出PEM编码的公钥,类似:MIGfMA0GCSqGSIb3DQ...(中间省略)...rAvxiOfQIDAQAB
# 2. 在Python中加密
#!/usr/bin/python
#coding:utf-8
import base64
import binascii
import Crypto
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQ...(中间省略)...rAvxiOfQIDAQAB"
#返回base64编码后的密文
def RSAEncrypt(message, pubkey):
binary = base64.b64decode(pubkey)
key = RSA.importKey(binary)
#PKCS#1建议的padding需要占用11个字节
length = (key.size() + 1) / 8 - 11
cipher = PKCS1_v1_5.new(key)
res = []
for i in range(0, len(message), length):
res.append(cipher.encrypt(message[i:i+length]))
result = ''.join(res)
return binascii.hexlify(result)
print RSAEncrypt("123456", PUBLIC_KEY)
#coding:utf-8
import base64
import binascii
import Crypto
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQ...(中间省略)...rAvxiOfQIDAQAB"
#返回base64编码后的密文
def RSAEncrypt(message, pubkey):
binary = base64.b64decode(pubkey)
key = RSA.importKey(binary)
#PKCS#1建议的padding需要占用11个字节
length = (key.size() + 1) / 8 - 11
cipher = PKCS1_v1_5.new(key)
res = []
for i in range(0, len(message), length):
res.append(cipher.encrypt(message[i:i+length]))
result = ''.join(res)
return binascii.hexlify(result)
print RSAEncrypt("123456", PUBLIC_KEY)
由于该厂在Java代码中使用 "RSA/NONE/PKCS1Padding",和PKCS1_v1_5的默认padding一致,因此不需要特殊处理。
注:
如果希望使用 NoPadding,看起来Python的Crypto没有直接提供支持,可能需要用PKCS1_OAEP并自己填充ASCII 0(但不确定,好像OAEP也包含了某种padding,encrypt的文档里说"长度需要 - 2 - 2倍hashcode长度"),几年前用PHP的时候踩过坑,详见
https://www.felix021.com/blog/read.php?2169
RSA: Java bouncy castle 与 PHP openssl_public_encrypt 兼容的那点事儿
# 3. 解密
反向处理就好,先base64 decode,分段解密再拼起来
PRIVATE_KEY = "MIICXQIBAAKBgQ....."
# 输入是base64编码的密文
def RSADecrypt(message, prikey):
binary = base64.b64decode(prikey)
key = RSA.importKey(binary)
length = (key.size() + 1) / 8
cipher = PKCS1_v1_5.new(key)
message = binascii.unhexlify(message)
res = []
for i in range(0, len(message), length):
res.append(cipher.decrypt(message[i:i+length], 'sentinel: random message'))
result = ''.join(res)
return result
# 输入是base64编码的密文
def RSADecrypt(message, prikey):
binary = base64.b64decode(prikey)
key = RSA.importKey(binary)
length = (key.size() + 1) / 8
cipher = PKCS1_v1_5.new(key)
message = binascii.unhexlify(message)
res = []
for i in range(0, len(message), length):
res.append(cipher.decrypt(message[i:i+length], 'sentinel: random message'))
result = ''.join(res)
return result
注:decrypt方法的文档里说,"sentinel" 应当是一个无意义的字符串,并且尽量应当和典型的明文长度相似(这里偷懒了)。因为PKCS1_v1_5没有完善的完整性校验,某个构造的输入可能可以被正确解码,虽然看起来是一串没有意义的随机文字。加上sentinel以后,当解密出错的时候,会返回指定的 sentinel 继续后续处理流程,从而可以躲避选择密文攻击的检测。
关于这个选择密文攻击的细节可参考这篇文章:SSL.TLS协议安全系列:SSL的Padding Oracle攻击
# 4. 其他
可以用这个命令来生成RSA的公私钥对
引用
$ openssl genrsa -out test.pem 1024
$ openssl rsa -in test.pem -pubout -out test_public.pem
$ openssl rsa -in test.pem -pubout -out test_public.pem
RSA.importKey(key) 方法的 key 也可以直接使用 PEM 文件的内容,也可以用 base64 decode 以后的 binary data。
欢迎扫码关注:
转载请注明出自 ,如是转载文则注明原出处,谢谢:)
RSS订阅地址: https://www.felix021.com/blog/feed.php 。