一、关于RSA
1. 简介
RSA加密算法是一种非对称加密算法,在公开密钥加密和电子商业中被广泛使用。RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。
1973年,在英国政府通讯总部工作的数学家克利福德·柯克斯(Clifford Cocks)在一个内部文件中提出了一个与之等效的算法,但该算法被列入机密,直到1997年才得到公开。
RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
所以对极大整数做因数分解的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。假如有人找到一种快速因数分解的算法的话,那么用RSA加密的信息的可靠性就会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的RSA钥匙才可能被强力方式破解。到当前为止,世界上还没有任何可靠的攻击RSA算法的方式。只要其钥匙的长度足够长,用RSA加密的信息实际上是不能被破解的。
2. 如何生成
示例为常见的Linux或者mac平台
1 | # 1. 生成公钥 《默认生成PKCS1格式》 |
3. 密钥格式
常见的为PKCS#8和PKCS#1,除此之外还有其他的比如PKCS#5、PKCS#12等。
关于PKCS#8和PKCS#1:
PKC#8:定义了一种编码和传输密钥的方法,它并不特定于OpenSSL;
1
2
3
4
5
6
7
8
9
10
11# 公钥
-----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----
# 私钥
-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----
# 私钥加密的格式
-----BEGIN ENCRYPTED PRIVATE KEY-----
-----END ENCRYPTED PRIVATE KEY-----PKCS#1:定义了一种使用RSA密钥的方法(无论它是如何加载到应用程序中,是否使用PKCS#8)来执行和验证数据的数字签名。
1
2
3
4
5
6
7# 公钥
-----BEGIN RSA PUBLIC KEY-----
-----END RSA PUBLIC KEY-----
# 私钥
-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----
二、Python加密模块示例
RSA 有两种填充方式,一种是 PKCS1_v1_5,另一种是 PKCS1_OAEP
1. rsa模块
由python实现,封装得较深,使用简单,比如签名和验签,两行代码就搞定了,很符合python简约的语言理念。
加密与解密
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34import rsa
import base64
from urllib import request
# 生成密钥
def create_new_keys(len):
(public_key, private_key) = rsa.newkeys(len)
with open('public1.pem', 'wb') as f:
f.write(public_key.save_pkcs1())
with open('private1.pem', 'wb') as f:
f.write(private_key.save_pkcs1())
# 加密
def rsa_encrypt(msg):
with open(PUBLIC_FILE_PATH, 'rb') as public_file:
# 加载pkcs8格式公钥
public_key = rsa.PublicKey.load_pkcs1_openssl_pem(public_file.read())
code = rsa.encrypt(msg.encode('utf-8'), public_key)
code = base64.b64encode(code).decode('utf-8')
code = request.quote(code)
return code
# 解密
def rsa_decrypt(code):
code = request.unquote(code)
with open(PRIVATE_FILE_PATH, 'rb') as private_file:
# 加载pkcs1格式公钥
private_key = rsa.PrivateKey.load_pkcs1(private_file.read())
code = base64.b64decode(code.encode('utf-8'))
msg = rsa.decrypt(code, private_key).decode('utf-8')
return msg
签名与验签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# 签名
def sign(data):
# 只能加载PKCS1格式的私钥,如果私钥是PKCS8格式的,需要将PKCS8转PKCS1再使用
pri_key = rsa.PrivateKey.load_pkcs1(open('./pri.pem').read())
signature = base64.b64encode(rsa.sign(data.encode('utf-8'), pri_key, 'MD5'))
return signature
# 验签
def verify(signature, data):
# 加载pkcs1格式公钥
pub_key = rsa.PublicKey.load_pkcs1(open('./pub.pem').read())
# 加载pkcs8格式公钥
# pub_key = rsa.PublicKey.load_pkcs1_openssl_pem(open('./pub.pem').read())
return rsa.verify(data, base64.b64decode(signature), pub_key)
2. Crypto模块
pip install pycrypto
签名与验签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import MD5
import base64
def RSA_sign(data):
# key处理方式一:
privateKey = '''
MIGvAgEAAiIMzxsBkAn8f9vA5z8phs0z5JT9d9xWS+9hnrMurYY7yySRAgMBAAEC
IgTmnOudI+UDOp51G/qUhCDtkYhYKvTgENlK1DsroFYXtN0CET4oCSZvz+1GEKsQ
bE4Ny7y7AhE0wXdLJpQgJnD4cmGmQo8VIwIRNghBpAsw+nedB8gYDmZJxP8CEStE
eQiDrXzoykKZ3Qi1EhCtAhE20ZvBe0I1fj48Pryi+0gtVA=='''
private_keyBytes = base64.b64decode(privateKey)
priKey = RSA.importKey(private_keyBytes)
# key处理方式二:
# privateKey = open('./pri.pem').read()
# priKey = RSA.importKey(privateKey)
signer = PKCS1_v1_5.new(priKey)
# 哈希算法可以采用MD5,也可以用别的比如SHA
# data需要字节化后才能传进MD5.new()中
hash_obj = MD5.new(data.encode('utf-8'))
signature = base64.b64encode(signer.sign(hash_obj))
return signature
def verify(signature, data):
# key处理同样是上面两种方式
publicKey = '''
MD0wDQYJKoZIhvcNAQEBBQADLAAwKQIiDM8bAZAJ/H/bwOc/KYbNM+SU/XfcVkvv
YZ6zLq2GO8skkQIDAQAB'''
public_keyBytes = base64.b64decode(publicKey)
pubKey = RSA.importKey(public_keyBytes)
# pubKey = RSA.importKey(publicKey)
hash_obj = MD5.new(data.encode('utf-8'))
verifier = PKCS1_v1_5.new(pubKey)
return verifier.verify(hash_obj, base64.b64decode(signature)) # bool
上面的签名代码做了如下几件事:
- 它将Base64解码为PKCS#8
- 它将PKCS#8解码为内存中的实际密钥(请注意,可能需要在此处提供密码)
- 它使用所述密钥将数据(使用SHA-1或者MD5等算法进行hash)执行PKCS#1 v1.5签名
- 它使用Base64将签名进行编码
3. pycryptodome模块
pip install pycryptodome
该模块和上面的pycrypto很像,但pycrypto最后一次更新是在13年,安装后引用方式是一样的,api也非常相似,安装其中一个就好了。
加密与解密
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
# 生成密钥
def create_new_keys(len):
rsa = RSA.generate(2048) # 返回的是密钥对象
public_pem = rsa.publickey().exportKey('PEM') # 生成公钥字节流
private_pem = rsa.exportKey('PEM') # 生成私钥字节流
with open('public.pem','wb') as f:
f.write(public_pem)
with open('private.pem','wb') as f:
f.write(private_pem)
# 加密
def rsa_encrypt(plain):
with open('public.pem','rb') as f:
key = RSA.importKey(f.read())
rsa = PKCS1_v1_5.new(key)
cipher = rsa.encrypt(plain)
return base64.b64encode(cipher)
# 解密
def rsa_decrypt(cipher):
with open('private.pem','rb') as f:
key = RSA.importKey(f.read())
rsa = PKCS1_v1_5.new(key)
plain = rsa.decrypt(base64.b64decode(cipher),'ERROR') # 'ERROR'必需
return plain
签名与验签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
# 签名
def sign(data):
key = RSA.import_key(open('private_key.pem').read())
h = SHA256.new(data)
rsa = pkcs1_15.new(key)
signature = rsa.sign(h)
# 验签
def verify(signature, data):
key = RSA.import_key(open('public_key.pem').read())
hash_obj = SHA256.new(data)
rsa = pkcs1_15.new(key)
return rsa.verify(hash_obj, signature):
4. cryptography模块
签名与验签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.exceptions import InvalidSignature
# 签名
def sign(data_file_name, private_key_file_name):
# 从PEM文件中读取私钥数据
pri_key = open(private_key_file_name, 'rb').read()
key_file.close()
# 从PEM文件数据中加载私钥
private_key = serialization.load_pem_private_key(
pri_key,
password=None,
backend=default_backend()
)
# 签名
signature = private_key.sign(
data,
padding.PKCS1v15(), # 指定填充方式为PKCS1v15
hashes.SHA256() # 指定hash方式为sha256
)
return signature
# 验签
def verify(data, signature, public_key_file_name):
# 从PEM文件中读取公钥数据
pub_key = open(public_key_file_name, 'rb').read()
key_file.close()
# 从PEM文件数据中加载公钥
public_key = serialization.load_pem_public_key(
pub_key,
backend=default_backend()
)
verify_ok = False
try:
# 验签
public_key.verify(
signature,
data,
padding.PKCS1v15(), # 指定填充方式为PKCS1v15
hashes.SHA256() # 指定hash方式为sha256
)
# 签名验证失败会触发名为InvalidSignature的exception
except InvalidSignature:
print('invalid signature!')
else:
verify_ok = True
return verify_ok
if __name__ == '__main__':
data = 'fxx'
# 指定签名的密钥
private_key_file = r'Key.pem'
public_key_file = r'Key_pub.pem'
# 签名并返回签名结果
signature = sign(data, private_key_file)
print(signature)
# 验证签名
verify_ok = verify(data, signature, public_key_file)
print(verify_ok)
5. 加密分块
由于RSA在加密过程中,每次加密只能加密最大长度的字符串,如果你的加密数据超长,在加密过程中需要分段加密,同理,解密也是分段解密的。
1024位的证书,加密时最大支持117个字节,解密时为128;
2048位的证书,加密时最大支持245个字节,解密时为256。
加密时支持的最大字节数:证书位数/8 -11(比如:2048位的证书,支持的最大加密字节数:2048/8 - 11 = 245,其中,11位字节为保留字节。
加密较长字符串时,如下:
1 | from Crypto.Cipher import PKCS1_v1_5 |