科普文:Java基础系列之【BCrypt、Argon2、PBKDF2 安全地存储和验证密码】
总结:PBKDF2是算力消耗性的,Scrypt是资源消耗性的。PBKDF2(过时)<Bcrypt(开始淘汰)<Scrypt< Argon2(含Argon2d、Argon2i、Argon2id)PBKDF2和Scrypt都是密钥派生函数(KDFs),它们通过故意减慢计算速度来实现密钥延长,特别是通过用一个可调参数来控制速度。不同之处在于,Scryp需要大量且可调整的内存量才能进行高效计算。这样能防止
在本地工程中,需要存储很多账号密码:mysql、redis、es等等;只是用明文存储账号密码,有一定的风险,这个时候就需要一种策略来对本地密码进行加密,确保不是明文。
本文是对BCrypt、Argon2、PBKDF2 安全地存储和验证密码的讲解,希望对大家有所帮助。
密码密钥派生算法
密码通常是以加密的形式存储在服务器上,这样即使数据泄露也能最大限度地保护用户的隐私安全。这意味着服务器并不知道你原始的密码是什么。
因此,当你忘记密码时,系统无法告诉你密码是多少,只能通过验证你的身份信息后重置密码。
密钥派生函数(Key Derivation Function)就是从一个密码产生出一个或多个密钥,具体就是从一个master key,password或者passphrase派生出一个或多个密钥,派生的过程使用PRF(Pseudo Random Function)。是一种实现key stretching(密钥延长算法,即一种更慢的哈希算法,用于将初始密钥转换成增强密钥,在计算过程中刻意延长时间或者消耗空间,这样有利于保护弱密码)的方法。
●最简单的KDF可以直接使用某种密码学散列算法,如:SHA256。将一个密码转换为一个散列值作为密钥。但易受字典攻击。
●HKDF(HMAC-based key derivation),基于HMAC的密钥派生算法
●从一个密码派生出一个或多个密钥:PBKDF2、Bcrypt、Scrypt、Argon2
密码加密策略
慢哈希算法阶段 - Argon2、Bcrypt、Scrypt和PBKDF2
Argon2[2]、Bcrypt[3]、Scrypt[4]和PBKDF2[5]是目前主流的慢哈希算法,它们与SHA256等快速哈希算法的主要差异点如下:
- 计算速度更慢,需要消耗更多CPU和内存资源,从而对抗硬件加速攻击;
- 使用更复杂的算法,组合密码学原语,增加破解难度;
- 可以配置资源消耗参数,调整安全强度;
- 特定优化使并行计算困难;
- 经过长时间的密码学分析,仍然安全可靠。
1:BCrypt
-
定义:BCrypt 是一种基于Blowfish加密算法的密码散列函数,专为密码存储设计。
-
优点:自动包含salt,且可调整迭代次数以适应未来的硬件进步。
-
缺点:计算密集型,可能会占用较多服务器资源。
Java示例
import org.mindrot.jbcrypt.BCrypt;
public class BcryptExample {
public static void main(String[] args) {
String password = "mySecurePassword";
String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
System.out.println("Original Password: " + password);
System.out.println("Hashed Password: " + hashedPassword);
// 检查输入的密码是否正确
boolean matches = BCrypt.checkpw(password, hashedPassword);
System.out.println("Does the password match? " + matches);
}
}
2:Argon2
-
定义:Argon2 是一种现代的密码散列函数,获得了2015年的密码散列竞赛冠军。
-
优点:支持内存和CPU消耗的调整,对GPU攻击有很好的防御能力。
-
缺点:实现较为复杂,资源消耗较高。
Java示例
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.commons.text.StringEscapeUtils;
import org.argon2.Argon2;
import org.argon2.Argon2Factory;
import org.argon2.exceptions.VerificationException;
public class Argon2Example {
public static void main(String[] args) {
String password = "mySecurePassword";
Argon2 argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id);
String hashedPassword = argon2.hash(1, 1024 * 1024, 1, password.toCharArray());
System.out.println("Original Password: " + password);
System.out.println("Hashed Password: " + hashedPassword);
// 检查输入的密码是否正确
boolean matches = argon2.verify(hashedPassword, password.toCharArray());
System.out.println("Does the password match? " + matches);
}
}
3:PBKDF2(Password-Based Key Derivation Function 2)
-
定义:PBKDF2 是一种使用HMAC-SHA256作为PRF的密码散列函数。
-
优点:支持可配置的迭代次数,可以在未来增加计算成本以提高安全性。
-
缺点:不如BCrypt和Argon2安全,特别是在面对GPU攻击时。
Java示例
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
public class PBKDF2Example {
private static final int ITERATIONS = 10000; // Number of iterations
private static final int KEY_LENGTH = 256; // Key length in bits
public static void main(String[] args) {
String password = "mySecurePassword";
byte[] salt = generateSalt();
String hashedPassword = pbkdf2(password, salt);
System.out.println("Original Password: " + password);
System.out.println("Hashed Password: " + hashedPassword);
}
private static byte[] generateSalt() {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16]; // Salt length in bytes
random.nextBytes(salt);
return salt;
}
private static String pbkdf2(String password, byte[] salt) {
try {
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
byte[] hash = skf.generateSecret(spec).getEncoded();
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if(hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
return null;
}
}
}
小结
通过以上介绍,我们可以看出,为了保证用户的密码安全,系统只能采用重置密码的方式来解决忘记密码的问题。
同时,合理的密码加密策略也是必不可少的,它可以大大增强系统的安全性。
BCrypt 和 Argon2 是目前最推荐使用的密码散列函数,它们的安全性和性能都非常出色。
1、PBKDF2(CPU-Hard algorithm)
是一种基于密钥派生出密钥的算法,需要消耗很多算力,可防止暴力破解加密。
passphrase -> [dklen, salt, c] > 1000] -> hash
DK = PBKDF2(PRF, Password, Salt, c, dkLen)
其中,
passphrase:用于用户认证或者加密程序的操作步骤
dklen:派生所产生的密钥的长度
salt:盐值是一串随机生成的比特
c:迭代的次数
DK:期望的密钥derived key
PRF(Pseudorandom function):伪随机数产生的密钥,如:hmac-sha256
2、Scrypt(Memory-Hard algorithm)
是一种password-base KDF算法,比起PBKDF2需要消耗更多的资源。Scrypt内部用的是PBKDF2算法,不过内部会长时间的维护一组比特数据,这些数据会在生成复杂的salt的过程中反复加密。
3、区别
总结:
- PBKDF2是算力消耗性的,Scrypt是资源消耗性的。
- PBKDF2(过时)<Bcrypt(开始淘汰)<Scrypt< Argon2(含Argon2d、Argon2i、Argon2id)
PBKDF2和Scrypt都是密钥派生函数(KDFs),它们通过故意减慢计算速度来实现密钥延长,特别是通过用一个可调参数来控制速度。不同之处在于,Scryp需要大量且可调整的内存量才能进行高效计算。这样能防止专有硬件ASIC/FPGA的暴力破解。
更多推荐
所有评论(0)