【问题标题】:How to derive an AES 256 bit key from an arbitary byte array it Java?如何从 Java 的任意字节数组中派生 AES 256 位密钥?
【发布时间】:2020-10-06 05:13:43
【问题描述】:

给定一个任意 Java 字节数组,例如 1024 字节数组,我想派生一个 AES-256 位密钥。该数组是从 ECHD 通过javax.crypto.KeyAgreement 使用byte[] secret = keyAgreement.generateSecret() 生成的

我目前的解决方案是将输入字节数组视为密码。使用 PBKDF2 密钥派生函数输入数组作为密码和盐,如下所示。

更新:我已将 UTF-8 设置为编码以解决 cmets 和答案中指出的问题。

private byte[] deriveAes256bitKey(byte[] secret)
    throws NoSuchAlgorithmException, InvalidKeySpecException {

    var secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    var password = new String(secret, UTF_8).toCharArray();
    var keySpec = new PBEKeySpec(password, secret, 1024, 256);
    return secretKeyFactory.generateSecret(keySpec).getEncoded();
}

有没有更好的方法在 Java 中获取字节数组并将其转换为 AES-256 位密钥?

【问题讨论】:

  • 你的数组有多少熵?您应该使用已建立的密钥派生函数 (KDF)。哪一个取决于数组中有多少熵,但我相信 Java 中唯一可用的好的熵是 PBKDF2。 PBKDF2 包含额外的安全功能,以增强密码等低熵值的安全边际。但是,如果您有一个全熵数组,这些功能不会伤害您。正如一个答案指出的那样,您不应该以您正在做的方式将数组转换为字符串。您应该改为 base64 对其进行编码。如果你有 Bouncycastle 库,你还有其他的 KDF 选项。
  • 一种选择是使用 PBKDF 使用单次迭代。但由于处理 Java PBKDF API 的奇怪字符串,我不建议这样做。
  • @MaartenBodewes 我更新了上面的示例代码以使用 UTF_8 对奇怪的字符串处理没有影响吗?您能否详细说明奇怪的字符串处理的影响是什么?

标签: java security cryptography aes jce


【解决方案1】:

我会警惕使用new String(input).toCharArray() 创建密码。它不可移植(它使用平台默认编码),如果输入中有无效的字符序列,它的行为是不确定的。

考虑一下:

    System.out.println(new String(new byte[] {(byte) 0xf0, (byte) 0x82, (byte) 0x82, (byte) 0xac}, StandardCharsets.UTF_8));

f08282ac 是欧元符号 (€) 的超长编码。它被解码为替换字符 (�; 0xfffd),因为它是非法序列。 所有非法的 UTF-8 序列最终将作为替换字符,这不是你想要的。

您可以通过在将字节数组传递给SecretKeyFactory 之前对其进行序列化来避免解码问题(base64 对其进行编码,或者只是new BigInteger(input).toString(Character.MAX_RADIX))。但是,如果您不使用SecretKeyFactory,则可以避免这种情况。没必要。

PBKDF2(基于密码的密钥派生函数 2)旨在通过计算成本高和添加盐来使针对用户提供的密码的暴力攻击更加困难。

您在这里不需要它(您的输入很大且随机;没有人会针对它进行字典攻击)。您的问题只是输入长度与所需的密钥长度不匹配。

您可以将输入散列到正确的长度:

MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] keyBytes = md.digest(input);

【讨论】:

  • 真正需要的是一个 KBKDF,一个基于密钥的密钥派生函数。 SHA-256 可以被视为“穷人的 KDF”。不幸的是,默认情况下 Java 不包含 KBKDF,您可以投票支持 API 增强 here。我已经为 Bouncy Castle 加密库实现了 HKDF 的初始版本和其他一些版本。但如果您只需要一个密钥,那么 在实践中 SHA-256 就足够了。
  • @MaartenBodewes 您能否详细说明选择是什么以及使用 SHA-256 与 KBKDF 的优缺点,KBKDF 会做什么使它比 SHA-256 更好?也许 KBKDF 与 PBKDF 有何不同 如果两者都保密,密码和密钥之间有什么区别,它们不是同一件事。
  • 嗯,好的,但我认为目前的答案已经足够好了。 BWT,诸如此类的问题通常更多地与 IT 安全或堆栈交换的加密站点有关。
【解决方案2】:

这里需要一个 KBKDF 或基于密钥的密钥派生函数。 KBKDF 将包含足够熵的秘密值转换为特定大小的不同密钥。当您使用密钥强化(使用盐和工作因子或迭代计数)的密钥中的熵可能太少时,使用 PBKDF。如果输入值已经足够强大而不会被猜测/暴力破解,则不需要使用工作因子/迭代计数。

如果您只想要得到的 128 位值,通常 SHA-256 就足够了。但是,使用密钥派生函数可能仍会带来好处。首先,它是为函数显式定义的函数,因此更容易证明它是安全的。此外,通常可以向密钥派生函数添加其他数据,以便您可以例如派生更多密钥或一个密钥和一个 IV。或者你可以扩展可配置的输出大小,为不同的键或键/IV输出足够的数据。

也就是说,如果您使用 SHA-256(或 SHA-512,以防您需要更多位用于密钥/IV),大多数密码学家不会皱眉头。输出仍然应该使用输入中所有可能的位进行随机化,并且不可能反转函数。

【讨论】:

    猜你喜欢
    • 2013-06-16
    • 2017-02-12
    • 2020-07-26
    • 2017-04-26
    • 1970-01-01
    • 2017-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多