想要得到字符串的MD5哈希值怎么办?
先来了解一下MD5哈希值的概念。
MD5散列
一般128位的MD5散列被表示为32位十六进制数字。以下是一个43位长的仅ASCII字母列的MD5散列:
1 | MD5("The quick brown fox jumps over the lazy dog") |
即使在原文中作一个小变化(比如用c取代d)其散列也会发生巨大的变化:
1 | MD5("The quick brown fox jumps over the lazy cog") |
也就是说,给你一串字符串,你要把它转换成固定长度的散列。
##用Java来实现MD5哈希值
Java中可以这样实现:
1 | String password = "123456"; |
byte数值转换为int数值时的注意事项
那么问题来了,下面这行代码做的是啥?Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1)
这行语句其实是将byteData中的byte类型的数据转换成十六进制的数据进行表示。
为什么这么复杂?
是因为要考虑到byteData中的负数。
考虑一下下面这段代码,试图将byte类型表示为16进制形式的字符串
1 | private static void testByteInt() { |
运行结果:
1 | 1111111 |
从输出结果可以看出几个问题。
- String的几个函数接收byte类型的参数时自动将其转换为int类型
- int类型的负数
-127
用2进制数据来表示的时候,会用符号位补全,变成11111111111111111111111110000001
, 而这个2进制的数据再用16进制来表示的时候却成了ffffff81
,与-127
的16进制表示不一致。 - 负数的十六进制表现形式例如
-7f
不符合哈希值的要求。哈希值中不能带有负号。所以需要将其转换一下。 - 比较小的数值,例如
2
,转换成的16进制字符串也是2
,不符合哈希值的长度要求。哈希值要求每个byte用2个16进制的字符来表示。
要解决上面两个问题,前面的java程序给出的答案是:
- 用
& 0xff
来对byte数值进行补零扩展 - 用
+ 0x100
来保证每个数转换成16进制后会得到3个字符 - 用
.subString(1)
来去掉3个字符的第一个字符
这样就能得到2个16进制的字符了。
循环一遍,就能得到相应的MD5哈希字符串了。