2

Context:

I am using this tutorial and trying to understand and implement salted password hashing using Java. After spending some time on this topic, I figured out that the basic idea is to:

  1. Convert the password string to a character array.
  2. Generate a random salt using SecureRandom(or similar).
  3. Hash the password character array with a standard cryptographic hash function.
  4. Convert the salt and hash byte arrays to respective hexadecimal strings.
  5. Prepend the hexedSalt to hexedHash and save the resulting string along with the hexedSalt to the database.

Questions:

  • What's the point of prepending the constant PBKDF2_ITERATIONS to createHash(char[] password) method?
  • Is my understanding of the whole process correct?
  • Here is the link to my source code - which value should I save as hash and which value as salt?
  • You seem to be missing the part where the salt is added to the password before it is hashed. Did you forget to include that part or was your understanding that it didn't work that way? – PwdRsch Jun 11 '15 at 20:23
  • Yes. I did notice that they mentioned that salt needs to be prepended to the hash before running it through a crypto hash function, not sure where is it being implemented here? – Sandeep Chatterjee Jun 11 '15 at 20:32
4

PBKDF2 is a password hashing function(*); it uses a configurable number of iterations (to make it as slow as is appropriate) and a salt (to deter all kinds of parallelism in attacks). To verify a password, the hash is recomputed, and should yield the same value. To perform this recomputation, you need to use the same number of iterations and the same salt value; otherwise you will get a distinct output and will be none the wiser.

A new salt shall be generated for each new registered password (e.g. whenever a new user account is created, and also whenever a user changes his password), so it must be stored along with the hash value.

The iteration count should also be stored, because it may be modified at some point. In the code you link to, the count is a constant (static final), but its conceptual role is to counter the increase in performance of computers over time: you use the number of iterations to make each password hashing as slow as you can tolerate, because it will also make it slow for the attacker. When you buy a new, faster server, you may want to increase the iteration count (i.e. modifying the source code and recompiling the application, with a higher count). However, in that case, you do not want to invalidate existing, stored hashed passwords. Thus, you need to know, for each stored hash, how many iterations were used for that specific hash. This is why the iteration count is stored along with the salt and the hash value itself.

(*) Strictly speaking, a password-based key derivation function, but let's keep things simple.

  • I would rather store some kind of version number instead of an iteration count. It may be easier to add an additional PBKDF function instead of increasing the number of iterations (as it isn't clear if PBKDF(100, salt, PBKDF(100, salt, pw)) is identical to PBKDF(200, salt, pw), it likely isn't). Watch out for the strange character encoding that Java uses for the password; it's the least significant 8 bits of the Java char. That's neither UTF-8 or Windows encoding, so don't allow any char over 127 in value. – Maarten Bodewes Jun 13 '15 at 13:30
  • 1
    Technically, using the 8 least significant bits means using ISO-8859-1 (aka "latin-1"), of which Windows-1252 is a superset. Thus, you could "reliably" use all latin-1 characters (0 to 126, and 160 to 255). But yeah, truncating the values is still weird and dangerous. – Thomas Pornin Jun 14 '15 at 11:59
  • It's funny that I never made that connection. Java simply specifies last 8 bit of the code unit (char) without notifying the user that this is equal to Latin-1. Still, it's not the same as UTF-8, which is the default in the PKCS#5 specs. Well, kinda, they say that implementations could use UTF-8 if I remember correctly. Java probably uses char arrays directly as these can be more easily be wiped and are less vulnerable to timing attacks. – Maarten Bodewes Jun 14 '15 at 12:11
  • It shall be noted that "wiping an array" no longer reliably works in Java, not since Java VM began to use these efficient GC algorithms that move objects in RAM. When you "wipe" the array, you only erase the last copy of it. Use of char[] instead of String in some methods is a remnant of older days (back in the Java-1.1 era, before 1998). – Thomas Pornin Jun 14 '15 at 13:29
  • Yeah, I guess it is better than nothing. Note that there now is a Destroyable interface for keys in Java 8. It may be that there will be some kind of support for destroying key / password data. Currently you cannot reliably implement the FIPS requirements without going native / HW. – Maarten Bodewes Jul 2 '15 at 13:35
2

Basic password hashing with salts only involves the password and a random salt that are joined together before creating the hash. You don't mention that joining process in your explanation and it's important because otherwise the salt doesn't add any security.

The example you've linked to is using a more advanced type of password hashing using PBKDF2 which also has an iteration count (number of times to repeat hashing process). That's reflected in their code with

byte[] hash = pbkdf2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);

So they have to store that iteration count, salt, and final password hash to use when recomputing a hash for password comparison purposes. If you're going to implement this example then you will need to store this same data.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.