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


一、 RSA 和 AES 介绍

RSA加密算法是一种非对称加密算法。

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

  非对称加密算法:加密和解密用不同的密钥,一对秘钥: 使用公钥来加密信息,然后使用私钥来解密。

AES是高级加密标准, 是最常见的对称加密算法。

  一种分组加密标准,每个加密块大小为128位,允许的密钥长度为128、192和256位

  对称加密算法:加密和解密用相同的密钥,同一个秘钥即用来加密,也用来解密。

   

二、安装Crypto

crypto,pycrypto,pycryptodome的功能是一样的。

  crypto与pycrypto已经没有维护了,后面可以使用pycryptodome。

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

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

安装命令

pip install pycryptodome

三、RSA加解密

1、生成RSA公钥 私钥

# -*- coding:utf-8 -*-
# import rsa # 方法一引用
from x.logger import *
# 方法二引用
from Crypto import Random
from Crypto.PublicKey import RSA

class encryPa:
    # 方法一:rsa 模块生成RSA公钥私钥
    # def rsa_puiv(self):
    #     # 生成RSA公钥 私钥
    #     (pubkey, privkey) = rsa.newkeys(1024)
    #     rsa_key = pubkey.save_pkcs1()
    #     logs.debug(rsa_key.decode())
    #     rsa_iv = privkey.save_pkcs1()
    #     logs.debug(rsa_iv.decode())
    #     self.sava_Loc("./key_iv/rsa_pubkey.pem", rsa_key)
    #     self.sava_Loc("./key_iv/rsa_prikey.pem", rsa_iv)
    # 
    #     return rsa_key, rsa_iv

    # 方法二:crypto 模块生成RSA公钥私钥
    def crypto_rsa_puiv(self):
        random_g = Random.new().read
        rsa = RSA.generate(1024, random_g)
        # 生成RSA公钥
        crypto_key = rsa.public_key().exportKey()
        logs.debug(crypto_key.decode())
        # 生成RSA私钥
        crypto_iv = rsa.exportKey()
        logs.debug(crypto_iv.decode())
     
        self.sava_Loc("./key_iv/crypto_pubkey.pem", crypto_key)
        self.sava_Loc("./key_iv/crypto_privkey.pem", crypto_iv)
        return crypto_key, crypto_iv

    # 保存数据到文件
    def sava_Loc(self, fileName, fileData):
        with open(fileName, "wb+") as f:
            f.write(fileData)


if __name__ == "__main__":
    # encryPa().rsa_puiv()
    encryPa().crypto_rsa_puiv()
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBALIhbVPwic/U1YhX3Xr4BECzOlcK26V++LUbM+WVuOCKmgO1xGJlod0N
fdkPLMQw7tiW1gaGRqtaKQct5zNJoAN6/dftOxey6xfMooGvH1qpruUTcqeXvkAA
niVuVlVO9mYrxlmmg8IP7cFISc5VWFtxdGGelh1oAyd1x5zcamMnAgMBAAE=
-----END RSA PUBLIC KEY-----

 -----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQCyIW1T8InP1NWIV916+ARAszpXCtulfvi1GzPllbjgipoDtcRi
ZaHdDX3ZDyzEMO7YltYGhkarWikHLeczSaADev3X7TsXsusXzKKBrx9aqa7lE3Kn
l75AAJ4lblZVTvZmK8ZZpoPCD+3BSEnOVVhbcXRhnpYdaAMndcec3GpjJwIDAQAB
AoGAQ4B9UJjPcI2j2YYKbSX9XpNMoV2A/McP8nl7gh3psFV1pcBEylDfNMh/Dzk7
6qDscfE+67wInbiN4aWYN1/csFZFCOaUBOBGSCPp6Nblg2563/vhIx3lvch8XkdY
HqH8mwKWJs/0zTtCMbQEZl1F/3m6LABJSCsPhUtmfonuE2kCRQC70RJrn3fGgaLl
4bPnLVi2ShJ3MlYWQE3qfQmGs7taupbAa8ULeaYH6KNTLEF/xaMdOAKpWJ0/3IlX
LipuOCniWnKS0wI9APLMKq1oknw0Dd8l/os9hnia2bHUn+UabWxAbsLXhivPA51Q
zLEfJsjuY49ExdqhDBRLZFRFowyiH/Px3QJEZrvN36Cy25o9iJEU3vcm9089GoYm
ILW76O/MLipR6Sb3HvnUJpq7/sd5zxz+Fu38cxcYHZmjKjNvzIgsHW4LOw1ObpEC
PAiGTJVMD6CWv0nvbpF69oazgSMyQO7tT5w2Yu0qXgaZvRm/5X/uhREW9Z3pHz0+
3Eq2Hu6qh0ABTTfmbQJEej0n4Qnh62de+uc9mGs7C+4oB5lTPkhEnDtdIwjCF6iI
ttTLpJOLb+bi+xFkffV+MgF8yCUhVem50kS7IiHs+WnF1YU=
-----END RSA PRIVATE KEY-----

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxhCGE+ALdMd1eQKPOvbYq+kNi
GI43TJwSnLZu0SYxmUOWvOw6VAMRawuIraZEiUR5hUMeLhRPB/bybd9b//N27hr5
qk8GQylL1whCHklmadxWY09HjUCngHb8JcF/hFYKB36ut5MT4HbBeUfe2ubcfPXu
/nfz5CHADpJd9yaU6wIDAQAB
-----END PUBLIC KEY-----

