英文:
Java byterray to string must be equal python bytearray string when generate secret with SecretKeyFactory
问题
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
public class CryptoExample {
public static void main(String[] args) throws Exception {
byte[] PASSWORD = new byte[]{(byte) 0xFF, (byte) 0xFA, (byte) 0xAA, (byte) 0xBB, (byte) 0xBC,
(byte) 0xCC, (byte) 0xDD, (byte) 0xDD, (byte) 0xFF, (byte) 0xAA, (byte) 0xAA,
(byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xBB};
String salt = "salt";
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
PBEKeySpec spec = new PBEKeySpec(new String(PASSWORD, StandardCharsets.UTF_8).toCharArray(),
salt.getBytes(), 512, 256);
byte[] keyBytes = factory.generateSecret(spec).getEncoded();
System.out.println("Generated Key: " + Arrays.toString(keyBytes));
}
}
Please note that I've replaced the use of the explicit (char)
cast with StandardCharsets.UTF_8
encoding when converting the password to a char array. This should handle the conversion correctly without issues related to signed integer representation.
Ensure that you have the appropriate cryptographic libraries in your Java environment.
英文:
I have a task to rewrite some python crypto code to java. I'm new in python. Python code:
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
backend = default_backend()
PASSWORD = bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16))
key = PBKDF2HMAC(hashes.SHA256(), 32, salt, iterations, backend).derive(PASSWORD)
My java implementation:
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
byte[] PASSWORD = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
SecretKey tmp = factory.generateSecret(new PBEKeySpec(new String(PASSWORD).toCharArray(), salt, iterations, 256));
byte[] key = tmp.getEncoded();
As you can see PASSWORD is a byte array which I get from a hex string, i.e 010203....0F10, I can't change it (i.e. can't specify it as a string in python realization, as I understand server transforms PASSWORD to byte array also). All worked fine with this dummy PASSWORD i.e. keys generated by python and java code were equal. But I'm faced with a problem when the password changed to arbitrary, i.e. for example AFFFFFFFFDBGEHTH....
As I understand the problem with java byte array representation as signed integers. I.e. when I convert hex "FFFAAABBBCCCDDDDFFAAAAAAAAAAAABB" for example to byte array it will be byte array [-1, -6, -86, -69, -68, -52, -35, -35, -1, -86, -86, -86, -86, -86, -86, -69], but in python it will [255, 250, 170, 187, 188, 204, 221, 221, 255, 170, 170, 170, 170, 170, 170, 187]. Then when I convert java byte array to charArray for PBEKeySpec constructor - new PBEKeySpec(new String(new byte[]{-1, -6, -86, -69, -68, -52, -35, -35, -1, -86, -86, -86, -86, -86, -86, -69}).toCharArray()... it works as unexpected.
How I have to change my java code to receive the same key as in python? As I understand I have to encode java byte array string to the same value as in python .derive(...) method.
Thanks in advance.
UPDATE:
salt = b'salt'
PASSWORD = = bytes((255, 250, 170, 187, 188, 204, 221, 221, 255, 170, 170, 170, 170, 170, 170, 187))
key = PBKDF2HMAC(hashes.SHA256(), 32, salt, 512, backend).derive(PASSWORD)
and
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
password = new String(new byte[]{-1, -6, -86, -69, -68, -52, -35, -35, -1, -86, -86, -86, -86, -86, -86, -69});
var key = secretKeyFactory
.generateSecret(new PBEKeySpec(password.toCharArray(),
"salt".getBytes(), 512, 256))
.getEncoded();
should give the same result. It works for new byte[]{1,2,3,4,....16} password.
UPDATE2: I changed password to unsigned int[] but it not works anyway:
char[] password = new char[PASSWORD.length];
for (int i = 0; i<PASSWORD.length; password[i] = (char)(PASSWORD[i++] & 0xFF));
var key = secretKeyFactory
.generateSecret(new PBEKeySpec(password, "salt".getBytes(), 512, 256))
.getEncoded();
答案1
得分: 2
除了不同的摘要方法(参见第一个答案),问题在于使用 PBKDF2WithHmacSHA256
派生的密钥是 PBKDF2KeyImpl
的一个实例,该实例要求密码为字符串。这个字符串在 PBKDF2KeyImpl
中使用 UTF8 编码(参见类PBKDF2KeyImpl
的文档)。然而,在这里,密码是一个(任意的)字节序列,通常与 UTF8 不兼容,因此数据在进行 UTF8 解码时会被损坏。一个可能的解决方案是将 PBEKeySpec
替换为 BouncyCastle 的 PKCS5S2ParametersGenerator
,后者将密码作为字节数组(在 init
方法中):
import java.nio.charset.StandardCharsets;
import org.bouncycastle.crypto.PBEParametersGenerator;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
...
byte[] salt = "salt".getBytes(StandardCharsets.UTF_8);
int iterations = 512;
byte[] PASSWORD = new byte[] { (byte)255, (byte)250, (byte)170, (byte)187, (byte)188, (byte)204, (byte)221, (byte)221, (byte)255, (byte)170, (byte)170, (byte)170, (byte)170, (byte)170, (byte)170, (byte)187 };
PBEParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest());
generator.init(PASSWORD, salt, iterations);
byte[] keyBytes = ((KeyParameter)generator.generateDerivedParameters(256)).getKey();
// 使用 https://stackoverflow.com/a/9855338 中的 bytesToHex 方法
System.out.println(bytesToHex(keyBytes).toLowerCase()); // d8aa4772e9648572611fe6dca7f653353de934cdb3b29fab94eb13ba2b198b9f
现在的结果与 Python 代码的结果相匹配:
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
salt = b'salt'
iterations = 512
PASSWORD = bytes((255, 250, 170, 187, 188, 204, 221, 221, 255, 170, 170, 170, 170, 170, 170, 187))
key = PBKDF2HMAC(hashes.SHA256(), 32, salt, iterations).derive(PASSWORD)
print(key.hex()) # d8aa4772e9648572611fe6dca7f653353de934cdb3b29fab94eb13ba2b198b9f
英文:
Apart from the different digests (s. 1st answer), the problem is that the key derived with PBKDF2WithHmacSHA256
is an instance of PBKDF2KeyImpl
, which requires a string as password. This string is UTF8 encoded in PBKDF2KeyImpl
(see documentation of the class PBKDF2KeyImpl
). Here, however, the password is an (arbitrary) byte sequence, which is generally not compatible with UTF8, so that the data is corrupted during UTF8 decoding. A possible solution is to replace PBEKeySpec
with BouncyCastle's PKCS5S2ParametersGenerator
, which expects the password as byte array (in init
):
import java.nio.charset.StandardCharsets;
import org.bouncycastle.crypto.PBEParametersGenerator;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
...
byte[] salt = "salt".getBytes(StandardCharsets.UTF_8);
int iterations = 512;
byte[] PASSWORD = new byte[] { (byte)255, (byte)250, (byte)170, (byte)187, (byte)188, (byte)204, (byte)221, (byte)221, (byte)255, (byte)170, (byte)170, (byte)170, (byte)170, (byte)170, (byte)170, (byte)187 };
PBEParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest());
generator.init(PASSWORD, salt, iterations);
byte[] keyBytes = ((KeyParameter)generator.generateDerivedParameters(256)).getKey();
// with bytesToHex from https://stackoverflow.com/a/9855338
System.out.println(bytesToHex(keyBytes).toLowerCase()); // d8aa4772e9648572611fe6dca7f653353de934cdb3b29fab94eb13ba2b198b9f
The result now matches that of the Python code:
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
salt = b'salt'
iterations = 512
PASSWORD = bytes((255, 250, 170, 187, 188, 204, 221, 221, 255, 170, 170, 170, 170, 170, 170, 187))
key = PBKDF2HMAC(hashes.SHA256(), 32, salt, iterations).derive(PASSWORD)
print(key.hex()) # d8aa4772e9648572611fe6dca7f653353de934cdb3b29fab94eb13ba2b198b9f
答案2
得分: 1
你的Java代码中使用了SHA1
,而Python代码中使用了SHA256
,这就是导致结果不同的原因。请使用PBKDF2WithHmacSHA256
。
英文:
You're using SHA1
in your Java code and SHA256
in your Python code, which is why you're getting different results. Use PBKDF2WithHmacSHA256
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论