AES 一直是美国联邦政府使用的标准加密方法,已有 20 年的历史,直到今天,它仍然是保护数字数据的事实标准。事实上,AES 非常值得信赖。
如果您需要使用加密构建一个安全的 Web 应用程序,在下面的文章中,我们将一起了解 AES 是什么、其主要组件的工作原理以及如何在项目中正确使用 AES 加密和解密。
什么是AES
市面上有几十种加密算法,但最常用的是AES,即高级加密标准的缩写,也称为Rijndael。AES 是一种编码算法,可将纯文本数据转换为称为密文的版本,如果没有加密密钥(密码),人类或机器就无法理解该版本。
例如,在软件开发中使用 AES 将密码安全地存储在数据库中。将密码存储为纯文本将允许任何有权访问数据库的人登录用户帐户,因此加密密码是向身份验证系统添加安全层的第一步。
对称与非对称加密
AES 是一种对称密钥算法,这意味着使用相同的密钥(又名密码或密码)来加密和解密数据。此特性提供了以下各节中详述的优缺点。
非对称方法使用公钥进行加密,使用密钥进行解密。任何人都可以发送加密消息,但只有接收者知道如何解密它们。例如,用于安全 HTTP 通信 (HTTPS) 的 TLS 证书利用非对称加密。
为什么选择 AES 加密
首先,加密是软件开发的重要组成部分。您可以在任何地方使用它:您需要 API 密钥来与 Web 服务交互,您需要 TLS 证书才能通过 Internet 发送数据,您需要密码来实现用户帐户等。了解 AES 加密的工作原理以及如何使用它可以降低潜在数据泄露的风险。
AES 加密几乎是牢不可破的,因为暴力破解密钥需要数年时间。它非常安全,以至于政府使用它来加密绝密文件。它的数学特性使其比其他加密方法(如 Blowfish 或 Twofish)更不容易受到潜在攻击。
AES 加密也是最快的对称加密算法之一,因此在实际应用中大规模使用比 Serpent 等较慢的加密算法更实用。随着数据隐私对最终用户来说变得越来越重要,在数据密集型应用程序(如实施 E2E 加密的即时消息平台)中考虑加密速度至关重要——如果加密消息需要几分钟,软件将变得无法使用。
如何使用 AES 加密/AES 解密
1. AES算法概述
在不涉及算法的数学细节的情况下,您需要了解不同的参数,以便更好地掌握库如何实现 AES。
AES 算法可分为 3 个主要部分:
- 生成密钥
- 生成密码
- 使用密码加密/解密数据
生成 AES 密钥
AES 需要一个称为“密钥”的秘密密码来加密/解密数据。任何拥有密钥的人都可以解密您的数据,因此您需要它强大且对所有人隐藏 - 只有软件程序才能访问它。
密钥长度可以是 128、192、256 或 512 位。例如,使用 512 位密钥的 AES 密码缩写为 AES 512。密钥越长,安全性越高,但加密/解密速度也越慢。128 位相当于 base64 编码中的 24 个字符,44 位相当于 256 位。由于存储空间通常不是问题,并且版本之间的速度差异可以忽略不计,因此一个好的经验法则是使用 256 位密钥。
生成密码
密码是用于执行加密/解密的算法。为了处理任意长度和性质的数据,AES 的多种操作模式是可能的。每种模式都会改变一般算法,同时提供优点和缺点,我们将稍后阅读。
加密/解密数据
AES 对 16 字节的数据块进行操作,这些数据块表示为 4x4 二维数组。这些矩阵的字节内容使用密码(也称为分组密码)定义的数学函数进行转换,该函数为我们提供了密文 - 加密结果。
解密只是逆向操作。
在不涉及技术细节的情况下,数学函数保证了加密的强度,这就是为什么只要尊重函数的属性,AES 就被认为是牢不可破的——强密钥、正确实现的密码、唯一随机数、唯一初始化向量等。
2. 使用 PyCryptodome 在 Python 中实现 AES
每种编程语言都提供自己的 AES 算法实现。虽然可以从头开始实现 AES,但如果您不是网络安全专家,强烈建议您改用已知库:代码中最轻微的错误都会导致数据泄露!
在 Python 中,您拥有 pycryptodome 库。请注意,pycrypto 不再维护,但其分支 pycryptodome 被维护。
加密密码需要 3 行代码:
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
data = b'secret data'
key = get_random_bytes(16)
cipher = AES.new(key, AES.MODE_EAX)
ciphertext, tag = cipher.encrypt_and_digest(data)
nonce = cipher.nonce
解密密码同样简单:
cipher = AES.new(key, AES.MODE_EAX, nonce)
data = cipher.decrypt_and_verify(ciphertext, tag)
根据您使用的密码,您需要存储不同的元素 - 标签、随机数、初始化向量、MAC 等。
存储密钥的最简单方法是在程序中使用环境变量,例如使用 .env 文件:
AES_KEY=MbQeThWmZq4t6w9z
其他元素可以简单地使用 JSON 或通过串联与加密数据一起存储,因为它们的长度是固定的:
ciphertext, tag = cipher.encrypt_and_digest(data)
nonce = cipher.nonce
stored_text = nonce + tag + ciphertext
但是,您可能会问,每种模式有何不同?
3. AES模式和用例
AES 实现带有数十种不同的密码,因此您可能想知道最好使用哪一种。使用正确的操作模式对于使程序更安全非常重要,因为某些模式具有攻击者可以利用的漏洞。
TL;DR:默认使用 GCM 模式以获得最大安全性。
ECB(电子密码簿)模式 (AES-ECB)
每个 16 字节的明文块都是独立加密的。不建议使用此模式,因为它是最不安全的。
优点:
- 简单
缺点:
- 最弱密码
- 需要填充以将数据放入 16 字节块中
Python 中的实现:
cipher = AES.new(key, AES.MODE_ECB)
CBC(密码块链接)模式 (AES-CBC)
在加密之前,每个明文块都会与前一个密文块进行异或编辑。初始化向量用于确保每个加密具有不同的密文结果。
优点:
- 每个密文都是不同的,因此无法知道两个哈希数据字符串是否源自同一纯文本
缺点:
- 需要填充以将数据放入 16 字节块中
- 一个纯文本块中的错误将影响以下所有块
- 如果未更改每个加密的初始化向量,则容易受到填充预言机攻击、选择明文攻击和选择密文攻击等攻击
- 由于您需要知道前一个块才能加密下一个块,因此无法并行化操作,这会导致算法变慢
Python 中的实现:
cipher = AES.new(key, AES.MODE_CBC)
cipher_text = cipher.encrypt(pad(data, AES.block_size))
iv = cipher.iv
decrypt_cipher = AES.new(key, AES.MODE_CBC, iv)
plain_text = decrypt_cipher.decrypt(cipher_text)
CFB(密码反馈)模式 (AES-CFB)
将分组密码转换为流密码。每个内容字节都经过异或编辑,其中有一个字节取自密钥流。密钥流是通过加密使用分组密码生成的最后一个密文来获取的。
优点:
- 流密码接受任何长度的数据(即不需要填充)
缺点:
- 损坏的数据无法恢复,因为每个密文都依赖于另一个密文进行解密
- 由于您需要知道前一个块才能加密下一个块,因此无法并行化操作,这会导致算法变慢
Python 中的实现:
cipher = AES.new(key, AES.MODE_CFB)
cipher_text = cipher.encrypt(data)
iv = cipher.iv
decrypt_cipher = AES.new(key, AES.MODE_CFB, iv=iv)
plain_text = decrypt_cipher.decrypt(cipher_text)
OFB(输出反馈)模式 (AES-OFB)
将分组密码转换为流密码。每个内容字节都经过异或编辑,其中有一个字节取自密钥流。密钥流是通过递归加密初始化向量来获取的。
优点:
- 流密码接受任何长度的数据(即不需要填充)
缺点:
- 损坏的数据无法恢复,因为您需要正确的初始化向量来解密整个内容。
- 无法并行化(较慢)
Python 中的实现:
cipher = AES.new(key, AES.MODE_OFB)
cipher_text = cipher.encrypt(data)
iv = cipher.iv
decrypt_cipher = AES.new(key, AES.MODE_OFB, iv=iv)
plain_text = decrypt_cipher.decrypt(cipher_text)
CTR(计数器)模式 (AES-CTR)
将分组密码转换为流密码。每个内容字节都经过异或编辑,其中有一个字节取自密钥流。密钥流是通过使用 ECB 加密一系列计数器块来生成的。
优点:
- 接受任何长度的数据(即不需要填充)
- 每个计数器块都可以单独加密。并行化使算法更快。
缺点:
- 损坏的数据无法恢复
- 每条消息都需要不同的 IV 才能真正安全
Python 中的实现:
cipher = AES.new(key, AES.MODE_CTR
cipher_text = cipher.encrypt(data)
nonce = cipher.nonce
decrypt_cipher = AES.new(key, AES.MODE_CTR, nonce=nonce)
plain_text = decrypt_cipher.decrypt(cipher_text)
GCM(伽罗瓦计数器模式)模式 (AES-GCM)
计数器模式 (CTR) 和身份验证的组合。使用消息身份验证代码 (MAC) 进行身份验证。
优点:
- 保证完整性(确定密文是否在传输过程中被修改,或者它是否真的来自某个来源)
- 接受流水线和并行化实现,并具有最小的计算占用空间
缺点:
- 复杂的实现,很难从头开始编写代码
Python 中的实现:
header = b"header"
#Encryption
cipher = AES.new(key, AES.MODE_GCM)
cipher.update(header)
cipher_text, tag = cipher.encrypt_and_digest(data)
nonce = cipher.nonce
#Decryption
decrypt_cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
decrypt_cipher.update(header)
plain_text = decrypt_cipher.decrypt_and_verify(cipher_text, tag)
EAX(先加密后验证后翻译)模式 (EAX)
使用 AES 进行身份验证加密的另一种方法。
优点:
- 检测任何未经授权的修改
- 实现起来比 GCM 更简单
缺点:
- 比 GCM 慢
Python 中的实现:
header = b"header"
#Encryption
cipher = AES.new(key, AES.MODE_EAX)
cipher.update(header)
cipher_text, tag = cipher.encrypt_and_digest(data)
nonce = cipher.nonce
#Decryption
decrypt_cipher = AES.new(key, AES.MODE_EAX, nonce=nonce)
decrypt_cipher.update(header)
plain_text = decrypt_cipher.decrypt_and_verify(cipher_text, tag)