Building A Multithread Password Cracker In Java
Tutorial for educational use.
Security is a major issue in IT applications. Unfortunately, millions of dollars are lost every year due to hacking of computer platforms. There is little that users can do about these hackings. On the other hand, users can act on the security of their passwords when they register on a website.
Choosing a sufficiently complex password is essential because without it, computer programs can be designed by malicious people to take advantage of the power of today’s computers to crack these passwords.
In this article, I teach you how to build a program to crack password encrypted with the SHA-256 cryptographic hash function. This program will be developed using the Java programming language and will take advantage of the multithreaded capabilities of the Java SDK to accelerate its execution.
The purpose of this tutorial is primarily educational and it may also allow you to test the strength of the passwords you choose when creating accounts on the Web.
What Is A Cryptographic Hash Function Of Type SHA-256?
First of all, I think it is important to define what the SHA-256 is. The SHA-256 is part of the family of secure hash algorithms (SHA) created by the NSA. The SHA-256 is a cryptographic hash function that inputs a character string and outputs a single 256-bit fixed-size character string, or 32 bytes. Represented in hexadecimal format, an SHA-256 is therefore 64 characters long.
The great advantage of cryptographic hash functions such as the SHA-256 is that they are one-way functions.
It is therefore very difficult to find the input value corresponding to a given SHA-256 signature. This is why Blockchain Bitcoin relies on the SHA-256 to secure its network.
Most websites store their users’ passwords in the form of signatures obtained from cryptographic hash functions such as the SHA-256. Thus, if their database were to be hacked, their users’ passwords would not be compromised immediately.
Hackers would still have to try to obtain each entry that matches the cryptographic signatures obtained. It is something very difficult and can take so long that the operation becomes impossible.
Nevertheless, I will show you the first solution they could try to implement if they wanted to try such a job.
Specifications Of Our Password Cracker
Our Password Cracker will take as input a password represented by a SHA-256 signature as well as an alphabet containing all the characters we will use to generate passwords that can match this SHA-256 signature. Finally, we will also define the maximum length we consider for the passwords to be tested.
In terms of operation, the easiest solution to implement when creating a Password Cracker is to implement a brute force algorithm that will test all possible password combinations from a given alphabet.
A brute-force solution can give results but it can run for several years without finding matches as well.
In order to overcome these limitations of the brute-force solution that I will implement, it is possible to refine the search from dictionaries containing the most commonly used passwords for example.
This allows a Password Cracker program to test combinations of characters frequently used as passwords by users as a priority.
This is beyond the scope of this article, but it is good to know that it exists.
As for our Password Cracker program, I will also design it so that its work can be parallelized in order to take advantage of the multithreading capabilities of the Java programming language and to find a matching password more quickly.
Creating The PasswordCracker Class
The first step will be to define a number of static variables within our PasswordCracker class:
- An ALPHABET variable that is an array of characters and that represents our alphabet.
- An integer type PASSWORD_MAX_LENGTH variable representing the maximum number of characters we consider for the password to be found.
- A PASSWORD_SHA_256_TO_FIND variable representing the password in SHA-256 format whose corresponding password our program will have to find in plaintext.
A BYTES_SHA_256_TO_FIND variable representing the SHA-256 in the form of a bytes array to facilitate comparison with passwords subsequently encoded.
- A START_TIME variable of long type that will be used to store the start date in milliseconds of the Password Cracker execution.
In order to be able to encode the password in SHA-256 format as a bytes array, I will define a utility method hexStringToByteArray transforming a hexadecimal character string into a bytes array.
All this gives us the following initialization code for the PasswordCracker class:
Implementing The Core Of The Password Cracker
The core of the Password Cracker will be located within a Cracker class implementing the Runnable interface. This will allow us to divide the test work into several instances of Cracker whose execution will be submitted to the different cores available on the host machine of the program.
Each Cracker instance will work on a subset of passwords of different lengths. Thus, the Cracker class will have a start property and an end property representing respectively the minimum and maximum length of the passwords to be tested by the Cracker.
A Cracker will also have a found property representing the plaintext password that may have been found following its execution.
Within the Cracker run method, I will generate all passwords of length between start and end from the input alphabet. For each generated password, I will then obtain its SHA-256 signature based on the MessageDigest class provided as standard in the JDK. The latter allows to obtain an instance of the SHA-256 implementation of the JDK.
If the SHA-256 signature of the password being tested, returned as a bytes array, is equal to the bytes array contained in the BYTES_SHA_256_TO_FIND variable, then we have successfully cracked the password. It must therefore be stored in the found variable and set to true a static DONE boolean indicating that all Cracker instances must stop their work.
At the end of the run method, it remains to display the cracked password as well as the time taken by the program to find this password.
In the event that the current Cracker instance has not found the matching password, a message is displayed indicating failure for the subset of passwords whose length was between start and end.
The Cracker class thus looks like this:
Parallelization Of Password Cracker Work
Now that we have an operational Cracker class, we need to write the code to run each Cracker instance on the cores available on the host machine.
It is therefore necessary to retrieve at runtime the number of cores available on the host machine.
This is done by calling the static method availableProcessors of the Runtime class of the JDK. Once the number of available cores is obtained, I will divide the maximum length of the searched password, stored in the PASSWORD_MAX_LENGTH variable, by this number.
The result will give us the length of the passwords to be tested in each subset.
Considering a machine with 8 cores and a password with a maximum length of 30 characters, we would have the following subsets to share between each Cracker:
Subset [1, 4]
Subset [5, 8]
Subset [9, 12]
Subset [13, 16]
Subset [17, 20]
Subset [21, 24]
Subset [25, 28]
Subset [29, 30]
The JDK offers the API Executor to facilitate the parallelization of threads embedded within an implementation of the Runnable interface. The API entry point is the creation of a thread pool by calling the static method newFixedThreadPool with the number of threads you want to create as input.
In the case of our Password Cracker, we will create a thread for each available core.
We obtain at the output an ExecutorService object with which we will launch the work of each Cracker instance corresponding to a given subset of passwords of different lengths. This is done via a call to its submit method.
Once the ExecutorService work is completed, it is essential to call its shutdown method to ensure that the program gives back the hand.
We then obtain the following complete code for the PasswordCracker class:
Password Cracker In Action
Once the Password Cracker code is finished, it is time to put it into action and try to crack the following SHA-256 signature password:
In order to facilitate the validation of the proper functioning of the program, here is the initial password that produces this signature SHA-256 :
As you can see, I have chosen a password of 5 characters in length here to make sure that the program runs in a relatively short time. However, you can test on longer passwords by changing the value of the PASSWORD_MAX_LENGTH variable.
After 28 seconds of execution, our Password Cracker finally finds the password corresponding to the SHA-256 signature we had as input.
The Password Checker operating in multithreaded mode that we have just developed is therefore perfectly functional.
To Go Further
The Password Cracker we have just built uses a rather simplistic brute force method that will prove to be limited to longer passwords. This helps you better understand why you are always advised to set passwords of at least 10 characters on the Internet.
To go further, it would be interesting to recover a dictionary of the passwords most frequently used by Internet users and to make our Password Cracker work on these passwords as a priority. This would maximize the chances of finding the password corresponding to the SHA-256 signature we had at the input.
If this search via a dictionary did not yield anything, it would be time to fall back on our brute-force algorithm.