如何使用RSA 对数据加解密和签名验签?一篇文章带你搞定

前言加密是指利用某个值(密钥)对明文的数据通过一定的算法变换加密(密文)数据的过程,它的逆向过程叫解密。

业务场景:一般情况下,互联网上流动的数据不会被加密,无法避免这些数据泄露窃取,实际数据上传过程中,为了保证数据不被泄露、实现安全数据传输,出现了各种加密技术,本次主要分享如何通过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.htmlpycryptodome文档地址:https://www.pycryptodome.org/en/latest/src/cipher/classic.html2、字符串和Bytes互相转换使用encode()和decode()方法

加密与解密操作的时候,要确保我们操作的数据类型是Bytes。

3、生成秘钥对

秘钥对一般由密钥签发者来生成提供。通过RSA校验生成自己的公钥,私钥,这里生成固定的备用。

代码语言:javascript复制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 长度的数据。

秘钥对生成如下格式:

代码语言:javascript复制公钥

-----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加密方法写成一个类,支持包含中文的长字符串分段加解密。

注:经过分段加密的数据,在进行解密的时候我们也要分成多段解密,然后解密之后再进行拼接成原串,加密签名与解密验签注意保持原串一致。

代码语言:javascript复制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)文中包含所有源码,自己动手创建两套公钥私钥、测试文本即可,快动手试一下吧。