前言
密码加密是每个计算机软件系统都绕不开的话题,加密也有很多种方式,选择加密方式需要从安全性、可靠性、性能等多方面综合考虑,在众多加密方式中Argon2是一个非常优秀的方案,下面是主流的一些加密方式对比
| 特性 | Argon2id | bcrypt | scrypt | PBKDF2 |
|---|---|---|---|---|
| 核心机制 | 同时消耗 CPU + 内存,且内存访问顺序混合 | 基于Blowfish加密,仅消耗 CPU | 消耗 大量内存(需构造伪随机序列) | 仅消耗 CPU(反复执行HMAC) |
| 抗GPU/ASIC | 极强(内存成本极高,难以并行) | 中等(内存占用仅4KB,易被FPGA/GPU加速) | 强(内存占用大,受限于内存带宽) | 极弱(内存占用极低,GPU可暴力破解) |
| 可调参数 | 3个(时间t、内存m、并行度p) | 1个(工作因子cost) | 2个(CPU成本N、内存成本r) | 1个(迭代次数) |
| 默认内存占用 | 19 MiB(建议值) | 4 KB(固定极小) | 16 MB+(可调大) | 极低(可忽略) |
| 侧信道攻击 | Argon2id 混合模式可抵抗 | 部分抵抗 | 抵抗 | 抵抗 |
| 优点 | 安全性最高,抗GPU/ASIC攻击能力最强 | 成熟稳定,广泛使用,内置盐值 | 安全性高,有效抵抗硬件暴力破解 | 合规性好 |
Argon2是什么?
Argon2 是当前最先进、最推荐的密码哈希算法。它是2015年"密码哈希竞赛"的冠军,设计上就是为了对抗现代GPU和ASIC破解硬件。它允许你独立配置时间成本、内存成本和并行度三个参数,灵活性极高,并于2021年正式成为IETF标准(RFC 9106)
Argon2有三种变体,各自有不同的应用场景:
| 变体 | 说明 |
|---|---|
| Argon2d | 使用数据依赖的内存访问方式。它的速度最快,对GPU和ASIC破解的抵抗性最强,但由于内存访问模式与数据相关,容易受到侧信道攻击。因此,它适用于没有侧信道攻击威胁的场景,比如加密货币。 |
| Argon2i | 使用数据独立的内存访问方式。它专门为抵抗侧信道攻击而设计,更适合密码哈希和基于密码的密钥派生。但它的安全性略低于Argon2d。 |
| Argon2id | 混合模式,也是官方推荐和默认的变体。它结合了前两者的优点:前半部分使用Argon2i(抵抗侧信道),后半部分使用Argon2d(抵抗GPU破解)。对于绝大多数场景,尤其是密码存储,你应该选择 Argon2id。 |
Argon2id在springboot中使用
引入argon2依赖
xml
<dependency>
<groupId>de.mkammerer</groupId>
<artifactId>argon2-jvm</artifactId>
<version>2.11</version>
</dependency>
实现工具类,该工具类可以直接复制使用,简单易懂,根据项目实际情况配置一下参数即可以实现加密和验证操作。
java
import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;
/**
* Argon2 是当前推荐的密码哈希算法(密码哈希竞赛冠军),Argon2id 兼顾抗 GPU 与侧信道攻击,适合存储用户密码
*/
public class Argon2IdUtil {
// 构建 Argon2id(自动加盐)
private static final Argon2 ARGON2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id);
// 哈希参数, 低配置:3, 65536, 4,高安全:4, 131072, 8(消耗内存更多)服务端可按需调整,内存越大越安全
//迭代次数
private static final int ITERATIONS = 3;
//内存(KB)
private static final int MEMORY = 65536;
//并行度
private static final int PARALLELISM = 4;
/**
* 生成密码哈希
* @param rawPassword 原始明文密码
* @return 哈希字符串(内置salt,直接入库)
*/
public static String hash(String rawPassword) {
char[] chars = rawPassword.toCharArray();
try {
return ARGON2.hash(ITERATIONS, MEMORY, PARALLELISM, chars);
} finally {
// 清空内存明文,防止密码残留
ARGON2.wipeArray(chars);
}
}
/**
* 校验密码
* @param hash 数据库存储的hash
* @param rawPassword 前端传入明文
* @return 是否匹配
*/
public static boolean verify(String hash, String rawPassword) {
char[] chars = rawPassword.toCharArray();
try {
return ARGON2.verify(hash, chars);
} finally {
ARGON2.wipeArray(chars);
}
}
public static void main(String[] args) {
String hash = Argon2IdUtil.hash("123456");
System.out.println("加密后的密文:"+hash);
System.out.println("校验密码:"+Argon2IdUtil.verify(hash, "123456"));
}
}
运行上面工具类,结果如下:

为了帮助更多像你一样的读者,我将持续在专栏中分享技术干货和实用技巧。如果你觉得这篇文章对你有帮助,可以考虑关注我的专栏,谢谢。