作为我的应用程序端到端加密的一部分,我遵循使用 RSA 交换临时 AES256 密钥的标准程序。目前,当 A 决定发送给 B 的密钥时,他使用 B 的公钥对其进行加密。所有这些都通过与服务器的 TLS 连接进行。但是 B 有点盲目相信它来自 A 的密钥。当然,并不是任何人都可以向 B 发送 AES256 密钥。您需要先登录到服务器,然后它才愿意将消息传递给 A。AES256 密钥也仅在 A 和 B 之间的会话设置期间发送,B 之前必须接受该会话。在向 B 发送任何内容之前,服务器还将检查 X 和 B 之间是否存在请求的会话开始,并且 B 想要与之建立会话的确实是 X。
我认为让 A 包含 B 的密钥的签名散列可以让 B 确信密钥确实来自 A。但是,android 中的 java 无法加密签名散列,因为它恰好是 2048 位(如预期的那样) . 直接传输签名哈希(带有 SHA512 的 Java RSA)会不会有任何危险?我的理解是,哈希是蛮力强制的,在这一点上,你不妨还是蛮力使用 AES 密钥。
编辑:因为似乎人们正在解决问题的核心:通过 UDP 加密,所以已经完成了。我会写下我目前的做法。这是我自己的个人项目,所以我欢迎任何改进。我对当前的方法没有感情上的依恋。
(所有这些都发生在现有的 TLS 连接上,因为“命令”套接字是 TCP)
- A 使用 java 的 SecureRandom 生成 AES256 密钥。
- A 从服务器获取 B 的公钥副本。
- A 使用 android 的 RSA/NONE/OAEPWithSHA1AndMGF1Padding 将 RSA 加密密钥发送给 B。这是为 OAEP 填充选择的,它(根据我的阅读)听起来像是使 RSA 的输入看起来尽可能随机的最佳方法,以避免输出具有基于输入的某些特征。
- B 收到消息并向服务器发送“ok”命令,该命令告诉 A。
(其余的发生在普通的 UDP 上)
- A 使用 android cipher 的 AES/GCM/NoPadding 向 B 发送消息。选择 GCM 是因为(根据我的阅读)它是将数据“切碎”成安全片段的最佳方法。GCM 应该包含一个 MAC,所以我假设它负责为我进行身份验证。为每个数据块实例化一个新的密码实例。我假设 android 的 cipher.init 负责 IV(每个块都是新的)。加密后。发送数据 [IV 长度;IV;AES256/GCM 数据]。从我读到的内容来看,IV 就像一种盐,用于防止预先计算的攻击。通常在数据库中,盐以纯文本形式存储,因此在这种情况下,它是以纯文本形式传输的。(如果不是这会产生无限回归问题。)
- B 执行相反的过程,并使用上述方法向 A 发送响应。
两个会话之间的每个会话都使用一个新的 AES256 密钥来实现前向保密。在重放方面,明文数据具有序列号。在会话开始时使用 LTE 时,我在日志中看到 MAC 解密失败。没有一个合理的解释为什么。