如何从 DH 密钥获取 AES 密钥

How to get AES secret key from DH secret key

我有以下代码将 DH 密钥转换为 AES 密钥。这曾经一直有效,直到 Oracle JRE 8u161 他们开始限制在 java.security 文件中创建小于 1024 的 DH 密钥。现在,我将在最后一行得到 NoSuchAlgorithmException: Unsupported secret key algorithm AES

PrivateKey privKey = null;
PublicKey pubKey = null;
PublicKey agreement = null;

KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");
keyAgreement.init(privKey);
keyAgreement.doPhase(pubKey, false);
keyAgreement.doPhase(agreement, true);
SecretKey key = keyAgreement.generateSecret("AES");

我试着把最后一行改成这个。我可以使用新密钥进行加密和解密,但这不适用于之前生成的旧密钥。

byte[] encodedKey = keyAgreement.generateSecret();
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");

我发现了一个类似的 SO 问题 What metod KeyAgreement.generateSecret(String algorithm) does? 但我仍然不知道如何在不破坏现有密钥的情况下解决这个问题。

通常,密钥大小要求在 CipherKeyAgreement class 本身中检查,而不是在提供商的服务实施中检查。当然,无论 [EDIT 是什么,都会尝试测试另一个提供程序,例如 Bouncy Castle 提供程序:这似乎在这种情况下有效,因此密钥大小限制在随附的默认提供程序中Java 运行时,使用 "BC" 提供程序似乎工作正常,]。

如果使用其他提供商不起作用,则使用 Bouncy Castle 轻量级 API(org.bouncycastle.** classes)使用另一个 DH 软件实现,绕过 KeyAgreement class 一共。然而,应避免走出 JCA / KeyAgreement

不用说,不使用 < 1024 位密钥的要求是有原因的,它们不再被认为是安全的。尽快升级您的安全系统!

Oracle/Sun-providers 的问题不是 java.security 中的 DH keysize 限制,它仅适用于 TLS/SSL(即 JSSE),但该项目在发行说明中稍微靠下您已链接:

  • Stricter key generation

The generateSecret(String) method has been mostly disabled in the javax.crypto.KeyAgreement services of the SunJCE and SunPKCS11 providers. Invoking this method for these providers will result in a NoSuchAlgorithmException for most algorithm string arguments. The previous behavior of this method can be re-enabled by setting the value of the jdk.crypto.KeyAgreement.legacyKDF system property to true (case insensitive). Re-enabling this method by setting this system property is not recommended.

后面几段基本上说了,不是很清楚,正确使用DH需要合适的KDF,但是这个操作没有provide/defineKDF,所以不能保证合适,应该使用无参数 generateSecretKey() 方法获取原始 DH 值并自己应用合适的 KDF;他们以 SP800-56Ar2 和纯哈希为例。

BouncyCastle 反其道而行之;在 1.60 中,它有几个 KeyAgreement 算法,其中 KDF 编码为 DHwithSHA256CKDF.