Java 计算字符串 SHA-1 摘要的十六进制表示

发布于 2024-10-06 20:47:46 字数 433 浏览 7 评论 0原文

我将用户密码作为 sha1 哈希存储在数据库上。

不幸的是我得到了奇怪的答案。

我将字符串存储为这样:

MessageDigest cript = MessageDigest.getInstance("SHA-1");
              cript.reset();
              cript.update(userPass.getBytes("utf8"));
              this.password = new String(cript.digest());

我想要这样的东西 -->

aff--> “0c05aa56405c447e6678b7f3127febde5c3a9238”

而不是

aff --> �V@\D~fx����:�8

I'm storing the user password on the db as a sha1 hash.

Unfortunately I'm getting strange answers.

I'm storing the string as this:

MessageDigest cript = MessageDigest.getInstance("SHA-1");
              cript.reset();
              cript.update(userPass.getBytes("utf8"));
              this.password = new String(cript.digest());

I wanted something like this -->

aff --> "0c05aa56405c447e6678b7f3127febde5c3a9238"

rather than

aff --> �V@\D~fx����:�8

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

梦行七里 2024-10-13 20:47:46

使用apache通用编解码器库:

DigestUtils.sha1Hex("aff")

结果是0c05aa56405c447e6678b7f3127febde5c3a9238

就是这样:)

Using apache common codec library:

DigestUtils.sha1Hex("aff")

The result is 0c05aa56405c447e6678b7f3127febde5c3a9238

That's it :)

枯寂 2024-10-13 20:47:46

发生这种情况是因为 cript.digest() 返回一个字节数组,您试图将其打印为字符串。您想将其转换为可打印的十六进制字符串。

简单的解决方案:使用 Apache 的 commons-codec 库

String password = new String(Hex.encodeHex(cript.digest()),
                             CharSet.forName("UTF-8"));

This is happening because cript.digest() returns a byte array, which you're trying to print out as a character String. You want to convert it to a printable Hex String.

Easy solution: Use Apache's commons-codec library:

String password = new String(Hex.encodeHex(cript.digest()),
                             CharSet.forName("UTF-8"));
蓝咒 2024-10-13 20:47:46

哈希算法的一次迭代并不安全。太快了。您需要通过多次迭代哈希来执行密钥强化。

此外,您没有对密码加盐。这会给预先计算的字典(例如“彩虹表”)带来漏洞。

您可以使用 Java 运行时内置的代码,而不是尝试滚动自己的代码(或使用一些粗略的第三方膨胀软件)来正确执行此操作。有关详细信息,请参阅此答案

正确地对密码进行哈希处理后,您将获得一个 byte[]。将其转换为十六进制 String 的一个简单方法是使用 BigInteger 类:

String passwordHash = new BigInteger(1, cript.digest()).toString(16);

如果您想确保字符串始终有 40 个字符,您可能需要执行一些操作在左侧填充零(您可以使用 String.format() 来完成此操作。)

One iteration of a hash algorithm is not secure. It's too fast. You need to perform key strengthening by iterating the hash many times.

Furthermore, you are not salting the password. This creates a vulnerability to pre-computed dictionaries, like "rainbow tables."

Instead of trying to roll your own code (or using some sketchy third-party bloatware) to do this correctly, you can use code built-in to the Java runtime. See this answer for details.

Once you have hashed the password correctly, you'll have a byte[]. An easy way to convert this to a hexadecimal String is with the BigInteger class:

String passwordHash = new BigInteger(1, cript.digest()).toString(16);

If you want to make sure that your string always has 40 characters, you may need to do some padding with zeroes on the left (you could do this with String.format().)

绅士风度i 2024-10-13 20:47:46

如果您不想向项目添加任何额外的依赖项,您也可以使用

MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(message.getBytes("utf8"));
byte[] digestBytes = digest.digest();
String digestStr = javax.xml.bind.DatatypeConverter.printHexBinary(digestBytes);

If you don't want to add any extra dependencies to your project, you could also use

MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(message.getBytes("utf8"));
byte[] digestBytes = digest.digest();
String digestStr = javax.xml.bind.DatatypeConverter.printHexBinary(digestBytes);
泪冰清 2024-10-13 20:47:46

crypt.digest() 方法返回一个 byte[]。该字节数组是正确的 SHA-1 总和,但加密哈希值通常以十六进制形式显示给人类。散列中的每个字节将产生两个十六进制数字。

要安全地将字节转换为十六进制,请使用:

// %1$ == arg 1
// 02  == pad with 0's
// x   == convert to hex
String hex = String.format("%1$02x", byteValue);

此代码片段可以是用于将字符转换为十六进制

