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

2021-02-26 15:14:45 浏览数 (1)

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   ('' * 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(''))

运行结果:

代码语言: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进制处理,解密结果与加密前相同。

0 人点赞