I'm having following java code, but I need to convert into javascript using window.crypto.subtle.importKey
But the outcome is seems like different for Java outcome and Javascript outcome.
The following is my Java code
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.enterprise.inject.Default;
@Default
public class SecurityPasswordEncoder {
private SecretKeyFactory factory;
public SecurityPasswordEncoder() throws NoSuchAlgorithmException {
this.factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
}
public String encode(CharSequence rawPassword) {
return null;
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return false;
}
public boolean matches(String rawPassword, String encodedPassword, String salt) throws InvalidKeySpecException {
byte[] osalt = hexStringToByteArray(salt);
byte[] oValidate = new byte[32];
String sValidate;
PBEKeySpec pbeKeySpec = new PBEKeySpec(rawPassword.toCharArray(), osalt, 10000, 384);
Key secretKey = this.factory.generateSecret(pbeKeySpec);
System.arraycopy(secretKey.getEncoded(), 0, oValidate, 0, 32);
sValidate = byteArrayToHexString(oValidate);
return (sValidate.equals(encodedPassword));
}
private byte[] hexStringToByteArray(String s) {
byte[] b = new byte[s.length() / 2];
for (int i = 0; i < b.length; i++) {
int index = i * 2;
int v = Integer.parseInt(s.substring(index, index + 2), 16);
b[i] = (byte) v;
}
return b;
}
private String byteArrayToHexString(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
private char[] hexArray = "0123456789ABCDEF".toCharArray();
}
The following is my javascript code.
function deriveAKey(password, salt, iterations, hash) {
// First, create a PBKDF2 "key" containing the password
window.crypto.subtle.importKey(
"raw",
stringToArrayBuffer(password),
{"name": "PBKDF2"},
false,
["deriveKey"]).
then(function(baseKey){
// Derive a key from the password
return window.crypto.subtle.deriveKey(
{
"name": "PBKDF2",
"salt": stringToArrayBuffer(salt),
"iterations": iterations,
"hash": hash
},
baseKey,
{name:"HMAC","hash":"SHA-1"}, // Key we want.Can be any AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH", or "HMAC")
true, // Extractable
["sign", "verify"] // For new key
);
}).then(function(aesKey) {
// Export it so we can display it
return window.crypto.subtle.exportKey("raw", aesKey);
}).then(function(keyBytes) {
// Display key in Base64 format
var keyS = arrayBufferToString(keyBytes);
var keyB64 = btoa (keyS);
console.log(keyB64);
}).catch(function(err) {
alert("Key derivation failed: " + err.message);
});
}
//Utility functions
function stringToArrayBuffer(byteString){
var byteArray = new Uint8Array(byteString.length/2);
for (let i = 0; i < byteArray.length; i++) {
const index = i * 2;
byteArray[i] = parseInt(byteString.substring(index, index + 2), 16);
}
return byteArray;
}
function arrayBufferToString(buffer){
var byteArray = new Uint8Array(buffer.length * 2);
var byteString = '';
var hexArray = "0123456789ABCDEF".split("");
for (let j = 0; j < buffer.length; j++) {
let v = bytes[j] & 0xFF;
byteString[j * 2] = hexArray[v >>> 4];
byteString[j * 2 + 1] = hexArray[v & 0x0F];
}
return String.fromCodePoint(byteString);
}
The JavaScript code you posted needs to be modified as follows to derive the same hexadecimal encoded key as the Java code:
Test:
If the passphrase
my passphrase
and the hex encoded salt01020304abacadae
from the JavaScript code are used in the Java code,85CE92FBA21475DF45CB742576A3356A9E2B0011AC084611814E4E282880245E
is derived as hex encoded key and the Java code returnstrue
for:Note: If no
CryptoKey
instance is needed, but only the binary data, thenderiveBits()
is more efficient thanderiveKey()
.