如何为AES / CTR / NoPadding选择合适的IV(初始化矢量)?

时间:2011-01-05 19:46:55

标签: encryption aes ctr-mode

我想加密由webapp编写的cookie,我希望将cookie的大小保持在最低限度,因此我选择了AES / CTR / NoPadding。

你会建议使用哪种IV足够随机并且仍然保持app无状态。我知道我可以生成随机IV并将其附加到消息中,但这会增加cookie的大小。

此外,对于128位AES,推荐的IV大小是什么?

大家都这样做了吗?是否存在任何“经过尝试和真实”的方式?我不想重新发明轮子。

5 个答案:

答案 0 :(得分:48)

CTR安全性要求您永远不会重复使用相同密钥的两个邮件加密的IV。实际上它甚至更严格:CTR模式通过加密计数器的连续值(IV只是该计数器的初始值)来工作,只有当相同的计数器值没有被使用两次时才能实现适当的安全性;这意味着使用IV加密值实际上“消耗”了一系列连续的IV值,这些值不能与其他加密一起重复使用。

这样做的简单方法是使用加密安全随机数生成器,并为每条消息创建一个新的16字节随机IV。我强调“加密安全”,因为这很重要;一个基本的随机数发生器是不够的。使用Java,使用java.util.SecureRandom。使用Win32,请致电CryptGenRandom()。通过随机选择,可能的128位IV空间足够大,以至于碰撞极不可能。实际上,这就是AES使用128位块(因此意味着128位IV)的原因。

将解密消息的实体必须知道IV,因此您必须将其与加密消息一起存储。这是额外的16个字节。我知道这个开销是你想要避免的,尽管16个字节对于cookie来说并不是那么多。 cookie的有效最大长度取决于Web浏览器,但4000个字符似乎“无处不在”。一个16字节的IV,当用字符编码时(例如使用Base64),将使用大约22个字符,即远小于最大cookie大小的1%:也许你能买得起?

现在我们可以变得时髦并尝试通过欺骗来减少IV长度:

  • 使用哈希函数生成IV:服务器端,使用计数器,从0开始,每次需要新的IV时递增。要获得IV,您可以使用合适的散列函数对计数器进行散列,例如SHA-256,并保留哈希值的前16个字节。散列函数的“随机化属性”足以使IV在CTR要求方面足够随机。这需要一个加密安全的哈希函数,因此需要SHA-256(避免使用MD5)。然后,您只需将计数器值存储在cookie中,并且计数器将短于16个字节(例如,如果您的客户数不超过40亿,则计数器将适合4个字节)。但是,有一个隐藏的成本:服务器(我想服务器正在你的系统中执行加密)必须确保它永远不会重用计数器值,所以它必须以一种持续存在的方式存储“当前计数器”服务器重新启动,如果扩展到多个前端,也不会失败。这似乎并不容易。

  • 使用外部唯一值:可能,cookie可能是上下文的一部分,该上下文提供足够的数据来生成对每个加密都唯一的值。例如,如果请求还包含(明确地)“用户ID”,则可以将用户ID用作IV源。设置类似于上面的设置:您获得所有数据,将其填入SHA-256,SHA-256输出的前16个字节是您需要的IV。只有当给定的加密消息的数据没有变化,并且它确实是唯一的时,这才有效。这种情况很少发生:例如,只有在不需要为同一用户重新加密新消息的情况下,“用户ID”才有用,并且如果再也没有可能重用用户ID(例如,旧用户退出,新用户来,并选择现在免费的用户ID。

使用通过加密安全PRNG生成的随机16字节IV仍然是“安全”方式,也是我推荐的方式。如果你在cookie中发现空间紧张,那么这意味着你接近4 kB的限制,此时你可能想要使用压缩(在加密之前的数据;加密后,压缩非常不太可能工作)。使用zlib(在Java中,您可以通过java.util.zip访问zlib)。

警告:在上述所有情况中,我并不是说任何关于cookie加密是否有助于提供您尝试实现的任何安全特性。通常,当需要加密时,您实际上需要加密和完整性,然后您应该使用组合加密和完整性模式。查找GCMCCM。此外,cookie加密主要用于一个目的,这是为了避免在服务器端存储一些用户特定数据的成本。如果您想为其他内容加密Cookie,例如要验证有效用户,那么你做错了:加密不是正确的工具。

答案 1 :(得分:4)

我没有直接回答你的问题,但要添加一些东西。

首先,加密cookie对我来说没有意义。如果您想要保密数据,则无论如何都不应将其存储在cookie中。如果你想要完整性(即不可能篡改cookie的内容),你应该使用键控哈希(例如HMAC)。

另一个注意事项是从不使用全部为了方便的IV。

IV与你的块大小相等。在AES-128的情况下,块大小为128,密钥大小为128,因此IV为128位。

执行此操作的最佳方法是创建随机AES密钥并将其用作IV。该随机IV可以是公共的,只要它不会在具有相同密钥的后续加密中重复使用

修改

您可能需要查看此Wiki页面以获取有关使用哪种模式的更多信息。但是,除非您确定要使用ECB,否则永远不要使用ECB。即便如此,请与专家核实。据我所知,CBC是最安全的(与PCBC一起)。

http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation

答案 2 :(得分:2)

如果你没有使IV随机(即你使用一些重复的数字组),如果cookie总是以相同的明文开头,那么找出密钥会更容易。

AES-128的IV大小为128位。 IIRC,IV与密码块的大小相同。 128位是16个字节。如果将其存储为ASCII十六进制字符串,则为32个字节。那太真了吗?这个时代的32字节和年龄并不多......

答案 3 :(得分:0)

在Cookie中包含一个大的随机数。 64或128位数可能足够大。它需要足够大才能很难获得重复。一定要在这个数字中加入足够的熵。不要只使用gettime()。如果您有权访问CRNG,请在此处使用。

在应用程序中存储256位主密钥。使用SHA256导出密钥信息。再次,为此使用CRNG。

$keyblob = sha256( concat("aeskeyid", $masterkey , $randomnumberwithcookie ) )
$aeskey = $keyblob[0..15]
$aesiv = $keyblob[16..31]

您可能还想为HMAC导出密钥。

$mackeyblob = sha256( concat("hmackeyid", $masterkey , $randomnumberwithcookie ) )

或者,您可以使用SHA512将上述两个哈希操作合并为一个。

$keyblob = sha512( concat("randomkeyid", $masterkey , $randomnumberwithcookie ) )
$aeskey = $keyblob[0..15]
$aesiv = $keyblob[16..31]
$hmackey = $keyblob[32..63] 

答案 4 :(得分:0)

可以通过使用CBC并将HMAC存储在消息前面来避免随机IV。使用随机选择的常数IV是可以的。但你必须确保消息都是不同的。

加密消息总是不同时就是这种情况。具有序列号的许可证密钥将符合此条件。具有用户ID或会话ID的cookie也会匹配它。

如果将hmac存储在消息前面,则可以使用带有随机常数IV的CBC。哈希将累积第一个块中消息中传播的所有变体。您还可以添加一些随机字节或最好是序列号,如果您可以确保它是唯一的或在很长时间内不重复使用。

甚至不要考虑使用带有常数IV的CTR。