前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python crypto模块实现RSA和AES加密解密

Python crypto模块实现RSA和AES加密解密

作者头像
Python碎片公众号
发布2021-02-26 15:14:45
9.5K0
发布2021-02-26 15:14:45
举报
文章被收录于专栏:Python碎片公众号的专栏

Python 的 crypto 是用于RSA加密解密,AES加密解密的。

一、RSA和AES简介

RSA加密算法是一种非对称加密算法。RSA 是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。RSA就是他们三人姓氏开头字母拼在一起组成的。

非对称加密算法也就是加密和解密用不同的密钥,使用一对秘钥对来进行加密和解密,使用公钥来加密信息,然后使用私钥来解密。当然了,加密解密的过程是非常复杂的,关于加密解密的原理可以搜索一下相关的文章。

AES是高级加密标准(Advanced Encryption Standard)的缩写,AES 是最常见的对称加密算法。

对称加密算法也就是加密和解密用相同的密钥,同一个秘钥即用来加密,也用来解密。关于加密解密的原理可以搜索一下相关的文章。

我们这里主要介绍 crypto 的使用,来实现 RSA 和 AES 的加密解密。

二、安装Crypto

crypto这个模块的安装比较特殊,可能会有点小坑,安装时需要注意。

crypto,pycrypto,pycryptodome的功能是一样的。crypto与pycrypto已经没有维护了,后面可以使用pycryptodome。

但是,三个名字,再分Linux和Windows操作系统,再分Python2和Python3,叠加起来就有12种情况了,非常复杂。

在 Windows 中,不管是 Python2 和 Python3 ,都不能用 crypto 和 pycrypto ,可以用 pycryptodome 。

在 Linux 中,不管是 Python2 和 Python3 ,都不能用 crypto ,可以用 pycrypto 和 pycryptodome 。

所以,总结一句话,直接都用 pycryptodome 就行了,(保证pip正常能用的情况下)安装命令如下:

代码语言:javascript
复制
pip install pycryptodome

安装之前,最好先把 crypto 和 pycrypto 卸载了(uninstall),避免不必要的麻烦。

三、生成RSA公钥和私钥

代码语言:javascript
复制
# coding=utf-8
from Crypto import Random
from Crypto.PublicKey import RSA
 
 
random_generator = Random.new().read
rsa = RSA.generate(2048, random_generator)
# 生成私钥
private_key = rsa.exportKey()
print(private_key.decode('utf-8'))
print("-" * 30 + "分割线" + "-" * 30)
# 生成公钥
public_key = rsa.publickey().exportKey()
print(public_key.decode('utf-8'))

运行结果:

