本文博客
原文
一、概述
java.security.
MessageDigest
类用于为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。简单点说就是用于生成
散列码
。
信息摘要是安全的单向哈希函数,它接收任意大小的数据,输出固定长度的哈希值。关于信息摘要和
散列码
请参照《
数字证书简介
》
MessageDigest
通过其getInstance系列静态函数来进行实例化和初始化。MessageDigest 对象通过使用
update
方法处理数据。任何时候都可以调用
reset
方法重置摘要。一旦所有需要更新的数据都已经被更新了,应该调用
digest
方法之一完成哈希计算并返回结果。
对于给定数量的更新数据,
digest
方法只能被调用一次。
digest
方法被调用后,
MessageDigest
对象被重新设置成其初始状态。
MessageDigest
md
=
MessageDigest
.
getInstance
(
"SHA"
);
try
{
md
.
update
(
toChapter1
);
MessageDigest
tc1
=
md
.
clone
();
byte
[]
toChapter1Digest
=
tc1
.
digest
();
md
.
update
(
toChapter2
);
...
etc
.
}
catch
(
CloneNotSupportedException
cnse
)
{
throw
new
DigestException
(
"couldn't make digest of partial content"
);
}
注意1
:即时给定
MessageDigest
的实现是不可复制的,则仍然能够通过getInstance方法实例化几个实例计算来同时进行摘要信息的计算。
注意2
:由于历史原因,此类是抽象的,是从
MessageDigestSpi
扩展的。应用程序开发人员只应该注意在此
MessageDigest
类中定义的方法;超类中的所有方法是供希望提供自己的信息摘要
算法
实现的加密服务提供者使用的。
注意3
:
MessageDigest
并不是单实例的。如下代码所示:
try
{
MessageDigest
mdTemp1
=
MessageDigest
.
getInstance
(
"MD5"
);
MessageDigest
mdTemp2
=
MessageDigest
.
getInstance
(
"MD5"
);
MessageDigest
mdTemp3
=
MessageDigest
.
getInstance
(
"MD5"
);
System
.
out
.
println
(
"mdTemp1==mdTemp2?:"
+(
mdTemp1
==
mdTemp2
));
System
.
out
.
println
(
"mdTemp2==mdTemp3?:"
+(
mdTemp2
==
mdTemp3
));
}
catch
(
NoSuchAlgorithmException
e
)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
运行结果
mdTemp1
==
mdTemp2
?:
false
mdTemp2
==
mdTemp3
?:
false
构造方法摘要 | |
protected | MessageDigest(String algorithm) 创建具有指定算法名称的MessageDigest 实例对象。 |
方法摘要 | |
Object | clone() 如果实现是可复制的,则返回一个副本。 |
byte[] | digest() 通过执行诸如填充之类的最终操作完成哈希计算。 |
byte[] | digest(byte[] input) 使用指定的字节数组对摘要进行最后更新,然后完成摘要计算。 |
int | digest(byte[] buf, int offset, int len) 通过执行诸如填充之类的最终操作完成哈希计算。 |
String | getAlgorithm() 返回标识算法的独立于实现细节的字符串。 |
int | getDigestLength() 返回以字节为单位的摘要长度,如果提供程序不支持此操作并且实现是不可复制的,则返回 0。 |
static MessageDigest | getInstance(String algorithm) 生成实现指定摘要算法的 MessageDigest 对象。 |
static MessageDigest | getInstance(String algorithm, Provider provider) 生成实现指定提供程序提供的指定算法的 MessageDigest 对象,如果该算法可从指定的提供程序得到的话。 |
static MessageDigest | getInstance(String algorithm, String provider) 生成实现指定提供程序提供的指定算法的 MessageDigest 对象,如果该算法可从指定的提供程序得到的话。 |
Provider | getProvider() 返回此信息摘要对象的提供程序。 |
static boolean | isEqual(byte[] digesta, byte[] digestb) 比较两个摘要的相等性。 |
void | reset() 重置摘要以供再次使用。 |
String | toString() 返回此信息摘要对象的字符串表示形式。 |
void | update(byte input) 使用指定的字节更新摘要。 |
void | update(byte[] input) 使用指定的字节数组更新摘要。 |
void | update(byte[] input, int offset, int len) 使用指定的字节数组,从指定的偏移量开始更新摘要。 |
void | update(ByteBuffer input) 使用指定的 ByteBuffer 更新摘要。 |
二、实际实践
2.1、创建
MessageDigest
对象
计算信息摘(即
散列码
)要做的第一步是创建
MessageDigest
对象
实例。像所有的引擎类一样,获取某类报文摘要算法(即
散列算法
,比如
MD5
)的
MessageDigest
对象的途径是调用
MessageDigest
类中的
getInstance
静态
factory
方法:
public
static
MessageDigest
getInstance
(
String
algorithm
)
注意
:算法名不区分大小写。例如,以下所有调用都是相等的:
MessageDigest
.
getInstance
(
"SHA"
);
MessageDigest
.
getInstance
(
"sha"
);
MessageDigest
.
getInstance
(
"sHa"
);
调用程序可选择指定提供者名称,以保证所要求的算法是由已命名提供者实现的:
public
static
MessageDigest
getInstance
(
String
algorithm
,
String
provider
);
调用 getInstance 将返回已初始化过的
MessageDigest
对象。因此,它不需要进一步的初始化。
2.2、向
MessageDigest
传送要计算的数据
计算数据的摘要的第二步是向已初始化的
MessageDigest对象
提供传送要计算的数据。这将通过一次或多次调用以下某个
update
(更新)方法来完成:
public
void
update
(
byte
input
);
public
void
update
(
byte
[]
input
);
public
void
update
(
byte
[]
input
,
int
offset
,
int
len
);
2.3、计算摘要
通过调用 update 方法向
MessageDigest对象
提传送要计算的数据后,你就可以调用以下某个 digest(摘要)方法来计算摘要(即
生成
散列码
):
public
byte
[]
digest
();
public
byte
[]
digest
(
byte
[]
input
);
public
int
digest
(
byte
[]
buf
,
int
offset
,
int
len
);
前两个方法返回计算出的摘要。后一个方法把计算出的摘要储存在所提供的 buf 缓冲区中,起点是 offset。len 是 buf 中分配给该摘要的字节数。该方法返回实际存储在 buf 中的字节数。
对第二个接受输入字节数组变量的 digest 方法的调用等价于用指定的输入调用:
public
void
update
(
byte
[]
input
)
,接着调用不带参数的 digest 方法.
三、例子演示
3.1
、★ 编程思路:
java.security包中的
MessageDigest
类提供了计算消息摘要(
即生成
散列码
)的方法,首先生成对象,执行其
update( )
方法可
以将原始数据传递给该对象,然后执行其
digest( )
方法即可得到消息摘要。具体步骤如下:
(1)
生成MessageDigest对象
MessageDigest
m
=
MessageDigest
.
getInstance
(
"MD5"
);
MessageDigest类也是一个工厂类,其构造器是受保护的,不允许
直接使用new MessageDigist( )来创建对象,而必须通过其静态方法
getInstance( )
生成
MessageDigest对象
。
其中传入的参数指定计算消息摘要所使用的算法,常用的有"
MD5
","
SHA
"等。
(2)
传入需要计算的字符串
m
.
update
(
x
.
getBytes
(
"UTF8"
));
分析:x为需要计算的字符串,update传入的参数是字节类型或字节类型数组,对于字符串,需要先使用getBytes( )方法生成字符串数组。
(3)
计算消息摘要
byte
s
[
]=
m
.
digest
(
);
分析:执行MessageDigest对象的digest( )方法完成计算,计算的结果通过字节类型的数组返回。
(4)
处理计算结果
必要的话可以使用如下代码将计算结果(byte数组)转换为字符串。
static
String
convertToHexString
(
byte
data
[])
{
StringBuffer
strBuffer
=
new
StringBuffer
();
for
(
int
i
=
0
;
i
<
data
.
length
;
i
++)
{
strBuffer
.
append
(
Integer
.
toHexString
(
0xff
&
data
[
i
]));
}
return
strBuffer
.
toString
();
}
3.2、示例一
★完整程序如下:
public
class
MessageDigestDemo
extends
Thread
{
public
void
run
()
{
String
text
=
"abc"
;
byte
data
[]
=
null
;
MessageDigest
m
;
try
{
data
=
text
.
getBytes
(
"UTF8"
);
m
=
MessageDigest
.
getInstance
(
"MD5"
);
m
.
update
(
data
);
byte
resultData
[]
=
m
.
digest
();
System
.
out
.
println
(
convertToHexString
(
resultData
));
}
catch
(
NoSuchAlgorithmException
e
)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
static
String
convertToHexString
(
byte
data
[])
{
StringBuffer
strBuffer
=
new
StringBuffer
();
for
(
int
i
=
0
;
i
<
data
.
length
;
i
++)
{
strBuffer
.
append
(
Integer
.
toHexString
(
0xff
&
data
[
i
]));
}
return
strBuffer
.
toString
();
}
}
★运行结果
900150983cd24fb0d6963f7d28e17f72
3.3、示例二
在这里我们将对计算生成的md5使用 sun.misc.BASE64Encoder进行简单的加密。
public
String
md5sumWithEncoder
(
String
text
)
throws
NoSuchAlgorithmException
,
UnsupportedEncodingException
{
/*确定计算方法*/
MessageDigest
md5
=
MessageDigest
.
getInstance
(
"MD5"
);
BASE64Encoder base64en
=
new
BASE64Encoder
();
/*加密后的散列码字符串*/
String
strMd5
=
base64en
.
encode
(
md5
.
digest
(
text
.
getBytes
(
"utf-8"
)));
return
strMd5
;
}
调用函数
String
str
=
"0123456789"
System
.
out
.
println
(
md5sumWithEncoder
(
str
));
输出
eB5eJF1ptWaXm4bijSPyxw==
3.4、示例三
关于此请参考《
一点关于计算MD5的封装》