消息摘要算法又称散列算法,其核心在于散列函数的单向性。即通过散列函数可获得对应的散列值,但不可通过该散列值反推其原始信息。这是消息摘要算法的安全性的根本所在。
消息摘要算法主要分为三大类:MD(Message Digest
,消息摘要算法)、SHA(Secure Hash Algorithm
,安全散列算法)和MAC(Message Authentication Code
,消息认证码算法)。
MD系列算法包括MD2、MD4和MD5
共3种算法;
SHA算法主要包括其代表算法SHA1
和SHA1算法的变种SHA2系列算法(SHA224、SHA256
、SHA384、SHA512
)以及SHA3(SHA3-224、SHA3-256、SHA3-384、SHA3-512等);SHA-2的算法和SHA1是一样的,区别在于其生成的摘要的长度;SHA-3是与SHA-2算法不同的,可替换的加密散列算法。
MAC算法综合了上述两种算法,主要包括HmacMD5
、HmacSHA1
、HmacSHA256
、HmacSHA384和HmacSHA512
算法。
尽管上述内容列举了各种消息摘要算法,但仍不能满足应用需要。基于这些消息摘要算法,又衍生出了RipeMD
系列(包含RipeMD128、RipeMD160、RipeMD256、RipeMD320)、Tiger、GOST3411和Whirlpool算法。
java.security 包提供 java.security.MessageDigest 类进行消息摘要处理,输出字节数组。
消息摘要的一个特点是每次输出的摘要值都相同,下面的Test-Case对此进行了验证。
public byte[] md5Jdk8(String data) throws Exception {
return MessageDigest.getInstance("MD5").digest(data.getBytes("UTF-8"));
}
@Test
public void md5Jdk8Test() throws Exception {
final byte[] md5_1 = md5Jdk8("我是阿汤哥");
final byte[] md5_2 = md5Jdk8("我是阿汤哥");
assertEquals(md5_1, md5_2);
}
要使用BC实现需要添加provider,Security.addProvider(new BouncyCastleProvider())
,同时可以将字节数组转换为16进制编码的字符串进行输出。
public String md5Bc(String data) throws Exception {
// 加入Bouncy Castle Provider支持
Security.addProvider(new BouncyCastleProvider());
final byte[] md5s = MessageDigest.getInstance("MD5").digest(data.getBytes("UTF-8"));
return new String(Hex.encode(md5s), "UTF-8");
}
@Test
public void md5BcTest() throws Exception {
final String md5_1 = md5Bc("我是阿汤哥");
final String md5_2 = md5Bc("我是阿汤哥");
System.out.println("MD5 first : " + md5_1);
System.out.println("MD5 second : " + md5_2);
assertEquals(md5_1, md5_2);
}
org.apache.commons.codec.digest.DigestUtils
,虽然DigestUtils
只是对MessageDigist
的简单封装,但为实际开发提供了不少便利。
public String md5Codec(String data) throws Exception {
return DigestUtils.md5Hex(data);
}
@Test
public void md5CodecTest() throws Exception {
final String md5_1 = md5Codec("我是阿汤哥");
final String md5_2 = md5Codec("我是阿汤哥");
System.out.println("MD5 first : " + md5_1);
System.out.println("MD5 second : " + md5_2);
assertEquals(md5_1, md5_2);
}
输出结果
MD5 first : 15e13fd90ab9822cf0ea4e1d29301535
MD5 second : 15e13fd90ab9822cf0ea4e1d29301535
SHA(Secure Hash Algorithm,安全散列算法)是消息摘要算法的一种,被广泛认可为MD5算法的继任者。它是由美国国家安全局(NSA,National Security Agency)设计,经美国国家标准与技术研究院(NIST,National Institute of Standards and Technology)发布的一系列密码散列函数。SHA算法家族目前共有SHA1、SHA-2(SHA224、SHA256、SHA384、SHA512)、SHA3(SHA3-224、SHA3-256、SHA3-384、SHA3-512等)。
SHA算法的实现方式与MD5相似,只需要在参数指定对应的算法名称即可。SHA为SHA-1的缩写。
public byte[] sha256jdk(String data) throws Exception {
return MessageDigest.getInstance("SHA-256").digest(data.getBytes("UTF-8"));
}
@Test
public void sha256jdkTest() throws Exception {
final byte[] sha256_1 = sha256jdk("我是阿汤哥");
final byte[] sha256_2 = sha256jdk("我是阿汤哥");
assertEquals(sha256_1, sha256_2);
}
public String sha256Bc(String data) throws Exception {
Security.addProvider(new BouncyCastleProvider());
final byte[] digest = MessageDigest.getInstance("SHA-256").digest(data.getBytes("UTF-8"));
return new String(Hex.encode(digest), "UTF-8");
}
@Test
public void sha256BcTest() throws Exception {
final String sha256_1 = sha256bc("我是阿汤哥");
final String sha256_2 = sha256bc("我是阿汤哥");
System.out.println("SHA256 first : " + sha256_1);
System.out.println("SHA256 second : " + sha256_2);
assertEquals(sha256_1, sha256_2);
}
public String sha256Codec(String data) throws Exception {
return DigestUtils.sha256Hex(data);
}
@Test
public void sha256CodecTest() throws Exception {
final String sha256_1 = sha256Codec("我是阿汤哥");
final String sha256_2 = sha256Codec("我是阿汤哥");
System.out.println("SHA256 first : " + sha256_1);
System.out.println("SHA256 second : " + sha256_2);
assertEquals(sha256_1, sha256_2);
}
执行结果
SHA256 first : 7a78507d8bf0387f52683974184252b8b41cedd6ca2373df8dec6e6e2ca219e9
SHA256 second : 7a78507d8bf0387f52683974184252b8b41cedd6ca2373df8dec6e6e2ca219e9
MAC(Message Authentication Code,消息认证码算法)是含有密钥散列函数算法,兼容了MD和SHA算法的特性,并在此基础上加入了密钥。因为MAC算法融合了密钥散列函数(keyed-Hash),通常我们也把MAC称为HMAC(keyed-Hash Message Authentication Code)。MAC算法和HMAC算法基本上可等同对待。
MAC算法主要集合了MD和SHA两大系列消息摘要算法。MD系列算法有HmacMD2、HmacMD4和HmacMD5三种算法;
SHA系列算法有HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384和HmacSHA512五种算法。
经MAC算法得到的摘要值也可以使用十六进制编码表示,其摘要值长度与参与实现的算法摘要值长度相同。例如,HmacSHA1算法得到的摘要长度就是SHA1算法得到的摘要长度,都是160位二进制数,换算成十六进制编码为40位。
MAC与MD和SHA的主要区别在于加入了密钥,密钥是双方事先约定的,第三方不可能知道。
JDK7支持的算法包括HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384和HmacSHA512。
生成密钥
public byte[] generateKey() throws NoSuchAlgorithmException {
// 初始化 key generator
final KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5");
// 生成密钥
final SecretKey secretKey = keyGenerator.generateKey();
// 获取密钥
return secretKey.getEncoded();
}
生成消息摘要
public byte[] getMessageDigist(byte[] key, byte[] message) throws NoSuchAlgorithmException, InvalidKeyException {
// 还原密钥
final SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacMD5");
// 实例化 Mac
final Mac mac = Mac.getInstance(secretKeySpec.getAlgorithm());
// 初始化 Mac
mac.init(secretKeySpec);
// 生成消息摘要
return mac.doFinal(message);
}
BouncyCastle作为补充,提供了HmacMD2、HmacMD4和HmacSHA224三种算法支持。
此外BouncyCastle还支持消息摘要的十六进制编码。
public byte[] generateKey() throws NoSuchAlgorithmException {
// 添加BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
// 初始化 key generator
final KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD4");
// 生成密钥
final SecretKey secretKey = keyGenerator.generateKey();
// 获取密钥
return secretKey.getEncoded();
}
public byte[] getMessageDigist(byte[] key, byte[] message) throws NoSuchAlgorithmException, InvalidKeyException {
// 添加BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
// 还原密钥
final SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacMD4");
// 实例化 Mac
final Mac mac = Mac.getInstance(secretKeySpec.getAlgorithm());
// 初始化 Mac
mac.init(secretKeySpec);
// 生成消息摘要
return mac.doFinal(message);
}
@Test
public void testMac() throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
// 获取密钥
final byte[] secretKey = generateKey();
// 获取消息摘要
final byte[] md_1 = getMessageDigist(secretKey, "我是阿汤哥".getBytes("UTF-8"));
final byte[] md_2 = getMessageDigist(secretKey, "我是阿汤哥".getBytes("UTF-8"));
System.out.println(new String(Hex.encode(md_1), "UTF-8"));
assertArrayEquals(md_1, md_2);
}
执行结果
9fd5f598eca3f4422c88a622b81e5e82
除了MD、SHA和MAC这三大主流消息摘要算法外,还有一些我们不了解的消息摘要算法,包括RipeMD列、Tiger、Whirlpool和GOST3411算法。
RipeMD系列算法和MAC系列算法相结合,又产生了HmacRipeMD128和HmacRipeMD160两种算法。
Bouncy Castle对RipeMD系列算法和HmacRipeMD系列算法进行了实现。
CRC(Cyclic Redundancy Check,循环冗余校验)是可以根据数据产生简短固定位数的一种散列函数,主要用来检测或校验数据传输/保存后出现的错误。生成的散列值在传输或储存之前计算出来并且附加到数据后面。在使用数据之前,对数据的完整性做校验。一般来说,循环冗余校验的值都是32位的二进制数,以8位十六进制字符串形式表示。它是一类重要的线性分组码,编码和解码方法简单,检错和纠错能力强,在通信领域广泛地用于实现差错控制。
消息摘要算法与CRC算法同属散列函数,并且CRC算法很可能就是消息摘要算法的前身。
在JDK中CRC算法是由java.util.zip.CRC32类实现的。
HMAC的一个典型应用是用在“挑战/响应”(Challenge/Response)身份认证中,认证流程如下:
(1)先由客户端向服务器发出一个验证请求。
(2)服务器接到此请求后生成一个随机数并通过网络传输给客户端(此为挑战)。
(3)客户端将收到的随机数提供给ePass,由ePass使用该随机数与存储在ePass中的密钥进行HMAC-MD5
运算并得到一个结果作为认证证据传给服务器(此为响应)。
(4)与此同时,服务器也使用该随机数与存储在服务器数据库中的该客户密钥进行HMAC-MD5
运算,如果服务器的运算结果与客户端传回的响应结果相同,则认为客户端是一个合法用户。
hmac
梁栋. Java加密与解密的艺术(第2版). 机械工业出版社. Kindle 版本.