Java字符串到SHA1


158

我正在尝试用Java创建一个简单的String到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));
}

当我通过它时toSHA1("password".getBytes()),我[�a�ɹ??�%l�3~��.知道它可能是像UTF-8这样的简单编码修复程序,但是有人可以告诉我我应该怎么做才能得到我想要的东西5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8吗?还是我这样做完全错误?


该算法SHA1没有连字符,不知道这是否会有所作为。
Scrum Meister

最好在调用时指定字符编码getBytes(),例如usetoSHA1("password".getBytes("UTF-8"))
Qwerky 2011年

Answers:


183

更新
您可以使用Apache Commons Codec(1.7+版)为您完成此工作。

DigestUtils.sha1Hex(stringToConvertToSHexRepresentation)

感谢@ Jon Onstott的建议。


旧答案
将您的字节数组转换为十六进制字符串。Real的方法告诉您如何

return byteArrayToHexString(md.digest(convertme))

和(从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;
}

顺便说一句,您可以使用Base64获得更紧凑的表示形式。Apache Commons Codec API 1.4拥有这个不错的实用程序,可以消除所有麻烦。请参考这里


4
base64和sha1有很大的不同-不要建议使用它们作为替代。
Ryan A.

13
@RyanA .:据我所知,他建议使用base64作为对SHA1哈希值进行十六进制编码的替代方法(而不是完全替代SHA1)。
helmbert

我还没有尝试过,但是您愿意解释一下它是如何工作的吗?
吉瓦伊,2013年

11
为什么不使用类似的库DigestUtils.sha1Hex("my string")而不是重新发明轮子(尽管知道如何手工转换为十六进制很有趣)?
乔恩·昂斯托特

3
因为在编写此答案时,DigestUtils(1.7已于2012年9月发布)没有该功能。感谢您指出了这一点。+1
Nishant

67

这是我将字符串转换为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;
}

7
可能希望指定这是java.util.Formatter,并且最后需要使用formatter.close()以避免警告。
Eric Chen

应该encryptPassword("test")echo test|sha1sumlinux终端输出相同的结果吗?他们没有。
图兰斯·科尔多瓦

@TulainsCórdova关于控制台调用:如果使用echo test,则将包含换行符的输出传递给sha1sum。如果您想对纯字符串进行哈希运算而不用换行符结尾,则可以使用echo -n test | sha1sum。该-n参数使echo省略换行符。
MrSnrub '18

问题较少,但总的来说更多:您的encryptPassword()声音就像用于存储身份验证数据的声音。请注意,您的编码容易受到字典攻击,因为没有应用种子。检查您的安全环境,这是否对您的应用程序有问题!
EagleRainbow


32

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

如果您需要一个String,则必须以byte[]一种可以表示为的方式对其进行格式化String(否则,请保留该位置byte[])。

代表任意byte[]可打印字符的两种常见方式是BASE64或简单的十六进制字符串(即,每个byte字符由两个十六进制数字表示)。似乎您正在尝试生成十六进制字符串。

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

更好的解决方案是指定要使用的编码为String-到- byte[]这样的转换:myString.getBytes("UTF-8")。在这里,选择UTF-8(或可以代表每个Unicode字符的另一种编码)是最安全的选择。


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

警告:对于以“ 0”开头的哈希,哈希生成不正确。您将收到一个包含39个字符的字符串。
菲林

@philn您可以建议解决方案吗?
Nikita Koksharov,

1
我猜想,如果您从具有足够前导零的byte []创建一个大整数,那么这些0将丢失。因此,十六进制字符串表示形式“ 0”将不存在,从而导致具有39个甚至更少的字符的哈希。我在上面使用了petrnohejls解决方案,并且效果很好...
philn

25

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

无需深入了解。


51
我不同意,深入细节是关键
2011年

12
问题是您是否有时间了解细节。整个过程通常都是按时完成的,并不是每个人都是学生,或者不是每个人都愿意学习所有细节。
DaTroop 2012年

DigestUtils返回一个字节数组,因此要获取字符串表示形式,您需要通过Hex.encodeHexString运行它。Java:是2014年,但我们仍然没有一步法sha
ryber 2014年

5
一步SHA-1方法:String result = DigestUtils.sha1Hex("An input string"); o)
乔恩·昂斯托特

18

如前所述,使用apache commons编解码器。Spring员工也推荐使用它(请参阅Spring文档中的DigestUtils)。例如:

DigestUtils.sha1Hex(b);

绝对不会在这里使用评分最高的答案。


6

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

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

结果

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


1
@Devenv是SHA-1,这三个点表示它将保持其原始代码并将其转换为sha1。OP的原始问题是正确打印字符串时。
爱德华多·丹尼斯

4

消息摘要(哈希)为byte [] in字节[] out

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

new String(md.digest(subject), StandardCharsets.UTF_8)

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

[�a�ɹ??�%l3~��.

二进制到文本编码

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

2fd4e1c67a2d28fced849ee1bb76e7391b93eb12

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

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

new BigInteger(1, token).toString(16)

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

使用库编码为十六进制

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

为了对大多数与字节有关的问题提供一个可行的解决方案,我实现了一个实用程序来处理以下情况:bytes-java(Github)

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

String hex = Bytes.wrap(md.digest(subject)).encodeHex();

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

String hex =  Bytes.from(subject).hashSha1().encodeHex();

2

SHA1哈希的基础64表示形式:

String hashedVal = Base64.getEncoder().encodeToString(DigestUtils.sha1(stringValue.getBytes(Charset.forName("UTF-8"))));

1

这样做不起作用的原因是,当您调用时String(md.digest(convertme)),您正在告诉Java将一系列加密字节解释为String。您想要的是将字节转换为十六进制字符。


0

将字节数组转换为十六进制字符串。

public static String toSHA1(byte[] convertme) {
    final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
    MessageDigest md = null;
    try {
        md = MessageDigest.getInstance("SHA-1");
    }
    catch(NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    byte[] buf = md.digest(convertme);
    char[] chars = new char[2 * buf.length];
    for (int i = 0; i < buf.length; ++i) {
        chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
        chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
    }
    return new String(chars);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.