Java 中的安全密码哈希:最佳实践和代码示例

在数字安全领域,密码哈希是防止未经授权访问的关键防线。然而,哈希算法的格局已经发生了重大变化,一些方法变得过时,并且出现了更新、更安全的技术。本文深入探讨了为什么像 SHA-512 这样的传统方法已经不够用了,加盐和减慢哈希过程的重要性,并为现代密码哈希技术提供了实用的 Java 代码示例。

SHA-512 用于密码哈希的不足之处
SHA-512 是 SHA-2 系列的一部分,是一种加密哈希函数,曾经是保护密码的标准。但是,由于以下原因,它现在被认为不足以进行密码哈希处理:

速度:SHA-512 旨在实现快速。不幸的是,这使得它容易受到暴力攻击,攻击者可以快速尝试数百万种密码组合。
缺乏盐渍:虽然 SHA-512 本身不包含加盐功能,但它通常是在没有加盐的情况下实现的,因此容易受到彩虹表攻击。
腌制的关键作用
加盐涉及在散列之前向每个密码添加一个随机字符串。这种做法可以阻止彩虹表攻击,在这种攻击中,预先计算的哈希表用于破解密码。通过确保每个密码哈希是唯一的,加盐可以有效地消除这种威胁。

减慢哈希过程
现代密码哈希算法故意减慢哈希过程以阻止攻击。这种方法增加了破解每个密码所需的计算和时间资源,从而使暴力攻击变得不切实际。以下是他们如何实现这一目标:

  1. 计算密集型哈希
    多次迭代:这些算法多次应用哈希函数(数千或数百万次迭代)。每次迭代都需要时间来处理。例如,如果单个 SHA-256 哈希只需几分之一毫秒,则对每个密码重复此过程数千次会显着增加总体计算时间。
    可调工作系数:在像 BCrypt 这样的算法中,有一个工作因子或成本参数来确定哈希循环运行多少次。随着硬件的速度越来越快,可以增加此系数,以确保哈希过程不会变得太快。
  2. 内存密集型操作
    增加内存使用量:某些算法(如 Argon2)旨在使用大量内存以及 CPU 资源。这使得攻击者更难使用 GPU 或定制硬件并行化攻击,而每个处理单元可用的高速内存通常有限。
    3.内置腌制
    每个密码的唯一盐:现代哈希方法会自动为每个密码生成唯一的盐。盐是在哈希之前添加到密码中的随机值。这意味着即使两个用户具有相同的密码,他们的哈希值也会有所不同。加盐还可以防止使用预先计算的哈希表(彩虹表)来反转哈希。
    对不同类型攻击的有效性
    暴力攻击:这些算法的时间和资源密集使得暴力攻击(尝试所有可能的密码组合)变得不切实际,尤其是对于强密码。
    彩虹桌攻击:由于每个密码哈希都使用唯一值加盐,因此预先计算的哈希表变得无用。
    自定义硬件攻击:内存和处理要求使得攻击者使用专用硬件(如 ASIC 或 GPU)来加速破解过程变得更加困难和昂贵。
    现实世界的影响
    合法的用户体验:对于合法用户来说,这些哈希算法所花费的额外时间(通常是几分之一秒)在登录或创建帐户期间可以忽略不计。
    攻击者经验:对于试图破解密码的攻击者来说,这个时间会迅速增加。使用旧的哈希方法可能需要数天时间的工作,而使用现代算法可能需要数年时间,这实际上使暴力攻击对于强密码来说变得不切实际。
    现代密码哈希技术
  3. 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);
    }
}
  1. 氩2
    Argon2 是 2023 年密码哈希竞赛的获胜者,提供可定制的抵抗 GPU 和基于内存的攻击的能力。

例:

import org.bouncycastle.crypto.generators.Argon2BytesGenerator;
import org.bouncycastle.crypto.params.Argon2Parameters;

public class Argon2Hashing {
    public static String hashPassword(String password) {
      
        // Set realistic values for Argon2 parameters
        int parallelism = 2; // Use 2 threads
        int memory = 65536; // Use 64 MB of memory
        int iterations = 3; // Run 3 iterations
        int hashLength = 32; // Generate a 32 byte (256 bit) hash
      
        Argon2BytesGenerator generator = new Argon2BytesGenerator();
        Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
                .withSalt(salt) // You need to generate a salt
                .withParallelism(parallelism) // Parallelism factor
                .withMemoryAsKB(memory) // Memory cost
                .withIterations(iterations); // Number of iterations

        generator.init(builder.build());
        byte[] result = new byte[hashLength];
        generator.generateBytes(password.toCharArray(), result);
        return Base64.getEncoder().encodeToString(result);
    }
}
  1. 多溴联苯二氟化物
    PBKDF2(基于密码的密钥派生函数 2)是 RSA Laboratories 的 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 加盐(不推荐)

尽管 SHA-512 存在漏洞,但了解 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);
    }
}
  1. 使用 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);
    }
}
  1. 使用 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);
    }
}
  1. 使用 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 支持
语言:JavaScript/Node.js、Python、Java、Ruby、PHP、C#/.NET、Go
框架:Spring Security、Ruby on Rails、Django、Express
Argon2 支持
语言:C、Python、JavaScript/Node.js、PHP、Ruby、Java、Rust
框架:拉拉维尔、交响乐、凤凰城
PBKDF2 支持
语言:Java、Python、C#/.NET、Ruby、PHP、JavaScript/Node.js、Go
框架:Spring Framework, ASP.NET, Django
选择正确的算法
安全需求:Argon2 提供最高的安全性,尤其是针对 GPU 攻击,但需要更复杂的配置。
兼容性和遗留系统:PBKDF2 受到广泛支持,可能是需要遵守某些标准或传统兼容性的系统的选择。
平衡性和易用性:BCrypt 在安全性和性能之间提供了良好的平衡,易于实现,并且在许多框架和语言中都得到广泛支持。
结论
随着网络威胁的发展,我们保护敏感信息的方法也必须随之发展。采用 BCrypt、Argon2 和 PBKDF2 等现代密码哈希技术对于保护用户数据至关重要。这些方法提供了针对最常见的密码破解策略的强大防御机制,确保即使发生数据泄露,也会将对密码完整性的影响降至最低。开发人员和安全专业人员必须随时了解加密实践的最新进展,并不断相应地更新其安全措施。

在我的 GitHub 存储库中检查源代码和测试。

  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小徐博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值