转自:https://blog.csdn.net/u012611878/article/details/54000607
http://www.crazyit.org/thread-12283-1-1.html
在java中实现MD5是很简单的,在包java.security有个类MessageDigest。官方文档如下
MessageDigest 类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。信息摘要是安全的单向哈希函数,它接收任意大小的数据,输出固定长度的哈希值。MessageDigest 对象开始被初始化。该对象通过使用 update 方法处理数据。任何时候都可以调用 reset 方法重置摘要。一旦所有需要更新的数据都已经被更新了,应该调用 digest 方法之一完成哈希计算。
对于给定数量的更新数据,digest 方法只能被调用一次。digest 被调用后,MessageDigest 对象被重新设置成其初始状态。
JAVA代码如下:
import java.security.MessageDigest;
public class MyMD5 {
public static void main(String[] args) {
try{
MessageDigest md5 = MessageDigest.getInstance("MD5");//申明使用MD5算法
md5.update("a".getBytes());//
System.out.println("md5(a)="+byte2str(md5.digest()));
md5.update("a".getBytes());
md5.update("bc".getBytes());
System.out.println("md5(abc)="+byte2str(md5.digest()));
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 将字节数组转换成十六进制字符串
* @param bytes
* @return
*/
private static String byte2str(byte []bytes){
StringBuilder builder = new StringBuilder();
for (int j = 0; j < b.length; j++) {
// 把字节按位与,转换为int
int v = b[j] & 0xFF;
if (v < 16) {
// 如果转换后的数字小于十六,先添加一个0到总的字符串里面
builder.append(0);
}
// 把按位与后的int,转换为十六进制的String追加到总的字符串里面
builder.append(Integer.toHexString(v));
}
}
}
为什么把byte转换为16进制字符串的时候要先按位与呢?
- StringBuilder builder = new StringBuilder();
- for (int j = 0; j < b.length; j++) {
- // 把字节按位与,转换为int
- int v = b[j] & 0xFF;
- if (v < 16) {
- // 如果转换后的数字小于十六,先添加一个0到总的字符串里面
- builder.append(0);
- }
- // 把按位与后的int,转换为十六进制的String追加到总的字符串里面
- builder.append(Integer.toHexString(v));
- }
这里我们主要来讨论一下:为何要 b[j] & 0xFF 呢?这句话起了什么作用呢?
讨论的时候,我们首先要知道按位与的作用,总结就是一句话:两边数字每位比较,如果两边都是1,结果为1。下面通过一个表格来进行简单说明。
0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 |
上面两行二进制数据,分别表示整数5和68,那么执行按位与以后,结果为多少呢?按照两边(上下)相同、结果为1的来看,结果为:
0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
这里转换为十进制的意义不大,我们只是为了通过上面这两个表格,阐述按位与的规则。
那么现在我有两个byte,要转换为16进制的字符串,这两个byte的值分别为100和-120。把整数转换为十六进制字符串的方式,可以通过JDK里面自带的Integer.toHexString(int i ) 方法来转换,这个方法的作用就是把int转换为十六进制字符串。
需要注意的是:Integer.toHexString方法要求的参数类型为int,也就是说:把byte传入此方法以后,会被自动转换为int传入进去。对应的二进制数据转换如下两个表格:
100的byte转换为int之后的变化
-120的byte转换为int之后的变化
大家通过上面的两个转换发现,把正数从byte转换为int,没有任何变化;而负数在转换为byte以后,左边会多出一堆的0。那么这对我们转换十六进制字符串的时候,就会造成很大的困扰:实际上我们的数据并没有那么多位数,只是因为进制转换以后自动在左边补符号位了。
那么当-120转换为int以后,再转换为十六进制字符串以后,我们会得到一个很离谱的字符串:ffffffee
这是不合理的、也是不正确的,因为-120转换为无符号十六进制数字,得到的结果为88,是两位数。
那么我们怎么才能得到实际要的数据呢?其实非常简单,我们只需要把左边补的符号位,全变成0即可!那么我们让int类型的-120和无符号byte的最大值(255、0xFF)按位与一下,看看得到什么:
于是大家就发现,-120左边的1全部被去掉了,变成了最后一行的结果。
那么这个结果,其实就是-120转换为无符号整数以后对应的数字:十进制136、16进制88。在Java里面,并不强调无符号整数这个概念,但是在计算机历史中,无符号整数依然有它存在的道理。
好了,为什么要按位与的原因,我们已经从最后一个图里面看到了:就是为了把自动补的符号位全给去掉。