Understanding the ForgeRock Password Storage Scheme

Understand how ForgeRock stores user passwords.

Authors

Published: February 16, 2024


You have been charged with migrating your authentication system from ForgeRock to another platform. How do you move the users without knowing the users’ passwords or making them reset their passwords in the new system?

The short answer is “move the password hash”. In this post you will learn what a password hash is, how ForgeRock stores them and how to use this to transparently and safely migrate your users’ credentials.

What Is Password Hashing?

Password hashing might be a scary phrase. It sounds complicated and high-tech. In truth, the nitty-gritty details can be.

But don’t fret. The good news is that for the purposes of this post you can think of it in simple terms. Password hashing is taking a string of characters, applying an algorithm (or function), and coming up with a different set of characters.

The algorithm chosen to hash the characters determines the difficulty of getting the original password from the hash. Because you want to store passwords safely, the ideal algorithm makes it simple to go from password to hash, and impossible to go from hash to password.

Thankfully, many smart people have figured out a way to hash passwords so it is easy to reproduce a hash if you know the source, but very difficult to get to the original set of characters from the hashed characters. You may have heard of some of these hashing functions such as:

  • SHA-256
  • PBKDF2
  • Bcrypt

There are two more aspects of hashing that you will need to know for this article. One is the salt and the other is hashing iterations.

If you understand the concept of a password salt and iterations, feel free to skip to the next section.

Salt

You can think of salt as random characters sprinkled on the password before it is run through the algorithm. This is done so that two users using the same password will still have different hashes.

Add salt to password.

Let’s break down the concept of a salt with an example using a simple hashing function. Please do not use this in production!

The hashing function for this example will be to use the next letter of the existing letter. The letter ‘a’ will become ‘b’, the letter ‘b’ will become ‘c’, and so on.

John chooses ‘cat’ for his password and Sally also chooses ‘cat’ for her password.

The hash for both of their passwords would become: ‘dbu’, (‘c’ becomes ‘d’, ‘a’ becomes ‘b’, ‘t’ becomes ‘u’).

If an attacker stole these hashes, they would know that John and Sally shared the same password. They could also see if any other users had the same password.

By adding a salt to each of their passwords, this can be avoided.

Salts should be chosen randomly by the password hashing component. For our example, let’s say John is issued a salt ‘orj’ and Sally is issued ‘nei’.

By appending a salt to the end of a user’s password we can change their password hash.

John’s salted password becomes ‘catorj’ and Sally’s becomes ‘catnei’.

Using the next letter algorithm on the salted passwords, the hashes become ‘dbupsk’ and ‘dbuofj’, respectively.

You can now see that even though they have the same password, their hash is different.

This is a simplified example. As mentioned above, the next letter hashing algorithm is not sophisticated and has many issues. You can see the passwords start with the same letters and you can find the hashed letters for ‘cat’ in both if you know the algorithm. The hashing functions such as PBKDF2 mentioned above solve these problems and many others making the one way hash safe and secure.

Iterations

An iteration means calling the hashing algorithm one time; the term “iterations” implies calling the hashing algorithm multiple times. In our example above, one iteration of our hashing function with the password ‘cat’ produced ‘dbu’. If you were to perform a second iteration, the hash would become ‘ecv’: ‘d’ becomes ‘e’, ‘b’ becomes ‘c’, ‘u’ becomes ‘v’.

Increasing the number of iterations means that it takes longer to arrive at the final hash. If you iterate one thousand times, it will take longer than iterating ten times. You want to do this because you want to arrive at the hash quickly, but not too quickly.

Why quickly? Because you want to make sure your user can get through the authentication process quickly. They’ll:

  • present their password
  • you’ll add in the salt
  • you’ll hash it one thousand times
  • you’ll compare the computed value with the stored value

Why not too quickly? Because if an attacker has a program submitting millions of passwords from a list, you want to slow them down and make it painful. For example, waiting 0.1 seconds when you are submitting one password is fine and won’t be noticed. Waiting for one million passwords when each takes 0.1 seconds is more than a day.

Record the number of iterations when you store the hash so that you can compare the correct computed password hash to the one stored.

How Do I Use This Info To Migrate Users?

Secure identity and access management systems never store user passwords. They only store the hash. If they used a salted hash, they typically store the salt as well. By transferring the hash, the salt and the number of iterations to another system, you can derive the same hash if you are using the same hashing algorithm.

When a user enters their password on your site, apply the salt and apply the same hashing algorithm the same number of times that was used in the old system. Then compare the hash you get with the stored hash. They will match if the same password was used.

This is how you are able to migrate a user without ever knowing the actual password.

