Password Storage Using Java

Read the original article: Password Storage Using Java


This is the eighth entry in the blog series on using Java Cryptography securely. The first few entries talked about architectural details, Cryptographically Secure Random Number Generators, encryption/decryption, and message digests. Later we looked at What???s New in the latest Java version. All of this equipped us to talk in detail about some of the most common Cryptographic applications. We started by looking at the symmetric cryptography-based application with Message Authentication Code.

Password being such a central piece of any authentication-based system, every developer would be involved with it at some point in his or her career. These are usually stored in databases. Due to various vulnerabilities like SQL Injection, Remote Code Execution, etc., these databases could be compromised[16]. It becomes exceedingly important to make sure these stored passwords can???t be cracked offline easily.

Historical methods of storing passwords[15] have fallen short against growing computing powers, modern computer architectures, and enhanced attacks. Currently, the most secure way to store passwords is using Password Based Encryption (PBE), which provides functions (called Key Derivation Functions (KDFs)) that will convert low entropy user passwords into random, unpredictable, and most importantly one-way, irreversible bytes of data. It should be these bytes of data which should be stored and never plain text passwords to safeguard against offline attacks. KDFs used to generate these random bytes of data are commonly called as password hashing algorithms. They can also be extended to store any kind of sensitive information such as PII (Personally Identifiable Information) which your business needs to protect against offline attacks.

Skip to the TL; DR

In this post, we will be talking about various KDFs based password hashing algorithms to be used for any password storage requirements.

Password-Based Key Derivation Functions

Construction of KDFs has evolved over time. There are twoツ?broad categories of password hashing algorithmsツ?that are widely implemented:

  1. Adaptive Functions: Designed to iterate over inner crypto operations 1000s of times, to make password computations slower. Prominent functions are PBKDF2[3][4][9] which iterates over a HMAC function and bcrypt[10] which iterates over a blowfish based encryption scheme.
  2. Memory Hard Functions: Memory hard functions are designed with significant internal memory, which effectively decimates traditional brute forcing techniques even with utilizing modern computer architectures. Prominent functions in this category are Argon2[7] and script[8].

Each of these algorithms has some set of parameters that needs to be configured judiciously. Before getting into a full-fledged conversation about various algorithms, let???s talk about some of the commonalities:

1. Salt Generation: When designing salting features of your application, make sure:

  • Unique salt is generated for each password.
  • To store salt and corresponding hashed passwordツ?far from each other; like different data stores.
  • Salt is generated using a cryptographically strong random number generator discussed in the CSPRNG post and in theツ?Catchup post???s DRBG section.

Summarizing,

info-iconツ? Salts: Should be CSPRNG, unique per password and stored separately from password hashes.

ツ?

2.ツ?Work Factor: Work Factors are parameters used for each password hash computation with the sole purpose of making hash calculations slower, thus more computationally expensive, which in turn makes offline password cracking slower. For adaptive functions, the only work factor involved is the numberツ?of crypto iterations per calculation. With memory hard functions, we additionally have a few more parameters such as memory and CPU threads which adds more complexity to hashing, making it that much harder for offline cracking. Things to consider while thinking about Work Factors:

  • Work factor parameters should be individually tuned for each authentication server application. General guidance would be any interactive application should take at least 1 secツ?throughput and for non-interactive 5 secs are acceptable values.
  • Work Factors should be re-evaluated from time to time (ideally yearly), to keep up with hardware advances.
  • They are typically stored in password hash outputs making it a good idea to configure different iterations for each password.

Summarizing,

info-iconツ? Work Factors: Should be a fine balance between security and performance, configured unique to server, different per user, and re-tuned periodically

ツ?

Let’s start discussing the algorithms to be considered in detail followed by some of the runner-ups:ツ?


Argon2

A memory-hard function, winner of Password Hashing Competition making it the only function designed specifically for password hashing. Argon2 provides maximum mitigation against dedicated modern GPUs and parallelized brute forcing techniques, making it the best choice for any modern-day password storage scheme.

Being the newest addition in the password hashing algorithms arsenal, library implementations were sparse a few years ago, but most mainstream providers have caught up. Unfortunately, not JDK.

HowTo: How Does Argon2 Work?

This elegant algorithm is based on Blake2-like hashing and a configurable amount of memory table (m) to be re-filled multiple times(t) using some degree of parallelism(p).

HowTo: Diagrammatic Representation of Argon2

ARgon2

Note: Values in green are conservative secure configurations. Values in grape color are tuned parameter values on a t2.medium EC2 instance with 2 CPUs and 4GB RAM.

HowTo: Design Input Parameters Securely?

  • Salt: In addition to commonalities discussed above, choose salt size >= 64 bits (16 bytes)
  • Mode of Operation(m): Few different modes available, choose Argon2id to resist few different categories of attacks[11]. If Argon2id mode is not available choose Argon2i.
  • Work Factors:
    • Memory Size(m): >>1MB
    • Parallelism(p): Choose a value based on twice the no of cores in your CPU.
    • No of Iteration(t): t no of times, memory array of size m will be re-filled, using p no of threads.

