Java 加密解密之讯息摘要算法(MD5 SHA MAC)

Java 加密解密之消息摘要算法(MD5 SHA MAC)

Java 加密解密之消息摘要算法(MD5 SHA MAC)
 
消息摘要

消息摘要(Message Digest)又称为数字摘要(Digital Digest)。它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生。如果消息在途中改变了,则接收者通过对收到消息的新产生的摘要与原摘要比较,就可知道消息是否被改变了。因此消息摘要保证了消息的完整性。消息摘要采用单向Hash 函数将需加密 的明文"摘要"成一串128bit的密文,这一串密文亦称为数字指纹(Finger Print),它有固定的长度,且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致 。这样这串摘要便可成为验证明文是否是"真身"的"指纹"了。
 
HASH函数的抗冲突性使得如果一段明文稍有变化,哪怕只更改该段落的一个字母,通过哈希算法作用后都将产生不同的值。而HASH算法的单向性使得要找到到哈希值相同的两个不
同的输入消息,在计算上是不可能的。所以数据的哈希值,即消息摘要,可以检验数据的完整性。哈希函数的这种对不同的输入能够生成不同的值的特性使得无法找到两个具有相同哈希值的输入。因此,如果两个文档经哈希转换后成为相同的值,就可以肯定它们是同一文档。
所以,当希望有效地比较两个数据块时,就可以比较它们的哈希值。例如,可以通过比较邮件发送前和发送后的哈希值来验证该邮件在传递时是否修改。 

 

消息摘要算法

消息摘要算法的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密,只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。消息摘要算法不存在 密钥的管理与分发问题,适合于分布式网络相同上使用。由于其加密计算的工作量相当可观,所以以前的这种算法通常只用于数据量有限的情况下的加密,例如计算机的口令就是 用不可逆加密算法加密的。近年来,随着计算机相同性能的飞速改善,加密速度不再成为限制这种加密技术发展的桎梏,因而消息摘要算法应用的领域不断增加。

 

消息摘要算法的特点:

① 无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。
② 消息摘要看起来是“随机的”。这些比特看上去是胡乱的杂凑在一起的。
③ 一般地,只要输入的消息不同,对其进行摘要以后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出。
④ 消息摘要函数是无陷门的单向函数,即只能进行正向的信息摘要,而无法从摘要中恢复出任何的消息,甚至根本就找不到任何与原信息相关的信息。
⑤ 好的摘要算法,无法找到两条消息,是它们的摘要相同。

 

现有的消息摘要算法

消息摘要算法包含MD、SHA和MAC三大系列,常用于验证数据的完整性,是数据签名算法的核心算法。 
MAC与MD和SHA不同,MAC是含有密钥的散列函数算法,我们也常把MAC称为HMAC。

 

JDK对消息摘要算法的支持

JDK6支持MD2/MD5/SHA/SHA256/SHA384/SHA512/HmacMD5/HmacSHA1/ HmacSHA256/HmacSHA384/HmacSHA512
 

使用到 十六进制工具类Hex.java  见:   java byte数组与十六进制字符串互转


MD和SHA系列的java实现:

DigestUtils.java

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * reference apache commons <a
 * href="http://commons.apache.org/codec/">http://commons.apache.org/codec/</a>
 * 
 * support MD2/MD5/SHA/SHA256/SHA384/SHA512
 * @author Aub
 * 
 */
public class DigestUtils {

