Convert Java code to Frontend Javascript code

144 views Asked by At

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

There are 1 answers

0
Topaco On BEST ANSWER

The JavaScript code you posted needs to be modified as follows to derive the same hexadecimal encoded key as the Java code:

function deriveAKey(password, salt, iterations, hash) {
    // Import password
    window.crypto.subtle.importKey(
        "raw",
        new TextEncoder().encode(password),
        {name: "PBKDF2"},
        false,
        ["deriveKey"])
    .then(function(baseKey){
        // Derive key from password
        window.crypto.subtle.deriveKey(
            {name: "PBKDF2", salt: hex2ab(salt), iterations: iterations, hash: hash},
            baseKey,
            {name:"HMAC", hash: hash, length: 256}, 
            true,                               
            ["sign", "verify"])
        .then(function(aesKey) {
            // Export key
            window.crypto.subtle.exportKey("raw", aesKey)
            .then(function(keyBytes) {
                // Display key 
                console.log(ab2hex(keyBytes).toUpperCase());
            });
        }).catch(function(err) {
            alert("Key export failed: " + err.message);
        });   
    }).catch(function(err) {
        alert("Key derivation failed: " + err.message);
    });
}

const hex2ab = hex => new Uint8Array(hex.match(/[\da-f]{2}/gi).map((x) => parseInt(x, 16)));    
const ab2hex = ab => Array.prototype.map.call(new Uint8Array(ab), x => ('00' + x.toString(16)).slice(-2)).join('');

deriveAKey("my passphrase", "01020304abacadae", 10000, "SHA-1");


Test:
If the passphrase my passphrase and the hex encoded salt 01020304abacadae from the JavaScript code are used in the Java code, 85CE92FBA21475DF45CB742576A3356A9E2B0011AC084611814E4E282880245E is derived as hex encoded key and the Java code returns true for:

new SecurityPasswordEncoder().matches("my passphrase", "85CE92FBA21475DF45CB742576A3356A9E2B0011AC084611814E4E282880245E", "01020304abacadae")

Note: If no CryptoKey instance is needed, but only the binary data, then deriveBits() is more efficient than deriveKey().