Kotlin PBKDF2 Secure Password Hashing | ninjasquad
Introduction
Hello and welcome to the article about Kotlin password hashing with PBKDF2 (to be even more specific- with PBKDF2WithHmacSHA512).
In this hands-on guide, we will:
- learn why password hashing is important,
- discover a couple of definitions every programmer should be aware of,
- and finally- how to hash a password with Kotlin and PBKDF2.
What Is Password Hashing and Is It Important?
Well, password hashing is the process of taking a password and applying a mathematical function (the hash function) to it to produce a fixed-size string of characters- the hash value (also known as digest).
It’s worth mentioning, that it is a one-way operation. We simply can’t reverse the process and obtain the key from a hash value. So even if attackers gain access to the hashed password in our database, obtaining the original password is difficult.
But difficult, doesn’t mean impossible. Although the process can’t be reversed, attackers can try to crack the password using different combinations of characters and computing the hash value for each combination. And because of that, password hashing algorithms are designed to be computationally expensive, meaning that it takes a long time to compute the hash value for a given password.
So, given these points, we can clearly see that password hashing is important not only when working with Kotlin.
What Does Salt Mean?
Before we dive into the Kotlin password hashing with pbkdf2, let’s learn more about salt.
Salt is a random value that is generated and added to the input data before it is hashed. Thanks to that, the resulting hash will be different even if the original passwords are the same, making it even harder to crack the hashed passwords using precomputed hash tables (also known as rainbow tables).
Kotlin / Java Password Hashing Algorithms
And as the last thing before the practice part, let’s take a look at a couple of password hashing algorithms, which we can use when working with Kotlin:
- MD5 (Message Digest Algorithm 5), which produces a 128-bit hash value. Nevertheless, it’s vulnerable to collision attacks and should not be used for password hashing or other sensitive applications.
- SHA-512 (Secure Hash Algorithm 512-bit), which produces a 512-bit hash value. Technically, it is considered a secure option, but we have better alternatives.
- BCrypt– a password hashing function based on the Blowfish cipher and including salt to protect against dictionary attacks.
- SCrypt– a key derivation function designed for GPU cracking attacks. It uses a large amount of memory, making it more expensive to crack.
- PBKDF2 (Password-Based Key Derivation Function 2), which is also a key derivation function. It is designed to be resistant to dictionary attacks and uses salt and iterative hashing to increase the cost of attacks.
Kotlin With PBKDF2 (PBKDF2WithHmacSHA512) In Practice
So, with all of that being said, we can finally start the coding part. As I mentioned in the beginning, we will see how to hash passwords in Kotlin with PBKDF2WithHmacSHA512.
Don’t worry about this monster- it’s a name of an algorithm, which we will use when requesting an instance of SecretKeyFactory. Without going into much detail here, our little monster will generate a 512 bits hash value. (And if you’re interested in a more detailed answer, then check out this StackOverflow topic).
Random Salt
Firstly, let’s implement a function responsible for generating random salt:
fun generateRandomSalt(): ByteArray { val random = SecureRandom() val salt = ByteArray(16) random.nextBytes(salt) return salt }
16-byte salt will be enough. Whatsoever, we create a SecureRandom instance using the default constructor.
But, if we would like to pick a specific algorithm, then we can use the getInstance method instead:
SecureRandom.getInstance("PKCS11")
We can find the list of all supported algorithms right here.
Lastly, if we would like to convert the ByteArray to a hex String, we can use the formatHex:
private fun ByteArray.toHexString(): String = HexFormat.of().formatHex(this) // so that later, we can simply run: salt.toHexString()
Generate Hash
As the last thing in our Kotlin password hashing with PBKDF2, let’s implement the generateHash:
private const val ALGORITHM = "PBKDF2WithHmacSHA512" private const val ITERATIONS = 120_000 private const val KEY_LENGTH = 256 private const val SECRET = "SomeRandomSecret" fun generateHash(password: String, salt: String): String { val combinedSalt = "$salt$SECRET".toByteArray() val factory: SecretKeyFactory = SecretKeyFactory.getInstance(ALGORITHM) val spec: KeySpec = PBEKeySpec(password.toCharArray(), combinedSalt, ITERATIONS, KEY_LENGTH) val key: SecretKey = factory.generateSecret(spec) val hash: ByteArray = key.encoded return hash.toHexString() }
As we can see in the first line of our function, we combine the salt with an additional SECRET value. Oftentimes, the salt value is generated for each user in our system, which means that we have to persist it in the database, as well. So, in case our database data are leaked, we can reduce the risk of password cracking through this additional step.
For simplicity, the secret value is stored as a constant, but in a real-life scenario, we’d rather pass it as an environment variable
In the following lines, we define a SecretKeyFactory instance and a KeySpec with the desired password, salt, iterations and key length values.
Recommended Iterations And Key Length
When it comes to the number of iterations, in 2021 OWASP recommended 120,000 iterations for PBKDF2-HMAC-SHA512. The key length is equivalent to the chance for a hash collision, so it’s good for it to be at least 128 bits.
Kotlin Password Hashing With PBKDF2 Summary
And that would be all for this article covering Kotlin password hashing with the PBKDF2 topic.
If you enjoyed this content, then you might also be interested in one of the following posts:
Let me know in the comments section about your thoughts! 🙂
Source: Internet