Java 中的 KDF(密钥派生函数)详解
1. 什么是 KDF(密钥派生函数)?
KDF(Key Derivation Function,密钥派生函数)是一种用于从密码(Password)或主密钥(Master Key) 生成安全加密密钥的算法。它的主要作用是增强密钥的安全性,防止暴力破解或彩虹表攻击。
为什么需要 KDF?
如果直接使用用户的密码作为加密密钥,会带来以下问题:
- 密码强度不足:用户密码通常较短,容易被暴力破解。
- 相同密码导致相同密钥:如果不同用户使用相同的密码,则他们的密钥也会相同,降低安全性。
- 防止彩虹表攻击:攻击者可以提前计算哈希值,并存储在数据库中,加速破解。
- 适配不同的加密算法:如 AES-256 需要 256 位的密钥,而用户输入的密码可能长度不足,需要扩展。
KDF 通过 哈希计算、多次迭代、加盐(Salt) 使密钥更难被破解,从而提高安全性。
2. KDF 的正向逆向特性
2.1 KDF 不可逆
原因如下:
- 哈希函数的单向性:大多数 KDF 依赖 SHA-256 等哈希函数,这些函数不可逆。
- 加盐增加随机性:KDF 使用随机盐值,即使密码相同,密钥也不同。
- 多次迭代增加计算成本:即使使用暴力破解,也需要消耗大量计算资源。
- 输出长度固定:KDF 可以从短密码生成固定长度的密钥,原始信息量不足以反推出密码。
2.2 KDF 是一种确定性算法
它的计算方式是固定的,只要输入条件不变,输出也不会变化。例如:
[
KDF(Password, Salt, Iterations, HashFunction) = DerivedKey
]
只要 Password、Salt、迭代次数、哈希函数 都一样,计算出来的 DerivedKey 也一定相同。
通俗举例
想象你用同样的面粉(密码)、酵母(盐值)、搅拌方式(KDF 算法)和烤箱温度(迭代次数)去做面包,结果肯定一样。
实际应用
- 密码存储:如果两次登录时,输入的密码相同,系统用相同的 KDF 计算,结果也相同,就能验证密码是否正确。
- 密钥交换:如果双方使用相同的 KDF 参数,他们可以在不同设备上生成相同的加密密钥。
注意
- 如果 盐值不同,即使密码相同,派生密钥也会不同,防止攻击者利用相同密码匹配多个用户。
- 如果 迭代次数或哈希函数不同,即使密码和盐相同,结果也会不同。
3. Java 中的 KDF 实现
Java 提供了多种 KDF 实现,最常见的是 PBKDF2,其他算法(如 bcrypt、scrypt 和 HKDF)可以通过外部库实现。
3.1 PBKDF2(Password-Based Key Derivation Function 2)
PBKDF2 是 Java 原生支持 的 KDF,常用于密码存储、AES 密钥生成。
示例代码
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;
import java.util.Base64;
public class PBKDF2Example {
public static void main(String[] args) throws Exception {
String password = "mySecurePassword";
byte[] salt = generateSalt();
byte[] derivedKey = deriveKey(password, salt, 10000, 256);
System.out.println("Derived Key: " + Base64.getEncoder().encodeToString(derivedKey));
}
public static byte[] deriveKey(String password, byte[] salt, int iterations, int keyLength) throws Exception {
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, keyLength);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
return skf.generateSecret(spec).getEncoded();
}
public static byte[] generateSalt() {
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
return salt;
}
}
3.2 Bcrypt(适合存储密码)
Bcrypt 适用于密码存储,比 PBKDF2 更安全。
示例代码
import org.mindrot.jbcrypt.BCrypt;
public class BcryptExample {
public static void main(String[] args) {
String password = "mySecurePassword";
String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt(12));
System.out.println("Hashed Password: " + hashedPassword);
System.out.println("Password Match: " + BCrypt.checkpw(password, hashedPassword));
}
}
3.3 Scrypt(防止 GPU/ASIC 破解)
Scrypt 比 Bcrypt 更抗硬件破解,适用于密码存储和区块链钱包。
示例代码(使用 Bouncy Castle 库)
import org.bouncycastle.crypto.generators.SCrypt;
import java.util.Base64;
public class ScryptExample {
public static void main(String[] args) {
String password = "mySecurePassword";
byte[] salt = "randomSalt".getBytes();
byte[] derivedKey = SCrypt.generate(password.getBytes(), salt, 16384, 8, 1, 32);
System.out.println("Derived Key: " + Base64.getEncoder().encodeToString(derivedKey));
}
}
3.4 HKDF(密钥扩展)
HKDF 适用于从已有密钥生成多个密钥,常用于 HTTPS/TLS。
示例代码
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
public class HKDFExample {
public static void main(String[] args) throws Exception {
byte[] ikm = "initialKeyMaterial".getBytes(StandardCharsets.UTF_8);
byte[] salt = "randomSalt".getBytes(StandardCharsets.UTF_8);
byte[] info = "contextInfo".getBytes(StandardCharsets.UTF_8);
byte[] derivedKey = hkdfExpand(ikm, salt, info, 32);
System.out.println("Derived Key: " + Base64.getEncoder().encodeToString(derivedKey));
}
public static byte[] hkdfExpand(byte[] ikm, byte[] salt, byte[] info, int length) throws Exception {
Mac hmac = Mac.getInstance("HmacSHA256");
hmac.init(new SecretKeySpec(salt, "HmacSHA256"));
byte[] prk = hmac.doFinal(ikm);
hmac.init(new SecretKeySpec(prk, "HmacSHA256"));
hmac.update(info);
return Arrays.copyOf(hmac.doFinal(new byte[0]), length);
}
}
4. Java KDF 选择指南
KDF 算法 | 适用场景 | Java 内置支持 | 安全性 |
---|---|---|---|
PBKDF2 | 存储密码、派生密钥 | ✅(内置) | 中等 |
Bcrypt | 存储用户密码 | ❌(需 JBCrypt) | 高 |
Scrypt | 防止硬件破解 | ❌(需 Bouncy Castle) | 更高 |
HKDF | 密钥扩展 | ❌(需手动实现) | 高 |