-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCxhCGE+ALdMd1eQKPOvbYq+kNiGI43TJwSnLZu0SYxmUOWvOw6
VAMRawuIraZEiUR5hUMeLhRPB/bybd9b//N27hr5qk8GQylL1whCHklmadxWY09H
jUCngHb8JcF/hFYKB36ut5MT4HbBeUfe2ubcfPXu/nfz5CHADpJd9yaU6wIDAQAB
AoGARkFeiMMlUwjErWRU0GJZ4hmJKJ5CTtWFoYIfW61v+cpIKzTbI1yTmEWu22mq
d86B6Lxaq2ueQwcKb7kXYNBkOOFKmR0BUE7Douff1U0OeblsvaVwMOnsWujd2pz7
Qyxiq8Aeo27hXBKtzYqDep9qS9062ot/iuC7YVcADaoN76ECQQDQFaY4ipFARDJI
P6kDzJONZwk4TsL16bo5FJEEahpGB+FEeE1WZSKOEdlMDXxcEmigsOfQXDZXEKeK
3ZpumI7LAkEA2mSBz+Rpo/PBQv7Pw5IZsX62L8kcIdYDWcO2BQLZO5UvvKjyV+EP
y3MQKAvSfrPPZLf0hINreG0i+QkNCxAuYQJABhfMMTWlgSs+Ca2Lhmf0HhzZQUPV
I5w/brJyeJ2evVQ3vWiEYzY858oQAdEXRh7n32SutQQuNyhur3tDrV9cNwJAYGex
rhXEVG2LyIk3Kbvav7GRtvJuCrmwbPWp6fhh8J1gn0VtBUICeOroHOVrHCc/y9Qw
ijaBQH3czFYkfde8wQJBAKo8ijH1/4Ts94xAtM3tqLL/lzlPJeweavvVlHTK+3U3
Dp6HGIKEv+MtUVjPTNCSphBMOQJAsC/xbOg9R0CvezQ=
-----END RSA PRIVATE KEY-----

进程已结束,退出代码为 0
运行结果展示

2、RSA 加解密

RSA 模块

# 导入模块
import rsa
# rsa 公钥加密
def use_rsa_keyencry(self):
    # 读取生成的公钥
    with open("./key_iv/rsa_pubkey.pem", "rb") as f:
        file_pub = f.read()
    logs.debug(file_pub)

    # 秘钥解析
    rsa_key = rsa.PublicKey.load_pkcs1(file_pub)
    # logs.debug(rsa_key)
    # 需要加密的信息
    txt = "zhangsanlisiwangwanma/123456".encode("utf-8")
    # rsa加密
    enc_txt = rsa.encrypt(txt, rsa_key)

    logs.debug(enc_txt)
    return enc_txt


# rsa 私钥解密
def use_rsa_ivencry(self):
    # 读取与公钥匹配的私钥
    with open("./key_iv/rsa_prikey.pem", "rb") as f:
        file_pri = f.read()
    logs.debug(file_pri)
    # 秘钥解析
    rsa_iv = rsa.PrivateKey.load_pkcs1(file_pri)
    # 需要解密的信息
    en_txt = self.use_rsa_keyencry()
    # 解密
    den_txt = rsa.decrypt(en_txt, rsa_iv).decode()
    logs.debug(den_txt)

运行结果

