Java String到SHA1

Bri*_*ian 148 java string sha1

我正在尝试用Java创建一个简单的String to SHA1转换器,这就是我所拥有的......

public static String toSHA1(byte[] convertme) {
    MessageDigest md = null;
    try {
        md = MessageDigest.getInstance("SHA-1");
    }
    catch(NoSuchAlgorithmException e) {
        e.printStackTrace();
    } 
    return new String(md.digest(convertme));
}
Run Code Online (Sandbox Code Playgroud)

当我通过它时toSHA1("password".getBytes()),我[?a?????%l?3~??.知道它可能是一个简单的编码修复,如UTF-8,但有人可以告诉我应该做什么来获得我想要的东西是5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8什么?或者我这样做完全错了?

Nis*_*ant 176

更新
您可以使用Apache Commons Codec(版本1.7+)为您完成这项工作.

DigestUtils.sha1Hex(stringToConvertToSHexRepresentation)

感谢@ Jon Onstott提出这个建议.


旧答案
将您的字节数组转换为十六进制字符串.真实的如何告诉你如何.

return byteArrayToHexString(md.digest(convertme))
Run Code Online (Sandbox Code Playgroud)

和(从Real的方法复制)

public static String byteArrayToHexString(byte[] b) {
  String result = "";
  for (int i=0; i < b.length; i++) {
    result +=
          Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
  }
  return result;
}
Run Code Online (Sandbox Code Playgroud)

顺便说一下,使用Base64可以获得更紧凑的表示.Apache Commons Codec API 1.4,有这个很好的实用工具来消除所有的痛苦.请参考这里

  • @RyanA.:据我所知,他建议使用base64替代对SHA1哈希进行十六进制编码(不完全替代SHA1). (12认同)
  • 为什么不使用像`DigestUtils.sha1Hex("my string")`这样的库而不是重新发明轮子(尽管知道如何手动转换为十六进制很有趣)? (9认同)
  • base64和sha1非常不同 - 不建议将它们作为替代品. (4认同)
  • 因为当这个答案写成DigestUtils(1.7于2012年9月发布)时没有这个功能.感谢您指出了这一点.+1 (3认同)

pet*_*ejl 66

这是我将字符串转换为sha1的解决方案.它适用于我的Android应用:

private static String encryptPassword(String password)
{
    String sha1 = "";
    try
    {
        MessageDigest crypt = MessageDigest.getInstance("SHA-1");
        crypt.reset();
        crypt.update(password.getBytes("UTF-8"));
        sha1 = byteToHex(crypt.digest());
    }
    catch(NoSuchAlgorithmException e)
    {
        e.printStackTrace();
    }
    catch(UnsupportedEncodingException e)
    {
        e.printStackTrace();
    }
    return sha1;
}

private static String byteToHex(final byte[] hash)
{
    Formatter formatter = new Formatter();
    for (byte b : hash)
    {
        formatter.format("%02x", b);
    }
    String result = formatter.toString();
    formatter.close();
    return result;
}
Run Code Online (Sandbox Code Playgroud)

  • 可能要指定这是java.util.Formatter并且在末尾需要formatter.close()以避免警告. (7认同)
  • @TulainsCórdova关于控制台调用:如果您使用“echo test”,包含换行符的输出将通过管道传输到“sha1sum”。如果你想散列一个没有尾随换行符的纯字符串,你可以使用`echo -n test | sha1sum`。`-n` 参数使 `echo` 省略换行符。 (2认同)

Jan*_*fer 53

使用Guava Hashing类:

Hashing.sha1().hashString( "password", Charsets.UTF_8 ).toString()
Run Code Online (Sandbox Code Playgroud)


Joa*_*uer 31

SHA-1(和所有其他散列算法)返回二进制数据.这意味着(在Java中)他们产生了一个byte[].这byte阵并没有表示任何特定的字符,这意味着你不能简单地把它变成一个String像你这样.

如果你需要a String,那么你必须以byte[]一种可以表示为a的方式格式化String(否则,只需保持byte[]周围).

表示任意byte[]可打印字符的两种常用方法是BASE64或简单的十六进制字符串(即每byte两个十六进制数字表示).看起来你正在尝试生成一个hex-String.

还有另一个陷阱:如果你想获得一个Java的SHA-1 String,那么你需要将其转换Stringbyte[]第一(如SHA-1的输入是byte[]也).如果您只是myString.getBytes()按照自己的方式使用,那么它将使用平台默认编码,因此将依赖于您运行它的环境(例如,它可能会根据您操作系统的语言/区域设置返回不同的数据).

更好的解决方案是指定要用于编码String-到- byte[]这样的转换:myString.getBytes("UTF-8").选择UTF-8(或代表每个Unicode字符的其他编码)是最安全的选择.


Nik*_*rov 27

这是一个简单的解决方案,可以在将字符串转换为十六进制格式时使用:

private static String encryptPassword(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {

    MessageDigest crypt = MessageDigest.getInstance("SHA-1");
    crypt.reset();
    crypt.update(password.getBytes("UTF-8"));

    return new BigInteger(1, crypt.digest()).toString(16);
}
Run Code Online (Sandbox Code Playgroud)

  • 我想如果您从具有足够前导零的 byte[] 创建一个大整数,这些 0 就会丢失。因此,十六进制字符串表示形式“0”将不存在,从而导致具有 39 个甚至更少字符的散列。我使用了上面的 petrnohejls 解决方案,它工作正常...... (3认同)
  • 警告:对于以“0”开头的散列,散列生成不正确。您将收到一个包含 39 个字符的字符串。 (2认同)

DaT*_*oop 25

只需使用apache commons编解码器库.他们有一个名为DigestUtils的实用程序类

无需深入细节.

  • 我不同意,进入细节是有点重要的 (49认同)
  • 问题是你是否有时间了解细节.重点通常是按时完成.不是每个人都是学生或者有学习所有细节的奢侈品. (12认同)
  • 一步SHA-1方法:`String result = DigestUtils.sha1Hex("输入字符串")`; o) (5认同)

kaz*_*uar 18

如前所述,使用apache commons编解码器.这也是Spring的推荐(参见Spring doc中的DigestUtils).例如:

DigestUtils.sha1Hex(b);
Run Code Online (Sandbox Code Playgroud)

绝对不会在这里使用最受好评的答案.


Edu*_*nis 5

由于您需要使用Base64编码,因此无法正确打印。使用Java 8,您可以使用Base64编码器类进行编码。

public static String toSHA1(byte[] convertme) {
    md = MessageDigest.getInstance("SHA-1");
    return Base64.getEncoder().encodeToString((md.digest(convertme));
}
Run Code Online (Sandbox Code Playgroud)

结果

这将为您提供预期的输出 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8

  • 使用 Base64encodeToString(..) 我得到 `W6ph5Mm5Pz8GgiULbPgzG37mj9g=` 而不是 `5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8` (2认同)

pat*_*ckf 5

消息摘要(哈希)是 byte[] in byte[] out

\n

消息摘要被定义为一个函数,它接受原始字节数组并返回原始字节数组(也称为byte[])。例如,SHA-1(安全哈希算法 1)的摘要大小为 160 位或 20 字节。原始字节数组通常不能解释为像UTF-8这样的字符编码,因为并非每个顺序中的每个字节都是合法的编码。所以将它们转换为String

\n
new String(md.digest(subject), StandardCharsets.UTF_8)\n
Run Code Online (Sandbox Code Playgroud)\n

可能会创建一些非法序列或具有指向未定义的Unicode映射的代码指针:

\n
[\xef\xbf\xbda\xef\xbf\xbd\xc9\xb9??\xef\xbf\xbd%l\xef\xbf\xbd3~\xef\xbf\xbd\xef\xbf\xbd.\n
Run Code Online (Sandbox Code Playgroud)\n

二进制到文本编码

\n

为此,使用了二进制到文本的编码。对于哈希,最常用的是HEX 编码或 Base160基本上,一个字节可以具有从到255(或-128到有符号)的值127,这相当于0x00-的十六进制表示0xFF。因此,十六进制将使输出所需的长度加倍,这意味着 20 字节的输出将创建一个 40 个字符长的十六进制字符串,例如:

\n
2fd4e1c67a2d28fced849ee1bb76e7391b93eb12\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,不需要使用十六进制编码。您还可以使用诸如base64之类的东西。十六进制通常是首选,因为它更容易被人类阅读,并且具有定义的输出长度而不需要填充。

\n

您可以仅使用 JDK 功能将字节数组转换为十六进制:

\n
[\xef\xbf\xbda\xef\xbf\xbd\xc9\xb9??\xef\xbf\xbd%l\xef\xbf\xbd3~\xef\xbf\xbd\xef\xbf\xbd.\n
Run Code Online (Sandbox Code Playgroud)\n

但请注意,这BigInteger会将给定的字节数组解释为数字而不是字节字符串。这意味着不会输出前导零,并且生成的字符串可能短于 40 个字符。

\n

使用库编码为十六进制

\n

您现在可以从 Stack Overflow 复制并粘贴未经测试的字节到十六进制方法,或使用大量依赖项,例如Guava

\n

为了找到大多数字节相关问题的首选解决方案,我实现了一个实用程序来处理这些情况:bytes-java (GitHub)

\n

要转换您的消息摘要字节数组,您可以这样做

\n
2fd4e1c67a2d28fced849ee1bb76e7391b93eb12\n
Run Code Online (Sandbox Code Playgroud)\n

或者你可以只使用内置的哈希功能

\n
new BigInteger(1, token).toString(16)\n
Run Code Online (Sandbox Code Playgroud)\n