代码语言:javascript
复制
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAziBz4OOXA+HOa9tBxvr/ARp3p1cAKYD9E8a13CMY1ejrs7Of
7jv6cA5nHyaFFapbtZCwwOntCTE1pOqph2JRDGBQEtUYPKxJW6WiXdB/3a3XdPfw
pdMW6WSbPP9sINPOOZmDr0RFtftIzVYuMDcsHjB8+lzjIxTaIjD1GLwlpOlJxnZ1
S6k3yEfbJdIjkobfoKL/sLh1AKlEX8fuBnFP5LRt7OMiB9k5Cpw8DpdPbn5IkjKK
nDv3njzHI+Z+FygUS9HCJ3YeDn9kUtU/v+IlDG+lLIFNC7s2bsvQVsWYyxQLtAi5
IdLArxoxo/ZjpT0ghWfm8thMrlK3W8E0veKC1wIDAQABAoIBABlYdzc3dPnGucWc
Y8WuVk3R7dWXRo9T64qTfAgyDps69USW+TrniB9gPgDgESw9UuKMBJfsC7f+I40A
R9E8Xx/o9d+i8USAoNKSltj3Ssm81wnsdNxHDQGs28PP2oFc2fZOMJmMCRBb8jNB
fhNyrUuXBb8ZTdqh9UKQB4s/k1doGV5oK4BFjKlVk62Mh3v1t3Ow29YnuygMYPCa
6KuxTrL7EMNpEAAiB1CqReZuWRPV9FzB0pQVzY6wEoESK7Dv/D7NqwlCLjwj8lwe
bR8a9eYjqRmF7/swWYRNRdRJ/HhaRVrEtRZCd4/B3Kd1rr3Z95rNXd+mk7ruIq2x
X8sHphUCgYEAz07q4RtrFGMCDm25dqoZFOnJon7tlyHuvlEI/PqMouneQWBGV8iG
LEmabgcxgM8bqhbGeQdiggUE6roC/C8jKqGP8evkO/tGU3nnxYAhenvxA1Je43/m
rGK5fZU4I8S+qWSU+54Ajg/eueNFMQZxqbknKxfRYPyDl+aAqp16wzsCgYEA/op+
UZru1RN2mC/u9qAOQNhq7DpmO4tKJVNSPVloOqulsFKL8KoH/AGWhfEGfOT0mequ
wRnVMq4eAwnhk8GfUFhQp23uP3ZAwOryXLQ9/V0f2ICo1lCWvpoclH9SoYlR6i33
jI/b/OVMYwu7kEEnIZKMOWKU1K27L2ktv9lLjRUCgYEAtpdAw3J91t/5gLHeB4l8
O/JKmWgIZfQKLvB65S7czeUE0Do22ryQiNNvRHfrfxNrv1z+BL2fYvt+wCuD05DE
/Zh9i0wEo1wHLiESJd9cHWCrAAz97IzLpIvz6ouSfdadQ9+GZTReMsIBn2Jwn+SY
GjAtMoRw82aVlSX9r8iyxt8CgYABGGN2wm3oqM7H1Nz4XrPw/31mStIJy42kH3mp
Hete5UIvQgusG28xXGhjZygZ/Xo391SNLClIsIschDxeQGUJGXgvYD+4MjARJHGq
iHQD1RS//726PlqHs24QDYQzgb3CfjQkfLH7opjzjCdgdYdPk6vay1vMlnrJt3Ak
9TBoCQKBgQCFqV8cI1WBMu+AntTAJnHxC0SznBOBts05i4s+fJCLFmaE+iEEKMsQ
tl1CHjvq8ArkO6yBoV2EJdnZKu+98RyY8NouyjezEGNiCzGs9vb/492SA9Pt3A/v
3yo4ss+6abLbyz88fVGQe/Dx5VXGImlT+NcsKEupqd9He2psD6VlVg==
-----END RSA PRIVATE KEY-----
------------------------------分割线------------------------------
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAziBz4OOXA+HOa9tBxvr/
ARp3p1cAKYD9E8a13CMY1ejrs7Of7jv6cA5nHyaFFapbtZCwwOntCTE1pOqph2JR
DGBQEtUYPKxJW6WiXdB/3a3XdPfwpdMW6WSbPP9sINPOOZmDr0RFtftIzVYuMDcs
HjB8+lzjIxTaIjD1GLwlpOlJxnZ1S6k3yEfbJdIjkobfoKL/sLh1AKlEX8fuBnFP
5LRt7OMiB9k5Cpw8DpdPbn5IkjKKnDv3njzHI+Z+FygUS9HCJ3YeDn9kUtU/v+Il
DG+lLIFNC7s2bsvQVsWYyxQLtAi5IdLArxoxo/ZjpT0ghWfm8thMrlK3W8E0veKC
1wIDAQAB
-----END PUBLIC KEY-----

上面的代码中,生成了一对公钥和私钥。

先导入 Random ,实例化一个随机生成值的对象,所以我们每次运行的结果不一样,因为初始值是随机生成的。

然后导入了 RSA ,通过 RSA 的 generate() 方法实例化一个对象 rsa 。再通过 rsa 的 exportKey() 方法和 publickey() 方法生成公钥和私钥。

generate(bits, randfunc=None, e=65537) 有三个参数,在使用时需要注意。

bits 是一个字节大小的值,必须大于等于1024,通常建议写1024的倍数,FIPS(Federal Information Processing Standards) (美国联邦信息处理标准)定义了1024,2048,3072这三个。

randfunc 是一个随机函数,默认是 Crypto.Random.get_random_bytes ,可以自己设置,也可以保持默认。

