# 摘要及认证码算法
摘要及认证码算法
# 1. 摘要算法(Digester)
摘要算法是一种能产生特殊输出格式的算法,这种算法的特点是:无论用户输入什么长度的原始数据,经过计算后输出的密文都是固定长度
这种算法的原理是根据一定的运算规则对原数据进行某种形式的提取,这种提取就是摘要,被摘要的数据内容与原数据有密切联系,只要原数据稍有改变,输出的“摘要”便完全不同,因此,基于这种原理的算法便能对数据完整性提供较为健全的保障
但是,由于输出的密文是提取原数据经过处理的定长值,所以它已经不能还原为原数据,即消息摘要算法是不可逆的,理论上无法通过反向运算取得原数据内容,因此它通常只能被用来做数据完整性验证
- MD2
- MD5
- SHA-1
- SHA-256
- SHA-384
- SHA-512
- SM3
示例
public static void main(String[] args) {
String content = "{Digest}测试一下‘’";
System.out.println(DigestUtil.md5Hex(content));
System.out.println(DigestUtil.md5Hex(content));
System.out.println(DigestUtil.md5Hex16(content));
System.out.println(DigestUtil.md5Hex16(content));
System.out.println(DigestUtil.sha1Hex(content));
System.out.println(DigestUtil.sha1Hex(content));
System.out.println(DigestUtil.sha256Hex(content));
System.out.println(DigestUtil.sha256Hex(content));
}
输出
592964b71679e531c4f852d488ab5013
592964b71679e531c4f852d488ab5013
1679e531c4f852d4
1679e531c4f852d4
436ebe0e5c5d9a53599b03908e8a6f141ac8294d
436ebe0e5c5d9a53599b03908e8a6f141ac8294d
262708353e288309993a5fab3024ecafebf8df30e8a069092f85d91753b8621e
262708353e288309993a5fab3024ecafebf8df30e8a069092f85d91753b8621e
# 2. 认证码算法(HMac)
HMAC,全称为“Hash Message Authentication Code”,中文名“散列消息鉴别码”,主要是利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出
一般消息鉴别码用于验证传输于两个共同享有一个密钥的单位之间的消息。HMAC 可以与任何迭代散列函数捆绑使用。MD5 和 SHA-1 就是这种散列函数。HMAC 还可以使用一个用于计算和确认消息鉴别值的密钥
- HmacMD5
- HmacSHA1
- HmacSHA256
- HmacSHA384
- HmacSHA512
- HmacSM3
对比摘要算法,简单来说就是加了一个密钥,安全性更佳,摘要算法可能存在一种这样的情况,将密钥放在内容中使用摘要算法加密,如下将数据按一行排列,最后加上密钥
- MD5: appCode=00000&......&appKey=Esfrgggw74dfa8dddd
这种虽然也使原有的摘要算法更安全了一点,但是对比 Hmac 的处理,Hmac 是在字节进行处理,安全性更好
Hmac 工具类
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
/**
* HmacUtil
*
* @author wliduo[i@dolyw.com]
* @date 2022/7/12 13:54
*/
public class HmacUtil {
/**
* 数据类型-十六进制
*/
private static final String DATA_TYPE_HEX = "Hex";
/**
* 数据类型-Base64
*/
private static final String DATA_TYPE_BASE64 = "Base64";
/**
* 获取私钥
*
* @return
*/
public static String getKey() {
// 默认返回随机字符私钥
return RandomUtil.randomString("abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789",30);
}
/**
* 私钥签名
*
* @param content
* @return
*/
public static String sign(String algorithm, String content, String privateKeyStr, String encryptDataType) {
HMac mac = new HMac(algorithm, StrUtil.bytes(privateKeyStr, CharsetUtil.CHARSET_UTF_8));
// 签名
byte[] signed = mac.digest(content);
if (DATA_TYPE_BASE64.equals(encryptDataType)) {
// 将签名字节数组转为Base64字符返回
return Base64.encode(signed);
}
if (DATA_TYPE_HEX.equals(encryptDataType)) {
// 将签名字节数组转为十六进制字符返回
return HexUtil.encodeHexStr(signed);
}
return StrUtil.str(signed, CharsetUtil.CHARSET_UTF_8);
}
/**
* 私钥验证签名
*
* @param content
* @return
*/
public static String signVerify(String algorithm, String content, String signStr, String privateKeyStr, String encryptDataType) {
HMac mac = new HMac(algorithm, StrUtil.bytes(privateKeyStr, CharsetUtil.CHARSET_UTF_8));
// 原有签名
byte[] signed = null;
if (DATA_TYPE_BASE64.equals(encryptDataType)) {
// 将签名按Base64解析为字节数组
signed = Base64.decode(signStr);
}
if (DATA_TYPE_HEX.equals(encryptDataType)) {
// 将签名按十六进制解析为字节数组
signed = HexUtil.decodeHex(signStr);
}
// 再次签名
byte[] signedNew = mac.digest(content);
Boolean verify = mac.verify(signedNew, signed);
return verify.toString();
}
/**
* 测试
*
* @param args
*/
public static void main(String[] args) {
String algorithm = HmacAlgorithm.HmacMD5.getValue();
String key = getKey();
System.out.println(key);
String content = "{HmacUtil}测试一下‘’";
// String encryptDataType = DATA_TYPE_BASE64;
String encryptDataType = DATA_TYPE_HEX;
// 私钥签名
String sign = sign(algorithm, content, key, encryptDataType);
// 私钥验证签名
String signVerify = signVerify(algorithm, content, sign, key, encryptDataType);
System.out.println(sign);
System.out.println(signVerify);
}
}
参考