/*
 * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle or the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */ 
import java.io.*;

public class UnicodeFormatter  {

   static public String byteToHex(byte b) {
      // Returns hex String representation of byte b
      char hexDigit[] = {
         '0', '1', '2', '3', '4', '5', '6', '7',
         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
      };
      char[] array = { hexDigit[(b >> 4) & 0x0f], hexDigit[b & 0x0f] };
      return new String(array);
   }

   static public String charToHex(char c) {
      // Returns hex String representation of char c
      byte hi = (byte) (c >>> 8);
      byte lo = (byte) (c & 0xff);
      return byteToHex(hi) + byteToHex(lo);
   }
}

请注意,在 Java 中使用字节非常容易出错。我会仔细检查所有内容并测试一些奇怪的情况。

您还应该考虑使用比 SHA-1 更强的东西。
http://csrc.nist.gov/groups/ST/hash/statement.html

The crypt.digest() method returns a byte[]. This byte array is the correct SHA-1 sum, but crypto hashes are typically displayed to humans in hex form. Each byte in your hash will result in two hex digits.

To safely convert a byte to hex use this:

// %1$ == arg 1
// 02  == pad with 0's
// x   == convert to hex
String hex = String.format("%1$02x", byteValue);

This code snippet can be used for converting a char to hex:

/*
 * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle or the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */ 
import java.io.*;

public class UnicodeFormatter  {

   static public String byteToHex(byte b) {
      // Returns hex String representation of byte b
      char hexDigit[] = {
         '0', '1', '2', '3', '4', '5', '6', '7',
         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
      };
      char[] array = { hexDigit[(b >> 4) & 0x0f], hexDigit[b & 0x0f] };
      return new String(array);
   }

   static public String charToHex(char c) {
      // Returns hex String representation of char c
      byte hi = (byte) (c >>> 8);
      byte lo = (byte) (c & 0xff);
      return byteToHex(hi) + byteToHex(lo);
   }
}

Note that working with bytes in Java is very error prone. I would double check everything and test some strange cases as well.

Also you should consider using something stronger than SHA-1.
http://csrc.nist.gov/groups/ST/hash/statement.html

蘑菇王子 2024-10-13 20:47:46

使用 Google Guava

Maven:

<dependency>
   <artifactId>guava</artifactId>
   <groupId>com.google.guava</groupId>
   <version>14.0.1</version>
</dependency>

示例:

HashCode hashCode = Hashing.sha1().newHasher()
   .putString(password, Charsets.UTF_8)
   .hash();            

String hash = BaseEncoding.base16().lowerCase().encode(hashCode.asBytes());

With Google Guava:

Maven:

<dependency>
   <artifactId>guava</artifactId>
   <groupId>com.google.guava</groupId>
   <version>14.0.1</version>
</dependency>

Sample:

HashCode hashCode = Hashing.sha1().newHasher()
   .putString(password, Charsets.UTF_8)
   .hash();            

String hash = BaseEncoding.base16().lowerCase().encode(hashCode.asBytes());
謸气贵蔟 2024-10-13 20:47:46

在java 17中 <引入了code>HexFormat

它允许你写:

var cript = MessageDigest.getInstance("sha1");
var hexformat = HexFormat.of();
this.password = hexformat.formatHex(cript.digest(userPass.getBytes(StandardCharsets.UTF_8)))

In java 17 HexFormat is introduced.

It allows you to write:

var cript = MessageDigest.getInstance("sha1");
var hexformat = HexFormat.of();
this.password = hexformat.formatHex(cript.digest(userPass.getBytes(StandardCharsets.UTF_8)))
十年九夏 2024-10-13 20:47:46

如果你使用 Spring,它非常简单:

MessageDigestPasswordEncoder encoder = new MessageDigestPasswordEncoder("SHA-1");
String hash = encoder.encodePassword(password, "salt goes here");

If you use Spring its quite simple:

MessageDigestPasswordEncoder encoder = new MessageDigestPasswordEncoder("SHA-1");
String hash = encoder.encodePassword(password, "salt goes here");
烟柳画桥 2024-10-13 20:47:46

不可逆存储密码涉及的不仅仅是简单的标准哈希算法。

  1. 进行多轮以使暴力攻击变慢
  2. 除了密码之外,还使用每条记录的“盐”作为哈希算法的输入,以降低字典攻击的可行性并避免输出冲突。
  3. 使用“pepper”(一种应用程序配置设置)作为哈希算法的输入,使带有未知“pepper”的被盗数据库转储变得无用。
  4. 填充输入以避免某些哈希算法中的弱点,例如,通过修改哈希,您可以在不知道密码的情况下将字符附加到密码。

