Issue
I want communicate between java and typescript with encrypted AES-GCM data (PBKDF2 hash used for password) .
I used random bytes for pbkdf2 :
randomBytes(Base64): wqzowTahVBaxuxcN8vKAEUBEo0wOfcg4e6u4M9tPDFk=
private String salt = "1234";
private static final String KEY_ALGORITHM = "AES";
private Key generateKey(byte[] randomBytes) throws Exception {
var randomPassword = new String(randomBytes);
KeySpec keySpec = new PBEKeySpec(randomPassword.toCharArray(), salt.getBytes(), 10000, 256);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
var kdf2SecurePassword = secretKeyFactory.generateSecret(keySpec).getEncoded();
return new SecretKeySpec(kdf2SecurePassword, KEY_ALGORITHM);
}
and this is typescript code :
private static importKey(randomBytes: ArrayBuffer) {
return crypto.subtle.importKey(
'raw',
randomBytes,
{name: 'PBKDF2', length: 256},
true,
['deriveKey']
);
}
private generateAESKey(baseKey, salt) {
const encSalt = new TextEncoder().encode(salt);
const algorithm = {
name: 'PBKDF2',
hash: 'SHA-256',
iterations: 10000,
salt: encSalt
};
return crypto.subtle.deriveKey(
algorithm,
baseKey,
{name: 'AES-GCM', length: 256},
true,
['encrypt', 'decrypt']
);
}
Result in java and typescript :
Java Key : hrG2Hw/bec9JoI+EcemfUxR/5lGw718kYOcCWRRbulk=
typescript Key : EGPcTUQUmYpNHoCDuD7rkIVaHkPSqEZYan4HnWfhFSc=
Why i have difference result ?
What part of code has wrong ?
UPDATE
Interesting, I try to generate pbkdf2 key with linux nettle-pbkdf2
command, result exactly match by javascript output :
USERNAME@HOSTNAME:~$ echo -n "wqzowTahVBaxuxcN8vKAEUBEo0wOfcg4e6u4M9tPDFk=" | base64 -d | nettle-pbkdf2 -i 10000 -l 32 --raw "1234" | base64
EGPcTUQUmYpNHoCDuD7rkIVaHkPSqEZYan4HnWfhFSc=
Solution
After too many research in internet and compare 3 programming languages (Java,Javascript,Python) and linu nettle-pbkdf2 command .
I noticed Java JCE and also Bouncycastle has a bug in PBKDF2 algorithm implementation .
this is python exmaple code :
#!/usr/bin/python3
import base64
import hashlib
b64 = "wqzowTahVBaxuxcN8vKAEUBEo0wOfcg4e6u4M9tPDFk="
bytePass = base64.b64decode(b64)
key = hashlib.pbkdf2_hmac('sha256', bytePass, '1234'.encode(), 10000, 32)
keyB64 = base64.b64encode(key)
print(keyB64)
result is same by javascript and linux nettle-pbkdf2 command :
EGPcTUQUmYpNHoCDuD7rkIVaHkPSqEZYan4HnWfhFSc=
in java when i change randomBytes to a simple string like Password123456
, result in java, javascript, python is similar .
Java : nCpOvPbsb62/jfYIlPClEEGNzpD7Wt/qwkIC00Rkx24=
JavaScript : nCpOvPbsb62/jfYIlPClEEGNzpD7Wt/qwkIC00Rkx24=
Python : nCpOvPbsb62/jfYIlPClEEGNzpD7Wt/qwkIC00Rkx24=
Note: Actually randomBits in my example codes is ECDH shared secret . I think Java can not calculate some bits on PBKDF2 or implementation have problem .
Anyway, I migrate from PBKDF2 to Argon2.
UPDATE
This PBKDF2 Source code do work in java without any problem : PBKDF2 Source Code
Answered By - mah454
Answer Checked By - Terry (JavaFixing Volunteer)