Python 这是AES GCM文件加密的良好实践吗?

Python 这是AES GCM文件加密的良好实践吗?,python,encryption,cryptography,aes,aes-gcm,Python,Encryption,Cryptography,Aes,Aes Gcm,我用这个加密一个文件,然后用AES-GCM解密一个文件: (如果尚未安装,请先安装pycryptodomepip) 这是否被认为是一种良好的加密实践,以及此文件加密实施的潜在弱点是什么? 备注:我有10000个文件要加密。如果每次我加密一个文件时,我都调用KDF(使用一个高count值),这将是非常低效的 更好的解决方案是:只调用KDF一次(使用nonce1),然后对每个文件执行以下操作: nonce2 = Crypto.Random.new().read(16) cipher, tag =

我用这个加密一个文件,然后用AES-GCM解密一个文件:

(如果尚未安装,请先安装pycryptodome
pip)

这是否被认为是一种良好的加密实践,以及此文件加密实施的潜在弱点是什么?


备注:我有10000个文件要加密。如果每次我加密一个文件时,我都调用KDF(使用一个高
count
值),这将是非常低效的
更好的解决方案是:只调用KDF一次(使用
nonce1
),然后对每个文件执行以下操作:

nonce2 = Crypto.Random.new().read(16)
cipher, tag = AES.new(key, AES.MODE_GCM, nonce=nonce2).encrypt_and_digest(plain)

但这是否意味着我必须为每个文件将
nonce1 | nonce2 | ciphertext |标记
写入磁盘?这会在每个文件中添加一个额外的16字节的nonce1…

这似乎很好,但我有一个建议,即不要在加密和密钥派生中使用相同的nonce(nonce表示仅使用同一个nonce使用一次的密钥,因此如果不想使用另一个nonce,则可以将nonce的
md5
散列传递给加密函数(IV)第二,如果您对更好的安全性感兴趣,我认为您可以切换到
加密
。这是使用
加密
模块进行加密的示例代码,该模块还具有使用
128位
密钥进行加密的优点,该密钥是安全的,它会处理其余的,例如
IV
(nonces),解密和验证(使用
HMAC
)完成)。因此,您上面的所有代码都可以总结在这几行代码中,从而降低了复杂性,因此可以说代码更安全

from cryptography.fernet import Fernet
plaintext = b"hello world"
key = Fernet.generate_key()
ctx = Fernet(key)
ciphertext = ctx.encrypt(plaintext)
print(ciphertext)
decryption = ctx.decrypt(ciphertext)
print(decryption)

编辑:请注意,您使用的nonce也会削弱密钥,因为nonce是用密文发送的,现在用于
PBKDF
的salt是没有意义的,现在攻击者只需猜测您的密码(假设使用默认计数),在这种情况下,这是非常简单的,暴力强迫不会超过
26^5
次尝试(总长度为5的小写字母总数)。

改进代码的建议是为GCM应用12字节的nonce。目前使用的是16字节的nonce,这应该更改,请参见第节注释和

对于GCM的安全性来说,关键是不能多次使用密钥/nonce对。因为在每次加密的代码中都会生成一个随机nonce,所以可以防止此问题

您的代码也将nonce应用于密钥派生,这在原则上没有安全问题,因为这不会导致多次使用同一个密钥/nonce对

然而,这样做的一个缺点可能是盐的长度由当前长度决定。如果不需要(例如,如果应该使用更大的盐),另一种方法是为每个加密生成一个随机salt,通过KDF导出密钥和nonce。在这种情况下,连接的数据
salt | ciphertext | tag
将传递给接收者。另一种方法是完全分离nonce和密钥生成,并为每个en生成加密用于密钥生成的随机nonce和随机salt。在这种情况下,连接的数据
salt | nonce | ciphertext |标记
必须传递给收件人。请注意,与nonce和标记一样,salt也不是秘密,因此它可以与密文一起发送

代码应用的迭代计数为100000。通常,以下情况适用:迭代计数应尽可能高,以满足您的环境的承受能力,同时保持可接受的性能。如果100000满足您的环境的此标准,则这是正常的

您使用的连接顺序是
nonce | tag | ciphertext
。只要双方都知道这一点,这就不是问题。通常按照惯例,使用
nonce | ciphertext | tag
顺序(例如,Java隐式地将标记附加到密文中),如果您想遵守此约定,也可以在代码中使用

同样重要的是,要使用最新的、维护的库,PyCryptodome就是这样(不同于它的前身,传统的PyCrypto,它根本不应该使用)

编辑:
PyCryptodome的PBKDF2实现默认使用16字节作为生成密钥的长度,对应于AES-128。对于摘要,默认情况下应用HMAC/SHA1。发布的代码使用这些标准参数,其中没有一个是不安全的,但当然可以在必要时进行更改。
注意:尽管SHA1本身是不安全的,但这不适用于上下文 然而,为了支持SHA1从生态系统中灭绝,可以使用SHA256


编辑:(关于问题的更新):

在编辑的问题中呈现的用例是10000个文件的加密。为每个文件执行发布的代码,以便通过KDF生成相应数量的密钥,从而导致相应的性能损失。您认为这是非常低效的。但是,不应忘记,当前代码侧重于安全性,而不是性能。在我的回答中,我指出,例如,迭代计数是一个参数,允许在一定范围内调整性能和安全性

PBKDF(基于密码的密钥派生函数)允许从弱密码派生密钥。为确保加密安全,派生时间会故意增加,以便攻击者破解弱密码的速度不会快于破解强密钥(理想情况下)。如果派生时间缩短(例如,通过减少迭代次数或多次使用同一密钥)这通常会导致安全性降低。或者简言之,性能提高(通过更快的PBKDF)通常会降低安全性。这会为性能更高(但较弱)的解决方案带来一定的回旋余地

性能更高的解决方案是y
from cryptography.fernet import Fernet
plaintext = b"hello world"
key = Fernet.generate_key()
ctx = Fernet(key)
ciphertext = ctx.encrypt(plaintext)
print(ciphertext)
decryption = ctx.decrypt(ciphertext)
print(decryption)