数据只要算法也被称为(HASH)算法,散列算法。由于其不可逆性,会被用作敏感信息的加密,可以实现数据签名、数据完整性校验等功能。
SHA算法
安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。该算法经过加密专家多年来的发展和改进已日益完善,并被广泛使用。该算法的思想是接收一段明文,然后以一种不可逆的方式将它转换成一段(通常更小)密文,也可以简单的理解为取一串输入码(称为预映射或信息),并把它们转化为长度较短、位数固定的输出序列即散列值(也称为信息摘要或信息认证代码)的过程。散列函数值可以说是对明文的一种“指纹”或是“摘要”所以对散列值的数字签名就可以视为对此明文的数字签名。
MD算法
MD2算法
Rivest在1989年开发出MD2算法。在这个算法中,首先对信息进行数据补位,使信息的字节长度是16的倍数。然后,以一个16位的检验和追加到信息末尾,并且根据这个新产生的信息计算出散列值。后来,Rogier和Chauvaud发现如果忽略了检验将和MD2产生冲突。MD2算法加密后结果是唯一的(即不同信息加密后的结果不同)。
MD4算法
为了加强算法的安全性,Rivest在1990年又开发出MD4算法。MD4算法同样需要填补信息以确保信息的比特位长度减去448后能被512整除(信息比特位长度mod 512 = 448)。然后,一个以64位二进制表示的信息的最初长度被添加进来。信息被处理成512位damg?rd/merkle迭代结构的区块,而且每个区块要通过三个不同步骤的处理。Den boer和Bosselaers以及其他人很快的发现了攻击MD4版本中第一步和第三步的漏洞。Dobbertin向大家演示了如何利用一部普通的个人电脑在几分钟内找到MD4完整版本中的冲突(这个冲突实际上是一种漏洞,它将导致对不同的内容进行加密却可能得到相同的加密后结果)。毫无疑问,MD4就此被淘汰掉了。尽管MD4算法在安全上有个这么大的漏洞,但它对在其后才被开发出来的好几种信息安全加密算法的出现却有着不可忽视的引导作用。
MD5算法
1991年,Rivest开发出技术上更为趋近成熟的md5算法。它在MD4的基础上增加了"安全-带子"(safety-belts)的概念。虽然MD5比MD4复杂度大一些,但却更为安全。这个算法很明显的由四个和MD4设计有少许不同的步骤组成。在MD5算法中,信息-摘要的大小和填充的必要条件与MD4完全相同。Den boer和Bosselaers曾发现MD5算法中的假冲突(pseudo-collisions),但除此之外就没有其他被发现的加密后结果了。
MAC算法
MAC算法 (Message Authentication Codes) 带秘密密钥的Hash函数:消息的散列值由只有通信双方知道的秘密密钥K来控制。此时Hash值称作MAC。
MAC算法原理(以直联银联pos和POS中心通讯为例)。
- 将欲发送给POS中心的消息中,从消息类型(MTI)到63域之间的部分构成MAC ELEMEMENT BLOCK (MAB)。
- 对MAB,按每8个字节做异或(不管信息中的字符格式),如果最后不满8个字节,则添加“0X00”。
package com.aimuti.week01.day01;
import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.digests.SHA224Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
/**
* MD:消息摘要(关于数字签名的核心算法),生成的消息摘要都是128位的
* SHA:安全散列,固定摘要长度信息
* SHA-1:160 SHA224:224 SHA256:256 SHA384:384
* MAC:消息认证码
*/
/**
* SHA-1和MD5比较
* 因为二者均由MD4导出,SHA-1和MD5彼此很相似。相应的,他们的强度和其他特性也是相似,但还有以下几点不同:
* 1、对强行攻击的安全性:最显著和最重要的区别是SHA-1摘要比MD5摘要长32位。使用强行技术,
* 产生任何一个报文使其摘要等于给定报摘要的难度对MD5是2^128数量级的操作,而对SHA-1则是2^160数量级的操作。
* 这样,SHA-1对强行攻击有更大的强度。
* 2、对密码分析的安全性:由于MD5的设计,易受密码分析的攻击,SHA-1显得不易受这样的攻击
* 3、速度:在相同的硬件上,SHA-1的运行速度比MD5慢
*/
/**
* SHA算法
* 安全hash算法,主要适用于数字签名标准里面定义的数字签名算法
* 思想:接收一段明文,然后以一种不可逆方式将他转换成一段密文
*/
public class Day01 {
private static String src = "http://aimuti.niu";
private static String decodeKey = "bbbbbbbbbb";
/**
* jdk版本的sha算法
*/
@Test
public void jdkSHA1(){
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA");
messageDigest.update(src.getBytes());
byte[] digest = messageDigest.digest();
System.out.println("jdk SHA 1: " + org.apache.commons.codec.binary.Hex.encodeHexString(digest));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
/**
* commons-codec的sha算法
*/
@Test
public void bcSHA1(){
Digest digest = new SHA1Digest();
digest.update(src.getBytes(),0,src.length());
byte[] shaBytes = new byte[digest.getDigestSize()];
digest.doFinal(shaBytes,0);
System.out.println("bc SHA 1: " + org.bouncycastle.util.encoders.Hex.toHexString(shaBytes));
}
/**
* 用bouncy castle实现:SHA224
*/
@Test
public void bcSHA224(){
Digest digest = new SHA224Digest();
digest.update(src.getBytes(),0,src.length());
byte[] sha224Bytes = new byte[digest.getDigestSize()];
digest.doFinal(sha224Bytes,0);
System.out.println("bc SHA 224: " + org.bouncycastle.util.encoders.Hex.toHexString(sha224Bytes));
}
/**
* 用bouncy castle与jdk结合实现SHA224
*/
@Test
public void bcSHA224b(){
try {
Security.addProvider(new BouncyCastleProvider());
MessageDigest messageDigest = MessageDigest.getInstance("SHA224");
messageDigest.update(src.getBytes());
System.out.println("bc and jdk SHA224: " + org.apache.commons.codec.binary.Hex.encodeHexString(messageDigest.digest()));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* bcprov的sha算法
*/
@Test
public void ccSHA1(){
System.out.println("cc SHA 1:" + DigestUtils.sha1Hex(src.getBytes()));
}
/**
* md2:首先对信息进行数据补位,使信息的字节长度使16的倍数,然后以一个16位的检验和追加到
* 信息末尾,并且根据这个新产生的信息计算出散列值
*
* md4:MD4算法同样需要填补信息以确保信息的比特位长度减去448后能被512整除,然后,
* 一个以64位二进制表示的信息的最初长度被添加进来。信息被处理成512位damg?rd/merkle迭代结构的区块,
* 而且每个区块要通过三个不同步骤的处理
*
* md5:在md4的基础上增加了“安全-带子”的概念,信息-摘要的大小和填充的必要条件和md4完全相同
* MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,
* 算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
*
* 在MD5算法中,首先需要对信息进行填充,使其位长对512求余的结果等于448。
* 因此,信息的位长(Bits Length)将被扩展至N*512+448,N为一个非负整数,N可以是零。
* 填充的方法如下,在信息的后面填充一个1和无数个0,直到满足上面的条件时才停止用0
* 对信息的填充。然后,在这个结果后面附加一个以64位二进制表示的填充前信息长度。
* 经过这两步的处理,信息的位长=N*512+448+64=(N+1)*512,
* 即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求。
*/
@Test
public void jdkMD2(){
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD2");
byte[] mdBytes = messageDigest.digest(src.getBytes());
System.out.println("md2 decode: " + Hex.toHexString(mdBytes));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
@Test
public void bcmMD4(){
/*Digest digest = new MD4Digest();
digest.update(src.getBytes(), 0, src.length());
byte[] mdBytes = new byte[digest.getDigestSize()];
digest.doFinal(mdBytes, 0);
System.out.println("md4 decode: " + Hex.toHexString(mdBytes));*/
Security.addProvider(new BouncyCastleProvider());
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD4");
byte[] mdBytes = messageDigest.digest(src.getBytes());
System.out.println("md4 decode: " + Hex.toHexString(mdBytes));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
@Test
public void jdkMD5(){
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] mdBytes = messageDigest.digest(src.getBytes());
System.out.println("md5 decode: " + Hex.toHexString(mdBytes));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
@Test
public void bcmMD5(){
Digest digest = new MD5Digest();
digest.update(src.getBytes(),0,src.length());
byte[] mdBytes = new byte[digest.getDigestSize()];
digest.doFinal(mdBytes,0);
System.out.println("md5 decode: " + Hex.toHexString(mdBytes));
}
@Test
public void ccMD5(){
String md5String = DigestUtils.md5Hex(src.getBytes());
System.out.println("common md5: " + md5String);
}
/**
* MAC算法:带秘钥钥匙的hash函数,消息的散列值由只有通信双方知道的秘密秘钥k来控制
* 此时hash值称作Mac
* 从消息类型(MTI)到63域之间的部分构成MAC ELEMENT BLOCK(MAB)
* 对MAB按每8个字节做异或(不管信息中的字符格式),如果最后不满8个字节,则添加“0x00“
*/
@Test
public void jdkHmacMD5(){
try {
//初始化keyGenerator
KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5");
//产生秘钥
SecretKey secretKey = keyGenerator.generateKey();
//获得秘钥
byte[] keyBytes = org.apache.commons.codec.binary.Hex.decodeHex(decodeKey.toCharArray());
//还原秘钥
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes,"HmacMD5");
//实例化MAC
Mac mac = Mac.getInstance(secretKeySpec.getAlgorithm());
//初始化mac
mac.init(secretKeySpec);
//执行摘要
byte[] result = mac.doFinal(src.getBytes());
System.out.println("jdk mac: " + Hex.toHexString(result));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void bcHmacMD5(){
HMac hMac = new HMac(new MD5Digest());
hMac.init(new KeyParameter(Hex.decode(decodeKey)));
hMac.update(src.getBytes(),0,src.length());
byte[] hMacBytes = new byte[hMac.getMacSize()];
hMac.doFinal(hMacBytes,0);
System.out.println("bc mac: " + Hex.toHexString(hMacBytes));
}
}