/*
 *    Copyright 2012-2013 The Haohui Network Corporation
 */
package com.haohui.common.utils;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * <pre>
 * HeX 之后 46 位
 * hmac
  HMAC的原理和应用
  hmac的原理
  计算HMAC需要一个散列函数hash(可以是md5或者sha-1)和一个密钥key。用L表示hash函数输出字符串长(md5是16),用B表示数据块的长度(md5和sha-1的分割数据块长都是64)。密钥key的长度可以小于等于数据块长B,如果大于数据块长度,可以使用hash函数对key进行转换,结果就是一个L长的key。
  然后创建两个B长的不同字符串:
  innerpad = 长度为B的 0×36
  outterpad = 长度为B的 0×5C
  计算输入字符串str的HMAC:
  hash(key ^ outterpad, hash(key ^ innerpad, str))
  hmac的应用
  hmac主要应用在身份验证中,它的使用方法是这样的:
  1. 客户端发出登录请求(假设是浏览器的GET请求)
  2. 服务器返回一个随机值,并在会话中记录这个随机值
  3. 客户端将该随机值作为密钥,用户密码进行hmac运算,然后提交给服务器
  4. 服务器读取用户数据库中的用户密码和步骤2中发送的随机值做与客户端一样的hmac运算,然后与用户发送的结果比较,如果结果一致则验证用户合法
  在这个过程中,可能遭到安全攻击的是服务器发送的随机值和用户发送的hmac结果,而对于截获了这两个值的黑客而言这两个值是没有意义的,绝无获取用户密码的可能性,随机值的引入使hmac只在当前会话中有效,大大增强了安全性和实用性。大多数的语言都实现了hmac算法,比如php的mhash、python的hmac.py、java的MessageDigest类,在web验证中使用hmac也是可行的,用js进行md5运算的速度也是比较快的。
 * </pre>
 * 
 * @project baidamei
 * @author cevencheng <cevencheng@gmail.com>
 * @create 2012-11-14 下午5:38:38
 */
public class HMacMD5 {
	/**
	 * 计算参数的md5信息
	 * 
	 * @param str
	 *            待处理的字节数组
	 * @return md5摘要信息
	 * @throws NoSuchAlgorithmException
	 */
	private static byte[] md5(byte[] str) throws NoSuchAlgorithmException {
		MessageDigest md = MessageDigest.getInstance("MD5");
		md.update(str);
		return md.digest();
	}

