确定性生成具有密码学安全性的密钥和IVEC

13

背景

我正在设计一个系统,为静态网页的用户提供动态认证方案的开发。其动机是预先生成大量难以生成但敏感的网页内容,并使用基于 cookie 的(嵌入可逆加密信息的)认证机制,在 Web 服务器本身强制执行。使用 AEAD 模式的加密原语。

问题

我需要生成有效期为一周的 IVEC 和密钥(当前有效对),并且过去的 IVEC/密钥也有效,比如说两周(历史有效),任何使用历史有效密钥加密的数据都将被重新加密为当前有效的 IVEC/KEY。

我需要的是一个确定性 CSPRNG,它以随机数和口令作为种子,并能够以索引方式产生64位或128位的数字块。如果我使用自1970年1月1日起的“经过的周数”作为我假设的 CSPRNG 的一个索引元素,我应该能够构建一个系统,随着时间的推移自动地更改密钥。

我考虑的方法

现在我没有看到 cryptopp 中具有这样的功能,或者我不知道术语是否足够好,因为 cryptopp 是最先进的加密库之一,我不确定能否找到另一个库,所以如果我找不到实现,我就应该自己编写。将预生成的固定随机数和口令与 UInt64SinceEpoch 和 128BitBlockIndexNumber 连接起来,生成静态字符串结构然后对其进行哈希(如下所示)是否可行?

RIPEMD160(RandomPreGeneratedFixedNonce:PassPhrase:UInt64SinceEpoch:128BitBlockIndexNumber);

注意:块编号将被分配并具有规律性结构,因此例如对于128位摘要,块0的前64位将用于 IVEC,所有元素1将用于128位密钥。

这是一个可行的方法(即加密安全的)吗?

-- 编辑:已接受的评论 --

经过一些思考,我决定将原本认为的口令和 nonce / salt 合并为一个 16 字节(具有密码学强度)的密钥,并使用 PKCS#5 中概述的技术派生出多个基于时间的密钥。由于没有使用口令,因此不需要盐。


请将以下有关编程的内容从英语翻译成中文。如果适用,请标记为C++。 - Jason S
2个回答

5
有趣的问题。
首先,您的初始向量不必是密码学强随机量,但它们应该是每个消息唯一的。IV实际上只是一种盐值,确保使用相同密钥加密的类似消息在加密后看起来不相似。您可以使用任何快速伪随机生成器生成IV,然后将其(最好加密)与加密数据一起发送。
当然,密钥应尽可能强大。
您提议散列包含一次性号码、口令短语和有效性数据的文本字符串,我认为这非常合理——它与使用口令短语生成密钥的其他系统基本一致。您应该多次散列——而不仅仅是一次——以使密钥生成计算上昂贵(这对于试图暴力破解密钥的任何人而言都是一个更大的问题,而不是对您而言)。
您还可以查看PKCS#5中设置的密钥生成方案(例如,在http://www.faqs.org/rfcs/rfc2898.html),它作为PasswordBasedKeyDerivationFunction在cryptopp中实现。这种机制已经被广泛使用,并且已知是相当安全的(请注意,PKCS#5建议至少对口令数据进行1000次散列)。您可以将有效期和索引数据附加到口令短语中,并像现有的PasswordBasedKeyDerivationFunction一样使用它。
您没有说您打算使用哪种加密算法来加密数据,但我建议您选择一些广泛使用且已知为安全的东西...特别是我建议您使用AES。我还建议使用SHA摘要函数之一(可能作为输入到PasswordBasedKeyDerivationFunction)。 SHA-2是当前的标准,但SHA-1对于密钥生成目的而言已足够。
您还没有说您要生成多长的密钥,但您应该知道密钥中的熵量取决于您使用的口令短语长度,除非口令短语非常长,否则远远不及理想要求的密钥长度。
这个方案中最薄弱的环节是口令短语本身,这总是限制您可以实现的安全级别。只要您盐化您的数据(正如您所做的那样),并使密钥生成变得昂贵以减慢暴力攻击,您就应该没问题了。

回答写得非常好。将IV与消息一起发送的问题在于它使消息变大了(在这种情况下是cookie)。然而,我不想设计一个IV永久静态的系统。我之前设计过类似的系统(研究原型),并确保生成的cookie以生成时间戳开头,这确保了每个消息都是唯一的,并且这些cookie具有2个线性增加的GUID,其中至少一个是每个cookie唯一的。更改的GUID在时间戳之后。该系统确实有一个硬编码的IV。 - Hassan Syed
我现在正在开发的系统原型,目前首先要进行真实性检验(HMAC-RIPEMD160),然后将摘要放在消息前面,并在摘要:消息上运行Blowfish。在设计我的系统时,我不知道AEAD模式。我需要在这个领域做更多的研究。我希望其中一个AEAD模式通过设计使用摘要。 - Hassan Syed
RFC你认为对于数据经常变化的消息,硬编码IV(或每周更改一次IV)有何看法? - Hassan Syed
就我所知,AES已经出现了侧信道攻击的报告。只要您掌控服务器,它可能还好,但如果您让不受信任的本地进程运行,我建议使用其他东西。 - David Thornley
@dajames:一次性密码本是一种仅使用一次的密钥流,通常与数据进行异或运算。每个好的或坏的流密码或块密码在OFB模式下(包括AES :))基本上都会生成这样的密钥流,因此应该仅与每个密钥-IV组合一次使用。是的,CBC没有这样的问题,但你是否应该依赖于代码中的“永远不要更改链接模式”注释?最好将显式消息计数器作为IV的一部分或类似的东西。 - blaze
显示剩余4条评论

1
我需要的是一种确定性CSPRNG,它以随机数和口令作为种子,并可以按索引方式生成64位或128位的数字块。如果我使用“自1970年1月1日以来的周数”作为假设CSPRNG的一个索引元素,我应该能够构建一个系统,随着时间的推移自动地改变密钥。

嗯,我认为解决方案的一部分是使用非基于时间的生成器。这样,如果双方都从相同的种子开始,那么它们都会产生相同的随机流。您可以在其上层叠加“自1970年第1周以来的周数”的逻辑。

要实现这一点,您将使用OFB_mode<T>::Encryption。它可以用作生成器,因为OFB模式使用AdditiveCipherTemplate<T>,它派生自RandomNumberGenerator

事实上,Crypto++在test.cpp中使用生成器,以便在出现故障时可以重现结果。以下是如何使用OFB_mode<T>::Encryption的方法。它也适用于CTR_Mode<T>::Encryption

SecByteBlock seed(32 + 16);
OS_GenerateRandomBlock(false, seed, seed.size());

for(unsigned int i = 0; i < 10; i++)
{
    OFB_Mode<AES>::Encryption prng;
    prng.SetKeyWithIV(seed, 32, seed + 32, 16);

    SecByteBlock t(16);
    prng.GenerateBlock(t, t.size());

    string s;
    HexEncoder hex(new StringSink(s));

    hex.Put(t, t.size());
    hex.MessageEnd();

    cout << "Random: " << s << endl;
}

调用OS_GenerateRandomBlock函数从/dev/{u|s}random中获取字节,然后将其作为模拟的共享种子。程序每次运行都会不同。在每次程序运行中,它打印类似以下内容:
$ ./cryptopp-test.exe
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD

还有另一个生成器可用,它执行相同的操作,但不是Crypto ++库的一部分。 它被称为AES_RNG,基于AES-256。 它只有标题实现,您可以在Crypto ++维基百科中找到它,链接如下RandomNumberGenerator

此外,请参阅Crypto ++维基百科上RandomNumberGenerator类的Reproducibility主题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接