BCrypt vs Argon2 Password Hashing in .NET: A Practical Deep Dive
Password storage is one place where shortcuts become incidents. Fast hashes are good for integrity checks, but bad for password protection.
This guide compares BCrypt and Argon2 in .NET with practical trade-offs and usage patterns.
Why It Matters
- Password hashing must be intentionally expensive.
- Attackers use GPU/ASIC parallelism for brute-force attacks.
- Algorithm choice impacts long-term security posture.
- Good defaults reduce human error in auth systems.
Core Concepts
1. Why Fast Hashes Are Not Enough
Algorithms like SHA-256 are designed for speed. That is the opposite of what password hashing needs.
2. BCrypt Basics
BCrypt is battle-tested and simple to configure.
- CPU-bound design
- Single tuning parameter (
work factor) - Low fixed memory cost
using BCrypt.Net;
public static class BcryptPasswordHasher
{
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);
}
3. Argon2 Basics
Argon2 is newer and designed to be memory-hard.
- Recommended variant for passwords: Argon2id
- Tunable memory/time/parallelism
- Stronger resistance to large-scale parallel cracking
using Isopoh.Cryptography.Argon2;
public static class Argon2PasswordHasher
{
public static string Hash(string password)
=> Argon2.Hash(password);
public static bool Verify(string password, string hash)
=> Argon2.Verify(hash, password);
}
4. Internal Difference Summary
- BCrypt: mostly CPU cost
- Argon2: CPU + memory cost
Memory hardness is the major advantage when defending against GPU-heavy attacks.
5. Parameter Tuning Reality
Benchmark on production-like hardware. Target latency often lands around 400ms-800ms per hash, depending on your SLA and login throughput.
6. Migration Strategy
When upgrading algorithm, rehash on next successful login and store new hash format.
Practical Example
Algorithm-selection helper pattern:
public interface IPasswordHasher
{
string Hash(string password);
bool Verify(string password, string hash);
}
public sealed class PasswordService
{
private readonly IPasswordHasher _hasher;
public PasswordService(IPasswordHasher hasher)
{
_hasher = hasher;
}
public string CreateHash(string password) => _hasher.Hash(password);
public bool Validate(string password, string storedHash) => _hasher.Verify(password, storedHash);
}
This keeps your domain logic clean and lets you switch hashing strategy without rewriting auth flows.
Common Mistakes
- Using SHA-256 directly for passwords.
- Keeping hash cost too low for modern hardware.
- Migrating algorithms without rehash-on-login plan.
- Ignoring memory pressure when tuning Argon2 under concurrency.
- Relying on hashing alone without rate limiting and MFA.
Quick Recap
- Use password-specific hashing, never plain fast hashes.
- BCrypt is simple and stable for many systems.
- Argon2id is generally stronger for modern threat models.
- Tune cost on real hardware, not guesswork.
- Combine with lockout, rate limit, and MFA for layered defense.
Next Steps
- Benchmark BCrypt vs Argon2 on your production-like environment.
- Define acceptable login latency and tune parameters accordingly.
- Implement rehash-on-login migration path.
- Add rate limiting and MFA to strengthen auth pipeline.
Top comments (0)