使用python进行AES对称加密解密

常用的对称加密算法简介

有时需要对数据进行对称加解密,常用的有:

  • DES(Data Encryption Standard,即数据加密标准)。
    由于这种加密算法可以被暴力破解,所以已经不再安全。因此除了用它来解密以前的密文外,不应该再使用DES了。
  • AES(Advanced Encryption Standard,即高级加密标准),已经取代DES成为新标准的对称加密算法。
    AES加密法属于Rijndael加密法的子集。区别为:
    • 在AES的规格中,区块长度固定为128比特,密钥长度只有128,192和256比特三种选择。
    • Rijndael的区块长度和密钥长度可以是位于128比特到256比特之间且为32比特整数倍的长度。

经典的对称区块加密模式

区块加密算法采用对称密钥,可以加密固定长度的较短(区块数量)的数据。为了处理任意长度的数据,加密算法必须指定加密模式。常用的加密模式如下:

  • ECB mode (Electionic CodeBook)
    最基本但是安全性最差的加密操作模式,不应该再使用此模式
  • CBC mode(Ciphertext Block Chaining)
  • CTR mode(Count TeR mode)
    可用于并行运算,
  • CFB mode(Cipher FeedBack)
  • OFB mode(Output FeedBack)
  • OpenPGP mode

现代的对称区块加密模式

经典的加密模式,比如CBC mode只能保证机密性,但不能保证完整性。
基于这个原因,经典模式经常会和MAC算法(比如Crypto.Hash.HMAC)搭配使用,但是这种结合不够直观,有效和安全。
因此,有一些新的同时具有加密和验证完整性的模式设计出来:

  • CCM mode(Counter with CBC-MAC)
    只能和128比特的AES加密算法搭配使用。
  • EAX mode(An AEAD mode designed for NIST)
  • GCM mode(Galois/Counter Mode)
    只能和128比特的AES加密算法搭配使用。
  • SIV mode(Synthetic Initialization Vector (SIV))
    只能和128比特的AES加密算法搭配使用。
  • OCB mode(Offset CodeBook mode)
    只能和128比特的AES加密算法搭配使用。

使用示例

安装依赖库

鉴于pycrypto已不再安全,建议使用pycryptodome。pycyrptodome 是pycrypto的分支,在安全性方面有较大提升。

pip install pycryptodome

示例

以经典的CBC模式为例

# -*- coding:utf-8  -*-
import json
from base64 import b64encode,b64decode
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
from Crypto.Random import get_random_bytes

class AESClassicCipher:
    def __init__(self, key):
        self.bs = AES.block_size
        self.key = key
        self.mode = AES.MODE_CBC

    def encrypt(self, data):
        cipher = AES.new(self.key, self.mode)
        ct_bytes = cipher.encrypt(pad(data, self.bs))
        iv = b64encode(cipher.iv).decode('utf-8')
        ct = b64encode(ct_bytes).decode('utf-8')
        return json.dumps({'iv':iv, 'ciphertext':ct})
    
    def decrypt(self, json_input):
        try:
            b64 = json.loads(json_input)
            iv = b64decode(b64['iv'])
            ct = b64decode(b64['ciphertext'])
            cipher = AES.new(self.key, self.mode, iv)
            plaintext = unpad(cipher.decrypt(ct), self.bs)
            return plaintext.decode('utf-8')
        except (ValueError, KeyError) as err:
            print("Incorrect decryption ", err)
            return None

if __name__ == "__main__":
    data = "你的CBC密文".encode('utf-8')
    key = get_random_bytes(16)
    
    aes_cipher = AESClassicCipher(key)

    encrypt_reuslt = aes_cipher.encrypt(data)
    print("Encryption was: ", encrypt_reuslt)

    plaintext = aes_cipher.decrypt(encrypt_reuslt)
    print("The message was: ", plaintext)

输出结果为

Encryption was:  {"iv": "B6VyPVmqTvpa9deaZgCGxg==", "ciphertext": "UJormRNFuE/oHynbtIVCIw=="}
The message was:  你的CBC密文

以现代的OCB模式为例

# -*- coding:utf-8 -*-
import json
from base64 import b64encode, b64decode
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes

class AESModernCipher:
    def __init__(self, key):
        self.bs = AES.block_size
        self.key = key
        self.mode = AES.MODE_OCB
        self.json_k = [ 'nonce', 'header', 'ciphertext', 'tag' ]

    def encrypt(self, header, data):
        header = header
        cipher = AES.new(self.key, self.mode)
        cipher.update(header)
        ciphertext, tag = cipher.encrypt_and_digest(data)
        json_v = [ b64encode(x).decode('utf-8') for x in [cipher.nonce, header, ciphertext, tag] ]
        return json.dumps(dict(zip(self.json_k, json_v)))
    
    def decrypt(self, json_input):
        try:
            b64 = json.loads(json_input)
            jv = {k:b64decode(b64[k]) for k in self.json_k}

            cipher = AES.new(self.key, self.mode, nonce=jv['nonce'])
            cipher.update(jv['header'])
            plaintext = cipher.decrypt_and_verify(jv['ciphertext'], jv['tag'])
            return plaintext.decode('utf-8')
        except (ValueError, KeyError) as err:
            print("Incorrect decryption ", err)
            return None

if __name__ == "__main__":
    data = "你的OCB密文".encode('utf-8')
    key = get_random_bytes(16)
    header = b'header'

    aes_cipher = AESModernCipher(key)
    
    encrypt_reuslt = aes_cipher.encrypt(header, data)
    print("Encryption was: ", encrypt_reuslt)

    plaintext = aes_cipher.decrypt(encrypt_reuslt)
    print("The message was: ", plaintext)

输出结果为:

Encryption was:  {"nonce": "Sa8Fah2cNmMnIST22XL2", "header": "aGVhZGVy", "ciphertext": "sQqARVVaflyuyB0BV6FE", "tag": "BBMDIbVbjv7lVCYBWIuOdw=="}
The message was:  你的OCB密文

可能问题

from Crypto.Cipher import AES

运行上面语句会报错:

from Crypto.Cipher import AES
ModuleNotFoundError: No module named ‘Crypto’

可能原因是安装有crypto, pycrypto, pycryptodome中的多个库。
解决办法:先将其全部卸载,然后再重新安装 pycryptodome.

pip uninstall crypto pycrypto pycryptodome
pip install pycryptodome

参考文章:

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lylhw13_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值