有关详细信息,请参阅

您还可以使用 http://en.wikipedia.org/wiki/Password-authenticated_key_agreement 方法,以避免将密码以明文形式传递到服务器。

There's more than just simple standard hash algorithms involved in storing passwords nonreversible.

  1. Do multiple rounds to make brute-force attacks slower
  2. Use a per-record "salt" as input to the hash algorithm besides the password to make dictionary attacks less feasible and avoid output collisions.
  3. Use "pepper", an application-configuration-setting as input to the hash algorithm to make a stolen database-dump with an unknown "pepper" useless.
  4. Pad the input to avoid weaknesses in some hash algorithms e.g. where you could append a character to the password without knowing the password, by modifying the hash.

For more info, see e.g.

You could also use a http://en.wikipedia.org/wiki/Password-authenticated_key_agreement method to avoid passing the password in cleartext to the server at all.

浪漫之都 2024-10-13 20:47:46

摘要() 返回一个字节数组,您将使用默认编码将其转换为字符串。你想要做的是对其进行 Base64 编码。

digest() returns a byte array, which you're converting to a string using the default encoding. What you want to do is base64 encode it.

饭团 2024-10-13 20:47:46

要使用 UTF-8,请执行以下操作:

userPass.getBytes("UTF-8");


要从摘要中获取 Base64 字符串,您可以执行以下操作:

this.password = new BASE64Encoder().encode(cript.digest());

由于 MessageDigest.digest() 返回一个字节数组,因此您可以将其转换使用 Apache 的 十六进制编码 转换为字符串 (更简单)。

例如

this.password = Hex.encodeHexString(cript.digest());

To use UTF-8, do this:

userPass.getBytes("UTF-8");


And to get a Base64 String from the digest, you can do something like this:

this.password = new BASE64Encoder().encode(cript.digest());

Since MessageDigest.digest() returns a byte array, you can convert it to String using Apache's Hex Encoding (simpler).

E.g.

this.password = Hex.encodeHexString(cript.digest());
空宴 2024-10-13 20:47:46

将 byte[] 转换为 base64 字符串怎么样?

    byte[] chkSumBytArr = digest.digest();
    BASE64Encoder encoder = new BASE64Encoder();
    String base64CheckSum = encoder.encode(chkSumBytArr);

How about converting byte[] to base64 string?

    byte[] chkSumBytArr = digest.digest();
    BASE64Encoder encoder = new BASE64Encoder();
    String base64CheckSum = encoder.encode(chkSumBytArr);
护你周全 2024-10-13 20:47:46
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
        messageDigest.reset();
        messageDigest.update(password.getBytes("UTF-8"));
        String sha1String = new BigInteger(1, messageDigest.digest()).toString(16);
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
        messageDigest.reset();
        messageDigest.update(password.getBytes("UTF-8"));
        String sha1String = new BigInteger(1, messageDigest.digest()).toString(16);
提笔落墨 2024-10-13 20:47:46

回声-n“aff”| sha1sum 产生正确的输出(echo 默认插入换行符)

echo -n "aff" | sha1sum produce the correct output (echo inserts a newline by default)

提赋 2024-10-13 20:47:46

你也可以使用这个代码(来自crackstation.net):

private static String toHex(byte[] array)
{
    BigInteger bi = new BigInteger(1, array);
    String hex = bi.toString(16);
    int paddingLength = (array.length * 2) - hex.length();
    if(paddingLength > 0)
        return String.format("%0" + paddingLength + "d", 0) + hex;
    else
        return hex;
}

you can use this code too(from crackstation.net):

private static String toHex(byte[] array)
{
    BigInteger bi = new BigInteger(1, array);
    String hex = bi.toString(16);
    int paddingLength = (array.length * 2) - hex.length();
    if(paddingLength > 0)
        return String.format("%0" + paddingLength + "d", 0) + hex;
    else
        return hex;
}
总以为 2024-10-13 20:47:46

您需要首先对结果进行十六进制编码。 MessageDigest 返回一个“原始”哈希值,而不是人类可读的哈希值。

编辑:

@thejh 提供了应该可以工作的代码的链接。就个人而言,我建议使用 BouncycastleApache Commons Codec 来完成这项工作。如果您想做任何其他与加密相关的操作,Bouncycastle 会很好。

You need to hex encode the result first. MessageDigest returns a "raw" hash, rather than a human readable one.

Edit:

@thejh provided a link to code which should work. Personally, I'd suggest using either Bouncycastle or Apache Commons Codec to do the job. Bouncycastle would be good if you want to do any other crypto-related operations.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文