SHA 算法每次为相同的密钥生成唯一的哈希字符串

com*_*mit 2 java encryption algorithm hash sha

我知道有很多关于散列和加密算法的文章。

我从他们那里弄清楚使用散列函数而不是加密将密码存储在数据库中

所以我决定使用 SHA-256 算法来生成散列密钥,并将该散列密钥存储到我的服务器数据库中,而不是普通密码。

现在我真的无法理解我应该如何使用它,因为每次我传递相同的密码来生成 SHA 密钥时,它都会给我与以前不同的密码,而不是我如何将它与数据库中存储的哈希密钥进行比较?

我正在使用 java 所以我的 java 代码是

public class Test {
public static void main(String...arg) throws IOException{
    System.out.println("First time");
    String string64 = getEncryptedPassword("FenilShah");
    System.out.println(string64);
    System.out.println(StringUtils.newStringUtf8(Base64.decodeBase64(string64)));

    System.out.println("\nSecond time");
    string64 = getEncryptedPassword("FenilShah");
    System.out.println(string64);
    System.out.println(StringUtils.newStringUtf8(Base64.decodeBase64(string64)));

    System.out.println("\nThird time");
    string64 = getEncryptedPassword("FenilShah");
    System.out.println(string64);
    System.out.println(StringUtils.newStringUtf8(Base64.decodeBase64(string64)));

}

 public static String getEncryptedPassword(String clearTextPassword)   {  


        try {
          MessageDigest md = MessageDigest.getInstance("SHA-256");
          md.update(clearTextPassword.getBytes());
          byte pass[] = md.digest();
          System.out.println(pass.toString());
          return Base64.encodeBase64String(StringUtils.getBytesUtf8(pass.toString()));
        } catch (NoSuchAlgorithmException e) {
          //_log.error("Failed to encrypt password.", e);
        }
        return "";
      }
 }
Run Code Online (Sandbox Code Playgroud)

所以输出是这样的

First time
[B@5bf825cc
W0JANWJmODI1Y2M=
[B@5bf825cc

Second time
[B@1abfb235
W0JAMWFiZmIyMzU=
[B@1abfb235

Third time
[B@1f4cc34b
W0JAMWY0Y2MzNGI=
[B@1f4cc34b
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 5

这是您最直接的问题:

byte pass[] = md.digest();
System.out.println(pass.toString());
Run Code Online (Sandbox Code Playgroud)

您没有返回字符串的哈希值。您正在返回调用toString()a的结果byte[]。Java 中的数组不会覆盖toString(),因此您将获得默认实现,它与对象的身份有关,而与字节数组中的数据无关

Object 类的 toString 方法返回一个字符串,该字符串由对象是其实例的类的名称、at 符号字符“@”和对象哈希码的无符号十六进制表示组成。

(数组也不会被覆盖hashCode(),所以它也是从默认实现中获得的Object......)

基本上,您当然需要一种不同的方式将 a 转换byte[]String... 或将字节数组直接存储在数据库中。如果您确实想转换为字符串,我建议您使用十六进制或 base64。对于 base64,我建议使用iharder.net 公共域库……或者如果您使用的是 Java 8,则可以使用java.util.Base64. (令人惊讶的是,在 XML 以外的上下文中将 base64 类放入标准库中花费了这么长时间,但我们走了。)

return Base64.getEncoder().encodeToString(md.digest());
Run Code Online (Sandbox Code Playgroud)

不过,您的代码还有一个问题:

md.update(clearTextPassword.getBytes());
Run Code Online (Sandbox Code Playgroud)

这使用平台默认编码将密码转换为字节数组。这不是一个好主意 - 这意味着您最终可能会根据您的代码运行的系统获得不同的哈希值。最好明确指定编码:

md.update(clearTextPassword.getBytes(StandardCharsets.UTF_8));
Run Code Online (Sandbox Code Playgroud)

此外,如果 SHA-256 缺失,则捕获异常,记录并继续使用空白字符串几乎肯定是错误的方法。你真的想用空字符串填充你的数据库,允许任何人使用任何密码登录吗?如果您的系统处于这种状态,那么您可以做的最好的事情几乎肯定是拒绝任何使用密码执行任何操作的请求。您可能希望将 转换NoSuchAlgorithmException为某种类型RuntimeException并重新抛出。

最后,存储一个简单的 SHA-256 哈希可能无论如何都不是一个好主意。

  • 你可能想的HMAC代替
  • 至少应该使用随机盐来避免数据库中具有相同值的相同密码。(否则,攻击者可以利用该信息对他们有利。)

我远不是安全专家,所以我不会就正确的做事方式向您提供更多建议 - 但我实际上建议尝试找到一个由安全专家编写的备受推崇的库,而不是尝试实现这一点你自己。安全很难做对。