确定性地生成加密安全密钥和 IVEC
背景
我正在设计一个系统,可以为静态 Web 内容的用户开发动态身份验证方案。其动机是预先生成大量生成复杂但敏感的 Web 内容,然后通过基于 cookie(嵌入可逆加密信息)的身份验证静态地提供这些内容,并由 Web 服务器单独强制执行。使用 AEAD 模式加密原语。
问题
我需要生成在一段时间内有效的 IVEC 和密钥,比如一周(当前有效的对)。并且过去的 IVEC/密钥的有效期为 2 周(历史有效),并且使用历史有效密钥加密的任何数据都将使用当前有效的 IVEC/KEY 重新加密。
我需要的是一个确定性的 CSPRNG,它是随机数和密码的种子,并且可以以索引方式生成 64 位或 128 位数字块。如果我使用自“1970 年 1 月 1 日”以来的几周作为我假设的 CSPRNG 的索引元素之一,我应该能够构建一个随着时间的推移自动更改密钥的系统。
我正在考虑的方法
现在我在 cryptopp 中没有看到这样的功能,或者我现在已经足够了解这些术语,并且由于 cryptopp 是最先进的加密库,所以我不知道有信心我会找到另一个。因此,如果我找不到那里的实现,我应该推出自己的实现。从连接的数据中生成静态字符串结构然后对其进行散列(如下所示)可以解决问题吗?
RIPEMD160(RandomPreGenerateFixedNonce:PassPhrase:UInt64SinceEpoch:128BitBlockIndexNumber);
注意:块编号将被分配并具有常规结构,因此例如对于 128 位摘要,块 0 的前 64 位将用于 ivec,所有元素 1 将用于 128 位密钥。
这是一种合理的方法(即加密安全)吗?
-- 编辑:发表接受评论 --
经过一番思考,我决定将我最初考虑的密码和随机数/盐合并到 16 字节(加密强)密钥中,并使用PKCS #5 中概述的技术用于派生多个基于时间的密钥。不需要盐,因为不使用密码。
Background
I am designing a system which enables the development of dynamic authentication schemes for a user of static web content. The motivation is to pre-generate large amounts of complex-to-generate, yet sensitive web-content, and then serve it statically with cookie-based (embedding reversably encrypted information) authentication in place, enforced by the web-server inately. Using an AEAD-mode encryption primitive.
The Problem
I need to generate IVEC's and keys that are valid for a duration of time, say one week (the current-valid pair). and that past IVECs/Keys are also valid for say 2 weeks(historically-valid) and any data encrypted with the historically valid secrets will just be re-encrypted with the current-valid IVEC/KEY.
What I need is a deterministic CSPRNG that seeds of a random number and a passphrase and that can produce in an indexed fashion 64-bit or 128-bit blocks of numbers. If I use a weeks-since-"jan 1 1970" as one of the index element of my hypothetical CSPRNG I should be able to build a system that innately changes keys automatically as time goes by.
Approach I am Considering
Now I don't see such functionality in cryptopp, or I do now know the terminology well enough, and as cryptopp is the most advanced of the encryption libraries out there, I don't have confidence I will find another one. So, If I can't find an implementation out there, I should roll my own. Will generating a static string structure out of the concatinated data and then hashing it (shown below) do the trick ?
RIPEMD160(RandomPreGeneratedFixedNonce:PassPhrase:UInt64SinceEpoch:128BitBlockIndexNumber);
Note: The blocknumbers will be assigned and have a regular structure, so for example for a 128-bit digest, the first 64-bits of block 0 will be for the ivec, and all of element 1 for the 128-bit key.
Is this a sound approach (--.i.e, cryptographically secure) ?
-- edit: post accept comment --
After some reflection, I have decided to merge what I originally considered the passphrase and the nonce/salt into a 16-byte (cryptographicall strong) key, and use the techniques outlined in the PKCS #5 to derive multiple time-based keys. There isn't a need for a salt, as passphrases aren't used.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
有趣的问题。
首先,您的初始向量不必是密码学上强的随机量,但它们应该每条消息都是唯一的。 IV 实际上只是一种盐值,可确保使用相同密钥加密的类似消息在加密后不会看起来相似。您可以使用任何快速伪随机生成器来生成 IV,然后将其(最好是加密的)与加密数据一起发送。
当然,按键的强度应与您实际制造的一样强。
在我看来,您对包含随机数、密码和有效性数据的文本字符串进行散列的建议似乎非常合理——它与使用密码生成密钥的其他系统所做的工作大致一致。您应该散列更多次 - 而不是一次 - 以使密钥生成的计算成本昂贵(对于任何试图暴力破解密钥的人来说,这将是一个比您更大的问题)。
您可能还想查看 PKCS#5 中规定的密钥生成方案(例如,在 http://www.faqs.org/rfcs/rfc2898.html),它在 cryptopp 中作为 PasswordBasedKeyDerivationFunction 实现。该机制已被广泛使用,并且已知相当安全(请注意,PKCS#5 建议对密码数据进行哈希处理至少 1000 次)。您可以将有效期和索引数据附加到密码短语中,并按原样使用PasswordBasedKeyDerivationFunction。
您没有说明您建议使用哪种加密算法来加密数据,但我建议您应该选择广泛使用且已知安全的加密算法……特别是我建议您使用 AES。我还建议使用 SHA 摘要函数之一(可能作为 PasswordBasedKeyDerivationFunction 的输入)。 SHA-2 是最新的,但 SHA-1 足以用于密钥生成目的。
您也没有说明您要生成的密钥长度,但您应该知道密钥中的熵量取决于您使用的密码短语的长度,除非密码短语非常< /em> long 远小于理想情况下所需的密钥长度。
该方案中最薄弱的环节是密码短语本身,这总是会限制您可以实现的安全级别。只要你对数据加盐(就像你正在做的那样)并使密钥生成成本高昂以减缓暴力攻击,你应该没问题。
Interesting question.
First, your Initial Vectors don't have to be cryptographically strong random quantities, but they should be unique per-message. The IV is really just a kind of salt value that ensures that similar messages encrypted using the same key don't look similar once they're encrypted. You can use any quick pseudo-random generator to generate the IV, and then send it (preferably encrypted) along with the encrypted data.
The keys, of course, should be as strong as you can practically make them.
Your proposal to hash a text string containing a nonce, passphrase, and validity data seems to me to be very reasonable -- it's broadly in line with what is done by other system that use a passphrase to generate a key. You should hash more many times -- not just once -- to make the key generation computationally expensive (which will be a bigger problem for anyone trying to brute-force the key than it will for you).
You might also want to have a look at the key-generation scheme set out in PKCS#5 (e.g. at http://www.faqs.org/rfcs/rfc2898.html) which is implemented in cryptopp as PasswordBasedKeyDerivationFunction. This mechanism is already widely used and known to be reasonable secure (note that PKCS#5 recommends hashing the passphrase data at least 1000 times). You could just append your validity period and index data to the passphrase and use PasswordBasedKeyDerivationFunction as it stands.
You don't say what encryption algorithm you propose to use to encrypt the data, but I would suggest that you should pick something widely-used and known to be secure ... and in particular I'd suggest that you use AES. I'd also suggest using one of the SHA digest functions (maybe as an input to PasswordBasedKeyDerivationFunction). SHA-2 is current, but SHA-1 is sufficient for key generation purposes.
You also don't say what key length you're looking to generate, but you should be aware that the amount of entropy in your keys depends on the length of the passphrase that you use, and unless the passphrase is very long that will be much less than the keylength ideally requires.
The weakest link in this scheme is the passphrase itself, and that's always going to limit the level of security you can achive. As long as you salt your data (as you are doing) and make the key-generation expensive to slow down brute-force attacks you should be fine.
好吧,我认为解决方案的一部分是使用非基于时间的生成器。这样,如果双方都以相同的种子开始,那么它们都会产生相同的随机流。您可以在此基础上添加“自 1970 年第 1 周以来的周数”逻辑。
为此,您可以使用
OFB_mode::Encryption
。它可以用作生成器,因为 OFB 模式使用从RandomNumberGenerator
派生的AdditiveCipherTemplate
。事实上,Crpyto++ 使用
test.cpp
中的生成器,以便在出现故障时可以重现结果。以下是使用OFB_mode::Encryption
的方法。它也适用于CTR_Mode::Encryption
:对
OS_GenerateRandomBlock
的调用从/dev/{u|s}random
获取字节并然后将其用作模拟共享种子。程序的每次运行都会有所不同。在程序的每次运行中,它都会打印类似于以下内容的内容:还有另一个可用的生成器可以执行相同的操作,但它不是 Crypto++ 库的一部分。它称为 AES_RNG,基于 AES-256。它是一个仅标头的实现,您可以在 Crypto++ wiki 的 RandomNumberGenerator 下找到它。
另请参阅 Crypto++ wiki 上
RandomNumberGenerator
类的主题 再现性 。Well, I think part of the solution is to use a non-time based generator. That way, if both sides start with the same seed, then they both produce the same random stream. You can layer your "weeks since Week 1, 1970" logic on top of that.
To do that, you would use
OFB_mode<T>::Encryption
. It can be used as a generator because OFB mode usesAdditiveCipherTemplate<T>
, which derives fromRandomNumberGenerator
.In fact, Crpyto++ uses the generator in
test.cpp
so that results can be reproduced if something fails. Here's how you would useOFB_mode<T>::Encryption
. It also applies toCTR_Mode<T>::Encryption
:The call to
OS_GenerateRandomBlock
fetches bytes from/dev/{u|s}random
and then uses that as a simulated shared seed. Each run of the program will be different. Within each run of the program, it prints similar to the following:There's another generator available that does the same, but its not part of the Crypto++ library. Its called
AES_RNG
, and its based on AES-256. Its a header only implementation, and you can find it at the Crypto++ wiki under RandomNumberGenerator.Also see the topic Reproducibility for
RandomNumberGenerator
class on the Crypto++ wiki.