1. Overview
bcrypt is a password-hashing algorithm. It’s based on the Blowfish encryption algorithm.
Instead of just hashing a password, the bcrypt algorithm adds a randomly generated salt and hashes the two together. This increases the security against attacks like dictionary or brute force attacks. Additionally, bcrypt uses a cost factor corresponding to the number of password iterations and hashing rounds used in the algorithm before generating the final hash. It makes the algorithm slower but much more secure by increasing the time, effort, and computational resources for password cracking.
In this tutorial, we’ll discuss how to compute a bcrypt hash in the Linux shell.
2. The Format of a bcrypt Hash
Here’s an example of the final output of hashing using bcrypt:
$2b$12$yv9bWwY.PbnVVgkkymRldeKkSxBpQtXszMdo2s.kI91i.fBIGS7VS
The string consists of 60 bytes. The algorithm uses the password, a cost factor, and a 16-byte salt to compute a 24-byte hash.
In the final string, the first four characters, $2b$, represent an identifier of the bcrypt algorithm. Other possible identifiers are $2a$, $2x$, and $2y$, which correspond to the algorithm’s older implementations.
The number after the hash identifier, namely 12, is the cost parameter. 12 corresponds to 212 iterations within the algorithm.
The next 22 characters after $12$ in the example, yv9bWwY.PbnVVgkkymRlde, correspond to the base64 encoding of the salt.
Finally, the remaining 31 characters are the base64-encoded string of the first 23 bytes of the computed hash. The computed hash is normally 24 bytes.
3. Using htpasswd
The htpasswd command is used to create and update flat files to store usernames and passwords for basic HTTP authentication. A flat file can be a plain text file having records on each line. /etc/passwd is an example of a flat file.
However, we can use the htpasswd command to compute and display the hashed output of a password using the bcrypt algorithm.
We need to install the apache2-utils package on Ubuntu to use htpasswd. The version we’ve used for this tutorial is 2.4.52-1ubuntu4.9.
Let’s now see how to use htpasswd:
$ htpasswd -bnBC 12 "" my_secret_password
:$2y$12$tnXnXe4ursqRfQ9lDQkzz.rUvx6eABmS1y.WR3EJlR.KVuHwot8dK
The -b option is for running htpasswd in batch mode, that is, it gets the password from the command line. If we don’t use the -b option, htpasswd prompts us to enter the password, and the typed password isn’t visible on the command line.
The -n option shows the hashed password on the standard output.
The -B option specifies that bcrypt should be used for hashing passwords. There are also other options for hashing. For example, the -m option uses MD5, while the -s option uses SHA.
The -C option can be used together with the -B option. It sets the bcrypt “cost”, or the time used by the bcrypt algorithm to compute the hash. htpasswd accepts values within 4 and 17 inclusively and the default value is 5. At higher cost parameters, the computed hash is more secure, but the computing time is longer. We pass the value 12 in our example.
We also pass an empty string, “”, for the username. The username isn’t important in our case since we want to compute the bcrypt hash of a password, which is independent of the username.
Finally, we pass the password, my_secret_password, to htpasswd. The output of htpasswd consists of the username and the hashed password. They’re separated by a colon. Since we use an empty string as the username, the first character in the output is the colon that separates the username from the hash.
3.1. Getting Rid of the Colon
One way to get rid of the colon at the beginning is to use the cut command:
$ htpasswd -bnBC 12 "" my_secret_password | cut -d : -f 2
$2y$12$crJ5Vv.8qVjpZncIAGxTnO/Rfd5ujemhnVBU4ESRMT4MgWmUGxGRm
The -d option of cut specifies the delimiter, which is the colon in our case. The -f option selects the desired fields. Since we want to print only the hash, we pass 2.
Notably, the hash is different from the previous run as htpasswd uses a randomly generated salt value in each run.
4. Using Python
We can use Python’s bcrypt module to compute the hash of passwords. We need to install the python3-bcrypt package on Ubuntu if it isn’t installed.
4.1. Python Script
We’ll use the following script, bcrypt_hash.py, to calculate the bcrypt hash of a password:
$ cat bcrypt_hash.py
#!/usr/bin/python3
import bcrypt
import sys
# Get the password from the command line
password = sys.argv[1]
# Convert the password to bytes
bytes = password.encode('utf-8')
# Generate the salt
salt = bcrypt.gensalt()
# Compute the password hash and print
hash = bcrypt.hashpw(bytes, salt)
print(hash)
Our Python script gets the password from the command line and prints its hash again on the command line. The script starts with importing the bcrypt and sys modules:
import bcrypt
import sys
The bcrypt module computes the bcrypt hash of a password, while the sys module is a built-in Python module that accesses system-specific parameters and functions. Since bcrypt isn’t a built-in module, it must be installed explicitly.
We get the password from the command line using the sys module:
# Get the password from the command line
password = sys.argv[1]
sys.argv is a list of command-line arguments. sys.argv[0] is the script’s name, hence sys.argv[1] contains the password we pass to the script as a first argument. We assign the password to the password variable.
Then, we convert the password to Python’s built-in bytes type as the function that computes the hash, bcrypt.hashpw(), requires the password to be passed as bytes:
# Convert the password to bytes
bytes = password.encode('utf-8')
Then, we generate a random salt using the gensalt() method of bcrypt:
# Generate the salt
salt = bcrypt.gensalt()
Finally, we compute the bcrypt hash of the password and print it:
# Compute the password hash and print
hash = bcrypt.hashpw(bytes, salt)
print(hash)
The first argument of bcrypt.hashpw() is the password and the second is the salt. It returns the hashed password as the output. We print the result using the print() function.
Let’s grant execute permissions to bcryrpt_hash.py using chmod:
$ chmod +x bcrypt_hash.py
Let’s now run the script twice:
$ ./bcrypt_hash.py my_secret_password
b'$2b$12$PnGvsg8jilGZ6oqjIB5XQuPEDpkTSAKNdGZWD/nFGyZ59b/6SRSsS'
$ ./bcrypt_hash.py my_secret_password
b'$2b$12$JDYOA8On5XYjODUWImjFEOQVi.cdTyNYVJNhtminOVXorKbqlw7fW'
The hash values are different in each run as expected because of the random salt values. The b prefixes show that the results are of the bytes type.
4.2. Python One-Liner
It’s also possible to run the script as a Python one-liner:
$ python3 -c "import bcrypt; print(bcrypt.hashpw(b'$password', bcrypt.gensalt()))"
b'$2b$12$BPBDDQO112tJl1xqOM384Ol7JhNaDHtUn68eTEEiZlbWp1eiz8uG2'
The version of Python on our system is 3.10.12, so we use the python3 command. The -c option lets us run Python commands from the command line.
The one-liner is a more compact form of bcryrpt_hash.py. We get the password from the password environment variable:
$ echo $password
my_secret_password
After importing the bcrypt module, we compute the bcrypt hash using bcrypt.hashpw(b’$password’, bcrypt.gensalt()). The first argument of bcrypt.hashpw() is b’$password’, which is the password converted to the bytes type. The second argument, on the other hand, is the salt generated using bcrypt.gensalt(). Finally, we print the output of bcrypt.hashpw() by passing its output to the print() function.
Consequently, we’ve succeeded in generating the bcrypt hash of a password from the command line using Python.
5. Using Perl
Perl’s Crypt::Bcrypt module is another alternative to compute the bcrypt hash of a password.
Additionally, we use the Data::Entropy::Algorithms module in our examples. We can install the two Perl modules using cpan:
$ sudo cpan Crypt::Bcrypt
$ sudo cpan Data::Entropy::Algorithms
Their installation requires root privileges, so we use cpan together with sudo.
5.1. Perl Script
We’ll use the following script to calculate the bcrypt hash of a password, bcrypt_hash.pl:
$ cat bcrypt_hash.pl
#!/usr/bin/perl
use Crypt::Bcrypt qw(bcrypt);
use Data::Entropy::Algorithms qw(rand_bits);
# Get the password from the command line
my $password = $ARGV[0];
# Generate the salt
my $salt = rand_bits(16*8);
# Compute the password hash and print
my $hash = bcrypt($password, '2b', 12, $salt); print $hash."\n";
Similar to our Python script in the previous section, bcrypt_hash.pl gets the password from the command line and prints its hash again on the command line.
First, the script imports the bcrypt() function from the *Crypt::Bcrypt* module and the rand_bits() function from the *Data::Entropy::Algorithms* module:
use Crypt::Bcrypt qw(bcrypt);
use Data::Entropy::Algorithms qw(rand_bits);
The Data::Ent**ropy::Algorithms module has a collection of functions that use entropy.
Then, we get the password from the command line:
# Get the password from the command line
my $password = $ARGV[0];
Perl passes the command-line arguments to a script using the built-in @ARGV array. $ARGV[0] is the first element in this array. It’s the password we pass to the script in our case.
Then, we generate a random salt using the imported rand_bits() function from the Data::Ent**ropy::Algorithms module:
# Generate the salt
my $salt = rand_bits(16*8);
The rand_bits(16*8) function returns 16 bytes of entropy as a string of octets.
Finally, we compute the hash using the bcrypt() function from the *Crypt::*Bcrypt module and print the result:
# Compute the password hash and print
my $hash = bcrypt($password, '2b', 12, $salt);
print $hash."\n";
The first argument of bcrypt() is the password. The second and third arguments, ‘2b’ and 12, are the version of the bcrypt algorithm and the cost, respectively. Finally, the last argument is the salt.
Let’s grant execute permissions to bcryrpt_hash.pl using chmod and then run it twice:
$ chmod +x bcrypt_hash.pl
$ ./bcrypt_hash.pl my_secret_password
$2b$12$KxU7DhRDtseVVpd6GsD6ru0G6bs6pIC30tl2dioBFzLOfVXVAb4xy
$ ./bcrypt_hash.pl my_secret_password
$2b$12$8SKIoCGLes62XymAudyj9.HFPS5pBB3OEnsoMmP87KuOaHwCRyRKy
As is apparent from the output, the script generates different hashes in each run.
5.2. Perl One-Liner
We can also shorten our Perl script as a one-liner using the password environment variable:
$ perl -e "use Crypt::Bcrypt qw(bcrypt); use Data::Entropy::Algorithms qw(rand_bits); print(bcrypt($password, '2b', 12, rand_bits(16*8)).\"\n\")"
$2b$12$hptVTA8pRZt/Gd8x6z2BSulbmoIjm7zbqRaHoUfCoI1gQdVQmhbiq
We import the necessary functions as before. Then, we pass the parameters directly to bcrypt() using bcrypt($password, ‘2b’, 12, rand_bits(16*8)) and print the result. The -e option of the perl command lets us execute Perl commands from the command line.
Consequently, we’ve again succeeded in generating the bcrypt hash of a password from the command line using Perl.
6. Using the Spring Boot CLI
It’s also possible to compute the bcrypt hash of a password using the Spring Boot CLI (Command Line Interface). The encodepassword command of the Spring Boot CLI directly computes the hash of the input password:
$ spring encodepassword my_secret_password
{bcrypt}$2a$10$CWME8cClhabeLKrliDQzNuQ3ZFpXgzkn9mOYdZ7yUoZKlXlAjIF.O
my_secret_password is the password we want to hash as before. We can get rid of the {bcrypt} prefix using cut:
$ spring encodepassword my_secret_password | cut -c 9-
$2a$10$CWME8cClhabeLKrliDQzNuQ3ZFpXgzkn9mOYdZ7yUoZKlXlAjIF.O
The -c 9- option of cut selects the characters in the output string starting from the ninth character till the end.
7. Conclusion
In this article, we discussed how to compute a bcrypt hash in the Linux shell. First, we discussed the format of the hash generated by the algorithm. Then, we saw how we can hash a password using htpasswd, Python, Perl, and the Spring Boot CLI.