【问题标题】:Generating AES key after ECDH在 ECDH 之后生成 AES 密钥
【发布时间】:2014-06-03 18:41:21
【问题描述】:

我正在使用 ECDH 在 Alice 和 Bob 之间共享一个秘密,一个在 Android 上运行,另一个在嵌入式上。我的理解是,最好的做法是双方都生成一个 AES 密钥来加密消息。有人可以发送一些示例,说明 Alice 和 Bob 在使用 ECDH 就共享密钥达成一致后如何生成相同的 AES 密钥?

【问题讨论】:

  • 你可能只会得到关于如何实现这一点的提示,在stackoverflow上要求代码通常被选为离题。

标签: android encryption aes


【解决方案1】:

最好遵循 NIST 关于密钥协议方案的建议,例如使用NIST SP 800-56Arecommendations in the implementers guide 以及NIST SP 800-108 中指定的密钥派生方法之一。它描述了如何将秘密转换为字节,然后可以使用其中一个 KDF 转换为(多个)密钥。

请注意,自 1.50 以来,密钥派生在 Bouncy Castle 的轻量级 API 中,因此也应存在于 Spongy Castle 中。最常见的 NIST 算法在 org.bouncycastle.crypto.generators.KDFCounterBytesGenerator 中。您可以在构造函数中简单地给它任何 HMAC(比如 HMAC SHA1),并在初始化期间给它org.bouncycastle.crypto.params.KDFCounterParameters

请注意,这比英仙座的答案遵循更好的加密实践,但可能更难理解/实施。

【讨论】:

  • 有人可以指定API调用吗?是 PBKDF2WithHmacSHA1AndUTF8 吗?实现 API 的类/接口是什么?
  • 不,第一个 PB 表示“基于密码”。您需要一个 KBKDF(基于密钥)KDF,因为您的共享密钥已经包含足够的熵。我会修改答案...我实际上已经创建了它们的初始实现:)
  • 您是否发现以下代码符合 KDF 的建议: KeySpec spec = new PBEKeySpec(ecdh_shared_secret, salt, 65536, 256); SecretKey tmp = factory.generateSecret(spec); SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
  • 现在你也应该注意 HKDF。我稍后会在家里尝试更新答案。
  • 最终我使用了以下函数: public static SecretKey createAESKey(byte[] aSecret) throws InvalidKeySpecException, NoSuchAlgorithmException { SecretKeySpec secret = new SecretKeySpec(aSecret, "AES");返回秘密; }
【解决方案2】:

在成功运行 ECDH 后,您将在两端获得相同的组元素。您现在需要将元素的统一编码定义为字节数组/八位字节流。使用散列函数(例如 SHA256)可以将数组缩减为 256 位值。如果您需要派生多个键,您可以在散列它们之前为每个键附加具有不同常量的字节数组。

一个简单的例子:假设你的椭圆曲线点是 (51357992175,89175716892)。您可以将该曲线点表示为字符串“(51357992175,89175716892)”。对于不同的密钥,您可以附加一些字符串常量,例如“MacKey”或“EncryptionKey”。要获取一些原始字节值,您可以使用 utf8 对这些字符串进行编码。总的来说,关键是:

macKey        = sha256("(51357992175,89175716892)MacKey".getBytes("UTF-8"))
encryptionKey = sha256("(51357992175,89175716892)EncryptionKey".getBytes("UTF-8"))

编辑,关于您从 cmets 引起的问题(“在我的情况下,我有一台服务器和许多客户端每天多次接近他,并且每次都需要使用不同的 AES 密钥基于共享秘密。”)关于如何生成大量密钥,每个通信事件一个:

您正在进入 - 相当复杂的 - 密码协议设计领域。 思考以下问题:

  • 如何保证为每条新发送消息生成不同的密钥?标准解决方案是使用计数器或随机数作为 nonce(使用一次的数字),每个都有自己的问题。 (计数器:即使在应用程序崩溃或备份恢复的情况下,它也必须是严格单调的。随机:它必须足够大以防止随机冲突 - 像 256 位就足够了 - 你必须持续信任随机数生成器做个好人。)
  • 如何防止消息重放攻击接收端?您可以跟踪使用的每个随机数(存储密集型),或者可能使用单调计数器以防随机数是单调的,并拒绝任何随机数小于或等于单调计数器的消息。
  • 您对消息延迟重新排序选择性消息阻塞有何反应?在许多情况下,能够注意到这些事件很重要,您必须决定在那时做什么。 (例如,TL​​S 需要两方之间的实时交互,如果出现任何故障,则中止连接,但无法检测到延迟(除非现在臭名昭著的心跳用于 ping 连接)。)

随着我们后面的扩展警告,回到您的问题:对于每个新密钥,您可以使用密钥派生中包含的 nonce(使用一次的数字)为此目的生成唯一密钥。我基本上是在用纯文本组件从NIST SP 800-108 重建类似于“5.1 KDF in Counter Mode”的东西,owlstead 已经提到过。在这一点上,我建议以任何方式阅读它,因为它不是那么长并且包含很多背景考虑。除了随机数之外,您应该尽可能缩小密钥的使用范围。例如在服务器端使用:

macKey = sha256(sha256("(51357992175,89175716892); MacKey; ServerToClient; 2755".getBytes("UTF-8")))

为用于从服务器到客户端通信的消息身份验证而生成的第 2755 个密钥。您可以看到以下组件:共享密钥(椭圆曲线点)、密钥的用途(mac)、通信方向、计数 nonce。此外,我在这里使用双重散列来防止散列上出现任何类型的length extension; HMAC在这里也一样好。在某些情况下,nonce 本身可以并且必须与消息一起发布。它本身不包含任何敏感信息。

【讨论】:

  • 相当简单的专有解决方案,但并非不正确或特别不安全,因此请点赞。
  • @owlstead 你是对的,这是一种临时的方法(我将这个问题解释为仅具有教育意义,否则典型的“不要实现自己的加密”建议适用)。不过,它会非常感兴趣,该解决方案如何比 NIST 方法更不安全(或者它违反了哪些加密最佳实践)。
  • 它不太可能不那么安全;所有关于秘密的信息都包含在 KDF 中。临时的 KDF(很像使用计数器的 KDF0/KDF1)至少存在哈希函数不是(设计为)PRF 的理论问题。
  • @owlstead 谢谢 :),我正在考虑自己问这样一个问题(在我有时间仔细查看 NIST 文件之后)。
  • 即使是教育目的(事实并非如此 :-))如何让 Alice 和 Bob 双方添加相同的常数以获得相同的对称密钥?他们现在需要就定义相同常量的机制达成一致。
猜你喜欢
  • 1970-01-01
  • 2017-10-28
  • 2012-11-02
  • 1970-01-01
  • 1970-01-01
  • 2019-05-03
  • 2020-02-14
  • 2018-05-31
  • 1970-01-01
相关资源
最近更新 更多