	/**
	 * 根据给定摘要算法创建一个消息摘要实例
	 * 
	 * @param algorithm
	 *            摘要算法名
	 * @return 消息摘要实例
	 * @see MessageDigest#getInstance(String)
	 * @throws RuntimeException
	 *             当 {@link java.security.NoSuchAlgorithmException} 发生时
	 */
	static MessageDigest getDigest(String algorithm) {
		try {
			return MessageDigest.getInstance(algorithm);
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	/**
	 * 获取 MD5 消息摘要实例
	 * 
	 * @return MD5 消息摘要实例
	 * @throws RuntimeException
	 *             当 {@link java.security.NoSuchAlgorithmException} 发生时
	 */
	private static MessageDigest getMd5Digest() {
		return getDigest("MD5");
	}

	/**
	 * 获取 SHA-1 消息摘要实例
	 * 
	 * @return SHA-1 消息摘要实例
	 * @throws RuntimeException
	 *             当 {@link java.security.NoSuchAlgorithmException} 发生时
	 */
	private static MessageDigest getShaDigest() {
		return getDigest("SHA");
	}

	/**
	 * 获取 SHA-256 消息摘要实例
	 * 
	 * @return SHA-256 消息摘要实例
	 * @throws RuntimeException
	 *             当 {@link java.security.NoSuchAlgorithmException} 发生时
	 */
	private static MessageDigest getSha256Digest() {
		return getDigest("SHA-256");
	}

	/**
	 * 获取 SHA-384 消息摘要实例
	 * 
	 * @return SHA-384 消息摘要实例
	 * @throws RuntimeException
	 *             当 {@link java.security.NoSuchAlgorithmException} 发生时
	 */
	private static MessageDigest getSha384Digest() {
		return getDigest("SHA-384");
	}

	/**
	 * 获取 SHA-512 消息摘要实例
	 * 
	 * @return SHA-512 消息摘要实例
	 * @throws RuntimeException
	 *             当 {@link java.security.NoSuchAlgorithmException} 发生时
	 */
	private static MessageDigest getSha512Digest() {
		return getDigest("SHA-512");
	}

	/**
	 * 使用MD5消息摘要算法计算消息摘要
	 * 
	 * @param data
	 *            做消息摘要的数据
	 * @return 消息摘要(长度为16的字节数组)
	 */
	public static byte[] encodeMD5(byte[] data) {
		return getMd5Digest().digest(data);
	}

	/**
	 * 使用MD5消息摘要算法计算消息摘要
	 * 
	 * @param data
	 *            做消息摘要的数据
	 * @return 消息摘要(长度为32的十六进制字符串)
	 */
	public static String encodeMD5Hex(byte[] data) {
		return Hex.encodeHexStr(encodeMD5(data));
	}

	/**
	 * 使用SHA-1消息摘要算法计算消息摘要
	 * 
	 * @param data
	 *            做消息摘要的数据
	 * @return SHA-1消息摘要(长度为20的字节数组)
	 */
	public static byte[] encodeSHA(byte[] data) {
		return getShaDigest().digest(data);
	}

	/**
	 * 使用SHA-1消息摘要算法计算消息摘要
	 * 
	 * @param data
	 *            做消息摘要的数据
	 * @return SHA-1消息摘要(长度为40的十六进制字符串)
	 */
	public static String encodeSHAHex(byte[] data) {
		return Hex.encodeHexStr(getShaDigest().digest(data));
	}

	/**
	 * 使用SHA-256消息摘要算法计算消息摘要
	 * 
	 * @param data
	 *            做消息摘要的数据
	 * @return SHA-256消息摘要(长度为32的字节数组)
	 */
	public static byte[] encodeSHA256(byte[] data) {
		return getSha256Digest().digest(data);
	}

	/**
	 * 使用SHA-256消息摘要算法计算消息摘要
	 * 
	 * @param data
	 *            做消息摘要的数据
	 * @return SHA-256消息摘要(长度为64的十六进制字符串)
	 */
	public static String encodeSHA256Hex(byte[] data) {
		return Hex.encodeHexStr(encodeSHA256(data));
	}

	/**
	 * 使用SHA-384消息摘要算法计算消息摘要
	 * 
	 * @param data
	 *            做消息摘要的数据
	 * @return SHA-384消息摘要(长度为43的字节数组)
	 */
	public static byte[] encodeSHA384(byte[] data) {
		return getSha384Digest().digest(data);
	}

	/**
	 * 使用SHA-384消息摘要算法计算消息摘要
	 * 
	 * @param data
	 *            做消息摘要的数据
	 * @return SHA-384消息摘要(长度为86的十六进制字符串)
	 */
	public static String encodeSHA384Hex(byte[] data) {
		return Hex.encodeHexStr(encodeSHA384(data));
	}

	/**
	 * 使用SHA-512消息摘要算法计算消息摘要
	 * 
	 * @param data
	 *            做消息摘要的数据
	 * @return SHA-512消息摘要(长度为64的字节数组)
	 */
	public static byte[] encodeSHA512(byte[] data) {
		return getSha512Digest().digest(data);
	}

	/**
	 * 使用SHA-512消息摘要算法计算消息摘要
	 * 
	 * @param data
	 *            做消息摘要的数据
	 * @return SHA-512消息摘要(长度为128的十六进制字符串)
	 */
	public static String encodeSHA512Hex(byte[] data) {
		return Hex.encodeHexStr(encodeSHA512(data));
	}

}

 

参考 org.apache.commons.codec.digest.DigestUtils
下载地址:
http://commons.apache.org/codec/download_codec.cgi

 


MAC系列的java实现

Hmac.java

import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Hmac<br/>
 * algorithm HmacMD5/HmacSHA/HmacSHA256/HmacSHA384/HmacSHA512
 * @author Aub
 */
public class Hmac {
	
	/**
	 * 根据给定密钥生成算法创建密钥
	 * 
	 * @param algorithm
	 *            密钥算法
	 * @return 密钥
	 * @throws RuntimeException
	 *             当 {@link java.security.NoSuchAlgorithmException} 发生时
	 */
	private static byte[] getHmacKey(String algorithm){
		//初始化KeyGenerator
		KeyGenerator keyGenerator = null;
		try {
			keyGenerator = KeyGenerator.getInstance(algorithm);
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e.getMessage());
		}
		//产生密钥
		SecretKey secretKey = keyGenerator.generateKey();
		//获得密钥
		return secretKey.getEncoded();
	}
	
	/**
	 * 获取 HmaMD5的密钥
	 * 
	 * @return  HmaMD5的密钥
	 * @throws RuntimeException
	 *             当 {@link java.security.NoSuchAlgorithmException} 发生时
	 */
	public static byte[] getHmaMD5key(){
		return getHmacKey("HmacMD5");
	}
	
	/**
	 * 获取 HmaSHA的密钥
	 * 
	 * @return  HmaSHA的密钥
	 * @throws RuntimeException
	 *             当 {@link java.security.NoSuchAlgorithmException} 发生时
	 */
	public static byte[] getHmaSHAkey(){
		return getHmacKey("HmacSHA1");
	}
	
	/**
	 * 获取 HmaSHA256的密钥
	 * 
	 * @return  HmaSHA256的密钥
	 * @throws RuntimeException
	 *             当 {@link java.security.NoSuchAlgorithmException} 发生时
	 */
	public static byte[] getHmaSHA256key(){
		return getHmacKey("HmacSHA256");
	}
	
	/**
	 * 获取 HmaSHA384的密钥
	 * 
	 * @return  HmaSHA384的密钥
	 * @throws RuntimeException
	 *             当 {@link java.security.NoSuchAlgorithmException} 发生时
	 */
	public static byte[] getHmaSHA384key(){
		return getHmacKey("HmacSHA384");
	}
	
	/**
	 * 获取 HmaSHA512的密钥
	 * 
	 * @return  HmaSHA384的密钥
	 * @throws RuntimeException
	 *             当 {@link java.security.NoSuchAlgorithmException} 发生时
	 */
	public static byte[] getHmaSHA512key(){
		return getHmacKey("HmacSHA512");
	}
	
	/**
	 * 转换密钥
	 * 
	 * @param key	二进制密钥
	 * @param algorithm	密钥算法
	 * @return 密钥
	 */
	private static Key toKey(byte[] key,String algorithm){
		//生成密钥
		return new SecretKeySpec(key, algorithm);
	}
	
	/**
	 * 使用HmacMD5消息摘要算法计算消息摘要
	 * 
	 * @param data 做消息摘要的数据
	 * @param key 密钥
	 * @return 消息摘要(长度为16的字节数组)
	 */
	public static byte[] encodeHmacMD5(byte[] data, Key key){
		Mac mac = null;
		try {
			mac = Mac.getInstance("HmacMD5");
			mac.init(key);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return new byte[0];
		}catch (InvalidKeyException e) {
			e.printStackTrace();
			return new byte[0];
		}
		return mac.doFinal(data);
	}
	
	/**
	 * 使用HmacMD5消息摘要算法计算消息摘要
	 * 
	 * @param data 做消息摘要的数据
	 * @param key 密钥
	 * @return 消息摘要(长度为16的字节数组)
	 */
	public static byte[] encodeHmacMD5(byte[] data, byte[] key){
		Key k = toKey(key, "HmacMD5");
		return encodeHmacMD5(data, k);
	}
	
	/**
	 * 使用HmacSHA消息摘要算法计算消息摘要
	 * 
	 * @param data 做消息摘要的数据
	 * @param key 密钥
	 * @return 消息摘要(长度为16的字节数组)
	 */
	public static byte[] encodeHmacSHA(byte[] data, Key key){
		Mac mac = null;
		try {
			mac = Mac.getInstance("HmacSHA1");
			mac.init(key);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return new byte[0];
		}catch (InvalidKeyException e) {
			e.printStackTrace();
			return new byte[0];
		}
		return mac.doFinal(data);
	}
	
	/**
	 * 使用HmacSHA消息摘要算法计算消息摘要
	 * 
	 * @param data 做消息摘要的数据
	 * @param key 密钥
	 * @return 消息摘要(长度为16的字节数组)
	 */
	public static byte[] encodeHmacSHA(byte[] data, byte[] key){
		Key k = toKey(key, "HmacSHA1");
		return encodeHmacSHA(data, k);
	}
	
	/**
	 * 使用HmacSHA256消息摘要算法计算消息摘要
	 * 
	 * @param data 做消息摘要的数据
	 * @param key 密钥
	 * @return 消息摘要(长度为16的字节数组)
	 */
	public static byte[] encodeHmacSHA256(byte[] data, Key key){
		Mac mac = null;
		try {
			mac = Mac.getInstance("HmacSHA256");
			mac.init(key);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return new byte[0];
		}catch (InvalidKeyException e) {
			e.printStackTrace();
			return new byte[0];
		}
		return mac.doFinal(data);
	}
	
	/**
	 * 使用HmacSHA256消息摘要算法计算消息摘要
	 * 
	 * @param data 做消息摘要的数据
	 * @param key 密钥
	 * @return 消息摘要(长度为16的字节数组)
	 */
	public static byte[] encodeHmacSHA256(byte[] data, byte[] key){
		Key k = toKey(key, "HmacSHA256");
		return encodeHmacSHA256(data, k);
	}
	
	
	/**
	 * 使用HmacSHA384消息摘要算法计算消息摘要
	 * 
	 * @param data 做消息摘要的数据
	 * @param key 密钥
	 * @return 消息摘要(长度为16的字节数组)
	 */
	public static byte[] encodeHmacSHA384(byte[] data, Key key){
		Mac mac = null;
		try {
			mac = Mac.getInstance("HmacSHA384");
			mac.init(key);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return new byte[0];
		}catch (InvalidKeyException e) {
			e.printStackTrace();
			return new byte[0];
		}
		return mac.doFinal(data);
	}
	
	/**
	 * 使用HmacSHA384消息摘要算法计算消息摘要
	 * 
	 * @param data 做消息摘要的数据
	 * @param key 密钥
	 * @return 消息摘要(长度为16的字节数组)
	 */
	public static byte[] encodeHmacSHA384(byte[] data, byte[] key){
		Key k = toKey(key, "HmacSHA384");
		return encodeHmacSHA384(data, k);
	}
	
	
	
	/**
	 * 使用HmacSHA512消息摘要算法计算消息摘要
	 * 
	 * @param data 做消息摘要的数据
	 * @param key 密钥
	 * @return 消息摘要(长度为16的字节数组)
	 */
	public static byte[] encodeHmacSHA512(byte[] data, Key key){
		Mac mac = null;
		try {
			mac = Mac.getInstance("HmacSHA512");
			mac.init(key);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return new byte[0];
		}catch (InvalidKeyException e) {
			e.printStackTrace();
			return new byte[0];
		}
		return mac.doFinal(data);
	}
	
	/**
	 * 使用HmacSHA512消息摘要算法计算消息摘要
	 * 
	 * @param data 做消息摘要的数据
	 * @param key 密钥
	 * @return 消息摘要(长度为16的字节数组)
	 */
	public static byte[] encodeHmacSHA512(byte[] data, byte[] key){
		Key k = toKey(key, "HmacSHA512");
		return encodeHmacSHA512(data, k);
	}
	
	
	private static String  showByteArray(byte[] data){
		if(null == data){
			return null;
		}
		StringBuilder sb = new StringBuilder("{");
		for(byte b:data){
			sb.append(b).append(",");
		}
		sb.deleteCharAt(sb.length()-1);
		sb.append("}");
		return sb.toString();
	}
	
	public static void main(String[] args) {
//		byte[] key = getHmaMD5key();
//		byte[] key = getHmaSHAkey();
//		byte[] key = getHmaSHA256key();
//		byte[] key = getHmaSHA384key();
		byte[] key = getHmaSHA512key();
		
		
		System.out.println("加密密钥: byte[]:"+showByteArray(key).length());
		
		String data = "Mac数据";
		System.out.println("加密前数据: string:"+data);
		System.out.println("加密前数据: byte[]:"+showByteArray(data.getBytes()));
		System.out.println();
//		byte[] encodeData = encodeHmacMD5(data.getBytes(), key);
//		byte[] encodeData = encodeHmacSHA(data.getBytes(), key);
//		byte[] encodeData = encodeHmacSHA256(data.getBytes(), key);
//		byte[] encodeData = encodeHmacSHA384(data.getBytes(), key);
		byte[] encodeData = encodeHmacSHA512(data.getBytes(), key);
		System.out.println("加密后数据: byte[]:"+showByteArray(encodeData).length());
		System.out.println("加密后数据: byte[]:"+encodeData.length);
		System.out.println("加密后数据: hexStr:"+Hex.encodeHexStr(encodeData));
		System.out.println();
	}
}