加密和解密密码

时间:2013-01-29 15:41:54

标签: java password-encryption

我正在使用此代码加密和解密密码

public class SecureDigester
{
    private static final char digits[] =
           { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                 'F' };

           private static String byteArrayToHexString(byte[] b)
           {
              StringBuffer hexString = new StringBuffer(b.length);
              for (int i = 0; i < b.length; i++)
              {
                 hexString.append(digits[(b[i] & 0xF0) >> 4]);
                 hexString.append(digits[b[i] & 0x0F]);
              }
              return hexString.toString();
           }

           public static String digest(String plaintext)
           {
              try
              {
                 MessageDigest md = MessageDigest.getInstance("SHA");
                 md.update(plaintext.getBytes("UTF-8"));
                 byte[] mdBytes = md.digest();
                 String hashString = byteArrayToHexString(mdBytes);
                 return hashString;
              } catch (Exception e)
              {
                 throw new RuntimeException(e);
              }
           }

}

在我的登录信息中,我使用此代码解密密码:

        String passwordDigest = SecureDigester.digest(password);
        if (!user.getPassword().equals(passwordDigest))
        {
            // authentication failed: bad password
        }

现在我有了忘记密码.jsp页面,该页面将用户的用户名和密码发送到他/她指定的电子邮件中。但是当我使用下面的代码时,我收到的加密密码也与​​我数据库中的加密密码不同。

String Email = req.getParameter("email");
User userItem = new UserDAO().findEmail(Email);
SendMailSSL sendEmail = new SendMailSSL();
String password = userItem.getPassword();
String EPassword = SecureDigester.digest(password);
sendEmail.send(userItem.getUsername(), EPassword, userItem.getEmail());

如何解决这个问题?

2 个答案:

答案 0 :(得分:4)

像SHA和MD5这样的加密哈希是单向哈希。无法反转单向散列。您可以从任何明文生成加密哈希,但是您无法确定仅给出哈希值的原始明文。

您的“解密”代码实际上并未解密SHA哈希值。相反,它会散列用户刚输入的密码,并将该散列与之前存储的散列进行比较。如果哈希匹配则表示密码匹配。

像这样散列密码是一个很好的安全方案,但其中一个结果就是你无法通过电子邮件向用户发送密码:你没有他们的密码!你只有不可逆转的哈希。这就是为什么现在具有良好安全性的网站如果忘记密码,就不会通过电子邮件向您发送原始密码。相反,他们提供了一些重置密码的方法。如果您遇到 能够通过电子邮件向您发送密码的网站,那就是一个巨大的红旗!

答案 1 :(得分:1)

看起来您将密码作为SHA1哈希值存储在数据库中。这很棒,你应该这样做(绝​​对是最好的做法)。使用SHA1(或任何其他单向散列)的“缺点”是您无法将散列“解密”回原始明文。这意味着以下代码不起作用。

String Email = req.getParameter("email");
User userItem = new UserDAO().findEmail(Email);
SendMailSSL sendEmail = new SendMailSSL();
String password = userItem.getPassword(); // the userItem returns an SHA1 hash
String EPassword = SecureDigester.digest(password); // this just rehashes the hash
sendEmail.send(userItem.getUsername(), EPassword, userItem.getEmail());

问题不在于此代码与方法一样多。由于您无法从SHA1哈希(这是一个好的事物)中恢复用户的明文密码,因此您需要向用户发送一个链接以重置其密码而不是电子邮件包含他们的密码。电子邮件是一个不安全的渠道,密码(除了必须更改的初始密码)不应该通过电子邮件发送。

这个问题有两种方法。

  • 从随机乱码中为用户生成新密码,然后对其进行SHA1哈希处理。将此值存储在数据库中。将新的随机密码发送给用户并强制用户在下次登录时进行更改。
  • 生成用户可用于重置密码的链接。链接需要很难猜测并在一定时间后(即在向用户发送电子邮件的5-10分钟内)到期。这使得攻击者很难利用“重置窗口”。每次重置链接应该是随机的,如果可能,不包含有关用户的标识信息。