使用Java加密和解密密码需要使用哪些API和算法?

5
我正在使用Java创建应用程序,我在谷歌上搜索了Java密码加密,但结果太多了,让我感到不知所措。如何使用Java加密和解密密码?以及加密和解密密码的最佳实践是什么?我猜MD5不是一个好选择,因为它是一种单向哈希。我正在使用Struts2作为我的框架,想知道他们是否提供密码加密。

5
出于各种安全原因,我强烈建议您使用单向哈希算法,而不是可以被解密的算法。单向哈希是最好的选择。请注意,我的翻译尽可能保留原意,同时让语言更通俗易懂。 - Pradeep Simha
1
你不会加密和解密密码,因为这是双向的。你会使用盐值和哈希值,因为它是单向的,因此没有人可以通过哈希值回到原始密码。使用 bcrypt - JB Nizet
你真的需要加密密码吗?在你的情况下,散列不可行吗?你的密钥来自哪里?用户输入的主密码? - CodesInChaos
1
MD5不再是一个安全的单向哈希函数(http://en.wikipedia.org/wiki/MD5)。 - MrSmith42
@MrSmith42 MD5的单向性(第一原像)仍然非常强大。碰撞是弱点,但这些不适用于密码哈希。虽然最好使用其他东西,但MD5的加密弱点对于密码哈希并不是立即关注的问题。选择一个良好的强化方案比选择SHA-2而不是MD5更为重要。 - CodesInChaos
6个回答

7

更新:

尝试使用JBCrypt:

String password = "MyPassword123";
String hashed = BCrypt.hashpw(password, BCrypt.gensalt(12));
System.out.println(hashed);  // $2a$12$QBx3/kI1SAfwBDFOJK1xNOXK8R2yC7vt2yeIYusaqOisYbxTNFiMy

这里下载jBCrypt-0.3,更多细节请查看README文件。


新代码可能会让读者误以为一个常量值是好的盐(这是一个常见的误解),所以除非你用一个像样的盐生成代码替换它,否则我不会撤回我的反对票。而且你仍然在使用快速哈希,这本身就值得被反对。 - CodesInChaos
@CodesInChaos:快速哈希是什么意思? - tokhi
2
任何通用哈希函数(如MD5、SHA-x等)都太快了,只需要一次迭代。你应该使用故意昂贵的构造,例如bcrypt、scrypt或至少PBKDF2。 - CodesInChaos
感谢@CodesInChaos的教诲,这是一个非常好的课。我也在hacker news上发现了一篇有趣的文章。 - tokhi
1
在选择scrypt、bcrypt和PBKDF2之间,取决于具体情况,但它们都比大多数自制的解决方案要好得多。你的新bcrypt代码看起来不错。 - CodesInChaos

4

我不建议使用MD5,因为它已经被破解了。相反,您可以使用SHA512,这是一种安全的哈希方法,您可以使用MessageDigest。以下代码是我在项目中使用的,非常完美。

public String encode(String password, String saltKey)
        throws NoSuchAlgorithmException, IOException {

    String encodedPassword = null;
    byte[] salt = base64ToByte(saltKey);

    MessageDigest digest = MessageDigest.getInstance("SHA-512");
    digest.reset();
    digest.update(salt);

    byte[] btPass = digest.digest(password.getBytes("UTF-8"));
    for (int i = 0; i < ITERATION_COUNT; i++) {
        digest.reset();
        btPass = digest.digest(btPass);
    }

    encodedPassword = byteToBase64(btPass);
    return encodedPassword;
}

private byte[] base64ToByte(String str) throws IOException {
    BASE64Decoder decoder = new BASE64Decoder();
    byte[] returnbyteArray = decoder.decodeBuffer(str);
    if (log.isDebugEnabled()) {
        log.debug("base64ToByte(String) - end");
    }
    return returnbyteArray;
}

@CodesInChaos..盐的创建对于OP试图解决的问题类型是独特的,他/她可以根据自己的喜好生成盐,这就是为什么我没有展示的原因。 - Pradeep Simha
@user962206 1) 对密码进行盐值哈希处理并将其存储在数据库中 2) 每次用户登录时,对密码进行哈希和盐值处理,并将其与数据库中的哈希和盐值处理后的密码进行比较 3) 如果两者匹配,则登录成功。 - Pradeep Simha
@user962206 关于加盐,这个线程会给你一些想法 https://dev59.com/63I-5IYBdhLWcg3wYnOQ - Pradeep Simha
@user962206,你不需要做任何事情,只需将盐作为参数传递给上面的代码,它就可以正常工作。:) digest.update(salt); 这行代码会处理它。 - Pradeep Simha
2
@user962206 这是一个常量,应该至少为10000。如果你能承受更高的计算负担,甚至可以更大。 - CodesInChaos
显示剩余6条评论

0

嗯,据我所知,我们有以下一些算法来保护密码。

  1. MD5 -
  2. PBKDF2 -
  3. SHA -
  4. BCrypt 和 SCrypt -

在这些算法中,BCrypt 和 SCrypt 是更安全的密码保护方式。


-1

有一个非常不错的Java项目专门解决这个问题。 基本上,它提供了两种加密用户密码的方式: - MD5
- SHA1

请查看链接: jasypt


1
这比仅提供链接的回答还要糟糕,因为你的摘要写得非常糟糕。 - CodesInChaos
您可能还想使用更好的术语更新您的答案。哈希不等于加密;加密是可逆的。 - Greg

-2

对我而言,我认为MD5是最好的方法,而且您无需解密密码,以防用户忘记密码,可以让用户生成一个新密码。对于登录,您可以仅比较数据库中存在的哈希值和用户输入的哈希值。


1
MD5不是最好的方式。实际上,MD5不应再用于哈希密码。请使用更强大的哈希算法。 - Pablo
你只是说MD5不是最好的方法,但没有给出解决方案吗? - haffane hatim
1
@haffanehatim 因为这个问题已经被讨论得够多了。像往常一样,使用bcrypt、scrypt或PBKDF2与每个用户唯一的盐值。不应直接使用通用哈希(包括MD5和SHA-2)来哈希密码。 - CodesInChaos
@haffanehatim 他在评论你的答案,而不是提供自己的答案。他的评论是正确的。并不需要自己下蛋才能检测出别人下的腐烂蛋。 - user207421

-3

始终使用单向哈希算法。

我建议使用MD5哈希。在将密码存储在数据库中时,请使用MD5哈希。这样,如果您的密码为pass,则在哈希后将存储为asjasdfklasdjf789asdfalsdfashdflasdf(32个字符)。

正如您所说,您想要解密密码。我建议不要这样做。在检查密码与数据库匹配时,您可以对密码进行哈希处理,并将该字符串与数据库中的内容进行比较。

if (DoHashMD5(myPass).equals(rs.getString(2))) {
    System.out.print("You are registered user!!!");
} else {
    System.out.print("Invalid user!!!");
}

这里的rs.getString(2)将是您的查询参数。


1
没有盐的单向哈希非常不安全,因为容易受到彩虹表攻击的威胁。 - JB Nizet
@JBNizet:我同意某些观点,但是我认为,即使是哈希本身也不安全。请看我的问题 - Fahim Parkar
@FahimParkar:嗯,这正是我所说的:没有盐的哈希非常不安全。这就是为什么我推荐使用bcrypt的原因。 - JB Nizet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接