b'dl\xe2*\x85\x00n\xbc\x9e\xc6\xd4@\x84\x05\xac\xc4F:\x16 \x98\xcf\x0fv\xab\x11\xd5|\xcd\xc2ssx\x1c\xef\x0es\x0c<\x1df\x83\xfd8\xca\xcf\x82\xac\xed\xd8O\x97\x95\xbb\xf9_\xae\xba`\xa6Py\xa7\xfd\xd3\xae\xa8_T\x16\xc1\x99\x10\x05B\x8fO8=\x9a\xb0\xe1\xda\xb4+\x91>O%\xb2q\x18\x7f\xe0&\xa6\x8fH;\x00\xcd?X\xaa\xaa\x1d+x\xa1!T\xc9\xb7\xb7\x12#\xe9\xaeY\xa5.\xda\xef\x9d\xa3#]\x95'
zhangsanlisiwangwanma/123456

 Crypto模块

from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as PKCS1_cipher
# crypto 模块公钥加密
def use_crypto_keyencry(self):
    msg = "需要加密的信息"
    # 读取公钥
    with open("./key_iv/crypto_pubkey.pem", "rb") as f:
        file_key = f.read()
    # RSA 的 importKey() 方法将读取到的公钥字符串 处理成可用的加密公钥
    crypt_pub = RSA.importKey(file_key)
    # 实例化一个加密对象 cipher 传入公钥对信息进行加密。
    cipher = PKCS1_cipher.new(crypt_pub)
    # 将结果转换成 base64 字符串
    encrypto_txt = base64.b64encode(cipher.encrypt(bytes(msg.encode("utf-8"))))
    logs.debug("crypto 加密数据:%s" % encrypto_txt.decode())
    return encrypto_txt

# crypto 模块私钥解密
def use_crypto_ivencry(self):
    msg = self.use_crypto_keyencry()
    with open("./key_iv/crypto_privkey.pem", "rb") as f:
        file_iv = f.read()
    # 使用 RSA 的 importKey()方法将 读取的私钥字符串 处理成可用的解密私钥
    crypt_pri = RSA.importKey(file_iv)
    # 实例化一个解密对象 cipher 传私钥对信息进行解密 解密结果与加密之前保持一致
    cipher = PKCS1_cipher.new(crypt_pri)
    # 将结果转换成 base64 字符串
    dencrypto_txt = cipher.decrypt(base64.b64decode(msg), 0)
    logs.debug("crypto解密数据:%s" % dencrypto_txt.decode())
    return dencrypto_txt

运行结果

crypto加密:wD28WBZN7Q7JfzBk3opIqb5Foj4NFuZeDIdnD4fnyJtB5EKtMeNImgJALh15vJ5xY5KO/39d0RMb+JMuhnuMwyBAPweY/WVmPVf5fTTcq3ZpyRBewoD0sQh86bifCaeP6ztlD9DYB0D+naqnEFUzIE7aBN4y6T1+7KhI+tQB3lM=
crypto解密:需要加密的信息

  Sign 签名验证

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

# 导入模块
from Crypto.Hash import SHA
from Crypto.Signature import PKCS1_v1_5 as PKCS1_signature
 # sign 私钥生成签名和公钥验证签名
    def use_sign(self, message):
        # message = "需要加密的信息alal"
        # 使用私钥生成签名
        with open('./key_iv/crypto_privkey.pem') as f:
            key = f.read()
            # RSA 的 importKey() 方法将读取的私钥字符串 处理成可用的私钥用于生成签名
            pri_key = RSA.importKey(key)
            # 实例化一个签名对象 signer  传入处理后的私钥
            signer = PKCS1_signature.new(pri_key)
            # 信息需要先转换成 sha 字符串
            digest = SHA.new()
            digest.update(message.encode("utf8"))
            # 对信息生成签名
            sign = signer.sign(digest)
            # 。生成的签名是字节串 将结果转换成 base64 字符串
            signature = base64.b64encode(sign)
            # logs.debug(signature.decode('utf-8'))

        # 使用公钥验证签名
        with open('./key_iv/crypto_pubkey.pem') as f:
            key = f.read()
            # RSA 的 importKey() 方法将读取的公钥字符串 处理成可用的公钥用于验证签名
            pub_key = RSA.importKey(key)
            # 实例化一个验证对象 verifier ,传入处理的公钥,
            verifier = PKCS1_signature.new(pub_key)
            digest = SHA.new()
            digest.update(message.encode("utf8"))
            # 对签名进行验证。验证结果是一个布尔值,验证成功返回 True , 不成功返回 False 。
            sign_result = verifier.verify(digest, base64.b64decode(signature))
            # logs.debug(sign_result)

        return signature, sign_result

  运行结果

