8

我的团队需要开发一种解决方案,byte[]在用 Java 编写的 Android 应用程序的上下文中加密二进制数据(存储为 )。加密后的数据会以多种方式传输和存储,期间不能排除数据损坏的可能。最终,另一个 Android 应用程序(同样是用 Java 编写的)必须解密数据。

已经决定加密算法必须是 AES,密钥为 256 位。但是,我想就我们应该使用哪种 AES 实现和/或“模式”做出明智的决定。我读过一种叫做 GCM 模式的东西,我们已经用它做了一些测试(使用 BouncyCastle/SpongyCastle),但我并不完全清楚 AES-GCM 到底是做什么用的,以及与普通模式相比它“购买”我们的是什么AES - 以及是否需要考虑任何权衡。

以下是我们的关注/要求/问题列表:

  • 填充:我们需要加密的数据并不总是 128 位的倍数,因此 AES 实现/模式应该添加填充,但仅在必要时。我的印象是,简单的 AES 实现(例如由 提供javax.crypto.Cipher)不会这样做,但最初的测试表明确实如此。所以我猜测填充要求本身并没有理由求助于 GCM 而不是“普通”AES。那是对的吗?

  • 身份验证:我们需要一种万无一失的方法来检测是否发生了数据损坏。但是,理想情况下,我们还希望检测何时尝试使用不正确的密钥进行解密。因此,我们希望能够区分这两种情况。我最终考虑 GCM 的原因首先是由于这个Stackoverflow 问题,其中一位响应者似乎暗示使用 AES-GCM 可以进行这种区分,尽管他没有提供详细的解释(更不用说代码了) .

  • 最小化开销:我们需要限制加密数据的存储和传输开销。因此,我们希望了解特定 AES 实现/模式的选择是否以及在多大程度上会影响开销量。

  • 加密/解密性能:虽然这不是主要问题,但我们想知道特定 AES 实现/模式的选择在 CPU 时间和内存占用方面影响加密和解密性能的程度。

提前感谢您提供任何建议、说明和/或代码示例。

编辑: delnan 有用地指出没有“普通 AES”之类的东西。所以澄清一下,我的意思是使用 Java 的内置 AES 支持。
像这样:Cipher localCipher = Cipher.getInstance("AES");

4

1 回答 1

10

2012 年的答案是选择 GCM,除非您有严重的兼容性问题。

GCM是一种经过身份验证的加密模式。它一次性为您提供机密性(加密)、完整性和身份验证 (MAC)。

到目前为止,正常的操作模式是 ECB(这是Java的默认值)、CBC、CTR、OFB 和其他一些。他们都只提供加密。但是,如果没有完整性,保密性本身很少有用。必须以一种特别的方式将这种经典模式与完整性检查结合起来。由于密码学很难做到正确,因此这种组合通常是不安全的,比必要的慢,甚至两者兼而有之。

密码学家(最近)创建了经过身份验证的加密模式来解决该问题。GCM 是最成功的一种:它已被NIST选中,它高效、无专利,并且可以携带Additional Authenticated Data(即保持清晰但您可以验证其真实性的数据) . 有关其他模式的描述,请参阅Matthew Green 的这篇优秀文章

解决您的担忧:

  • 填充:默认情况下,Java 使用 PKCS#7 填充。这行得通,但它通常容易受到填充 oracle 攻击,最好用MAC击败。GCM 已经嵌入了一个 MAC(称为 GMAC)。

  • 身份验证: AES-GCM 仅将一个 AES 密钥作为输入,而不是密码。它会告诉您 AES 密钥是否错误或负载是否已被篡改,但此类情况被视为一个。相反,您应该考虑使用适当的密钥派生算法(如PBKDF2bcrypt)从密码中派生 AES 密钥。我不认为总是可以判断密码是否不正确或有效负载是否已被修改,因为验证前者所需的数据总是可能被破坏。您可以加密一个小的已知字符串(使用 ECB AES),将其发送,并使用它来验证密码是否正确。

  • 最小化开销:归根结底,如果您需要身份验证,所有模式都会导致相同的开销(大约 10-20 个字节)。除非您使用非常小的有效负载,否则可以忽略这一点。

  • 性能: GCM 非常好,因为它是一种在线模式(不需要缓冲整个有效负载,因此内存更少),它是可并行化的,并且每个明文块需要一次 AES 操作和一次伽罗瓦乘法。像 ECB 这样的经典模式更快(每个块只有一个 AES 操作),但是 - 再次 - 您还必须考虑完整性逻辑,这可能最终会比 GMAC 慢。

话虽如此,人们必须意识到 GCM 安全性依赖于良好的随机数生成来创建 IV。

于 2012-11-16T18:26:36.613 回答