AES-256加密和初始化向量

10

我有一个关于在AES加密中使用初始化向量(IV)的问题。我正在参考以下文章/帖子来将加密集成到我的程序中:

[1] Java 256位AES基于密码的加密
[2] http://gmailassistant.sourceforge.net/src/org/freeshell/zs/common/Encryptor.java.html

最初,我按照第一个链接中erickson的解决方案进行操作,但从我所能看到的情况来看,PBKDF2WithHmacSHA1在我的实现上不受支持。因此,我转向第二个链接,以了解如何创建自己的迭代SHA-256哈希。

我的问题是如何创建IV。一种实现方法([1])是使用Cypher类的方法来派生IV,而另一种实现方法([2])则使用哈希的第二个16字节作为IV。很简单地说,为什么会存在区别,从安全角度来看哪一种更好?我也对IV的推导和用法感到困惑(我知道它们的用途,但并不清楚微妙的区别),所以任何澄清也非常欢迎。

我注意到第二个链接使用的是AES-128而不是AES-256,这意味着如果我想使用这种方法,我将不得不升级到SHA-512。这似乎是一个不幸的要求,因为为了确保远程安全哈希,用户的密码必须长16个字符,而这个应用程序是为手机设计的。

源代码可根据请求提供,但它还没有完成。

预先感谢你。


值得一看:https://dev59.com/c27Xa4cB1Zd3GeqPrppS。它讲述了在加密过程中设置“IV”的重要性。 - LCJ
5个回答

28

IV 不应该仅由密码生成。

IV 的作用是,即使使用相同的密钥和明文,也会产生不同的密文。如果 IV 仅由密码确定性地生成,每次得到的密文都将相同。在引用的示例中,随机选择了一种 ,因此即使使用相同的密码,也会生成新的密钥。

只需使用随机数生成器来选择 IV。这就是密码器内部正在执行的操作。


我想强调,您必须将 IV(如果使用第一种方法)或盐(如果使用第二种方法)与密文一起存储。如果所有内容都由密码派生,则无法获得良好的安全性;您需要在每条消息中添加一些随机性。


2
好的,这样更有意义了。那么还有一个问题:如果Cypher在内部使用随机数生成器,我该如何解密呢?据我所知,加密和解密都需要使用相同的IV。他们是否只是使用密钥作为生成器的种子来获取每个密钥相同的IV,还是有其他更复杂的操作? - MysteryMoose
2
@phobos51594 - 你必须将IV与密文一起存储。可以将IV视为第一个密文块,因此可以将其作为密码器实际输出的前缀进行存储。或者其他方式也可以。它不需要保密;只需将其与其余的密文一起存储即可。 - erickson
2
忽略之前的评论,我刚看到你的编辑。那样更有意义。因此,模式大致如下:哈希密码,使用密码器或安全随机特性生成IV,用哈希-IV-盐进行加密,存储以前的值,使用存储的值进行解密,丢弃IV,庆祝,重复。 - MysteryMoose
正如phobos51594所指出的那样,由于“PBKDF2WithHmacSHA1”算法导致NoSuchAlgorithmException,因此链接[1]在Android上无法使用,但是“PBEWITHSHA256AND128BITAES-CBC-BC”似乎可以正常工作。 - bob

4
我的理解是,初始化向量只是加密算法的随机输入,否则您将始终得到相同的输入结果。初始化向量与密文一起存储,它不以任何方式保密。只需使用安全的随机函数生成初始化向量。 PBKDF *算法用于从用户输入的密码中派生所需长度的加密算法的秘密密钥。
您链接的第一个实现仅允许Cipher对象生成初始化向量。然后它获取此生成的IV以将其与密文一起存储。
第二个实现使用散列字节的一部分。生成非重复IV的任何方法都足够好。
IV的最重要属性是它不会经常重复。

1
我假设我必须存储IV,除非我以某种方式从原始密钥派生它?因此,第一次实现实际上比我想象的更类似于第二次实现,因为它们都(以某种方式)从给定的密钥获取IV?如果是这样,我不能只使用密钥作为IV吗? - MysteryMoose
2
关键是必须保密。IV 是公开的。你不想把密钥用作 IV,否则你会向所有人广播你的秘密密钥!出于同样的原因,但程度较小,这就是为什么你不想使用哈希来基于密钥生成 IV。(就像 [2] 所做的那样)因为如果哈希算法存在弱点,攻击者可以从 IV 推导出你的密钥。 - AltF4
好的,那很有道理。所以[2]只是一种不好的做法。 - MysteryMoose
@phobos51594:这确实有点奇怪... [2]使用key + salt生成哈希,并从哈希中提取IV。现在随机性的问题取决于盐。如果有人每次传递相同的盐,他们将获得相同的IV,这是一个问题。[2]的问题是现在你必须记住盐和IV :-) - Peter Štibraný
在[2]中,由于IV完全来自盐+密码,因此您不需要存储IV。您只需要在运行时存储盐并将其与密码组合以生成IV即可。但是,这种设计使IV的强度等同于盐的强度。我认为代码中没有限制盐长度的保证。它可能非常小,具体取决于使用方式。 - AltF4
@AltF4:是的,你说得对。如果你记住了盐,你就可以推导出IV。 - Peter Štibraný

