Java 加密和解密密码

Java 加密和解密密码,java,password-encryption,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 byteArrayToHexStr

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

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);
              }
           }

}
现在我有了遗忘密码.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());

如何解决这个问题?

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

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


像这样散列密码是一个很好的安全方案,但这样做的一个后果是你不能用电子邮件向用户发送他们的密码:你没有他们的密码!你只有不可逆的散列。这就是为什么现在安全性好的网站如果你忘记了原始密码就不会给你发电子邮件。相反,它们提供了一些重置密码的方法。如果你遇到一个可以通过电子邮件向你发送密码的网站,那将是一个巨大的危险信号

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

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


像这样散列密码是一个很好的安全方案,但这样做的一个后果是你不能用电子邮件向用户发送他们的密码:你没有他们的密码!你只有不可逆的散列。这就是为什么现在安全性好的网站如果你忘记了原始密码就不会给你发电子邮件。相反,它们提供了一些重置密码的方法。如果你遇到一个可以通过电子邮件向你发送密码的网站,那将是一个巨大的危险信号

看起来您正在数据库中以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哈希中恢复用户的明文密码,这是一件好事,因此您需要向用户发送一个链接以重置其密码,而不是发送一封包含其密码的电子邮件。电子邮件是一个不安全的渠道,除了必须更改的初始密码外,任何密码都不应通过电子邮件发送

解决这个问题有两种方法

从随机胡言乱语中为用户生成新密码,然后将其哈希。将此值存储在数据库中。将新的随机密码发送给用户,并强制用户在下次登录时更改密码。 生成用户可用于重置密码的链接。该链接需要很难猜测,并在一定时间后(即发送电子邮件给用户后5-10分钟内)过期。这使得攻击者很难利用重置窗口进行攻击。每次重置链接都应该是随机的,如果可能,不包含有关用户的识别信息。
看起来您正在数据库中以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哈希中恢复用户的明文密码,这是一件好事,因此您需要向用户发送一个链接以重置其密码,而不是发送一封包含其密码的电子邮件。电子邮件是一个不安全的渠道,除了必须更改的初始密码外,任何密码都不应通过电子邮件发送

解决这个问题有两种方法

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

通过电子邮件发送密码是个好主意。电子邮件是其他人服务器上未加密的纯文本。最好发送一个临时链接来重置密码。更好的方法是获取并存储用户提供的问题和答案的附加安全问题,例如:谁是你的一年级老师?A:史密斯太太。Smith@JonathanM通常在通过电子邮件发送密码进行恢复后,用户必须立即更改密码。这消除了您提到的问题。您的密码是散列的,而不是加密的。加密意味着转换是可逆的;散列意味着它不是。@Hunter,是的,但他不想假设这就是他在做的,所以我给了他几个其他的选择。但是我如何才能发送正确的密码呢?我只想提供发送用户密码。通过电子邮件发送密码不是一个好主意。电子邮件是其他人服务器上未加密的纯文本。最好发送一个临时链接来重置密码。更好的方法是获取并存储用户提供的问题和答案的附加安全问题,例如:谁是你的一年级老师?A:史密斯太太。Smith@JonathanM通常在通过电子邮件发送密码进行恢复后,用户必须立即更改密码。这消除了您提到的问题。您的密码是散列的,而不是加密的。加密意味着转换是可逆的;散列意味着它不是。@Hunter,是的,但他不想假设这就是他在做的,所以我给了他几个其他的选择。但是我如何才能发送正确的密码呢?我只想提供发送用户密码。因此无法发送用户的纯文本密码?如果使用SHA1,则无法发送。SHA1是一个严格的单向散列。我强烈建议保留这种方法,因为通过电子邮件向用户发送密码的网站是不安全的。你可以在那里找到几篇关于链接的文章,这些链接包含可识别的信息以及针对他们的潜在攻击。我前几天刚读到一篇关于新的超级服务的文章。这是一个注册链接,但同样的事情也适用于重置链接。随机性越强,在重置窗口期间伪造密码的难度就越大,最好是5-10分钟。不,我没问你密码是如何破解的。如果它不在url中,您指的是什么其他身份信息?在我看来,良好的重置链接可能如下所示:。最后一位是随机生成的密钥,不能滥用。错误链接可能是以下内容:。即使我不知道ticks参数是如何计算的,我也可以通过多次为我自己的帐户请求重置链接来了解它。一旦我了解了链接是如何形成的,我就可以为另一个用户请求重置,然后伪造链接来重置他们的密码。如果链接不是随机的,则对其进行Base64编码是不好的。因此,无法发送用户的纯文本密码?如果使用SHA1,则无法发送。SHA1是一个严格的单向散列。我强烈建议保留这种方法,因为通过电子邮件向用户发送密码的网站是不安全的。你可以在那里找到几篇关于链接的文章,这些链接包含可识别的信息以及针对他们的潜在攻击。我前几天刚读到一篇关于新的超级服务的文章。这是一个注册链接,但同样的事情也适用于重置链接。随机性越强,在重置窗口期间伪造密码的难度就越大,最好是5-10分钟。不,我没问你密码是如何破解的。如果它不在url中,您指的是什么其他身份信息?在我看来,良好的重置链接可能如下所示:。最后一位是随机生成的密钥,不能滥用。错误链接可能是以下内容:。即使我不知道ticks参数是如何计算的,我也可以通过多次为我自己的帐户请求重置链接来了解它。一旦我了解了链接是如何形成的,我就可以为另一个用户请求重置,然后伪造链接来重置他们的密码。Base64编码链接如果不是随机的,则不好。如何实现发送链接以重置用户密码?所有哈希都是单向函数。如何实现发送链接以重置用户密码?所有哈希都是单向函数。