e=65537 是公共 RSA 指数,它必须是一个正整数。FIPS 标准要求公共指数至少65537(默认)。

四、使用公钥加密和使用私钥解密

代码语言:javascript
复制
from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as PKCS1_cipher
import base64
 
 
random_generator = Random.new().read
rsa = RSA.generate(2048, random_generator)
 
private_key = rsa.exportKey()
with open("private_a.rsa", 'wb') as f:
    f.write(private_key)
 
public_key = rsa.publickey().exportKey()
with open("public_a.rsa", 'wb') as f:
    f.write(public_key)
 
# 使用公钥对内容进行rsa加密
message = "需要加密的信息"
with open('public_a.rsa') as f:
    key = f.read()
    pub_key = RSA.importKey(str(key))
    cipher = PKCS1_cipher.new(pub_key)
    rsa_text = base64.b64encode(cipher.encrypt(bytes(message.encode("utf8"))))
    print(rsa_text.decode('utf-8'))
 
# 使用私钥对内容进行rsa解密
with open('private_a.rsa') as f:
    key = f.read()
    pri_key = RSA.importKey(key)
    cipher = PKCS1_cipher.new(pri_key)
    back_text = cipher.decrypt(base64.b64decode(rsa_text), 0)
    print(back_text.decode('utf-8'))

运行结果:

代码语言:javascript
复制
Tx4v9BSJIhQqXz/i/rfYKoaatxFE5ZFwaumTmYMrZr8btTYp5LicCJQksrWCinln7eQejbat3txUj2BGFNdioQLiXOpFhphI0W4lQps2NAX+s9le3ImS0olJO6HToHzLxuuPsfg4JFcqPQkvzOtznZijOC5q4prc46XHO7CIk6y6rZJhXz/MIBAKFt+C79dh/L64bu0HLXmtrkEFrjJN97GVhTEPeP+OusP7fEW1JqQGIXl/nVbttrjf3txw2op0tq4GT1f7YwUgMQTlWyFBlPo4CFu1yRwBt2xGjfSBhSn2g63JPXf2l05YxxhFnWVJaQ+Qkk/1mv970mRAAxq9Pg==
需要加密的信息

运行上面的代码,先将生成的公钥和私钥写入了文件中,使用的时候从文件中读取出来。

然后使用公钥对信息进行加密,加密之后再用私钥对加密结果解密,解密结果与加密前的信息一致。

加密时,使用 RSA 的 importKey() 方法对(从文件中读取的)公钥字符串进行处理,处理成可用的加密公钥。

从 Crypto.Cipher 中导入 PKCS1_v1_5 ,导入时记得重命名一下,如 PKCS1_cipher ,因为在 Crypto 的另一个模块 Crypto.Signature 中也有同名的类 PKCS1_v1_5 ,同时使用时不重命名会造成冲突。导入后,实例化一个加密对象 cipher ,传入的参数是公钥,通过 cipher 的 encrypt() 方法对信息进行加密。

加密的信息需要先转换成字节,加密的结果也是字节,为了显示得更友好,可以将结果转换成 base64 字符串。

解密时,使用 RSA 的 importKey() 方法对(从文件中读取的)私钥字符串进行处理,处理成可用的解密私钥。

实例化一个解密对象 cipher ,传入的参数是私钥,通过的 cipher 的 decrypt() 方法对信息进行解密。解密结果与加密之前保持一致。

五、私钥生成签名和公钥验证签名

代码语言:javascript
复制
from Crypto.PublicKey import RSA
from Crypto.Hash import SHA
import base64
from Crypto.Signature import PKCS1_v1_5 as PKCS1_signature
 
 
message = "需要加密的信息"
# 使用私钥生成签名
with open('private_a.rsa') as f:
    key = f.read()
    pri_key = RSA.importKey(key)
    signer = PKCS1_signature.new(pri_key)
    digest = SHA.new()
    digest.update(message.encode("utf8"))
    sign = signer.sign(digest)
    signature = base64.b64encode(sign)
    print(signature.decode('utf-8'))
 
