I’ve worked with .NET for many years. One thing I never compromise on is password storage. Plain text or weak hashing is a disaster waiting to happen.
In this post, I break down BCrypt and Argon2 with technical details, internal differences, clean code examples, and when to choose which.
Why We Need Slow and Memory-Hard Hashing
Normal cryptographic hashes (SHA-256, etc.) are designed to be fast. Attackers with GPUs or ASICs can try billions of password guesses per second.
We need password-specific algorithms that are deliberately slow on purpose. They also generate a unique random salt for every password. This makes brute-force and rainbow table attacks extremely expensive.
BCrypt – The Battle-Tested Classic
BCrypt was created in 1999 based on the Blowfish cipher. It uses a modified expensive key schedule that repeats many times.
Technical details:
- Fixed low memory usage (~4 KB per hash)
- Controlled by a single “cost” parameter (work factor)
- Cost = 13 means 2¹³ = 8,192 iterations
- Purely CPU-bound
It is very simple and stable, but not memory-hard. Modern GPUs can run many BCrypt hashes in parallel quite cheaply.
Install: BCrypt.Net-Next
using BCrypt.Net;
public static class PasswordHasher
{
private const int WorkFactor = 13;
public static string Hash(string password)
=> BCrypt.Net.BCrypt.HashPassword(password, WorkFactor);
public static bool Verify(string password, string hash)
=> BCrypt.Net.BCrypt.Verify(password, hash);
}
Argon2 – The Modern Standard
Argon2 won the Password Hashing Competition in 2015. It was built from scratch to be the best password hashing function.
Technical details:
- Memory-hard algorithm (the key advantage)
- Three variants: Argon2d, Argon2i, Argon2id
- Argon2id is recommended for passwords (hybrid approach)
- Configurable parameters: Memory size (m), Iterations (t), Parallelism (p)
The memory-hard property forces the attacker to use a lot of RAM for each attempt. This makes large-scale parallel attacks (on GPUs or ASICs) much more expensive.
Install: Isopoh.Cryptography.Argon2
using Isopoh.Cryptography.Argon2;
public static class PasswordHasher
{
public static string Hash(string password)
=> Argon2.Hash(password);
public static bool Verify(string password, string hash)
=> Argon2.Verify(hash, password);
}
This library uses secure defaults and handles salt + encoding automatically.
Key Internal Differences
-
Memory Usage:
- BCrypt: Fixed ~4KB (not memory-hard)
- Argon2: Configurable, typically 32MB–128MB+ (highly memory-hard)
-
Attack Resistance:
- BCrypt: Good against CPU brute force, but weak against massive GPU parallelism
- Argon2: Strong resistance to GPU, ASIC, and parallel attacks due to high memory requirement
-
Tuning:
- BCrypt: Only one parameter (cost)
- Argon2: Three tunable parameters (memory, time, threads)
-
Design Age:
- BCrypt: 1999, based on Blowfish
- Argon2: 2015, purpose-built for password hashing
When to Use Each
Use BCrypt when:
- Maintaining legacy systems
- Running on very low-memory servers
- Need maximum simplicity
Use Argon2 when:
- Building new applications
- Handling sensitive user data
- Want best protection against current and future hardware attacks
Important Things to Consider
- Always benchmark on your production hardware. Aim for 400-800ms per hash
- Argon2 consumes more RAM — test under concurrent load
- When switching algorithms, rehash passwords on next successful login
- Combine with login rate limiting and preferably 2FA