如何从Java中的任意字节数组派生出一个AES 256位密钥?

huangapple go评论76阅读模式
英文:

How to derive an AES 256 bit key from an arbitary byte array it Java?

问题

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();
}
英文:

Given an arbitrary Java byte array for example 1024 byte array I would like to derive an AES-256 bit key. The array is generated from ECHD via javax.crypto.KeyAgreement using byte[] secret = keyAgreement.generateSecret()

My current solution is to treat the input byte array as a password. Use the PBKDF2 key derivation function the input array as both the password and the salt as shown below.

UPDATE: I have set UTF-8 as the encoding to address issues pointed out in comments and answers.

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();
}

Is there a better way to take a byte array in Java and turn it into an AES-256 bit key?

答案1

得分: 3

我会谨慎使用 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);
英文:

I would be wary of using new String(input).toCharArray() to create the password. It's not portable (it uses the platform default encoding), and its behaviour is undefined if there are invalid character sequences in the input.

Consider this:

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

f08282ac is an over long encoding of the Euro sign (€). It's decoded to the replacement character (�; 0xfffd) because it's an illegal sequence. All illegal UTF-8 sequences will end up as the replacement char, which is not what you want.

You could avoid decoding problems by serialising the byte array before passing it to the SecretKeyFactory (base64 encode it, or simply new BigInteger(input).toString(Character.MAX_RADIX)). However, this can be avoided if you don't use the SecretKeyFactory. It's unnecessary.

PBKDF2 (Password-Based Key Derivation Function 2) is designed to make brute force attacks against user supplied passwords harder by being computationally expensive and adding salt.

You don't need that here (your input is large and random; nobody will be mounting dictionary attacks against it). Your problem is just that the input length doesn't match the required key length.

You can just hash the input to the correct length:

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

答案2

得分: 1

这里所需的是一个KBKDF或基于密钥的密钥派生函数。KBKDF将包含足够熵的秘密值转换为特定大小的不同密钥。当您拥有的是可能熵不足的口令,并且希望使用密钥强化(使用盐和工作因子或迭代次数)将其转换为密钥时,可以使用PBKDF。如果输入值已经足够强大,不容易被猜测或暴力破解,那么不需要使用工作因子/迭代次数。

通常情况下,如果您只想要一个结果为128位的值,SHA-256就足够了。然而,使用密钥派生函数仍然可能带来好处。首先,它是一个专门为该功能明确定义的函数,因此更容易证明它的安全性。此外,通常可以向密钥派生函数添加附加数据,以便您可以派生更多的密钥、密钥和初始化向量(IV),或者可以扩展可配置的输出大小,以输出足够的数据用于不同的密钥或密钥/IV。

话虽如此,大多数密码学家如果您使用SHA-256(或者如果需要更多位的密钥/IV,可以使用SHA-512)也不会太在意。输出仍然应该使用输入的所有可能位进行随机化处理,并且不可能反向函数。

英文:

What is required here is a KBKDF or Key Based Key Derivation Function. A KBKDF converts a secret value that contains enough entropy into a different key of a specific size. A PBKDF is used when you have a passphrase with potentially too little entropy into a key using key strenghtening (using the salt and work factor or iteration count). The work factor / iteration count doesn't need to be used if the input value is already strong enough not to be guessed / brute forced.

SHA-256 in general suffices if you only want a resulting 128 bit value. However, using a key derivation function may still offer benefits. First of all, it is a function that is explicitly defined for the function, so it is easier to prove that it is secure. Furthermore, it is generally possible to add additional data to the key derivation function so that you can e.g. derive more keys or a key and an IV. Or you can expand the configurable output size to output enough data for different keys or key / IV.

That said, most cryptographers won't frown too much if you use SHA-256 (or SHA-512 in case you require more bits for key / IV). The output is still supposed to be randomized using all possible bits from the input, and it is impossible to inverse the function.

huangapple
  • 本文由 发表于 2020年10月6日 13:13:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/64219736.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定