如何使用RSA 对数据加解密和签名验签?一篇文章带你搞定
共 9898字,需浏览 20分钟
·
2021-10-18 09:53
回复“书籍”即可获赠Python从入门到进阶共10本电子书
前言
加密是指利用某个值(密钥)对明文的数据通过一定的算法变换加密(密文)数据的过程,它的逆向过程叫解密。
业务场景:一般情况下,互联网上流动的数据不会被加密,无法避免这些数据泄露窃取,实际数据上传过程中,为了保证数据不被泄露、实现安全数据传输,出现了各种加密技术,本次主要分享如何通过python来实现非对称加密算法RSA加解密。
RSA算法简介
加密和解密使用相同的密钥叫对称加密方式,而非对称加密算法(公钥加密方式)RSA加密与解密分别用不同的密钥,公钥用于加密,私钥用于解密。
比如发送者S要给接受者R传输报文信息,为了避免信息泄露,秘钥签发者R事先通过RSA加密算法生成秘钥对,并且将公钥事先给到S,私钥则自己保留,S向R传输信息时,先用R提供的公钥加密报文,然后再将报文传输给R,R获取加密后的信息,再通过其单独持有的私钥解密报文,即使报文被窃听,窃听者没有私钥,也无法解密。公钥对外公开的,私钥自己保留,由于公钥是公开的,任何人都能拿到(会同时给到多个人),都可以使用公钥来加密发送伪造内容,因此,验证发送者的身份,确保报文的安全性显得非常重要。
考虑到一种情况:发送者S获取接收者R的公钥时,被中间人A获取到了这个公钥,通过公钥对信息加密,冒充S来给R发篡改的报文,同样R接受信息后也能够通过持有的私钥解密报文数据,接受者无法辨别数据发送者身份,存在报文非法修改风险,为了区分发送者的身份,那么这个时候我们就要用到签名。
签名原理:对报文做摘要,能防止被篡改。发送方对报文原文做加盐hash摘要,把加密原文和摘要一起发送给接收方,接收方解密后,用同样的hash方法计算并比对摘要,就能判断原文是否被篡改。
签名过程:发送者S同样也生成了一对秘钥,事先将公钥给到R,在发送消息之前,先用R给的公钥对报文加密,然后签名使用S自己私钥来签名,最后将加密的消息和签名一起发过去给R,接受者R在接收到发送者S发送的数据后,首先使用S的公钥对签名信息进行验签,确认身份信息,如果确认是发送者S,然后再R才利用私钥对加密消息进行解密,从而隔离非法数据包的接收。
这样一来,发送过程信息被获取,没有R的私钥无法解密信息,即使获取到发送者S的公钥,想要仿造发送信息没有S的私钥无法签名,同理R给S回复信息时,可以通过R的公钥加密,自己的私钥生成签名,S接收到数据使用同样的方式进行解密验证身份。私钥加签,公钥验签,这样就能确保只有私钥持有者也就是发送者能合法发送数据。
加签:
验签:
Python实现RSA加解密相关知识要点
1、首先安装加密库:pip install pycryptodome
python中要使用到crypto相关的库,使用的第三方库是 pycryptodome,其为pycrypto的延伸版本。
rsa文档地址:https://stuvel.eu/files/python-rsa-doc/index.html pycryptodome文档地址:https://www.pycryptodome.org/en/latest/src/cipher/classic.html
2、字符串和Bytes互相转换使用encode()和decode()方法
加密与解密操作的时候,要确保我们操作的数据类型是Bytes。
秘钥对一般由密钥签发者来生成提供。通过RSA校验生成自己的公钥,私钥,这里生成固定的备用。
from Crypto import Random
from Crypto.PublicKey import RSA
# 伪随机数生成器
random_gen = Random.new().read
# 生成秘钥对实例对象:2048是秘钥的长度
rsa = RSA.generate(2048, random_gen)
# 获取私钥,保存到文件
private_pem = rsa.exportKey()
with open('private.pem', 'wb') as f:
f.write(private_pem)
# 获取公钥保存到文件
public_pem = rsa.publickey().exportKey()
with open('public.pem', 'wb') as f:
f.write(public_pem)
生成秘钥对的时候,可以指定生成秘钥的长度,一般推荐使用 1024bit, 1024bit 的 rsa 公钥,加密数据时,最多只能加密 117byte 的数据,数据量超过这个数,则需要对数据进行分段加密;为保证更安全,尽量使用 2048bit ,最多只能加密245byte 长度的数据。
秘钥对生成如下格式:
公钥
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtas/LB41SKhFtu49b7TZ
oiTQ+ABoT6b8REs8PuuE5PZHByA/IXvEGrNl5osOV79OMmgAxiZGirXOfWZMoZuB
7Htu97Fyyh9fc9WLlkaCPbkN6LDp6EEiW+seQbcmJHRSgQUyyzu/T9/x9aA2aoHv
JeRO59kDmYfyoyg5+rFfgzy+YizZWqFRTLV9ZXKecIfAy7Opt3rnc2EaiFDr7/zw
KJmIxOfESwmhmRV1RyMj3O0yFD1xnkZ7ouh+4OExwNjTdUVpoQDz2HaU9QC/tEIX
TyJX36sJSyciLb+8itcfhegnBiNxRYZtwzsq8o9ASBcuXjzZPU7zlVGQTxIK7lWX
PwIDAQAB
-----END PUBLIC KEY-----
私钥
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAtas/LB41SKhFtu49b7TZoiTQ+ABoT6b8REs8PuuE5PZHByA/
IXvEGrNl5osOV79OMmgAxiZGirXOfWZMoZuB7Htu97Fyyh9fc9WLlkaCPbkN6LDp
6EEiW+seQbcmJHRSgQUyyzu/T9/x9aA2aoHvJeRO59kDmYfyoyg5+rFfgzy+YizZ
WqFRTLV9ZXKecIfAy7Opt3rnc2EaiFDr7/zwKJmIxOfESwmhmRV1RyMj3O0yFD1x
nkZ7ouh+4OExwNjTdUVpoQDz2HaU9QC/tEIXTyJX36sJSyciLb+8itcfhegnBiNx
RYZtwzsq8o9ASBcuXjzZPU7zlVGQTxIK7lWXPwIDAQABAoIBAAJSmhN20wItGi/g
dBgH05mympqPhF9/iFpZLRm2CiduDKXT+dEN4FZ7dH74Yfd2O/1oZGYkJ1YiEYdI
T3K1GcalV1uPjbx1OG1Mzb5fA86rnVI1yaMAcWK3RIl2wLD9Ob02wBnM5EFhrbOQ
8YRc4x4nzc7bo/BxEzjJzJMrdGF2cYeVMlASxA7IE09W+FFvPAWLR1nlfx6GoZuJ
edvQtrDxgRPLRluGWRdHmASoYHgONBJmV9NjLeOyGPwK32obdeE2vQLfaghDRvmI
GJ7LAc0yYRS7Aa82/BF8/x/r/BR3o7+bFg1t3n+SFXx8eCUSsBCKL3BNu1HiyWWj
2LGFlp0CgYEAyvC/CThHuAocIp4p7nyB6Wwx0fxxgLvcUtIfP77yOZDm3T2UK6BX
gLEMb7Fpmo6uH39ewubQtSxMrJH20DUA7MyRVJQUiM+bSvJ4jG/T+I1bi7BfVuL9
QDbW+zQvQ9CHZ0ZaijelolSBzMfO6/l4km6zgeM/Wf3ypkvvX+k9VfMCgYEA5SrA
u+rnUpPb1vCBZ+DZrPkA0hYB+N+eZ0wmrM4m0dP6xI084UpU0PEdL+o8FE9azlN7
l7tWtdmx003jy3pnzr6DMMIfgB92bHCfMMPq7ddSbfz6ul/kr67oUtMAdcy+odrY
Ah59+q9oyIeVZLtBKvlzaTp6httwm/kKL2n0UIUCgYEAhFz7rM7JcE8fxLB2Vvdc
YFvSLszBVx6weFBWU2R+Zm+NNHXqg33kNKrFmsATSdyP0zlnHCYhsFlBdTkKywgX
H1vZ2llu/0CxX/PADpENp0rDj9usg2Yvmcdq9pM11LxY5FIt0YK0BKmrs14LJzwi
mRec+zW150NMFYznhx4AhGMCgYAnjF9CjuFo4Nd5mnvan3UxYq9/kgi5GG5PyVaL
T/BnGbwXG4C8KIXGoTW2RSglISS8oq+bmdr2+yCzJKgBP5iWl04wpe+lvshDIpR2
Z/ktHpG9JYFnlJD0uKyjToKv0au8ZvYMN5LqJkdhA/UGM0Kl1fLS4CKxD0G5yRq2
4AQnuQKBgQDEyPS12CtCkOZMCwoONWGlnhsTmwxZJpVbCGmFucpuJagQiIIpHnJq
3LlU9Z3wh09kGFHJlSSzdMkHzZ4/x6ra4zuGObClHju6v3I5sY3/iPw97zDTiq0P
x7f9hR//cY9wnhjbaQkpvNooHXTHL3PZC8AN4Ud+aKzzbwFWrUBYxw==
-----END RSA PRIVATE KEY-----
计算公式如下:秘钥长度/8-11 = 最大加密量(单位:byte)
4、Base64编码
base64 是网络上最常见的用于传输8bit字节代码的编码方式之一,是一种基于64个可见字符来表示二进制数据的方法。用来将非ASCII字符数据转换成ASCII字符的一种方法,特别适合在HTTP协议下快速传输数据。比如邮件,ASCII 控制字符 、中文、图片二进制数据等。
基本原理
base64 将 ASCII 码 或者二进制编码成只包含 A~Z、a~z、0~9、+ 、/ 这64个字符(26个大写字符、26个小写字符、10个数字、+/)。通过3个8bit字节( 3 x 8 = 24 )编码成4个6位字节(4 x 6 = 24),在每个6位字节前补两个0,形成4个8字节形式。
编码规则
base64要求把每3个8bit字节转换为4个6bit的字节,然后把6bit的字节高两位添加为0,组成4个8bit的字节,理论上将比原来长1/3。如果要编码的二进制数据不是3的倍数,数据长度除以3的余数就是2或者1,转换的时候结果不足6位的,用0来填充,之后在6位前面补两个0,转换完空出的结果用 = 来补位,最后保证编码出来的字节为4的倍数。解码的时候,等号会自动被去掉。
注:由于标准的Base64编码后可能出现字符+和斜扛/,+和/在URL中不能直接作为参数,因此,Base64提供了urlsafe_b64encode方法将+和/分别转换为横杠-和下画线_,使用urlsafe_b64decode方法将横杠-和下画线_还原为字符+和斜扛/。
Python实现RSA加解密和签名验签类
本文将RSA加密方法写成一个类,支持包含中文的长字符串分段加解密。
注:经过分段加密的数据,在进行解密的时候我们也要分成多段解密,然后解密之后再进行拼接成原串,加密签名与解密验签注意保持原串一致。
from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_PKCS1_v1_5
import base64
from Crypto.Signature import PKCS1_v1_5 as Sig_pk
from Crypto.Hash import SHA
class RSACipher():
'''
RSA加密、解密、签名、验签工具类
备注:# RSA的加密机制有两种方案一个是RSAES-OAEP,另一个RSAES-PKCS1-v1_5
# 对同样的数据,用同样的key进行RSA加密, 每次的输出都会不一样;但是这些加密的结果都能正确的解密
'''
def read_xml(self,xmlfile):
'''
读取待加密明文方法
'''
with open(xmlfile, 'r', encoding="utf-8") as file:
# 用open()将XML文件中的内容读取为字符串
xmlstr = file.read()
print(xmlstr)
return xmlstr
def encrypt_file(self,encrypt_file):
'''
保存加密后密文方法
'''
with open(encrypt_file, 'rb') as f:
message = f.read()
return message
def Encrypt(self, message, publicKeyfile, out_file):
'''
加密方法
:param message: 需要加密的明文
:param publicKeyfile: 公钥文件
:param out_file: 输出密文
:return: 加密后的文本
'''
with open(publicKeyfile, 'r') as f:
publicKey = f.read()
pubKey = RSA.importKey(publicKey)
cipher = Cipher_PKCS1_v1_5.new(pubKey)
message = message.encode()
# 分段加密,加密长度byte为8的倍数,最长不超出最大加密量(单位:byte)=秘钥长度/8-11
length = len(message)
default_length = 245
offset = 0
res = bytes()
while length - offset > 0:
if length - offset > default_length:
_res = cipher.encrypt(message[offset:offset + default_length])
else:
_res = cipher.encrypt(message[offset:])
offset += default_length
res += _res
encrypt_text=base64.b64encode(res)
with open(out_file, 'wb') as f_w:
f_w.write(base64.b64encode(res))
return encrypt_text
def Decrypt(self,message, privateKeyfile, out_file):
'''
解密方法
:param message: 加密后的密文
:param privateKey: 私钥文件
:param out_file: 输出明文
:return: 解密后的文本
'''
with open(privateKeyfile, 'r') as f:
privateKey = f.read()
rsaKey = RSA.importKey(privateKey)
cipher = Cipher_PKCS1_v1_5.new(rsaKey)
randomGenerator = Random.new().read
message = base64.b64decode(message.decode())
res = []
for i in range(0, len(message), 256):
res.append(cipher.decrypt((message[i:i + 256]),randomGenerator))
plainText = bytes(b"".join(res)).decode()
print(plainText)
with open(out_file, 'w', encoding='utf-8') as f_w:
f_w.write(plainText)
return plainText
def sign(self,message, private_sign_file):
'''
签名方法
:param message: 需要签名的文本
:param private_sign_file: 私钥文件
:return: 签名信息
'''
with open(private_sign_file, 'r') as f:
private_sign = f.read()
message = message.encode()
private_key = RSA.importKey(private_sign)
# 根据sha算法处理签名内容
hash_value = SHA.new(message)
# 私钥进行签名
signer = Sig_pk.new(private_key)
signature = signer.sign(hash_value)
result=base64.b64encode(signature).decode()
return result # 将签名后的内容,转换为base64编码
def verify(self,message,public_sign_file,signature):
'''
验签方法
:param message: 需要验签的文本
:param public_sign_file: 公钥文件
:param signature: 签名信息
:return: 验签结果
'''
with open(public_sign_file, 'r') as f:
public_sign = f.read()
signature = base64.b64decode(signature)
# 将签名之前的内容进行hash处理
public_key = RSA.importKey(public_sign)
print(public_key)
# 验证签名
hash_value = SHA.new(message.encode())
verifier = Sig_pk.new(public_key)
return verifier.verify(hash_value, signature)
if __name__ == '__main__':
#创建RSA加密实例
rsacipher = RSACipher()
xmlfile = r'new1.xml'
message=rsacipher.read_xml(xmlfile) #待加密明文
encryptFile = "encrypt.txt" #加密后密文
publicKeyfile="rsa.pub" #公钥加密
# 加密
encrypt_text = rsacipher.Encrypt(message,publicKeyfile,encryptFile)
print('加密后:\n%s' % encrypt_text)
# 签名
private_sign_file="private.pem" # 私钥签名
signature = rsacipher.sign(message,private_sign_file)
print('签名:\n%s' % signature)
# 解密
decryptFile ="deencrypt.txt" #输出解密内容
privateFile = "rsa.key" #私钥解密
decrypt_text = rsacipher.Decrypt(encrypt_text,privateFile,decryptFile)
print('解密后:\n%s' % decrypt_text)
# 验签
pubic_sign_file = "public.pem" # 公钥验签
result = rsacipher.verify(decrypt_text,pubic_sign_file,signature)
print('验签:\n%s' % result)
文中包含所有源码,自己动手创建两套公钥私钥、测试文本即可,快动手试一下吧。
小伙伴们,快快用实践一下吧!如果在学习过程中,有遇到任何问题,欢迎加我好友,我拉你进Python学习交流群共同探讨学习。
------------------- End -------------------
往期精彩文章推荐:
欢迎大家点赞,留言,转发,转载,感谢大家的相伴与支持
想加入Python学习群请在后台回复【入群】
万水千山总是情,点个【在看】行不行
/今日留言主题/
随便说一两句吧~