NRGzy07A81TZfqTLBblV0aOOHJdXQG4Ddr9m7sHL5pSkvjfCvm4+5FXPiQpN9O80Kjc/mC2LevHkk/z1XLPYd+s+b9hRPuFJKCq555A5tahzMSWhv80vvhdgVeIej1FIlE+bOGNgRIBadKi3N6Anyii9/RvzfErLHYcO3SYHNog=
True

进程已结束,退出代码为 0

四、AES

 AES.MODE_OFB

加解密 

# -*- coding:utf-8 -*-
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex
from xx.logger import *

class aes_key():

    # 加解密钥 长度一般为:16, 24, 32
    key = 'aes_keysaes_keysaes_keys'
    # aes工作模式 mode对象, MODE_ECB,  MODE_CBC, MODE_CFB, MODE_OFB
    mode = AES.MODE_OFB

    def use_aes_encrypto(self, message):
        # 实例化一个对象 cryptor
        cryptor = AES.new(self.key.encode('utf-8'), self.mode, b'0000000000000000')
        # 对要加密的信息先进行处理; MODE_OFB 信息长度要处理成16的倍数
        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'))
        # 对加密结果进行16进制处理。
        result = b2a_hex(ciphertext)
        logs.debug(result.decode('utf-8'))
        return result

    def use_aes_decrypto(self, enc_re):
        cryptor = AES.new(self.key.encode('utf-8'), self.mode, b'0000000000000000')
        # a2b_hex 对密文进行16进制处理,然后进行 decrypto解密
        plain_text = cryptor.decrypt(a2b_hex(enc_re))
        logs.debug(plain_text.decode('utf-8').rstrip('\0'))

if __name__ == "__main__":
    message = "需要加密的信息123"
    # en_message = "6b6f8ad38dcbc28e65c2cd0360780f8c5070af38d0c3942a8e242a1de74e"
    
    en_message = aes_key().use_aes_encrypto(message)
    aes_key().use_aes_decrypto(en_message)

 运行结果


拓展

  问题1:引用crypto时提示:ModuleNotFoundError: No module named 'Crypto'

    解决方法:将site-package下的crypto文件夹该问Crypto

   问题2:运行提示:TypeError: a bytes-like object is required, not 'str'

    检查 bytes和str两种类型转换使用正确;转为bytes:encode()转为str:decode()

    

  问题3ValueError: Plaintext is too long.

    加密的plaintext最大长度是证书key位数/8 - 11,例如1024 bit的证书,被加密的串最长1024/8 - 11 = 117,

    解决办法是分块加密,然后分块解密证书key固定,加密的串长度也固定


注意

  从 Crypto.Cipher 中导入 PKCS1_v1_5 ,导入时记得重命名一下,如 PKCS1_cipher

  因为在 Crypto 的另一个模块 Crypto.Signature 中也有同名的类 PKCS1_v1_5

  同时使用时不重命名会造成冲突

高级加密标准AES的工作模式

mode对象: MODE_ECB, MODE_CBC, MODE_CFB, MODE_OFB

 

ECB模式(电子密码本模式:Electronic codebook)

  ECB是最简单的块密码加密模式,加密前根据加密块大小(如AES为128位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理。

CBC模式(密码分组链接:Cipher-block chaining)

  CBC模式对于每个待加密的密码块在加密前会先与前一个密码块的密文异或然后再用加密器加密。第一个明文块与一个叫初始化向量的数据块异或

CTR模式(计数器模式)

  CTR模式是一种通过将逐次累加的计数器进行加密来生成密钥流的流密码。自增的算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。

  这种加密方式简单快速,安全可靠,而且可以并行加密,但是在计算器不能维持很长的情况下,密钥只能使用一次。

CFB模式(密文反馈:Cipher feedback)

   与ECB和CBC模式只能够加密块数据不同,CFB能够将块密文(Block Cipher)转换为流密文(Stream Cipher)

OFB模式(输出反馈:Output feedback)

   OFB是先用块加密器生成密钥流(Keystream),然后再将密钥流与明文流异或得到密文流,解密是先用块加密器生成密钥流,再将密钥流与密文流异或得到明文,由于异或操作的对称性所以加密和解密的流程是完全一样的。