【问题标题】:How to encrypt and salt the password using BouncyCastle API in Java?如何在 Java 中使用 BouncyCastle API 加密和加盐密码?
【发布时间】:2025-12-16 08:40:01
【问题描述】:

我对 cryptography 还很陌生,我正在使用BouncyCasetle API加密 密码并将其存储在数据库中。对于加密,我正在使用SHA-1 算法,我想对密码加盐以防止它再次受到字典攻击。

任何帮助将不胜感激。

【问题讨论】:

  • 到目前为止你尝试过什么?而且我假设您计划存储一个单向的哈希,而不是实际加密。
  • 问题是我找不到正确的文档,其中我可以理解使用 BouncyCastle 发生的事情我一直在尝试使用 PBEParametersGenerator 并使用它的 pbeParamGen.init(passwordBytes, salt, iterations);
  • 还可以考虑使用 SRP 协议(​​安全远程密码协议)而不是存储散列密码。
  • @Puce,感谢您对 SRP 的提醒。坦率地说,我很震惊我还没有听说过。

标签: java encryption bouncycastle salt


【解决方案1】:

我建议为此使用基于密码的密钥派生函数而不是基本哈希函数。像这样的:

// tuning parameters

// these sizes are relatively arbitrary
int seedBytes = 20;
int hashBytes = 20;

// increase iterations as high as your performance can tolerate
// since this increases computational cost of password guessing
// which should help security
int iterations = 1000;

// to save a new password:

SecureRandom rng = new SecureRandom();
byte[] salt = rng.generateSeed(seedBytes);

Pkcs5S2ParametersGenerator kdf = new Pkcs5S2ParametersGenerator();
kdf.init(passwordToSave.getBytes("UTF-8"), salt, iterations);

byte[] hash =
    ((KeyParameter) kdf.generateDerivedMacParameters(8*hashBytes)).getKey();

// now save salt and hash

// to check a password, given the known previous salt and hash:

kdf = new Pkcs5S2ParametersGenerator();
kdf.init(passwordToCheck.getBytes("UTF-8"), salt, iterations);

byte[] hashToCheck =
    ((KeyParameter) kdf.generateDerivedMacParameters(8*hashBytes)).getKey();

// if the bytes of hashToCheck don't match the bytes of hash
// that means the password is invalid

【讨论】:

  • 在存储到数据库时,是否应该使用 Base64 对 byteArray 进行编码,而在返回时是否应该使用相同的 Base64 进行解码?密码为 varchar 无法存储 byteArray,我发现将值存储到 varbinary 时遇到困难。
  • @Zingo,Base64 正是用于将二进制数据填充到字符字段中。但是,不要在 SQL 数据库中执行哈希相等检查,因为 Base64 区分大小写,而 varchar 类型通常不区分大小写。相反,由于无论如何您都必须检索该行才能获得salt,因此请同时获取hash 并在Java 中进行比较。
  • 谢谢(:对于您的输入,我刚刚检查过它确实有效,感谢您提供的任何链接,我可以在其中详细了解 BouncyCastle 中使用的 API。
  • @Zingo,您在寻找javadoc 还是guide
  • 谢谢我正在寻找一些指南。再次感谢您的投入:)
【解决方案2】:

那么你可以做的是得到一个:

StringBuilder salt=new StringBuilder();
salt.append("MySuperSecretSalt");
MessageDigest md = MessageDigest.getInstance("SHA-256");
String text = "This is text to hash";
salt.append(text);    
md.update(salt.toString().getBytes("UTF-8")); // Change this to "UTF-16" if needed
byte[] digest = md.digest();

您的摘要现在包含您的字符串+盐的哈希值,因此它有助于防止彩虹表。

【讨论】:

  • Nota bene: 使用常量字符串基本上违背了盐的目的。每个散列密码的盐必须是唯一的,以防止单个密码猜测与整个表进行大规模比较。
  • 嗨,感谢您的输入,我必须使用 BouncyCastle API 是否有任何文档可以指出我在哪里可以获得与您刚刚使用的代码相同的等效代码。
  • @JeffreyHantin 我将生成随机盐并存储相同的盐,以便在需要时使用它进行解密。这是正确的方法吗?
  • @JeffreyHantin 你是对的,我只是想展示一个简单的例子。干杯
  • @Zingo 如果你不想解码数据,你不想使用散列,你应该使用像 AES 这样的东西,你可以从散列中获得一个安全密钥例如。