共计 7625 个字符,预计需要花费 20 分钟才能阅读完成。
本文介绍了使用 BCrypt、Argon2 和 PBKDF2 等现代算法在 Java 中进行安全密码哈希,并通过加盐和计算强度来提高安全性。
在数字安全领域,密码哈希是防止未经授权访问的关键防线。然而,哈希算法的格局已经发生了重大变化,一些方法已经过时,新的更安全的技术出现了。本文探讨了为什么传统的方法如 SHA-512 不再足够,盐值和减缓哈希过程的重要性,并提供了现代密码哈希技术的实用 Java 代码示例。
对于密码哈希的不足之处
SHA-512 是 SHA- 2 系列的一部分,是一种加密哈希函数,曾经是保护密码的标准。然而,由于以下原因,它现在被认为不适合用于密码哈希:
-
速度:SHA-512 设计成快速的。不幸的是,这使得它容易受到暴力破解攻击的威胁,攻击者可以快速尝试数百万个密码组合。
-
缺乏盐值:虽然 SHA-512 本身没有包含盐值,但通常在实现时没有加入盐值,使其容易受到彩虹表攻击的威胁。
盐值的关键作用
盐值是在哈希之前向每个密码添加一个随机字符串。这种做法可以防止使用预先计算的哈希表破解密码的彩虹表攻击。通过确保每个密码哈希是唯一的,盐值有效地消除了这种威胁。
减缓哈希过程
现代密码哈希算法有意地减慢哈希过程以阻止攻击。这种方法通过增加计算和时间资源来破解每个密码,使得暴力破解攻击变得不切实际。以下是它们实现这一目标的方式:
1. 计算密集型哈希
多次迭代:这些算法对哈希函数进行多次迭代(数千次或数百万次)。每次迭代都需要一定的处理时间。例如,如果单个 SHA-256 哈希只需花费几毫秒的时间,那么为每个密码重复这个过程数千次将显著增加总体计算时间。
可调节的工作因子:在像 BCrypt 这样的算法中,有一个工作因子或成本参数,确定哈希循环运行的次数。随着硬件速度变快,可以增加这个因子,确保哈希过程不会变得太快。
2. 内存密集型操作
增加内存使用:某些算法(例如 Argon2)旨在除了 CPU 资源之外还使用大量内存。这使得攻击者难以使用 GPU 或自定义硬件并行化攻击,因为这些设备每个处理单元可用的高速内存通常有限。
3. 内置盐值
每个密码的唯一盐:现代哈希方法会自动生成每个密码的唯一盐。盐是在哈希之前添加到密码的随机值。这意味着即使两个用户使用相同的密码,它们的哈希值也会不同。盐值还可以防止使用预先计算的哈希表(彩虹表)来反向破解哈希值。
对不同类型攻击的有效性
-
暴力破解攻击:这些算法的时间和资源消耗使得暴力破解攻击(尝试每个可能的密码组合)变得不切实际,特别是对于强密码。
-
彩虹表攻击:由于每个密码哈希都使用唯一的盐值进行加密,预先计算的哈希表变得无用。
-
定制硬件攻击:内存和处理要求使得攻击者更难以负担和使用专门的硬件(如 ASIC 或 GPU)来加速破解过程。
现实世界的影响
-
合法用户体验:对于合法用户,在登录或账户创建过程中,这些哈希算法所花费的额外时间(通常仅为几分之一秒)可以忽略不计。
-
攻击者体验:对于试图破解密码的攻击者来说,这段时间会迅速累积。以前可能需要几天的破解时间,在现代算法下可能需要数年时间,从而使得暴力破解攻击对于强密码变得不切实际。
现代密码哈希技术
1. BCrypt
BCrypt 是一种广泛使用的哈希算法,它可以自动处理盐值,并有意地减慢哈希过程以阻止暴力破解攻击。
示例:
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class BCryptHashing {public static String hashPassword(String password) {BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return passwordEncoder.encode(password);
}
}
2. Argon2
Argon2 是 2023 年密码哈希竞赛的获胜者,它提供可定制的抵御 GPU 和基于内存的攻击。
示例:
import org.bouncycastle.crypto.generators.Argon2BytesGenerator;
import org.bouncycastle.crypto.params.Argon2Parameters;
public class Argon2Hashing {public static String hashPassword(String password) {
// 生成 Argon2 参数的实际值
int parallelism = 2; // 使用 2 个线程
int memory = 65536; // 使用 64MB 内存
int iterations = 3; // 运行 3 次迭代
int hashLength = 32; // 生成一个 32 字节(256 位)的哈希
Argon2BytesGenerator generator = new Argon2BytesGenerator();
Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
.withSalt(salt) // 需要生成一个盐值
.withParallelism(parallelism) // 并行因子:默认为 1
.withMemoryAsKB(memory) // 内存成本:使用 64MB 的内存
.withIterations(iterations); // 迭代次数:运行 3 次迭代
generator.init(builder.build());
byte[] result = new byte[hashLength];
generator.generateBytes(password.toCharArray(), result);
return Base64.getEncoder().encodeToString(result);
}
}
3. PBKDF2
PBKDF2(基于密码的密钥派生函数 2)是 RSA 实验室的 PKCS 系列的一部分,旨在具有计算复杂性,提供可调节的迭代次数以增强安全性。
示例:
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import java.util.Base64;
public class PBKDF2Hashing {public static String hashPassword(String password) throws Exception {SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = factory.generateSecret(spec).getEncoded();
return Base64.getEncoder().encodeToString(hash);
}
}
4. SHA-512 with Salt (Not Recommended)
尽管 SHA-512 存在漏洞,但了解它对于学习目的还是有意义的。
示例:
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Base64;
public class SHA512Hashing {public static String hashWithSalt(String password) throws Exception {SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(salt);
byte[] hashedPassword = md.digest(password.getBytes());
return Base64.getEncoder().encodeToString(hashedPassword);
}
}
密码哈希验证
要使用任何哈希算法验证密码,通常的做法是使用与创建原始密码哈希时相同的算法和参数(例如盐值、迭代次数等)对输入密码进行哈希。然后,将新生成的哈希与存储的哈希进行比较。但是,对于像 BCrypt、Argon2 和 PBKDF2 这样的算法,通常可以使用内置函数来简化比较过程。
我们来看一下每个算法在 Java 代码中如何验证密码:
1. 使用 BCrypt 验证密码
BCrypt 有一个内置方法用于验证密码。
示例:
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class BCryptHashing {public static boolean verifyPassword(String inputPassword, String storedHash) {BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder.matches(inputPassword, storedHash);
}
}
2. 使用 Argon2 验证密码(使用 Bouncy Castle 库)
对于 Argon2,您需要存储用于最初哈希密码的盐值和其他参数。然后,使用这些参数对输入密码进行哈希,并将其与存储的哈希进行比较。
示例:
import org.bouncycastle.crypto.generators.Argon2BytesGenerator;
import org.bouncycastle.crypto.params.Argon2Parameters;
import java.util.Base64;
public class Argon2Hashing {public static boolean verifyPassword(String inputPassword, String storedHash, byte[] salt, int parallelism, int memory, int iterations, int hashLength) {Argon2BytesGenerator generator = new Argon2BytesGenerator();
Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
.withSalt(salt)
.withParallelism(parallelism)
.withMemoryAsKB(memory)
.withIterations(iterations);
generator.init(builder.build());
byte[] result = new byte[hashLength];
generator.generateBytes(inputPassword.toCharArray(), result);
String newHash = Base64.getEncoder().encodeToString(result);
return newHash.equals(storedHash);
}
}
3. 使用 PBKDF2 验证密码
与 Argon2 类似,您需要存储用于原始哈希的盐值和其他参数。
示例:
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.spec.KeySpec;
import java.util.Base64;
public class PBKDF2Hashing {public static boolean verifyPassword(String inputPassword, String storedHash, byte[] salt, int iterationCount, int keyLength) throws Exception {KeySpec spec = new PBEKeySpec(inputPassword.toCharArray(), salt, iterationCount, keyLength);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = factory.generateSecret(spec).getEncoded();
String newHash = Base64.getEncoder().encodeToString(hash);
return newHash.equals(storedHash);
}
}
4. 使用 SHA-512 验证密码
对于 SHA-512,您必须存储用于哈希的盐值。然后,使用相同的盐值对输入密码进行哈希,并比较哈希值。
示例:
import java.security.MessageDigest;
import java.util.Base64;
public class SHA512Hashing {public static boolean verifyPassword(String inputPassword, String storedHash, byte[] salt) throws Exception {MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(salt);
byte[] hashedInputPassword = md.digest(inputPassword.getBytes());
String newHash = Base64.getEncoder().encodeToString(hashedInputPassword);
return newHash.equals(storedHash);
}
}
重要注意事项:
-
– 对于 BCrypt、Argon2 和 PBKDF2,当可用时务必使用它们各自的库方法进行验证,因为这些方法会安全地处理比较过程。
-
– 对于 SHA-512 以及其他没有内置验证方法的哈希算法,确保实现安全的比较以避免时序攻击。
-
– 始终安全地存储盐值,并在需要时存储其他参数(如迭代次数)与哈希密码一起。
跨语言和框架的应用情况:
BCrypt
Argon2 支持
PBKDF2
选择合适的算法:
-
– 安全需求:Argon2 提供了最高的安全性,特别是对抗 GPU 攻击,但需要更复杂的配置。
-
– 兼容性和传统系统:PBKDF2 得到广泛支持,可能是需要遵守特定标准或传统兼容性的系统的选择。
-
– 平衡和易用性:BCrypt 在安全性和性能之间提供了良好的平衡,易于实现,并得到了许多框架和语言的广泛支持。
结论
随着网络威胁的不断演变,我们保护敏感信息的方法也必须跟进。采用现代密码哈希技术,如 BCrypt、Argon2 和 PBKDF2,对于保护用户数据至关重要。这些方法提供了强大的防御机制,可以有效对抗最常见的密码破解策略,确保即使发生数据泄露,对密码完整性的影响也被最小化。开发人员和安全专业人员必须及时了解加密实践的最新进展,并相应地更新其安全措施 文章来源:https://www.toymoban.com/diary/java/607.html
文章来源地址 https://www.toymoban.com/diary/java/607.html
到此这篇关于 Java 中的安全密码哈希:实践密码哈希技术和代码示例的文章就介绍到这了, 更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持 TOY 模板网!
原文地址:https://www.toymoban.com/diary/java/607.html
如若转载,请注明出处:如若内容造成侵权 / 违法违规 / 事实不符,请联系站长进行投诉反馈,一经查实,立即删除!