【问题标题】:SHA algorithm generates each time unique hash string for a same keySHA 算法每次为同一个键生成唯一的哈希字符串
【发布时间】:2015-01-06 10:25:47
【问题描述】:

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

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

所以我决定使用 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 "";
      }
 }

所以输出是这样的

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

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

Third time
[B@1f4cc34b
W0JAMWY0Y2MzNGI=
[B@1f4cc34b

【问题讨论】:

    标签: java algorithm encryption hash sha


    【解决方案1】:

    这是您最直接的问题:

    byte pass[] = md.digest();
    System.out.println(pass.toString());
    

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

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

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

    基本上,您当然需要另一种方式将byte[] 转换为String... 或将字节数组直接存储在数据库中。如果您确实想要转换为字符串,我建议您使用 hex 或 base64。对于 base64,我建议使用 iharder.net public domain library...,或者如果您使用的是 Java 8,则可以使用 java.util.Base64。 (令人惊讶的是,在 XML 等环境中将 base64 类添加到标准库中花了这么长时间,但我们开始了。)

    return Base64.getEncoder().encodeToString(md.digest());
    

    您的代码还有一个问题:

    md.update(clearTextPassword.getBytes());
    

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

    md.update(clearTextPassword.getBytes(StandardCharsets.UTF_8));
    

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

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

    • 您可能需要HMAC 代替
    • 您应该至少使用随机盐来避免相同的密码在数据库中具有相同的值。 (否则,攻击者可以利用该信息为自己谋利。)

    我远不是安全专家,因此我不会就正确的做事方式向您提供更多建议 - 但实际上我建议您尝试找到由安全专家编写的备受推崇的库,而不是尝试自己实施。安全性很难做到正确。

    【讨论】:

    • 嗨乔恩。你总是有很大的帮助。我第一次来安全的东西。我同意你的建议并使用了 HMAC。那么我还需要在 HMAC 中使用盐吗?并且您建议使用 iharder.net 公共域库。但我使用的是 org.apache.commons.codec.binary.Base64 好不好?
    • @commit:应该没问题 - 我只建议 iHarder,因为它是 just base64 和公共域,出于许可原因,它既好又简单。至于在 HMAC 中使用盐 - 我怀疑这取决于 HMAC,而且我不是这方面的专家。正如我所说,我建议不要首先构建自己的身份验证系统:)
    猜你喜欢
    • 2015-05-22
    • 1970-01-01
    • 1970-01-01
    • 2013-10-07
    • 2018-07-27
    • 2013-09-17
    • 2011-09-01
    • 2020-06-02
    • 2019-02-03
    相关资源
    最近更新 更多