【问题标题】:Data encryption on Android, AES-GCM or plain AES?Android、AES-GCM 或普通 AES 上的数据加密?
【发布时间】:2012-11-05 09:12:19
【问题描述】:

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

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

以下是我们的顾虑/要求/问题列表:

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

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

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

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

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

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

【问题讨论】:

  • 什么是“普通”AES?除非您只处理 256 位数据。如果你有更多的数据,你必须使用 some 模式。也许您的库默认选择一种模式(据我所知,许多库在这里做出了相当不安全的选择)。所以你应该检查当你使用“普通”AES时实际使用的是哪种模式。
  • 感谢@delnan 的评论,我进行了编辑以澄清我的意思。

标签: android encryption aes bouncycastle aes-gcm


【解决方案1】:

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

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

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

Authenticated Encryption 模式已(最近)由密码学家创建以解决该问题。 GCM 是最成功的一种:被NIST 选中,效率高,无专利,可以携带Additional Authenticated Data(即保持清晰的数据) ,但您可以验证其真实性)。有关其他模式的说明,请参阅this excellent article of Matthew Green

解决您的疑虑:

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

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

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

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

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

【讨论】:

  • 我刚刚在 Jelly Bean 4.2 上使用 AES/EAX 测试了一些 Android 代码。它在 NoSuchAlgorithmException 那里失败了。这让我很奇怪。有官方支持的算法列表吗?
  • @Nobu-games 你试过this security.SO答案中的代码吗?
  • 我最终捆绑了 SpongyCastle。这样我就知道我的加密代码可以在任何 Android 设备上运行。只是让我担心最新的官方 Android 版本摆脱了旧版本中包含的某些内容(我可以在装有 Android 2.3 的三星 Galaxy S 上使用 AES/EAX 没有问题)。
  • 感谢@SquareRootOfTwentyThree 提供的信息丰富的回答。我有一个后续问题:我们想使用 SHA-256 将密码转换为 AES 密钥。为了区分错误的密码和被篡改的有效负载,我考虑发送一个重新散列的密码(即对密钥应用 SHA-256)以及有效负载,以便接收者能够验证他使用的是正确的密码。我意识到重新散列的密码本身也可能在传输过程中被破坏,所以这可能不会完全万无一失。但是,您认为这有意义吗?它是否“安全”(即不可利用)?
  • 我不会使用 SHA2 来推导密码。它太“快”了,因此对字典和彩虹攻击很敏感。具有随机盐和足够多的迭代次数的 PBKDF2 是优选的。我认为可以获取结果密钥,对其进行哈希处理(即使使用 SHA2)以获取检查值并将其发送过来(使用盐)。
猜你喜欢
  • 2021-09-24
  • 2016-09-22
  • 2021-06-03
  • 2017-10-21
  • 1970-01-01
  • 1970-01-01
  • 2016-11-14
  • 2015-03-27
  • 1970-01-01
相关资源
最近更新 更多