How Do I Decipher ForgeRock’s Hash?

Okay, now that you have the background and understand password hashing, let’s dig into migrating ForgeRock password hashes.

Password hash and salt storage will vary between implementations. One system may store the password and hash in different fields in a database and another may store them together in one field. Hashing algorithms can vary as well and could even vary between users. It is important to know these implementation details of the system you are getting the hashes from. By default, ForgeRock uses the PBKDF2-HMAC-SHA256 encryption with their Cloud Development Kit so that will be used in this article.

If your implementation is using a different hashing function, ForgeRock has documented their password storage schemes.

ForgeRock stores that hash in the following way:

"{PBKDF2-HMAC-SHA256} ";" <iterations> ":" base64(<digest> <salt>) where:

  • {PBKDF2-HMAC-SHA256} is a literal describing the hashing function used.
  • ";" is a literal separator
  • <iterations> is the number of times the hash is applied, as defined above
  • ":" is a literal separator
  • base64(<digest> <salt>) holds the password hash and salt and is base64 encoded

In the example:

{PBKDF2-HMAC-SHA256};10:8c7nLGEIXeZf45YQ92A2MD+v8olvKKl6iWXGQZoluJ/awqZnHwFvslIOx7xOZ9AV

  • PBKDF2-HMAC-SHA256 is the hashing function used
  • 10 is the number of iterations
  • 8c7nLGEIXeZf45YQ92A2MD+v8olvKKl6iWXGQZoluJ/awqZnHwFvslIOx7xOZ9AV is the base64 encoded password hash and salt

So how do you separate the password hash from the salt? The first thing you need to do is base64 decode the string. This gives you:

ñ�ç,a]æ_ã�÷``60?¯ò�o(©z�e�A�%¸���¦go²R�¼Ng�

What the heck is that?

I know, not very human readable. From here, you need to write code to get the values you are looking for.

Let’s look at an example Below is an excerpt from the script used in FusionAuth’s Migration From ForgeRock guide.

#Structure for storing parts from the password hash
Password_info = Struct.new(:encryption_scheme,:password_hash,:salt_hash,:itterations)

#parses the Forgerock password hash for the needed parts
def get_password_info_from_forgerock(hashstring)
  #Assuming PBKDF2-HMAC-SHA256 hash here,  If different, this must be customized.
  #The size of the hash algorithm.  Forgerock stores the PBKDF2-HMAC-SHA256 hash as "{PBKDF2-HMAC-SHA256}” <iterations> “:” base64(<digest> <salt>)
  #So the hash is 48 bytes with the last 16 being the salt hash
  algorithm_size = 32
  encryption_scheme = /(?<={).*(?=})/.match(hashstring)
  itterations = /(?<=}).*(?=:)/.match(hashstring)
  password_salt_hash = /(?<=:).*(?=$)/.match(hashstring)

  bytes = Base64.decode64(password_salt_hash.to_s)

  hash_bytes = bytes.byteslice(0, algorithm_size)
  salt_bytes = bytes.byteslice(algorithm_size..-1)

  hash_encode = Base64.strict_encode64(hash_bytes)
  salt_encode = Base64.strict_encode64(salt_bytes)

return Password_info.new(encryption_scheme.to_s,hash_encode,salt_encode,itterations.to_s)
end

This is Ruby code, but you can use any language which supports base 64 encoding and decoding.

Breaking the code down, you can see that it starts by accepting hashstring as an argument. hashstring will look like our example from above, {PBKDF2-HMAC-SHA256};10:8c7nLGEIXeZf45YQ92A2MD+v8olvKKl6iWXGQZoluJ/awqZnHwFvslIOx7xOZ9AV.

Next, it uses regular expressions to extract each part of the stored password information. At this point you should have the encryption scheme (PBKDF2-HMAC-SHA256) and number of iterations (10) in plain text.

It then takes the base64(<digest> <salt>) part of the stored information, password_salt_hash, and decodes it from base64. Once the decoding is done, it will then take the appropriate number of bytes of the decoded string and put it into variables, hash_bytes and salt_bytes. The appropriate number of bytes was defined in the ForgeRock password storage schemes. From there, it re-encodes those bytes back to base64.

You now have the four parts of information (the hashing algorithm used, the password hash, the number of iterations, and the salt) you need to move the password from ForgeRock to another system. Import this information into your new system and the next time the user enters their password from the old system, the hashes will match.

What if I told you, you can move the password without knowing the password?

More on passwords

Subscribe to The FusionAuth Newsletter

A newsletter for developers covering techniques, technical guides, and the latest product innovations coming from FusionAuth.

Just dev stuff. No junk.