	/**
	 * 将待加密数据data,通过密钥key,使用hmac-md5算法进行加密,然后返回加密结果。 参照rfc2104 HMAC算法介绍实现。
	 * 
	 * @author 尹星
	 * @param key
	 *            密钥
	 * @param data
	 *            待加密数据
	 * @return 加密结果
	 * @throws NoSuchAlgorithmException
	 */
	public static byte[] getHmacMd5Bytes(byte[] key, byte[] data) throws NoSuchAlgorithmException {
		/*
		 * HmacMd5 calculation formula: H(K XOR opad, H(K XOR ipad, text))
		 * HmacMd5 计算公式:H(K XOR opad, H(K XOR ipad, text))
		 * H代表hash算法,本类中使用MD5算法,K代表密钥,text代表要加密的数据 ipad为0x36,opad为0x5C。
		 */
		int length = 64;
		byte[] ipad = new byte[length];
		byte[] opad = new byte[length];
		for (int i = 0; i < 64; i++) {
			ipad[i] = 0x36;
			opad[i] = 0x5C;
		}
		byte[] actualKey = key; // Actual key.
		byte[] keyArr = new byte[length]; // Key bytes of 64 bytes length
		/*
		 * If key's length is longer than 64,then use hash to digest it and use
		 * the result as actual key. 如果密钥长度,大于64字节,就使用哈希算法,计算其摘要,作为真正的密钥。
		 */
		if (key.length > length) {
			actualKey = md5(key);
		}
		for (int i = 0; i < actualKey.length; i++) {
			keyArr[i] = actualKey[i];
		}

		/*
		 * append zeros to K 如果密钥长度不足64字节,就使用0x00补齐到64字节。
		 */
		if (actualKey.length < length) {
			for (int i = actualKey.length; i < keyArr.length; i++)
				keyArr[i] = 0x00;
		}

		/*
		 * calc K XOR ipad 使用密钥和ipad进行异或运算。
		 */
		byte[] kIpadXorResult = new byte[length];
		for (int i = 0; i < length; i++) {
			kIpadXorResult[i] = (byte) (keyArr[i] ^ ipad[i]);
		}

		/*
		 * append "text" to the end of "K XOR ipad" 将待加密数据追加到K XOR ipad计算结果后面。
		 */
		byte[] firstAppendResult = new byte[kIpadXorResult.length + data.length];
		for (int i = 0; i < kIpadXorResult.length; i++) {
			firstAppendResult[i] = kIpadXorResult[i];
		}
		for (int i = 0; i < data.length; i++) {
			firstAppendResult[i + keyArr.length] = data[i];
		}

		/*
		 * calc H(K XOR ipad, text) 使用哈希算法计算上面结果的摘要。
		 */
		byte[] firstHashResult = md5(firstAppendResult);

		/*
		 * calc K XOR opad 使用密钥和opad进行异或运算。
		 */
		byte[] kOpadXorResult = new byte[length];
		for (int i = 0; i < length; i++) {
			kOpadXorResult[i] = (byte) (keyArr[i] ^ opad[i]);
		}

		/*
		 * append "H(K XOR ipad, text)" to the end of "K XOR opad" 将H(K XOR
		 * ipad, text)结果追加到K XOR opad结果后面
		 */
		byte[] secondAppendResult = new byte[kOpadXorResult.length + firstHashResult.length];
		for (int i = 0; i < kOpadXorResult.length; i++) {
			secondAppendResult[i] = kOpadXorResult[i];
		}
		for (int i = 0; i < firstHashResult.length; i++) {
			secondAppendResult[i + keyArr.length] = firstHashResult[i];
		}

		/*
		 * H(K XOR opad, H(K XOR ipad, text)) 对上面的数据进行哈希运算。
		 */
		byte[] hmacMd5Bytes = md5(secondAppendResult);

		return hmacMd5Bytes;

	}
	
	public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
		byte[] macmd5 = HMacMD5.getHmacMd5Bytes("7e62ff29f6548e2e7cf5307315fb2b1".getBytes(), "".getBytes());
		System.out.println(Hex.byte2HexStr(macmd5));
		System.out.println(Hex.byte2HexStr("xxoo".getBytes()));
		System.out.println(Hex.byte2HexStr(HMacMD5.getHmacMd5Bytes("7e62ff29f6548e2e7cf5307315fb2b1".getBytes(), "xxoo".getBytes())));
	}
}
最近下载更多
mariopq  LV1 2022年1月13日
嘬一口  LV1 2019年9月12日
zw1991928  LV1 2019年8月13日
83277614  LV1 2019年3月4日
yangrd  LV1 2018年12月22日
小房子123  LV1 2018年11月15日
玖零定制问题修复  LV34 2018年7月12日
czmdym  LV2 2018年3月29日
nimeide  LV4 2018年1月9日
xiangqin  LV1 2017年10月20日
最近浏览更多
Eddie233  LV6 2024年8月26日
jack168  LV1 2022年10月13日
mariopq  LV1 2022年1月13日
小明9527  LV1 2020年7月20日
念久安 2020年6月18日
暂无贡献等级
水亦有情  LV2 2020年4月6日
SSong31  LV1 2019年12月16日
hillwell 2019年11月12日
暂无贡献等级
lxj  LV5 2019年11月12日
18101301716 2019年10月28日
暂无贡献等级
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友