4

密码学家应该使用安全的伪随机数生成器来生成IV。

应用程序开发人员应该使用现有的、现成的加密技术。我建议您使用带证书的SSL来保护网络流量,使用GPG来保护文件数据。

有很多细节可能会使实现不安全,比如时序攻击。当应用程序开发人员在AES 128和AES 256之间做出决策时,这几乎总是毫无意义的,因为您很可能留下了一个可以使额外密钥位无用的时序攻击。


1
我在原帖中没有提到这一点,但这不是针对任何类型的网络流量。我将加密一个文件,除非整个系统被破坏,否则该文件永远不会离开设备。 - MysteryMoose
所以你把一个加密密钥和一个加密文件放在同一台设备上?这是毫无意义的。 - Spike Gronim
时序攻击在网络应用中真的是一个问题吗?我认为网络速度的变化会抹掉这些信息。如果不行,那么简单地等待X毫秒直到加密开始就可以解决这个问题。 - Keynan

1

IV只是使用块链接的结果。我认为这不仅仅是一个简单的API设计问题。我假设您知道使用它的原因是为了避免相同的明文在多个块中显示为相同的密文。

从最后一个块的递归思考,第N个密文块以某种方式依赖于(N-1)个块等。当您到达第一个块,第0个块时,您需要一些数据才能开始。无论该数据是什么,只要您在尝试解密之前就知道即可。使用非机密随机数据作为初始化向量将导致使用相同密钥加密的相同消息变成完全不同的密文。

这类似于给哈希加盐的概念。而且那段源代码对我来说有点可疑。IV应该只是取决于没有任何依赖关系的加密时间新鲜随机位,就像nonce一样。 IV基本上是加密消息的一部分。如果您使用相同的密钥重新加密相同的数据,则不应该能够将消息相关联。(嘿,还要考虑通过密文长度进行相关性的后果。)


那一部分很有道理。我的困惑在于生成一个IV以实现最佳加密(在实际情况下)。 - MysteryMoose
试图避免传输加密数据的IV的负担,违背了使用IV的初衷。使用从秘密中派生出来的IV是没有意义的,它实际上违背了IV的目的。关键在于IV是新鲜不可预测的,以便以前加密的消息不能相关联。 - Rob
有时候新的IV会被计算为(上一个IV)+1,因此它可能是可预测的。虽然我不确定这是否会引入一些问题。 - Peter Štibraný
@Peter,我明白你的意思。但是,在会话中进行一系列加密时,您仍然需要将IV作为消息的一部分显式或隐式传输,即使这仅在第一次加密时完成。然而,请考虑一下在没有特定密码详细信息的情况下提供此建议的问题。使用可预测的IV,与使用随机IV相比,能够相关消息的可能性肯定会更高(或者相同)。 - Rob
谜题:使用相同的密钥,使用以0为起始值递增的IV的两个密码实例。如果我总是编码相同数量的消息,则会存在相关性。如果只有一个实例,则递增最大化周期,直到它被认为重复。因此,我猜测决定因素将是如果您使用随机生成的IVs生成碰撞的概率。 - Rob
@Rob。使用一个简单递增的IV作为每个块的IV是一种已知且目前被认为是安全的方法。请参见此处的“计数器”加密模式:http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation。 - Ch'marr

1

和这里的其他人一样,我一直以来都知道IV只是使用标准算法随机选择的。

然而,你提供的第二个参考似乎并不是这样做的。看起来他对密码进行了加盐和哈希处理。然后将该哈希值分成两半。一半是加密密钥,另一半是IV。因此,IV是从密码派生出来的。

我没有任何强烈的反对意见,但这是一个糟糕的设计。IV应该是独立的和随机的。也许如果哈希算法存在弱点或者你选择了弱密码,那么就会有问题。你不想从任何其他东西中推导出IV,否则就可能发动预计算攻击。


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