赞
踩
在之前的文章中(哈希算法及加密实现)提到加盐可以防止彩虹表攻击,本章将使用更好的方法来防止彩虹表攻击。
Hmac算法是一种基于密钥的消息认证码算法,它的全称是Hash-based Message Authentication Code,是一种更安全的消息摘要算法。
Hmac算法总是和某种哈希算法配合起来用。例如,我们使用MD5算法,对应的就是Hmac MD5算法,它相当于”加盐“的MD5: HmacMD5≈md5(secure_random_key,input)
因此,HmacMD5可以看作带有一个安全的key的MD5.使用HmacMD5而不是用MD5加salt,有如下好处:
import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import javax.crypto.KeyGenerator; import javax.crypto.Mac; import javax.crypto.SecretKey; public class Demo08 { public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException { // 产生密钥 // 获取HmacMD5密钥生成器 KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5"); // 生成密钥 SecretKey key = keyGen.generateKey(); System.out.println("密钥" + Arrays.toString(key.getEncoded())); System.out.println("密钥长度(64字节)" + key.getEncoded().length); System.out.println("密钥:" + HashTool.byteToHex(key.getEncoded())); // 使用密钥进行加密 // 获取HMac加密算法对象 Mac mac = Mac.getInstance("HmacMD5"); // 初始化密钥 mac.init(key); // 更新原始加密内容 mac.update("wbjxxmy".getBytes()); // 加密处理,并获取加密结果 byte[] doFinal = mac.doFinal(); // 加密结果处理成十六进制字符串 String ret = HashTool.byteToHex(doFinal); System.out.println("加密结果:" + ret); System.out.println("字节长度:" + doFinal.length); System.out.println("字符长度:" + ret.length()); } }
执行结果如图(图1):
与MD5相比,使用HmacMD5的步骤是:
有了Hmac计算的哈希和SecretKey,我们想要验证的话,该怎么办?这时,SecretKey不能从KeyGenerator生成,而是以下的方式恢复。
例如我的密码为wbjxxmy,由加密后转化成密钥的字节数组为[5, -11, 36, -15, -59, -53, -25, -18, 7, -10, -50, 3, -86, 16, 95, 93, -32, 5, 5, -2, -39, 91, 27, -117, 92, -17, -99, 49, -3, 51, 23, -58, 112, -95, 49, 9, -109, -5, 106, 41, -22, -14, 32, 10, 127, -87, 36, 116, -112, -112, -49, -77, 44, -18, -65, -91, 121, 38, -91, 41, 118, -111, 50, 69]
若要将字节数组恢复,可以调用SecretKey key = new SecretKeySpec(字节数组, 加密类型)构造方法恢复成key。代码如下:
import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; public class Demo09 { public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException { String password = "wbjxxmy"; byte[] miyao = new byte[] { 5, -11, 36, -15, -59, -53, -25, -18, 7, -10, -50, 3, -86, 16, 95, 93, -32, 5, 5, -2, -39, 91, 27, -117, 92, -17, -99, 49, -3, 51, 23, -58, 112, -95, 49, 9, -109, -5, 106, 41, -22, -14, 32, 10, 127, -87, 36, 116, -112, -112, -49, -77, 44, -18, -65, -91, 121, 38, -91, 41, 118, -111, 50, 69 }; // 恢复密钥(字节数组) SecretKey key = new SecretKeySpec(miyao, "HmacMD5"); // 床架你Hmac加密算法对象 Mac mac = Mac.getInstance("HmacMD5"); mac.init(key); mac.update(password.getBytes()); System.out.println(HashTool.byteToHex("加密结果:"+mac.doFinal())); } }
运行结果如图(图2):
图2加密结果的内容与图1加密结果的内容相同,所以验证成功。
若给定的密钥是一串十六进制的字符串,该如何验证呢?
道理很简单,我们可以先将十六进制的字符串转换成字节数组,再用以上的方法恢复key,就可以完成验证。代码如下:
import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; public class Demo10 { public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException { //将字符串还原成密钥字节数组 String s = "05f524f1c5cbe7ee07f6ce03aa105f5de00505fed95b1b8b5cef9d31fd3317c670a1310993fb6a29eaf2200a7fa924749090cfb32ceebfa57926a52976913245"; byte[] keyBytes = new byte[64]; for (int i = 0, k = 0; i < s.length(); i += 2, k++) { keyBytes[k] = (byte) Integer.parseInt(s.substring(i, i + 2), 16); } SecretKey key = new SecretKeySpec(keyBytes, "HmacMD5"); Mac mac = Mac.getInstance("HmacMD5"); mac.init(key); mac.update("wbjxxmy".getBytes()); // 7e0ed3f9ca30fae85273d86515fff8f1 System.out.println(HashTool.byteToHex(mac.doFinal())); } }
我通过将字符串两个两个截取,再通过Integer工具类的parseInt方法以十六进制的方式转换为int,再强制转换成byte类型,放入KeyBytes字节数组中,恢复成原来的字节数组,再通过之前的方法,得到恢复的密钥。
效果如图(图3):
图3恢复的加密结果与图1中加密的结果相同,所以验证通过。
以上就是通过用Hmac算法配合MD5的方式对于密码的加密,Hmac算法是一种标准的基于密钥的哈希算法,可以配合MD5,SHA-1等哈希算法,计算的摘要长度和原摘要算法长度相同。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。