Search code examples
javajavascriptnode.jssalt-cryptographypbkdf2

Failed to create Javascript analog of Java method for password hashing using SHA-256 and salt


I've been strugling for a while now by trying to complete next goal : I have a "Reset password" page that supposed to send new password to the server. I would like to hash it with salt, so I could save it in DB eventually. On Server side I have next methods that creates password hash :

public static String makeHash(String password, String salt) {
    try {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(password.getBytes("UTF-8"));

        byte byteData[] = md.digest(makeHash(salt.toLowerCase()));

        return Base64.getEncoder().encodeToString(byteData);
    } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
        log.error("Unable to make hash for pass. No hashing.", e);
    }

    return password;
}

private static byte[] makeHash(String val) throws NoSuchAlgorithmException, UnsupportedEncodingException {
    return MessageDigest.getInstance("SHA-256").digest(val.getBytes("UTF-8"));
}

I tried several Javascript libraries - crypto, crypto-js, SJCL , but couldn't manage to create same password as Java methods generates. For example, last working try out was :

var crypto = require('crypto');
crypto.pbkdf2('123', 'test@gmail.com', 1000, 60, 'sha256', function(err, key) {
  if (err)
    throw err;
  console.log(key.toString('Base64'));  // 'c5e478d...1469e50'
});

And it generated me this hash - Qr2lzotlRWj7BeJeFooMRj64auMPTb3PRhwLmfNcl4DCVAlFFibgOqZiyExZNO5i/icAUYoMjy73jSTd, while Java gives me - /pyQf3JCj5XoczfsYJ4LUb+y0DONGMl/AFzLiBTo8LA=.

I cannot change backend, since it running already for some time, so I was hoping that maybe someone could help me out with this.


Solution

  • You have to use the same algorithm on both sides. In Java you're using simply SHA-256 and in node you're using PBKDF2 with SHA-256.

    Node.js' crypto module provides the createHash(algorithm) function. Where you can specify SHA-256 directly. PBKDF2 is an algorithm that only uses different hashing functions under the hood.

    If you want hash passwords, then it is much safer to use PBKDF2 with a lot of iterations (> 86,000) and a random salt that you store alongside the password hash.

    Java has support for PBKDF2 in its standard library.

    If you really want to use SHA-256 directly and I strongly advise against it, you can use the following code:

    var crypto = require('crypto');
    var key = "123";
    var salt = "test@gmail.com";
    
    key = crypto.createHash('sha256')
            .update(key, "utf8")
            .update(makeHash(salt))
            .digest("base64");
    
    console.log(key);
    
    function makeHash(val) {
        return crypto.createHash('sha256').update(val, "utf8").digest();
    }
    

    Output:

    /pyQf3JCj5XoczfsYJ4LUb+y0DONGMl/AFzLiBTo8LA=
    

    Note that Hash.digest() takes an optional output encoding and not additional data.