我该如何在Java中生成MD5哈希值?

1092

有没有一种方法在Java中生成字符串的MD5哈希值?


2
https://dev59.com/9XVC5IYBdhLWcg3wZwTj - Leif Gruenwoldt
46
MD5 作为一种单向安全特性可能不太安全,但仍适用于通用的校验和应用。 - rustyx
34个回答

728

MessageDigest类可以为您提供MD5摘要的实例。

在使用字符串和加密类时,请务必始终指定要使用的字节表示的编码方式。如果仅使用string.getBytes(),它将使用平台默认值。(并非所有平台都使用相同的默认值)

import java.security.*;

..

byte[] bytesOfMessage = yourString.getBytes("UTF-8");

MessageDigest md = MessageDigest.getInstance("MD5");
byte[] theMD5digest = md.digest(bytesOfMessage);
如果您有很多数据,请查看可以重复调用的.update(xxx)方法。然后调用.digest()以获得结果哈希值。

9
请翻译以下编程相关内容的英文到中文:(请参见http://www.joelonsoftware.com/articles/Unicode.html获取更好的原理和解释) - Piskvor left the building
18
如果您需要将字节转换为十六进制字符串并保持长度不变,那么这个主题也是有用的。 - weekens
这非常容易和简单,我会推荐给所有的访客。 - Humphrey
2
那么,如何将这个thedigest转换为字符串,以便我们可以将其插入到mysql中? - Humphrey
7
最好是在可能的情况下使用yourString.getBytes(StandardCharsets.UTF_8)。这可以避免处理UnsupportedEncodingException异常。 - Hummeling Engineering BV
显示剩余4条评论

646

157
这里没有提到的一件事,让我感到意外。 MessageDigest类不是线程安全的。 如果要被多个线程使用,请创建一个新的实例,而不是尝试重复使用它们。 - mjuarez
43
它使用多种方法来改变其内部状态。缺乏线程安全怎么可能会令人惊讶呢? - Bombe
102
为什么我们需要知道 MessageDigest 的内部状态? - Dan Barowy
33
@DanBarowy,嗯,你正在改变它(即调用不返回值但导致其他方法返回不同值的方法),因此在未经证明之前,你应该始终假定这样做是不安全的线程。 - Bombe
4
MessageDigest 允许您分块输入数据。这是静态方法所无法实现的。虽然您可以争论他们应该添加一个静态方法以便在一次性传递所有数据时更加方便。 - user253751
显示剩余4条评论

277
如果你想要把答案作为字符串而不是字节数组返回,你可以像这样做:
String plaintext = "your text here";
MessageDigest m = MessageDigest.getInstance("MD5");
m.reset();
m.update(plaintext.getBytes());
byte[] digest = m.digest();
BigInteger bigInt = new BigInteger(1,digest);
String hashtext = bigInt.toString(16);
// Now we need to zero pad it if you actually want the full 32 chars.
while(hashtext.length() < 32 ){
  hashtext = "0"+hashtext;
}

12
@BalusC说的不太准确,BigInteger.toString方法会按照指定的进制返回完整的数字。例如,0x0606将打印为606,只是末尾的零被省略了。 - Spidey
11
小小的建议:在调用getInstance之后不需要立即使用m.reset()。另外一个小问题是,“your text here”需要用双引号括起来。 - David Leppik
从Java 11开始,您可以使用hashtext = "0".repeat(32 - hashtext.length()) + hashtext,而不是使用while循环。这样的话,编辑器就不会警告您在循环内执行字符串连接操作了。 - tom
我建议指定编码,而不是使用m.update(plaintext.getBytes()); 例如,可以使用m.update(plaintext.getBytes("UTF-8")); getBytes()不能保证编码,并且可能因系统而异,这可能导致在不同系统上对于相同的字符串产生不同的MD5结果。 - user1819780

265

您可能还想查看Apache commons codec项目的DigestUtils类,该类提供非常方便的方法来创建MD5或SHA摘要。


2
特别是那些以字符串形式返回“安全”编码表示的字节数据的方法。 - Rob
4
然而,如果不添加大量库或手动导入至少两个类,很难将DigestUtils类导入您的项目中。 - iuiz
在Maven仓库中也找不到。Grrrr. - sparkyspider
5
除非我疯了,否则应该在中央的Maven存储库中能找到:groupId=commons-codec artifactId=commons-codec version=1.5。 - Nick Spacek

174

发现了这个:

public String MD5(String md5) {
   try {
        java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
        byte[] array = md.digest(md5.getBytes());
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < array.length; ++i) {
          sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1,3));
       }
        return sb.toString();
    } catch (java.security.NoSuchAlgorithmException e) {
    }
    return null;
}
在下面的网站上,我不为此承担任何责任,但这是一个可行的解决方案!对于我来说,许多其他代码都无法正常工作,最终导致哈希值中缺少了0。这个解决方法似乎与PHP相同。 来源:http://m2tec.be/blog/2010/02/03/java-md5-hex-0093

