消息摘要算法平常使用的频率很高,经常我们用它来验证数据是否被篡改。 还有验证网络传输文件时,文件是否被篡改等等。
消息摘要算法主要分为三类: MD 、 SHA 、 MAC
主要用途 : 验证消息的完整性
主要特性:
MD系列算法一般有: MD2 、 MD4 、 MD5, 数字的值越大代表算法的版本越高,安全就越强。 即 MD5 > MD4 > MD2 。 自然破解难度MD5最高,平常用的最多的也是MD5算法。
MD系列算法输出信息的摘要长度都是固定的128bit ,即16字节;
因为JDK不支持MD4算法, 我们需引用第三方的依赖。在 pom.xml下加入这段才能使用MD4
org.bouncycastle
bcprov-jdk15on
1.62
代码示例 :
/**
* @program
* @Desc MD消息摘要算法 测试
* @Author 游戏人日常
* @CreateTime 2019/07/09--15:14
*/
public class MDTest {
private static final String src = "helloWorld";
public static void main(String[] args) {
jdkMD5();
jdkMD2();
bcMD4();
jdkAndBcMd4();
}
//JDK 实现
public static void jdkMD5() {
try {
//得到消息摘要对象
MessageDigest md = MessageDigest.getInstance("MD5");
//对src 字符串进行消息摘要
byte[] md5Bytes = md.digest(src.getBytes());
//对得到的字节数组 转为16进制
String result = new BigInteger(md5Bytes).toString(16);
System.out.println(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
//JDK 实现
public static void jdkMD2() {
try {
//得到消息摘要对象
MessageDigest md = MessageDigest.getInstance("MD2");
//对src 字符串进行消息摘要
byte[] md5Bytes = md.digest(src.getBytes());
//对得到的字节数组 转为16进制
String result = new BigInteger(md5Bytes).toString(16);
System.out.println(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
//结合JDK 来实现
public static void jdkAndBcMd4() {
try {
//加入BouncyCastle的支持 也可以不通过代码,需在Java/jdk1.8.0_181/jre/lib/security/java.security加入
Security.addProvider(new BouncyCastleProvider());
// 得到MD 消息摘要对象
MessageDigest md = MessageDigest.getInstance("MD4");
//对src 字符串进行消息摘要
byte[] mD4Bytes = md.digest(src.getBytes());
//对得到的字节数组 转为16进制
String result = new BigInteger(mD4Bytes).toString(16);
System.out.println(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
//通过 第三方 bouncy castle 来 实现
public static void bcMD4() {
//创建 MD4Digest 对象
Digest md4Digest = new MD4Digest();
//更新消息摘要 通过src字节数组
md4Digest.update(src.getBytes(), 0, src.getBytes().length);
//初始化字节数组大小
byte[] mD4Bytes = new byte[md4Digest.getDigestSize()];
//关闭摘要,然后得到摘要后的值
md4Digest.doFinal(mD4Bytes, 0);
//对得到的字节数组 转为16进制
String result = new BigInteger(mD4Bytes).toString(16);
System.out.println(result);
}
}
SHA系列算法从版本上一般分为两类: SHA-1 和 SHA-2
SHA-1就是我们常见的SHA1 , SHA-2主要有SHA224 、 SHA256 、 SHA384 、 SHA512等等,不同的数字代表输出消息的位数, 与MD系列算法一样,数字高表示算法的安全性越高,但不同于MD系列算法的是,SHA系列算法输出的信息摘要长度不一样。
代码示例:
**
* @program
* @Desc SHA 测试
* @Author 游戏人日常
* @CreateTime 2019/07/09--17:44
*/
public class SHATest {
public static final String src="helloWorld";
public static void main(String [] args){
jdkSHA1();
bcSHA224();
jdkSHA256();
jdkSHA384();
jdkSHA512();
}
//JDK实现
public static void jdkSHA1(){
try {
//得到消息摘要对象
MessageDigest md=MessageDigest.getInstance("SHA");
//对src 字符串进行消息摘要
byte[] md5Bytes = md.digest(src.getBytes());
//对得到的字节数组 转为16进制
String result = new BigInteger(md5Bytes).toString(16);
System.out.println(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
// 通过 第三方 bouncy castle 来 实现
public static void bcSHA224(){
//创建 MD4Digest 对象
Digest digest = new SHA224Digest();
//更新消息摘要 通过src字节数组
digest.update(src.getBytes(), 0, src.getBytes().length);
//初始化字节数组大小
byte[] mD4Bytes = new byte[digest.getDigestSize()];
//关闭摘要,然后返回摘要后的值
digest.doFinal(mD4Bytes, 0);
//对得到的字节数组 转为16进制
String result = new BigInteger(mD4Bytes).toString(16);
System.out.println(result);
}
//JDK实现
public static void jdkSHA256(){
try {
//得到消息摘要对象
MessageDigest md=MessageDigest.getInstance("SHA-256");
//对src 字符串进行消息摘要
byte[] md5Bytes = md.digest(src.getBytes());
//对得到的字节数组 转为16进制
String result = new BigInteger(md5Bytes).toString(16);
System.out.println(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
//JDK实现
public static void jdkSHA384(){
try {
//得到消息摘要对象
MessageDigest md=MessageDigest.getInstance("SHA-384");
//对src 字符串进行消息摘要
byte[] md5Bytes = md.digest(src.getBytes());
//对得到的字节数组 转为16进制
String result = new BigInteger(md5Bytes).toString(16);
System.out.println(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
//JDK实现
public static void jdkSHA512(){
try {
//得到消息摘要对象
MessageDigest md=MessageDigest.getInstance("SHA-512");
//对src 字符串进行消息摘要
byte[] md5Bytes = md.digest(src.getBytes());
//对得到的字节数组 转为16进制
String result = new BigInteger(md5Bytes).toString(16);
System.out.println(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
MAC算法不同于MD和SHA,因为在MD和SHA算法基础上添加了密钥。 所以安全性相比MD和SHA系列更高。
MD系列:HmacMD2 、HmacMD4 、HmacMD5
SHA系列: HmacSHA1 、 HmacSHA224 、HmacSHA256 、HmacSHA384 、HmacSHA512
摘要后的长度跟MD和SHA系列一样。 如HmacMD5摘要长度跟MD5一样, 即160bit,20字节;
代码示例:
/**
* @program
* @Desc MAC 测试
* @Author 游戏人日常
* @CreateTime 2019/07/09--19:44
*/
public class MACTest {
public static final String src="helloWorld";
public static void main(String [] args){
jdkHmacMD5();
HmacSHA1();
}
//JDK实现
public static void jdkHmacMD5(){
try {
//获取key生成器对象
KeyGenerator keyGenerator=KeyGenerator.getInstance("HmacMD5");
//获取密钥对象
SecretKey secretKey=keyGenerator.generateKey();
//获取密钥
//byte [] key=secretKey.getEncoded();
//可以自己设置密钥的来源, 必须是16进制, 长度是2的倍数
byte [] key=Hex.decode("123456789abcde");
//还原密钥 第二个参数是算法名称
SecretKey restoreSecretKey= new SecretKeySpec(key,"HmacMD5");
//获取Mac对象
Mac mac=Mac.getInstance(restoreSecretKey.getAlgorithm());
//初始化Mac
mac.init(restoreSecretKey);
//对src执行摘要
byte[] hmacMD5Bytes= mac.doFinal(src.getBytes());
//对摘要的信息字节数组 转为16进制
String result = new BigInteger(hmacMD5Bytes).toString(16);
System.out.println(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
//JDK实现
public static void HmacSHA1(){
try {
//获取key生成器对象
KeyGenerator keyGenerator=KeyGenerator.getInstance("HmacSHA1");
//获取密钥对象
SecretKey secretKey=keyGenerator.generateKey();
//获取密钥
//byte [] key=secretKey.getEncoded();
//可以自己设置密钥的来源, 必须是16进制, 长度是2的倍数
byte [] key=Hex.decode("123456789abcde");
//还原密钥 第二个参数是算法名称
SecretKey restoreSecretKey= new SecretKeySpec(key,"HmacSHA1");
//获取Mac对象
Mac mac=Mac.getInstance(restoreSecretKey.getAlgorithm());
//初始化Mac
mac.init(restoreSecretKey);
//对src执行摘要
byte[] hmacMD5Bytes= mac.doFinal(src.getBytes());
//对摘要的信息字节数组 转为16进制
String result = new BigInteger(hmacMD5Bytes).toString(16);
System.out.println(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
}
//其他的摘要实现一样,大致都差不多,就不实现了。
密钥不同,得到的摘要消息肯定不同。 密钥的来源可以自己来构建,但必须满足是16进制, 长度是2的倍数。
上面只是个简单的用户注册和登陆流程,为什么要对密码进行摘要,不进行摘要的话,一旦如果数据库中的数据泄露了,如: 用户名和密码, 别人就可以拿这个用户名和密码去登陆。 摘要的话,别人即使获取到用户名和密码, 也是登陆不成功。 但是现在的MD5被国人破解了。
我们可以看到这个是把secret+uid+psid+roleid+amount+type+htnonce 这些参数值进行摘要,生成的值和原来的各个参数来作为HTTP参数。 这个文档的参数是按照这个规则来进行摘要的,有的会用参数的key进行排序然后来进行摘要。 具体的规则还是客户端和服务器端进行商定。
这个工具源码: https://github.com/songshuilin/parseApk
上面列出三种摘要算法系列: MD系列 、 SHA系列 、 MAC系列。 但是我们还会见到CRC系列,在WinRAR 、WinZIP等软件中。
一般情况下安全性 MAC > SHA > MD > CRC
消息摘要特点: