13

背景

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

问题

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

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

我正在考虑的方法

现在我在 cryptopp 中看不到这样的功能,或者我现在已经足够了解术语了,而且由于 cryptopp 是目前最先进的加密库,我没有信心找到另一个。所以,如果我找不到那里的实现,我应该推出自己的。从连接的数据中生成一个静态字符串结构,然后对其进行散列(如下所示)可以解决问题吗?

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

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

这是一种合理的方法(--即加密安全)吗?

-- 编辑:发表接受评论 --

经过一番思考,我决定将我最初认为的密码短语和 nonce/salt 合并为一个 16 字节(加密强)密钥,并使用 PKCS #5 中概述的技术来派生多个基于时间的密钥。不需要盐,因为不使用密码。

4

2 回答 2

5

有趣的问题。

首先,您的初始向量不必是加密的强随机量,但它们应该是每个消息唯一的。IV 实际上只是一种盐值,可确保使用相同密钥加密的类似消息在加密后看起来不相似。您可以使用任何快速的伪随机生成器来生成 IV,然后将其与加密数据一起发送(最好是加密的)。

当然,钥匙应该尽可能地坚固。

您对包含随机数、密码和有效性数据的文本字符串进行散列的建议在我看来是非常合理的——它与其他使用密码生成密钥的系统所做的大致一致。您应该多次散列 - 而不仅仅是一次 - 以使密钥生成在计算上变得昂贵(对于任何试图暴力破解密钥的人来说,这将是一个比对您来说更大的问题)。

您可能还想查看 PKCS#5 中列出的密钥生成方案(例如http://www.faqs.org/rfcs/rfc2898.html),该方案在 cryptopp 中作为 PasswordBasedKeyDerivationFunction 实现。这种机制已经被广泛使用并且被认为是相当安全的(请注意,PKCS#5 建议对密码短语数据进行至少 1000 次散列)。您可以将有效期和索引数据附加到密码短语中,并按原样使用 PasswordBasedKeyDerivationFunction。

你没有说你建议使用什么加密算法来加密数据,但我建议你应该选择一些被广泛使用并且已知是安全的......特别是我建议你使用 AES。我还建议使用 SHA 摘要函数之一(可能作为 PasswordBasedKeyDerivationFunction 的输入)。SHA-2 是最新的,但 SHA-1 足以用于密钥生成目的。

您也没有说您要生成什么密钥长度,但您应该知道密钥中的熵量取决于您使用的密码的长度,除非密码长,否则将是远小于理想所需的密钥长度。

该方案中最薄弱的环节是密码本身,这总是会限制您可以达到的安全级别。只要您对数据进行加盐(就像您正在做的那样)并使密钥生成成本高昂以减缓蛮力攻击,您应该没问题。

于 2010-12-01T19:01:43.187 回答
1

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

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

为此,您将使用OFB_mode<T>::Encryption. 它可以用作生成器,因为 OF​​B 模式使用AdditiveCipherTemplate<T>,它派生自RandomNumberGenerator.

事实上,Crpyto++ 使用了生成器,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++ wiki 的RandomNumberGenerator下找到它。

另请参阅Crypto++ wiki 上的主题Reproducibility for class。RandomNumberGenerator

于 2015-02-16T20:47:07.983 回答