消息摘要算法包含MD、SHA和MAC共3大系列,常用于验证数据的完整性,是数字签名算法的核心算法。数字签名算法常用函数为散列函数,用于数据完整性校验。任何消息经过散列函数处理后,都会获得唯一的散列值。这一过程称为“消息摘要”, 其散列值成为“数字指纹”,自然其算法就是“消息摘要算法”了。如果其数字指纹唯一,就说明其消息是一致的。
消息摘要算法一直是非对称加密算法中一项举足轻重的关键算法。
消息摘要算法主要分为三大类:MD(Message Digest, 消息摘要算法)、SHA(Secure Hash Algorithm,安全散列算法)和MAC(Message Authentication Code,消息认证码算法)。
MD系列算法包括MD2、MD4和MD5共三种算法,SHA算法主要包括其代表算法SHA-1和SHA-1算法变种SHA-2系列算法(包括SHA-224、SHA-256、SHA-384和SHA-512),MAC算法综合了上述两种算法,主要包括HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384和HmacSHA512算法。
后来又衍生出了RipeMD系列(包含RipeMD128、RipeMD160、RipeMD256、RipeMD320)、Tiger、GOST3411和Whirlpool算法。
MD5产生128位二进制摘要信息,换算成32位十六进制字符串
在通用的登录模型中,我们保存在数据库中的密码通常是密文,即通过MD5运算过的值,登录验证时,将密码做MD5运算,然后与数据库保存的值做比较。这里的明文与密文存在对应关系,但仅限于明文到密文的转换,即便是消息摘要算法泄漏也不能反推密码,增强了系统安全性。
为了进一步加强安全性,往往将一些其他的不变信息,如用户名、Email地址串入原始密码中,使得密码破译难度加大。其中,不变信息称为“盐”, 而这种处理方式称为“加盐处理”。实际是对原始信息的一堆混淆。PBE中也采用了中做法。
MD实现细节如下面表格所示
算法 | 摘要长度 | 备注 |
---|---|---|
MD2 | 128 | Bouncy Castle |
MD5 | 128 | Java6 |
MD4 | 128 | Bouncy Castle |
package com.calvin.android.demo2.secrity;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
import java.security.MessageDigest;
import java.security.Security;
/**
* Author:cl
* Email:[email protected]
* Date:20-10-27
*/
public class MDCoder {
/**
* MD2消息摘要
* @param data 待做摘要处理的数据
* @return byte[] 消息摘要
* @throws Exception 异常
*/
public static byte[] encodeMD2(byte[] data) throws Exception {
Security.removeProvider("BC");
Security.addProvider(new BouncyCastleProvider());
//初始化MessageDigest
MessageDigest md = MessageDigest.getInstance("MD2");
//执行消息摘要
return md.digest(data);
}
/**
* MD5消息摘要
* @param data 待做摘要处理的数据
* @return byte[] 消息摘要
* @throws Exception 异常
*/
public static byte[] encodeMD5(byte[] data) throws Exception {
//初始化MessageDigest
MessageDigest md = MessageDigest.getInstance("MD5");
//执行消息摘要
return md.digest(data);
}
/**
* MD4消息摘要
* @param data 待做摘要处理的数据
* @return byte[] 消息摘要
* @throws Exception 异常
*/
public static byte[] encodeMD4(byte[] data) throws Exception {
Security.removeProvider("BC");
Security.addProvider(new BouncyCastleProvider());
//初始化MessageDigest
MessageDigest md = MessageDigest.getInstance("MD4");
//执行消息摘要
return md.digest(data);
}
public static String encodeMD4Hex(byte[] data) throws Exception {
//执行消息摘要
byte[] b = encodeMD4(data);
return new String(Hex.encode(b));
}
}
/**
* 测试MD2
* @throws Exception
*/
@Test
public void testEncodeMD2() throws Exception {
String str = "MD2消息摘要";
byte[] data1 = MDCoder.encodeMD2(str.getBytes());
byte[] data2 = MDCoder.encodeMD2(str.getBytes());
assertArrayEquals(data1, data2);
}
/**
* 测试MD5
* @throws Exception
*/
@Test
public void testEncodeMD5() throws Exception {
String str = "MD5消息摘要";
byte[] data1 = MDCoder.encodeMD5(str.getBytes());
byte[] data2 = MDCoder.encodeMD5(str.getBytes());
assertArrayEquals(data1, data2);
}
/**
* 测试MD4
* @throws Exception
*/
@Test
public void testEncodeMD4() throws Exception {
String str = "MD4消息摘要";
byte[] data1 = MDCoder.encodeMD4(str.getBytes());
byte[] data2 = MDCoder.encodeMD4(str.getBytes());
assertArrayEquals(data1, data2);
}
SHA算法基于MD4算法,摘要长度更长,安全性更高,是MD5算法继任者。SHA算法家族目前共有SHA1、SHA-224、SHA-256、SHA384和SHA512五种算法,后四种算法并称SHA-2算法。
Java及Bouncy Castle实现细节如下
算法 | 摘要长度 | 备注 |
---|---|---|
SHA-1 | 160 | Java 6 |
SHA-256 | 256 | Java 6 |
SHA-384 | 384 | Java 6 |
SHA-512 | 512 | Java 6 |
SHA-224 | 224 | Bouncy Castle |
package com.calvin.android.demo2.secrity;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
import java.security.MessageDigest;
import java.security.Security;
/**
* Author:cl
* Email:[email protected]
* Date:20-10-27
*/
public class ShaCoder {
public static byte[] encodeSHA(byte[] data) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA");
return md.digest(data);
}
public static byte[] encodeSHA256(byte[] data) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
return md.digest(data);
}
public static byte[] encodeSHA384(byte[] data) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-384");
return md.digest(data);
}
public static byte[] encodeSHA512(byte[] data) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-512");
return md.digest(data);
}
public static byte[] encodeSHA224(byte[] data) throws Exception {
//加入Bouncy Castle Provider支持
Security.removeProvider("BC");
Security.addProvider(new BouncyCastleProvider());
//初始化MessageDigest
MessageDigest md = MessageDigest.getInstance("SHA-224");
//执行消息摘要
return md.digest(data);
}
public static String encodeSHA224Hex(byte[] data) throws Exception {
//执行消息摘要
byte[] b = encodeSHA224(data);
//做十六进制编码处理
return new String(Hex.encode(b));
}
}
/**
* 测试SHA-1
* @throws Exception
*/
@Test
public void testEncodeSHA() throws Exception {
String str = "SHA1消息摘要";
//获得摘要信息
byte[] data1 = ShaCoder.encodeSHA(str.getBytes());
byte[] data2 = ShaCoder.encodeSHA(str.getBytes());
//校验
assertArrayEquals(data1, data2);
}
/**
* 测试SHA-256
* @throws Exception
*/
@Test
public void testEncodeSHA256() throws Exception {
String str = "SHA256消息摘要";
//获得摘要信息
byte[] data1 = ShaCoder.encodeSHA256(str.getBytes());
byte[] data2 = ShaCoder.encodeSHA256(str.getBytes());
//校验
assertArrayEquals(data1, data2);
}
/**
* 测试SHA-384
* @throws Exception
*/
@Test
public void testEncodeSHA384() throws Exception {
String str = "SHA384消息摘要";
//获得摘要信息
byte[] data1 = ShaCoder.encodeSHA384(str.getBytes());
byte[] data2 = ShaCoder.encodeSHA384(str.getBytes());
//校验
assertArrayEquals(data1, data2);
}
/**
* 测试SHA-512
* @throws Exception
*/
@Test
public void testEncodeSHA512() throws Exception {
String str = "SHA512消息摘要";
//获得摘要信息
byte[] data1 = ShaCoder.encodeSHA512(str.getBytes());
byte[] data2 = ShaCoder.encodeSHA512(str.getBytes());
//校验
assertArrayEquals(data1, data2);
}
@Test
public void testEncodeSHA224() throws Exception {
String str = "SHA224消息摘要";
//获得摘要信息
byte[] data1 = ShaCoder.encodeSHA224(str.getBytes());
byte[] data2 = ShaCoder.encodeSHA224(str.getBytes());
//校验
assertArrayEquals(data1, data2);
}
@Test
public void testEncodeSHA224Hex() throws Exception {
String str = "SHA224消息摘要";
//获得摘要信息
String data1 = ShaCoder.encodeSHA224Hex(str.getBytes());
String data2 = ShaCoder.encodeSHA224Hex(str.getBytes());
//校验
assertEquals(data1, data2);
}
DigestUtils类除了MD5算法外,还支持多种SHA系列算法,Commons Codec与Sun所提供的SHA算法本质毫无差别,关键在于Commons Codec提供了更为方便的实现。
public static byte[] encodeSHA(String data) throws Exception {
return DigestUtils.sha1(data);
}
public static String encodeSHAHex(String data) throws Exception {
return DigestUtils.sha1Hex(data);
}
MAC算法结合了MD5和SHA算法优势,并加入密钥支持,是一种更为安全的消息摘要算法。
MAC(Message Authentication Code, 消息认证码算法)是含有密钥散列函数算法,兼容了MD和SHA算法特性, 并在此基础上加入了密钥。因此,我们也常把MAC称为HMAC(keyed-Hash Message Authentication Code)。
MAC算法主要集合了MD和SHA两大系列消息摘要算法。MD系列算法有HmacMD2、HmacMD4和HmacMD5三种算法; SHA系列算法有HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384和HmacSHA512五种算法。
经过MAC算法的摘要值长度与参与实现的算法摘要值长度相同。
实现细节如下所示
算法 | 摘要长度 | 备注 |
---|---|---|
HmacMD5 | 128 | Java |
HmacSHA1 | 160 | Java |
HmacSHA256 | 256 | Java |
HmacSHA384 | 384 | Java |
HmacSHA512 | 512 | Java |
HmacMD2 | 128 | Bouncy Castle |
HmacMD4 | 128 | Bouncy Castle |
HmacSHA224 | 224 | Bouncy Castle |
package com.calvin.android.demo2.secrity;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* Author:cl
* Email:[email protected]
* Date:20-10-27
*/
public class MACCoder {
public static byte[] initHmacMD5Key() throws Exception {
//初始化KeyGenerator
KeyGenerator kg = KeyGenerator.getInstance("HmacMD5");
//产生密钥
SecretKey secretKey = kg.generateKey();
//获得密钥
return secretKey.getEncoded();
}
public static byte[] encodeHmacMD5(byte[] data, byte[] key) throws Exception{
//还原密钥
SecretKey secretKey = new SecretKeySpec(key, "HmacMD5");
//实例化Mac
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
//初始化Mac
mac.init(secretKey);
//执行消息摘要
return mac.doFinal(data);
}
/**
* 初始化 HmacSHA1 密钥
* @return byte[] 密钥
* @throws Exception
*/
public static byte[] initHmacSHAKey() throws Exception {
//初始化KeyGenerator
KeyGenerator kg = KeyGenerator.getInstance("HmacSHA1");
//产生密钥
SecretKey secretKey = kg.generateKey();
//获得密钥
return secretKey.getEncoded();
}
/**
* HmacSHA1 消息摘要
* @param data 待做摘要处理的数据
* @param key 密钥
* @return byte[] 消息摘要
* @throws Exception
*/
public static byte[] encodeHmacSHA(byte[] data, byte[] key) throws Exception{
//还原密钥
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA1");
//实例化Mac
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
//初始化Mac
mac.init(secretKey);
//执行消息摘要
return mac.doFinal(data);
}
/**
* 初始化 HmacSHA256 密钥
* @return byte[] 密钥
* @throws Exception
*/
public static byte[] initHmacSHA256Key() throws Exception {
//初始化KeyGenerator
KeyGenerator kg = KeyGenerator.getInstance("HmacSHA256");
//产生密钥
SecretKey secretKey = kg.generateKey();
//获得密钥
return secretKey.getEncoded();
}
/**
* HmacSHA256 消息摘要
* @param data 待做摘要处理的数据
* @param key 密钥
* @return byte[] 消息摘要
* @throws Exception
*/
public static byte[] encodeHmacSHA256(byte[] data, byte[] key) throws Exception{
//还原密钥
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA256");
//实例化Mac
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
//初始化Mac
mac.init(secretKey);
//执行消息摘要
return mac.doFinal(data);
}
/**
* 初始化 HmacSHA384 密钥
* @return byte[] 密钥
* @throws Exception
*/
public static byte[] initHmacSHA384Key() throws Exception {
//初始化KeyGenerator
KeyGenerator kg = KeyGenerator.getInstance("HmacSHA384");
//产生密钥
SecretKey secretKey = kg.generateKey();
//获得密钥
return secretKey.getEncoded();
}
/**
* HmacSHA384 消息摘要
* @param data 待做摘要处理的数据
* @param key 密钥
* @return byte[] 消息摘要
* @throws Exception
*/
public static byte[] encodeHmacSHA384(byte[] data, byte[] key) throws Exception{
//还原密钥
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA384");
//实例化Mac
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
//初始化Mac
mac.init(secretKey);
//执行消息摘要
return mac.doFinal(data);
}
}
@Test
public void testEncodeHmacMD5() throws Exception {
String str = "HmacMD5消息摘要";
//初始化密钥
byte[] key = MACCoder.initHmacMD5Key();
//获得摘要消息
byte[] data1 = MACCoder.encodeHmacMD5(str.getBytes(), key);
byte[] data2 = MACCoder.encodeHmacMD5(str.getBytes(), key);
//校验
assertArrayEquals(data1, data2);
}
@Test
public void testEncodeHmacSHA() throws Exception {
String str = "HmacSHA消息摘要";
//初始化密钥
byte[] key = MACCoder.initHmacSHAKey();
//获得摘要消息
byte[] data1 = MACCoder.encodeHmacSHA(str.getBytes(), key);
byte[] data2 = MACCoder.encodeHmacSHA(str.getBytes(), key);
//校验
assertArrayEquals(data1, data2);
}