15
在使用getBytes()时应明确指定字符编码,否则您的代码在不同平台或用户设置下会导致不同的结果。 - Paŭlo Ebermann
@PaŭloEbermann使用MessageDigest.getInstance("MD5");不够吗?我尝试在getBytes()中添加"MD5",但它返回了一个错误。 - Blaze Tama
2
@BlazeTama,“MD5”不是一种编码,而是一种消息摘要算法(不应该在新应用程序中使用)。编码是将字节转换为字符串和字符串转换为字节的算法对。例如,“UTF-8”,“US-ASCII”,“ISO-8859-1”,“UTF-16BE”等。请使用与计算此字符串哈希值的每个其他方相同的编码,否则您将获得不同的结果。 - Paŭlo Ebermann
6
作为字符集的一个例子,使用UTF-8是我认为最好且最兼容的方式。 byte[] array = md.digest(md5.getBytes(Charset.forName("UTF-8"))); - Richard
由于这不是我的解决方案,而且我没有亲自测试所有情况,所以我将保持不变,尽管我认为指定编码等可能是一个好主意。 - dac2009

98

我发现这是最清晰简洁的方法:

MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(StandardCharsets.UTF_8.encode(string));
return String.format("%032x", new BigInteger(1, md5.digest()));

3
好的,它不会落入截去前导零的陷阱。 - Markus Pscheidt
2
请注意,如果您使用的API级别<19,则此方法在Android上无法正常工作,但您只需要将第二行更改为md5.update(string.getBytes("UTF-8")); 这将添加另一个需要处理的已检查异常... - Fran Marzoa

97

我使用它的方法如下:

final MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(string.getBytes(Charset.forName("UTF8")));
final byte[] resultByte = messageDigest.digest();
final String result = new String(Hex.encodeHex(resultByte));

Hex是指:org.apache.commons.codec.binary.Hex,来自Apache Commons项目


16
如果您已经使用了Apache Commons Codec,那么可以使用以下内容:http://commons.apache.org/codec/api-release/org/apache/commons/codec/digest/DigestUtils.html#md5Hex(java.lang.String),该链接提供了一种计算MD5哈希值的方法。 - squiddle
15
我会将最后一行替换为:String result = Hex.encodeHexString(resultByte); - bluish

87
我刚刚下载了commons-codec.jar并获得了完美的类似于php中的md5。这里是说明文档
只需将其导入到您的项目中并使用即可。
String Url = "your_url";

System.out.println( DigestUtils.md5Hex( Url ) );

就是这样了。


1
这是一个方法,它提供与MySQL函数md5(str)相同的返回值。许多其他答案返回了其他值。 - rwitzel
1
这在Android上无法正常工作,因为Android捆绑了commons-codec 1.2,所以您需要使用此解决方法:https://dev59.com/BWox5IYBdhLWcg3wklCE#9284092 - EpicPandaForce
完美地为Gravatar的电子邮件MD5哈希工作!谢谢。 - Logesh S

36

不需要把它弄得太复杂。
DigestUtils 运行良好,并且在使用 md5 哈希时让您感到舒适。

DigestUtils.md5Hex(_hash);
或者
DigestUtils.md5(_hash);

你可以使用其他加密方法,例如shamd


35

找到了一个更加简洁的解决方案,可以从MD5哈希中获取字符串表示形式。

import java.security.*;
import java.math.*;

public class MD5 {
    public static void main(String args[]) throws Exception{
        String s="This is a test";
        MessageDigest m=MessageDigest.getInstance("MD5");
        m.update(s.getBytes(),0,s.length());
        System.out.println("MD5: "+new BigInteger(1,m.digest()).toString(16));
    }
}

这段代码是从这里提取出来的。


2
为什么这个回答只有-1分,而另一个更短、描述更少的回答却有+146分? - Nilzor
4
很好,使用BigInteger获取十六进制值+1。 - Dave.B
5
我刚刚发现,在某些情况下,这只会生成31个字符长的MD5校验和,而不是应该有的32个字符。 - kovica
3
这是因为我记得起始的零会被截断。使用以下代码可以解决这个问题:String.format("%032x", new BigInteger(1, hash));其中'hash'是散列的byte[]。 - Heshan Perera
1
这似乎要好得多。您甚至不需要捕获那么多的异常。 - JGFMK
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接