C# AES Rijndael - 检测无效密码
我正在使用 Rijndael 加密程序中的一些敏感数据。
当用户输入错误的密码时,大多数情况下会抛出 CryptographicException
并显示消息“Padding 无效且无法删除。”。
然而,CryptStream 不会抛出密码错误的异常,而是返回错误解密的流,概率非常小。换句话说,它解密为垃圾。
知道如何检测/防止这种情况吗?我能想到的最简单的方法是在加密时在消息的开头放置一个“幻数”,并在解密后检查它是否仍然存在。
但如果有更简单的方法,我很想听听!
I'm using Rijndael to encrypt some sensitive data in my program.
When the user enters an incorrect password, most of the time a CryptographicException
is thrown with the message "Padding is invalid and cannot be removed.".
However, with very small probability, the CryptStream does not throw an exception with the wrong password, but instead gives back an incorrectly decrypted stream. In other words, it decrypts to garbage.
Any idea how to detect/prevent this? The simplest way I can think of would be to put a "magic number" at the start of the message when encrypting, and check if it's still there after decrypting.
But if there's an easier way, I'd love to hear it!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
HMAC 正是您所需要的。它正是为此目的而设计的。它将密钥和消息(在本例中为您的密码)组合在一起,并以确保内容的真实性和完整性的方式对它们进行哈希处理,只要使用的哈希函数是安全的。您可以将 HMAC 附加到加密数据,稍后可以使用它来验证解密是否正确。
HMAC is what you need. It is exactly made for this purpose. It combines the key and the message (which in this case, will be your password) and hashes them in a way that it will ensure the authenticity and integrity of the content, as long as the hash function used is secure. You can attach the HMAC to the encrypted data, and it can be used later to validate if the decryption was made correctly.
校验和正是用于此目的。在加密之前获取数据的哈希值。加密数据并将其与哈希值一起放入存储中。解密后,获取解密数据的哈希值并与前者进行比较。如果您使用加密级哈希(即 SHA512),您的数据将是安全的。毕竟,这正是加密压缩软件的作用。
为了获得最终的安全性,您可以分别加密哈希值和数据,然后解密并比较。如果数据和哈希都解密为损坏的数据,那么它们匹配的机会非常小。
Checksums are exactly for this purpose. Get a hash of your data before encrypting. Encrypt the data and put it along with the hash into storage. After decrypting, get the hash of the decrypted data and compare it with the former. If you use a crypto grade hash (i.e. SHA512) your data will be safe. After all, this is exactly what encrypted compression software does.
For ultimate security, you can encrypt both the hashes and data separately then decrypt and compare. If both data and hash decrypts to corrupted data, there is very minuscule chances that they will match.
要检查您使用的密码是否正确,您可以使用
本质上的这段代码,检查解密过程中是否产生错误消息。
我将所有解码代码报告如下
To check if the password you are using is correct, you can use this code
in essence, check if an error message is generated during decryption.
I report all the decoding code below
虽然我在某种程度上同意 Teoman Soygul 关于 CRC/Hash 的帖子,但有一件非常重要的事情需要注意。切勿加密哈希值,因为这样可以更轻松地找到结果密钥。即使没有加密哈希值,您仍然为他们提供了一种简单的方法来测试他们是否已成功获得正确的密码;然而,我们假设这已经是可能的。因为我知道您加密了哪种数据,无论是文本,还是序列化对象,还是其他什么,我很可能可以编写代码来识别它。
也就是说,我使用了以下代码的派生来加密/解密数据:
Though I can agree somewhat with Teoman Soygul post about CRC/Hash there is one very important thing to note. Never encrypt the hash as this can make it easier to find the resulting key. Even without encrypting the hash you still gave them an easy way to test if they have successfully gained the correct password; however, let's assume that is already possible. Since I know what kind of data you encrypted, be it text, or serialized objects, or whatever, it's likely I can write code to recognize it.
That said, I've used derivations of the following code to encrypt/decrypt data:
我喜欢Can Gencer的回答;如果没有 HMAC,您就无法真正验证解密。
但是,如果您有非常大的明文,那么解密的成本可能会非常高。您可能做了大量工作才发现密码无效。如果能够快速拒绝错误密码,而无需完成所有这些工作,那就太好了。有一种使用 PKCS#5 PBKDF2 的方法。 (在 RFC2898 中标准化,您可以在 Rfc2898DeriveBytes)。
通常,数据协议要求使用 PBKDF2 从密码和盐生成密钥,循环次数为 1000 次或某个指定次数。然后也可以(可选)通过相同算法的延续来初始化向量。
要实现快速密码检查,请通过 PBKDF2 生成另外两个字节。如果您不生成和使用 IV,则只需生成 32 个字节并保留最后 2 个字节。将这对字节与您的密码文本相邻存储或传输。在解密方面,获取密码,生成密钥和(可能是一次性的)IV,然后生成 2 个附加字节,并根据存储的数据检查它们。如果这对不匹配,您知道您的密码错误,无需任何解密。
如果它们匹配,则不能保证密码正确。为此,您仍然需要完整明文的 HMAC。但是,在大多数“密码错误”的情况下,您可以节省大量的工作,甚至可能节省挂钟时间,并且不会影响整个系统的安全性。
ps:您写道:
避免将明文放入密文中。它只会暴露另一个攻击向量,使攻击者更容易消除错误的转弯。我上面提到的密码验证是另一种动物,不会暴露这种风险。
I like Can Gencer's answer; you cannot really verify a decryption without the HMAC.
But, if you have a very a very large plaintext, then the decrypting can be very expensive. You might do a ton of work just to find out that the password was invalid. It would be nice to be able to do a quick rejection of wrong passwords, without going through all that work. There is a way using the PKCS#5 PBKDF2. (standardized in RFC2898, which is accessible to your c# program in Rfc2898DeriveBytes).
Normally the data protocol calls for generation of the key from a password and salt using PBKDF2, at 1000 cycles or some specified number. Then maybe also (optionally) the initialization vector, via a contniuation of the same algorithm.
To implement the quick password check, generate two more bytes via the PBKDF2. If you don't generate and use an IV, then just generate 32 bytes and keep the last 2. Store or transmit this pair of bytes adjacent to your cryptotext. On the decrypting side, get the password, generate the key and (maybe throwaway) IV, then generate the 2 additional bytes, and check them against the stored data. If the pairs don't match you know you have a wrong password, without any decryption.
If they match, it is not a guarantee that the password is correct. You still need the HMAC of the full plaintext for that. But you can save yourself a ton of work, and maybe wall clock time, in most cases of "wrong password", and without compromising the security of the overall system.
ps: you wrote:
Avoid putting plaintext into the cryptotext. It only exposes another attack vector, makes it easier for an attacker to eliminate wrong turns. The password verification thing I mentioned above is a different animal, does not expose this risk.
我使用该代码来解密任何文件。
cryptostream.close()
检测到错误的密码。当使用错误的密钥解密文件时,将此行捕获为错误。当发生错误时,只需关闭输出流并释放它(将outputFile
设置为Nothing
),然后删除输出文件。它对我有用。I use that code to decrypt any file. Wrong password detected on
cryptostream.close()
. Catch this line as error when a wrong key is used to decrypt file. When error happens, just close the output stream and release it (setoutputFile
toNothing
), then delete output file. It's working for me.