前言
在之前的文章中(哈希算法及加密实现)提到加盐可以防止彩虹表攻击,本章将使用更好的方法来防止彩虹表攻击。
一、Hmac算法
Hmac算法是一种基于密钥的消息认证码算法,它的全称是Hash-based Message Authentication Code,是一种更安全的消息摘要算法。
Hmac算法总是和某种哈希算法配合起来用。例如,我们使用MD5算法,对应的就是Hmac MD5算法,它相当于”加盐“的MD5: HmacMD5≈md5(secure_random_key,input)
因此,HmacMD5可以看作带有一个安全的key的MD5.使用HmacMD5而不是用MD5加salt,有如下好处:
- HmacMD5使用的key长度是64个字节,更安全;
- Hmac是标准算法,同样适用于SHA-1等其他哈希算法;
- Hmac输出和原有的哈希算法长度一致。
二、使用HmacMD5
1.参考代码
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):
2.使用步骤
与MD5相比,使用HmacMD5的步骤是:
- 通过HmacMD5获取KeyGenerator实例keyGen;
- 通过KeyGenerator创建一个SecretKey实例key;
- 通过HmacMD5获取Mac实例mac;
- 用SecretKey初始化Mac实例
- 对Mac实例反复调用update(byte[])方法输入数据
- 调用Mac实例的doFinal()方法获取最终的哈希值。
3.验证
有了Hmac计算的哈希和SecretKey,我们想要验证的话,该怎么办?这时,SecretKey不能从KeyGenerator生成,而是以下的方式恢复。
3.1 字节数组验证
例如我的密码为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加密结果的内容相同,所以验证成功。
3.2 十六进制字符串的验证
若给定的密钥是一串十六进制的字符串,该如何验证呢?
道理很简单,我们可以先将十六进制的字符串转换成字节数组,再用以上的方法恢复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等哈希算法,计算的摘要长度和原摘要算法长度相同。