# 使用公钥验证签名
with open('public_a.rsa') as f:
    key = f.read()
    pub_key = RSA.importKey(key)
    verifier = PKCS1_signature.new(pub_key)
    digest = SHA.new()
    digest.update(message.encode("utf8"))
    print(verifier.verify(digest, base64.b64decode(signature)))

运行结果:

代码语言:javascript
复制
O8MiiUV45yNDJvMJfBigVXQswBRYMDpQcHESV2R54RBPcTBLesXh3Gl9iO3mI9hEKZeCM3njJvQ2CWiIk2mvLXqklW6Qqx23dx139yI6LWtOzfew8C+vLyWuELNHv215ne81aHNCI3sSSe947Fz/M6D13PbumkrCZHrX3IRlT/Fmb6yX5eXKtIX1azN3Op3VfZZKmZsCJ7Jjd4CgL4iKHsZ0S8j3BAIwI7+8XqQb7ToNZKrbpQSgYvI2vexfLaYRR243oZ4RBIM1+tZtV4PLCH8aEKkMMKU+Y8+12pv/5DDZcoueYxaENSNK+YU9scw2W3p9fu182DM3YpgWZ97QFw==
True

继续使用之前写入文件中的私钥和公钥,使用私钥来生成信息的签名,然后使用公钥来验证签名。

生成签名时,使用 RSA 的 importKey() 方法对(从文件中读取的)私钥字符串进行处理,处理成可用的私钥用于生成签名。

从 Crypto.Signature 中导入 PKCS1_v1_5 ,重命名为 PKCS1_signature 。然后实例化一个签名对象 signer ,传入的参数是私钥,通过的 signer 的 sign() 方法对信息生成签名。

信息需要先转换成 sha 字符串,使用 Crypto.Hash 中的 SHA 来转换。生成的签名是字节串,为了显示得更友好,可以将结果转换成 base64 字符串。

验证签名时,使用 RSA 的 importKey() 方法对(从文件中读取的)公钥字符串进行处理,处理成可用的公钥用于验证签名。

实例化一个验证对象 verifier ,传入的参数是公钥,通过的 verifier 的 verify() 方法对签名进行验证。验证结果是一个布尔值,验证成功返回 True , 不成功返回 False 。

六、AES加密和解密

代码语言:javascript
复制
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex
 
 
message = "需要加密的信息"
key = 'aes_keysaes_keysaes_keys'
mode = AES.MODE_OFB
cryptor = AES.new(key.encode('utf-8'), mode, b'0000000000000000')
length = 16
count = len(message)
if count % length != 0:
    add = length - (count % length)
else:
    add = 0
message = message + ('\0' * add)
ciphertext = cryptor.encrypt(message.encode('utf-8'))
result = b2a_hex(ciphertext)
print(result.decode('utf-8'))
 
cryptor = AES.new(key.encode('utf-8'), mode, b'0000000000000000')
plain_text = cryptor.decrypt(a2b_hex(result))
print(plain_text.decode('utf-8').rstrip('\0'))

运行结果:

代码语言:javascript
复制
6b6f8ad38dcbc28e65c2cd0360780f8c5070af38d0c3942a8e242a1de74e
需要加密的信息

先从 rypto.Cipher 导入 AES ,通过 AES 的 new() 方法实例化一个对象 cryptor 。

new() 的第一个参数 key 是加密和解密时使用的秘钥,长度是有限制的,一般为16, 24, 32。

第二个参数是 AES 中的一个 mode 对象,可以是 MODE_ECB, MODE_CBC, MODE_CFB, MODE_OFB 等,不同的值有不同含义,用法也有差异,这里就不多介绍了,具体的用法可以点进源码中查看。

后面还可以接其他参数或关键字参数,如上面的第三个参数。

对要加密的信息先进行处理,如上面 mode 为 MODE_OFB 时,信息长度要处理成16的倍数。

加密时,使用 cryptor 的 encrypt() 方法对信息进行加密,使用 binascii 中的 b2a_hex() 对加密结果进行16进制处理。

解密时,使用相同的 key 作为秘钥, 使用相同的 mode ,使用 cryptor 的 decrypt() 方法解密,先用 a2b_hex() 对密文进行16进制处理,解密结果与加密前相同。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-09-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python 碎片 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档