HowTo: Implement

  • JDK doesn???t support this implementation yet, we would need to depend on bouncycastle???s low level API (non-provider supplied) and place bouncycastle???s jar file on class path.
  • Argon2Parameters is used to configure various input parameters and Argon2BytesGenerator is used to actually generate the hashes.
// Build Argon2 Parameters
 Argon2Parameters.Builder argon2Parameters = (new Argon2Parameters.Builder())
     .withVersion(Argon2Parameters.ARGON2_id) // For password storage recommended mode of operation to protect against both side-channel and timing attacks.
     .withIterations(10) // No of times memory array will be filled
     .withMemoryAsKB(16777) // 16MB of memory assigned
     .withParallelism(4) // # of Parallel processing units
     .withSecret(plainTextPasswd.getBytes()) //password
     .withSalt(salt.getBytes()); // 32 bytes (256 bits) of CSPRNG unique salt
 
Argon2BytesGenerator argon2passwordGenerator = new Argon2BytesGenerator();
argon2passwordGenerator.init(argon2Parameters.build()); // Initializing Argon2 algorithm with configured parameters
 
// Array to store computed hash
byte[] passwdHash = new byte[32];
 
argon2passwordGenerator.generateBytes(plainTextPasswd.getBytes(), passwdHash); 

For a complete code example refer toツ?Argon2idPasswdStorage.java.

HowTo: Decide If This Is The Right Choice For Me?

  • If you are not compelled into using a government-approved password hashing algorithm, spend some time parameter tuning, and use Argon2.
  • Parameter tuning needs to be played with and prototyped[17]. Values used in the code are considered to be safe for typical modern application, while future-proofing us for foreseeable future.ツ?ツ?

PBKDF2

One of the most widely adopted government standardized password hashing algorithmツ?is PBKDF2. PBKDF2 was designed for generating keying material for symmetric encryption. Due to its inherent configurable property to make it run slower, treating keying material derived out of this algorithm as password hashes, turned out to be a far secure choice than storing salted hashed password. It was more a knee-jerk reaction to pick something from the available crypto tools at the time, to protect against the ever-increasing offline cracking incidences.

PBKDF2 is the only password hashing algorithm available right out of JDK.

HowTo: How Does PBKDF2 Work?

Password hashes are computed by applying HMAC algorithm to password and salt, repeatedly for iteration number of counts(c). This iteration count is responsible for slowing down the password generation, providing mitigation against brute forcing attacks.

HowTo: Diagrammatic Representation of PBKDF2

PBKDF2???

Note: Values in green are conservative secure configurations. Values in grape color are tuned parameter values on a t2.medium EC2 instance with 2 CPUs and 4GB RAM.

HowTo: Design Input Parameters Securely?

  • Salt: In addition to commonalities discussed above, choose salt size >= 64 bites (8bytes)
  • HMAC Algorithm: Choose from any SHA2 or SHA3 family as underlying hashing algorithm.
  • Length of output password: Output password size should be no more than the native hash???s output size.
  • Work Factor – Iteration Count(c): Government suggested value is >= 10,000 which is very conservative. You should be picking at least a 6 figure value.

HowTo: Implement

  • Use SecretKeyFactory to specify which digest to use with HMAC algorithm.
  • PBEKeySpec is used to configure different input parameters.
// Using SHA-512 algorithm with HMAC, to increase the memory requirement to its maximum, making it most secure pbkdf2 option.
SecretKeyFactory pbkdf2KeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512") ;

PBEKeySpec keySpec = new PBEKeySpec(charEnteredPassword, // Input character array of password
                                  salt, // CSPRNG, unique
                                  150000, // Iteration count (c)
                           32) ; // 256 bits output hashed password

// Computes hashed password using PBKDF2HMACSHA512 algorithm and provided PBE specs.
byte[] pbkdfHashedArray = pbkdf2KeyFactory.generateSecret(keySpec).getEncoded() ; 


Complete code can be referenced at PBKDF2PasswdStorage.java.

HowTo: Decideツ?If This Is The Right Choice For Me?

  • PBKDF2 is a very basic CPU intensive algorithm, which can no longer put a tough fight against modern GPUs with multiple cores and easily configurable parallel processing.
  • If you don???t have to adhere to government standards, and can use a 3rd party implementation, choose a memory hard function such as Argon2.

bcrypt

Bcrypt is slightly better than pbkdf2, which provides some memory intensive work in addition to CPU intensive operations. It is based on blowfish symmetric cipher. Like PBKDF2, this algorithm is also roughly two decades old.

HowTo: How Does bcrypt Work?

It relies on an expensive key setup phase running blowfish based encryption scheme applied a number of iterations times. Briefly, just as pbkdf2, cost factor defines how slow the password computation would be and should be configured accordingly.

HowTo: Diagrammatic Representation of bcrypt

bcrypt???

Note: Values in green are conservative secure configurations. Values in grape color are tuned parameter values on a t2.medium EC2 instance with 2 CPUs and 4GB RAM.

HowTo: Design Input Parameters S

[…]


Read the original article: Password Storage Using Java