3

I have a database with passwords that are hashed using the following python code:

result = str(CRYPT(digest_alg='pbkdf2(1000,20,sha512)', salt=True)(password)[0])

(details can be found here)

for password='123' it generates

pbkdf2(1000,20,sha512)$b3c56f341284f4be$54297564f7a3be8c6e9c10b27821f8105e0a8120

I need to validate password using java. I use the following code:

    validatePassword("123", "pbkdf2(1000,20,sha512)$b3c56f341284f4be$54297564f7a3be8c6e9c10b27821f8105e0a8120");



    private static boolean validatePassword(String originalPassword, String storedPassword) throws NoSuchAlgorithmException, InvalidKeySpecException
    {
        String[] parts = storedPassword.split("\\$");
        byte[] salt = fromHex(parts[1]);
        byte[] hash = fromHex(parts[2]);

        PBEKeySpec spec = new PBEKeySpec(originalPassword.toCharArray(), salt, 1000, hash.length * 8);
        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
        byte[] testHash = skf.generateSecret(spec).getEncoded();

        System.out.println(toHex(testHash));
        System.out.println(toHex(hash));

        return true;
    }


    private static byte[] fromHex(String hex) throws NoSuchAlgorithmException
    {
        byte[] bytes = new byte[hex.length() / 2];
        for(int i = 0; i<bytes.length ;i++)
        {
            bytes[i] = (byte)Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
        }
        return bytes;
    }

    private static String toHex(byte[] array)
    {
        StringBuilder sb = new StringBuilder();
        for(int i=0; i< array.length ;i++)
        {
            sb.append(Integer.toString((array[i] & 0xff) + 0x100, 16).substring(1));
        }
        return sb.toString();
    }

but result is the following:

80385948513c8d1826a3a5b8abc303870d41d794
54297564f7a3be8c6e9c10b27821f8105e0a8120

Please help what I am doing wrong?

iehrlich
  • 3,572
  • 4
  • 34
  • 43
krtl
  • 193
  • 11
  • I don't see how anyone could possibly answer this question without knowing what `CRYPT` is. – Aran-Fey Apr 21 '17 at 12:05
  • class gluon.validators.CRYPT(key=None, digest_alg='pbkdf2(1000, 20, sha512)', min_length=0, error_message='Too short', salt=True, max_length=1024) – krtl Apr 21 '17 at 12:07
  • you can find it at http://web2py.readthedocs.io/en/latest/validators.html – krtl Apr 21 '17 at 12:08

1 Answers1

3

There is kind of a "bug" in the code around web2py.

The hash LOOKS like a hex string, but it is sent to the hashlib.pbkdf2_hmac (a proxy to openssl's method) as just the character representation of the hex string. Meaning you should not use

byte[] salt = fromHex(parts[1]);

but

byte[] salt = parts[1].getBytes("utf-8");

In addition, you need to pass the KEYLENGTH instead of the salt length into PBEKeySpec's contructor.

The corrected part should read:

byte[] salt = parts[1].getBytes("utf-8");
byte[] hash = fromHex(parts[2]);
PBEKeySpec spec = new PBEKeySpec(originalPassword.toCharArray(), salt, 1000, 20*8);

Replace that and the code works. It took a while to find this out ;)

fjalvingh
  • 790
  • 1
  • 8
  • 20
  • Thank you so much!!! It really works! I apologize for the bug because I'm new to Java. – krtl Jul 31 '17 at 07:09
  • No need for excuses, @krtl - you did not have a bug, it looks like the Python code from Web2py has one ;) – fjalvingh Jul 31 '17 at 18:14
  • Yes, I already understood it, only little later. Anyway, you are great! Thank you again! – krtl Aug 01 '17 at 04:41