目录
加密算法
1、概述
1.1、加密算法分类
加密算法大致上分为单向加密算法、对称加密算法和非对称加密算法:
- 单向加密算法,是数据完整性验证的常用算法,包括MD5、SHA算法等
- 对称加密算法,是数据存储加密的常用算法,加密与解密的密钥相同,包括DES、ASE等
- 非对称加密算法,是数据传输加密的常用算法,加密和解密的密钥不同,包括RSA等。用公钥加密用私钥解密或用私钥加密用公钥解密。
对称加密也可用在数据传输上,但非对称加密在密钥管理上更有优势,相对对称加密,非对称加密在安全级别上等级更高,但时间效率上不如对称加密。
1.2、java中的算法
java对密码支持比较充分,可从以下三个方面来说:
- java api支持常用的加密算法:
- MessageDigest类可以构建MD5、SHA;
- Mac类可以构建HMAC;
- Cipher类可以构建DES、AES、Blowfish等对称加密算法和RSA、DSA、DH等非对称加密;
- Signature类可以用于数字签名和验证;
- Certificate类可以操作数字证书等。
- jps容器支持,比如tomcat可以简单的配置https,一般常见的web容器都会支持https。
- java工具支持,可以通过jdk自带的工具keytool完成密钥管理、证书管理;通过jarsigner可以完成代码签名。
1.3、依赖
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.60</version>
</dependency>
2、Base64(电子邮件传输算法)
2.1、Base64
1)、定义
Base64算法最早应用于解决电子邮件传输的问题。基于64个字符的编码算法,根据RFC2045的定义:Base64内容传送编码是一种以任意8位字节列组合的描述形式,这种形式不容易被人直接识别
。经过base64编码后的数据会比原来数据长约1/3,经过base64编码后的字符串的字符数是以4为单位的整数倍。
RFC2045还规定,在电子邮件中,每行76个字符,每行末尾需要添加一个回车换行符\r\n
,不论每行是否够76个字符,都需要添加一个回车换行符。但在实际应用中,往往根据实际需要忽略了这一要求。
2)、实现原理
Base64算法主要是对给定的字符以字符编码(如ASCII码、UTF-8码)对应的十进制数为基准,做编码操作:
- 将给定的字符串以字符为单转换为对应的字符编码(如ASCII码)
- 将获得的字符编码转换为二进制码
- 对获得的二进制码做分组转换操作,每3个8位二进制码为一组,转换为每4个6位二进制码为一组(不足6位时低位补0)。这是一个分组变化的过程,3个8位二进制码和4个6位二进制码的长度都是24位(38=46=24)
- 对获得的4个6位二进制码补位,向6位二进制码添加2位高位0,组成4个8位二进制码
- 将获得的4个8位二进制码转换为十进制码
- 将获得的十进制码转换为Base64字符表中对应的字符
字符 A
ASCII码 65
二进制码 01000001
4-6二进制码 010000 010000
4-8二进制码 00010000 00010000
十进制码 16 16
字符表映射码 Q Q = =
最后,字符串A
经过Base64编码后就得到了QQ==
这样一个字符串,`=``是当原文的二进制长度不足24位,最终转换为十进制码时也不足4项,补位用的。
3)、模型分析
- 甲方对数据做Base64编码处理
- 甲方将编码后的数据发送给乙方
- 乙方获得数据后对数据做Base64解码处理
4)、Java实现
java8提供了Base64算法实现,但是默认不添加任何换行符。
public class Base64Coder {
//字符编码
public final static String ENCODING = "UTF-8";
/**
* Base64编码
* @param data
* @return
* @throws UnsupportedEncodingException
*/
public static String encode(String data) throws UnsupportedEncodingException {
Base64.Encoder encoder = Base64.getEncoder();
byte[] bytes = data.getBytes(ENCODING);
return encoder.encodeToString(bytes);
}
/**
* Base64解码
* @param data
* @return
* @throws UnsupportedEncodingException
*/
public static String decode(String data) throws UnsupportedEncodingException {
Base64.Decoder decoder = Base64.getDecoder();
byte[] bytes = decoder.decode(data);
return new String(bytes, ENCODING);
}
/**
* Base64编码,严格执行RFC 2045,每76个字符添加一个\r\n
* @param data
* @return
* @throws UnsupportedEncodingException
*/
public static String encodeSafe(String data) throws UnsupportedEncodingException {
Base64.Encoder encoder = Base64.getMimeEncoder();
byte[] bytes = data.getBytes(ENCODING);
return encoder.encodeToString(bytes);
}
/**
* Base64解码,严格执行RFC 2045,解码忽略\r\n
* @param data
* @return
* @throws UnsupportedEncodingException
*/
public static String decodeSafe(String data) throws UnsupportedEncodingException {
Base64.Decoder decoder = Base64.getMimeDecoder();
byte[] bytes = decoder.decode(data);
return new String(bytes, ENCODING);
}
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "hello world";
String encode = encode(str);
System.out.println(encode);
String decode = decode(encode);
System.out.println(decode);
String encodeSafe = encodeSafe(str);
System.out.println(encodeSafe);
String decodeSafe = decodeSafe(encodeSafe);
System.out.println(decodeSafe);
}
}
aGVsbG8gd29ybGQ=
hello world
aGVsbG8gd29ybGQ=
hello world
5)、Bouncy Caste实现
Bouncy Caste实现的Base64算法也不添加回车换行符。
import org.bouncycastle.util.encoders.Base64;
public class Base64CoderByBC {
//字符编码
public final static String ENCODING = "UTF-8";
/**
* Base64编码
* @param data
* @return String
* @throws Exception
*/
public static String encode(String data) throws Exception {
byte[] bytes = Base64.encode(data.getBytes(ENCODING));
return new String(bytes, ENCODING);
}
/**
* Base64解码
* @param data
* @return
* @throws Exception
*/
public static String decode(String data) throws Exception {
byte[] bytes = Base64.decode(data.getBytes(ENCODING));
return new String(bytes, ENCODING);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
String encode = encode(str);
System.out.println(encode);
String decode = decode(encode);
System.out.println(decode);
}
}
6)、Commons Codec实现
它遵循RFC2045的相关定义,实现了Base64算法,同时也支持一般Base64算法的实现。
//一般的base64编码,不添加回车换行
public static byte[] encodeBase64(byte[] binaryData)
//isChunked为true,将按RFC2045标准执行,添加回车换行
public static byte[] encodeBase64(byte[] binaryData, boolean isChunked)
import org.apache.commons.codec.binary.Base64;
public class Base64CoderByCC {
//字符编码
public final static String ENCODING = "UTF-8";
/**
* Base64一般编码,不添加回车换行
* @param data
* @return String
* @throws Exception
*/
public static String encode(String data) throws Exception {
byte[] bytes = Base64.encodeBase64(data.getBytes(ENCODING));
return new String(bytes, ENCODING);
}
/**
* Base64严格编码,添加回车换行
* @param data
* @return
* @throws Exception
*/
public static String encodeSafe(String data) throws Exception {
byte[] bytes = Base64.encodeBase64(data.getBytes(ENCODING), true);
return new String(bytes, ENCODING);
}
/**
* Base64解码,通用
* @param data
* @return
* @throws Exception
*/
public static String decode(String data) throws Exception {
byte[] bytes = Base64.decodeBase64(data.getBytes(ENCODING));
return new String(bytes, ENCODING);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
String encode = encode(str);
System.out.println(encode);
String encodeSafe = encodeSafe(str);
System.out.println(encodeSafe);
String decode = decode(encode);
System.out.println(decode);
String decodeSafe = decode(encodeSafe);
System.out.println(decodeSafe);
}
}
aGVsbG8gd29ybGQ=
aGVsbG8gd29ybGQ=
hello world
hello world
2.2、Url Base64
1)、定义
Url Base64算法定义有别于RFC 2045,它不需要定义每行字符数以及行末尾回车换行符。同时,根据URL相关要求,符号+
和/
是不允许出现在URL中的,RFC4648中给出了相应的替代符号-
和_
。同样,符号=
用作参数分隔符,也不允许出现在URL中。符号=
在Base64算法中用作填充符,如果需要定长的Base64编码串,就需要有响应的替代符号。
根据RFC4648中的建议,~
和.
符都有可能替代=
符号。但~
符号与文件系统相冲突,不能使用;如果使用.
符号,某些文件系统认为该符号连续出现两次则为错误。
Base64编码后的字符串中,填充符可以重复出现,并且最多只能出现两次。如果使用.
符号作为填充符,则与某些系统相冲突。
Bouncy Castle和Commons Codec都实现了Url Base64算法,不同的是,Bouncy Castle使用.
作为填充符,而Commons Codecl则直接放弃了填充符,使用不定长Url Base64编码。
2)、Java实现
依然使用=
用于填充。
public class UrlBase64Coder {
//字符编码
public final static String ENCODING = "UTF-8";
/**
* UrlBase64编码
* @param data
* @return
* @throws UnsupportedEncodingException
*/
public static String encode(String data) throws UnsupportedEncodingException {
Base64.Encoder encoder = Base64.getUrlEncoder();
byte[] bytes = data.getBytes(ENCODING);
return encoder.encodeToString(bytes);
}
/**
* UrlBase64解码
* @param data
* @return
* @throws UnsupportedEncodingException
*/
public static String decode(String data) throws UnsupportedEncodingException {
Base64.Decoder decoder = Base64.getUrlDecoder();
byte[] bytes = decoder.decode(data);
return new String(bytes, ENCODING);
}
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "http://www.baidu.com";
String encode = encode(str);
System.out.println(encode);
String decode = decode(encode);
System.out.println(decode);
}
}
aHR0cDovL3d3dy5iYWlkdS5jb20=
http://www.baidu.com
3)、Bouncy Castle实现
使用.
作为填充符。
public class UrlBase64CoderByBC {
//字符编码
public final static String ENCODING = "UTF-8";
/**
* UrlBase64编码
* @param data
* @return
* @throws Exception
*/
public static String encode(String data) throws Exception {
byte[] bytes = UrlBase64.encode(data.getBytes(ENCODING));
return new String(bytes, ENCODING);
}
/**
* UrlBase64解码
* @param data
* @return
* @throws Exception
*/
public static String decode(String data) throws Exception {
byte[] bytes = UrlBase64.decode(data.getBytes(ENCODING));
return new String(bytes, ENCODING);
}
public static void main(String[] args) throws Exception {
String str = "http://www.baidu.com";
String encode = encode(str);
System.out.println(encode);
String decode = decode(encode);
System.out.println(decode);
}
}
aHR0cDovL3d3dy5iYWlkdS5jb20.
http://www.baidu.com
3)、Commons Codec实现
不填充,使用不定长编码。
public class UrlBase64CoderByCC {
//字符编码
public final static String ENCODING = "UTF-8";
/**
* UrlBase64编码
* @param data
* @return
* @throws Exception
*/
public static String encode(String data) throws Exception {
byte[] bytes = Base64.encodeBase64URLSafe(data.getBytes(ENCODING));
return new String(bytes, ENCODING);
}
/**
* UrlBase64解码
* @param data
* @return
* @throws Exception
*/
public static String decode(String data) throws Exception {
byte[] bytes = Base64.decodeBase64(data.getBytes(ENCODING));
return new String(bytes, ENCODING);
}
public static void main(String[] args) throws Exception {
String str = "http://www.baidu.com";
String encode = encode(str);
System.out.println(encode);
String decode = decode(encode);
System.out.println(decode);
}
}
aHR0cDovL3d3dy5iYWlkdS5jb20
http://www.baidu.com
3、消息摘要算法(Message Digest)
消息摘要算法包含三大系列,即MD 、SHA 和MC,常用于验证数据的完整性,是数字签名的核心算法。
任何消息经过散列函数处理后都会获得唯一的散列值(hashcode),该过程称为消息摘要,其散列值成为数字指纹,其算法即是消息摘要算法。
消息摘要算法又称为散列算法,其核心在于散列函数的单向性,即通过散列函数可获得对应的散列值,但不可通过该散列值获得其原始信息。
MD
,message digest,消息摘要算法,包括MD2、MD4、MD5SHA
,secure hash algorithm,安全散列算法,包括SHA-1,和SHA-2系列(SHA-224、SHA-256、SHA-384、SHA-512)MAC
,message authentication code,消息认证码算法,综合了MD和SHA算法,包括HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384、HmacSHA512其它
:RipeMD系列(RipeMD128、RipeMD160、RipeMD256、RipeMD320),Tiger、GOST3411和Whirlpool算法等
3.1、MD算法
1)、定义
MD5是由MD2、MD3、MD4改进而来,是典型的消息摘要算法。MD5算法对输入任意长度的消息进行运行,产生一个128位的消息摘要。如果将这个128位的信息摘要信息换算成十六进制,则可以得到一个32位的字符串(32位的数字字母混合码)。
算法 | 摘要长度 | 备注 |
---|---|---|
MD2 | 128 | Java7实现 |
MD5 | 128 | Java7实现 |
MD4 | 128 | Bouncy Castle实现 |
2)、模型分析
以用户注册/登录为示例:
3)、Java实现
Java实现了MD2和MD5
public class MDcoder {
public static byte[] encodeMD5(byte[] data) throws NoSuchAlgorithmException {
//初始化MessageDigest
MessageDigest md5 = MessageDigest.getInstance("MD5");
//执行消息摘要
md5.update(data);
return md5.digest();
}
public static byte[] encodeMD2(byte[] data) throws NoSuchAlgorithmException {
//初始化MessageDigest
MessageDigest md2 = MessageDigest.getInstance("MD2");
//执行消息摘要
md2.update(data);
return md2.digest();
}
public static void main(String[] args) throws NoSuchAlgorithmException {
String str = "hello world";
String s1 = HexUtil.encodeHexStr(encodeMD5(str.getBytes()));
System.out.println(s1);
String s2 = HexUtil.encodeHexStr(encodeMD2(str.getBytes()));
System.out.println(s2);
}
}
5eb63bbbe01eeed093cb22bb8f5acdc3
d9cce882ee690a5c1ce70beff3a78c77
4)、Bouncy Castle实现
Bouncy Castle实现了MD4算法。
public class MD4Coder {
/**
* MD4摘要
* @param data
* @return
* @throws Exception
*/
public static byte[] encodeMD4(byte[] data) throws Exception {
//加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
MessageDigest md = MessageDigest.getInstance("MD4");
return md.digest(data);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
byte[] bytes = encodeMD4(str.getBytes());
//aa010fbc1d14c795d86ef98c95479d17
System.out.println(HexUtil.encodeHexStr(bytes));
}
}
5)、Commons Codec实现
Commons Codec实现MD2和MD5算法,本质上是对Java原生API进行了包装。
public class MDCoderByCC {
public static byte[] encodeMD2(String data) throws Exception {
return DigestUtils.md2(data);
}
public static String encodeMD2Hex(String data) throws Exception {
return DigestUtils.md2Hex(data);
}
public static byte[] encodeMD5(String data) throws Exception {
return DigestUtils.md5(data);
}
public static String encodeMD5Hex(String data) throws Exception {
return DigestUtils.md5Hex(data);
}
public static void main(String[] args) throws Exception{
String str = "hello world";
byte[] encodeMD2 = encodeMD2(str);
System.out.println(HexUtil.encodeHexStr(encodeMD2));
System.out.println(encodeMD2Hex(str));
byte[] encodeMD5 = encodeMD5(str);
System.out.println(HexUtil.encodeHexStr(encodeMD5));
System.out.println(encodeMD5Hex(str));
}
}
6)、三种实现方式的差异
Java
:Sun提供的算法实现较为底层,支持MD2和MD5两种算法。但缺少了相应的进制转换实现,不能将其字节数组形式的摘要信息转换为十六进制字符串,这多少有点
不方便。Bouncy Castle
:Bouncy Castle是对Sun的友善补充,提供了对MD4算法的支持。支持多种形式的参数,支持十六进制字符串形式的摘要信息。Commons Codec
:如果仅仅需要实现MD5算法,则使用Commons Codec完成消息摘要处理是一个不错的选择。它支持多种形式的参数,支持十六进制字符串形式的摘要信息。
3.2、SHA算法
1)、定义
SHA算法是基于MD4算法的,已经成为消息摘要的首选,与MD不同的是其摘要更长,安全性更高。
SHA算法有SHA-1、SHA-224、SHA-256、SHA-384、SHA-512,除了SHA-1外,其他都是根据信息摘要的长度命名的。SHA-224是为了符合3DES的需要而定义的。
算法 | 摘要长度 | 备注 |
---|---|---|
SHA-1 | 160 | Java7实现 |
SHA-256 | 256 | Java7实现 |
SHA-384 | 384 | Java7实现 |
SHA-512 | 512 | Java7实现 |
SHA-224 | 224 | Councy Castle实现 |
2)、模型分析
如果甲乙双方进行明文消息通讯,但要求能够鉴别消息在传输过程中是否被篡改,就可参照如图所示的模型。当然,此模型对MD和SHA算法同样适用。
如图所示,甲乙双方分别作为消息发送者与接收者。我们也可以把甲方看做软件发布厂商,把乙方看做软件使用客户。甲方向乙方发送一则消息,需要经历以下几个步骤:
- 甲方公布消息摘要算法。这个算法可以被其他人获取,包括监听者。
- 甲方对待发送的消息做摘要处理,并获得摘要信息。
- 甲方向乙方发送摘要信息。这个数字指纹可以被其他人获取,包括监听者。
- 甲方向乙方发送消息。该消息可以是明文,有可能被监听者篡改。
- 乙方获得消息后,使用甲方提供的消息摘要算法对其做摘要处理,获得数字指纹并对比甲方传递过来的数字指纹是否匹配。
3)、Java实现
Java实现了SHA-1、SHA-256、SHA-384和SHA-512.
public class SHACoder {
/**
* SHA-1加密
*/
public static byte[] encodeSHA(byte[] data) throws NoSuchAlgorithmException {
MessageDigest sha = MessageDigest.getInstance("SHA");
sha.update(data);
return sha.digest();
}
/**
* SHA-256加密
*/
public static byte[] encodeSHA256(byte[] data) throws NoSuchAlgorithmException {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
sha256.update(data);
return sha256.digest();
}
/**
* SHA-384加密
*/
public static byte[] encodeSHA384(byte[] data) throws NoSuchAlgorithmException {
MessageDigest sha384 = MessageDigest.getInstance("SHA-384");
sha384.update(data);
return sha384.digest();
}
/**
* SHA-512加密
*/
public static byte[] encodeSHA512(byte[] data) throws NoSuchAlgorithmException {
MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
sha512.update(data);
return sha512.digest();
}
public static void main(String[] args) throws NoSuchAlgorithmException {
String str = "hello world";
String sha1 = HexUtil.encodeHexStr(encodeSHA(str.getBytes()));
System.out.println(sha1);
String sha256 = HexUtil.encodeHexStr(encodeSHA256(str.getBytes()));
System.out.println(sha256);
String sha384 = HexUtil.encodeHexStr(encodeSHA384(str.getBytes()));
System.out.println(sha384);
String sha512 = HexUtil.encodeHexStr(encodeSHA512(str.getBytes()));
System.out.println(sha512);
}
}
4)、Bouncy Castle实现
Bouncy Castle实现了SHA-224算法。
public class SHA224Coder {
/**
* SHA-224加密,由BouncyCastleProvider实现
*/
public static byte[] encodeSHA224(byte[] data) throws NoSuchAlgorithmException {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
MessageDigest sha224 = MessageDigest.getInstance("SHA-224");
sha224.update(data);
return sha224.digest();
}
public static void main(String[] args) throws Exception {
String str = "hello world";
byte[] encodeSHA224 = encodeSHA224(str.getBytes());
System.out.println(HexUtil.encodeHexStr(encodeSHA224));
}
}
5)、Commons Codec实现
Commons Codec提供了DigestUtils类,支持所有Java提供的SHA算法。本质上还是对Sun提供的API的封装。
public class SHACoderByCC {
public static byte[] encodeSHA1(String data) throws Exception {
return DigestUtils.sha1(data);
}
public static String encodeSHA1Hex(String data) throws Exception {
return DigestUtils.sha1Hex(data);
}
public static byte[] encodeSHA256(String data) throws Exception {
return DigestUtils.sha256(data);
}
public static String encodeSHA256Hex(String data) throws Exception {
return DigestUtils.sha256Hex(data);
}
public static byte[] encodeSHA384(String data) throws Exception {
return DigestUtils.sha384(data);
}
public static String encodeSHA384Hex(String data) throws Exception {
return DigestUtils.sha384Hex(data);
}
public static byte[] encodeSHA512(String data) throws Exception {
return DigestUtils.sha512(data);
}
public static String encodeSHA512Hex(String data) throws Exception {
return DigestUtils.sha512Hex(data);
}
}
6)、三种实现方式的差异
Java
:由于Sun提供了较为底层的SHA算法实现,如SHA-1、SHA-256、SHA-384和SHA-512四种算法,但缺少了对应的进制转换实现,多少有些遗憾。Bouncy Castle
:Bouncy Castle是对Sun的友善补充,提供了对SHA-224算法的支持,支持十六进制字符串形式的摘要信息,相当方便。Commons Codec
:Commons Codeci对Sun提供的SHA算法做了包装,支持多种形式的参数,支持十六进制字符串形式的摘要信息,相当方便。
3.3、MAC算法
1)、定义
MAC(Message Authentication Code,消息认证码算法)是含有密钥散列函数算法,兼容了MD和SHA算法的特性,并在此基础上加人了密钥。因为MAC算法融合了密钥散列函数
(keyed-Hash),通常我们也把MAC称为HMAC(keyed-Hash Message Authentication Code)。MAC算法和HMAC算法基本上可等同对待。
MAC算法集合了MD和SHA两大系列消息摘要算法,MD系列有HmacMD2、HmacMD4、HmacMD5,SHA系列有HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384、HmacSHA512。
经MAC算法得到的摘要值可以使用十六进制编码表示,其摘要值长度与参与实现的算法摘要值长度相同,比如HmacSHA1算法得到的摘要长度就是SHA1算法得到摘要长度,都是160位二进制数,换算成十六进制编码为40位。
算法 | 摘要长度 | 备注 |
---|---|---|
HmacMD5 | 128 | Java7实现 |
HmacSHA1 | 160 | Java7实现 |
HmacSHA224 | 224 | Java8实现 |
HmacSHA256 | 256 | Java7实现 |
HmacSHA384 | 384 | Java7实现 |
HmacSHA512 | 512 | Java7实现 |
HmacMD2 | 128 | Bounc Castle实现 |
HmacMD4 | 128 | Bounc Castle实现 |
HmacSHA224 | 224 | Bounc Castle实现 |
2)、模型分析
基于MAC算法的消息传递模型如下图:
- 甲方向乙方公布摘要算法
- 甲乙双方按照约定构建密钥,并由一方公布给对方(这里是甲方公布密钥给乙方)
- 甲方使用密钥对消息做摘要处理后,将摘要信息发送给乙方
- 甲方将消息发送给乙方
- 乙方收到消息后,使用密钥对消息做摘要处理,并对比甲方发送的摘要信息是否一致。
3)、Java实现
Java通过MAC类提供了HmacMD5、HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384和HmacSHA512六种算法。
public class MACCoder {
/**
* 初始化HmacMD5
*/
public static byte[] initHmacMD5Key() throws NoSuchAlgorithmException {
// 初始化KeyGenerator
KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5");
// 产生秘密密钥
SecretKey secretKey = keyGenerator.generateKey();
//获得编码后的密钥
return secretKey.getEncoded();
}
/**
* HmacMD5加密
* @param data 待加密数据
* @param key 密钥
*/
public static byte[] encodeHmacMD5(byte[] data, byte[] key) throws Exception {
//还原密钥
SecretKey secretKey = new SecretKeySpec(key, "HmacMD5");
//实例化Mac secretKey.getAlgorithm()即HmacMD5
Mac mac = Mac.getInstance("HmacMD5");
//初始化mac
mac.init(secretKey);
mac.update(data);
return mac.doFinal();
}
/**
* 初始化HmacSHA1
* @return
* @throws NoSuchAlgorithmException
*/
public static byte[] initHmacSHA1Key() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("HMacSHA1");
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* HmacSHA1加密
* @param data 待加密数据
* @param key 密钥
* @return
* @throws Exception
*/
public static byte[] encodeHmacSHA1(byte[] data, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA1");
Mac mac = Mac.getInstance("HMacSHA1");
mac.init(secretKey);
mac.update(data);
return mac.doFinal();
}
/**
* 初始化HmacSHA224
* @return
* @throws NoSuchAlgorithmException
*/
public static byte[] initHmacSHA224() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("HMacSHA224");
SecretKey secretKey = keyGenerator.generateKey();
System.out.println(secretKey.getClass());
return secretKey.getEncoded();
}
/**
* HmacSHA224加密
* @param data 待加密数据
* @param key 密钥
* @return
* @throws Exception
*/
public static byte[] encodeHmacSHA224(byte[] data, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA224");
Mac mac = Mac.getInstance("HMacSHA224");
mac.init(secretKey);
mac.update(data);
return mac.doFinal();
}
/**
* 初始化HmacSHA256密钥
* @return
* @throws Exception
*/
public static byte[] initHmacSHA256Key() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA256");
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* 初始化HmacSHA256
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encodeHmacSHA256(byte[] data, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA256");
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
mac.update(data);
return mac.doFinal();
}
/**
* 初始化HmacSHA384密钥
* @return
* @throws Exception
*/
public static byte[] initHmacSHA384Key() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA384");
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* HmacSHA384加密
* @param data 待加密的数据
* @param key 密钥
* @return byte[] 消息摘要
* @throws Exception
*/
public static byte[] encodeHmacSHA384(byte[] data, byte[] key) throws Exception{
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA384");
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
mac.update(data);
return mac.doFinal();
}
/**
* HmacSHA512加密
* @return
* @throws Exception
*/
public static byte[] initHmacSHA512Key() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA512");
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* HmacSHA512加密
* @param data 待加密的数据
* @param key 密钥
* @return byte[] 消息摘要
* @throws Exception
*/
public static byte[] encodeHmacSHA512(byte[] data, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA512");
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
mac.update(data);
return mac.doFinal();
}
public static void main(String[] args) throws Exception{
String str = "hello world";
System.out.println("HmacMD5****************************");
byte[] key = initHmacMD5Key();
System.out.println(HexUtil.encodeHexStr(key));
byte[] bytes = encodeHmacMD5(str.getBytes(), key);
System.out.println(HexUtil.encodeHexStr(bytes));
System.out.println("HmacSHA1***************************");
key = initHmacSHA1Key();
System.out.println(HexUtil.encodeHexStr(key));
bytes = encodeHmacSHA1(str.getBytes(), key);
System.out.println(HexUtil.encodeHexStr(bytes));
System.out.println("HmacSHA224**************************");
key = initHmacSHA224();
System.out.println(HexUtil.encodeHexStr(key));
bytes = encodeHmacSHA224(str.getBytes(), key);
System.out.println(HexUtil.encodeHexStr(bytes));
System.out.println("HmacSHA256***************************");
key = initHmacSHA256Key();
System.out.println(HexUtil.encodeHexStr(key));
bytes = encodeHmacSHA256(str.getBytes(), key);
System.out.println(HexUtil.encodeHexStr(bytes));
System.out.println("HmacSHA384***************************");
key = initHmacSHA384Key();
System.out.println(HexUtil.encodeHexStr(key));
bytes = encodeHmacSHA384(str.getBytes(), key);
System.out.println(HexUtil.encodeHexStr(bytes));
System.out.println("HmacSHA512***************************");
key = initHmacSHA512Key();
System.out.println(HexUtil.encodeHexStr(key));
bytes = encodeHmacSHA512(str.getBytes(), key);
System.out.println(HexUtil.encodeHexStr(bytes));
}
}
4)、Councy Castle实现
提供HmacMD2、HmacMD4和HmacSHA224三种算法支持。
public class MACCoderByBC {
/**
* 初始化HmacMD2密钥
* @return
* @throws Exception
*/
public static byte[] initHmacMD2Key() throws Exception {
//加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD2");
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* HmacMD2消息摘要
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encodeHmacMD2(byte[] data, byte[] key) throws Exception {
Security.addProvider(new BouncyCastleProvider());
SecretKey secretKey = new SecretKeySpec(key, "HmacMD2");
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
mac.update(data);
return mac.doFinal();
}
/**
* 初始化HmacMD4密钥
* @return
* @throws Exception
*/
public static byte[] initHmacMD4Key() throws Exception {
//加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD4");
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* HmacMD4消息摘要
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encodeHmacMD4(byte[] data, byte[] key) throws Exception {
Security.addProvider(new BouncyCastleProvider());
SecretKey secretKey = new SecretKeySpec(key, "HmacMD4");
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
mac.update(data);
return mac.doFinal();
}
/**
* 初始化HmacSHA224密钥
* @return
* @throws Exception
*/
public static byte[] initHmacSHA224Key() throws Exception {
//加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA224");
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* HmacSHA224消息摘要
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encodeHmacSHA224(byte[] data, byte[] key) throws Exception {
Security.addProvider(new BouncyCastleProvider());
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA224");
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
mac.update(data);
return mac.doFinal();
}
public static void main(String[] args) throws Exception {
String str = "hello world";
byte[] md2Key = initHmacMD2Key();
byte[] encodeHmacMD2 = encodeHmacMD2(str.getBytes(), md2Key);
System.out.println(HexUtil.encodeHexStr(encodeHmacMD2));
byte[] md4Key = initHmacMD4Key();
byte[] encodeHmacMD4 = encodeHmacMD4(str.getBytes(), md4Key);
System.out.println(HexUtil.encodeHexStr(encodeHmacMD4));
byte[] sha224Key = initHmacSHA224Key();
byte[] encodeHmacSHA224 = encodeHmacSHA224(str.getBytes(), sha224Key);
System.out.println(HexUtil.encodeHexStr(encodeHmacSHA224));
}
}
3.4、其他摘要算法
RipeMD
:RACE Integrity Primitives Evaluation Message Digest的简称,是由Hans Dobbertin等3人在对MD4,MD5缺陷分析的基础上,于1996年提出的。目前,RipeMD
算法共有4个标准,主要是对摘要值长度的区分,类似于SHA系列算法,包含RipeMD128、RipeMD160、RipeMD256和RipeMD320共4种算法。HmacRipeMD128和HmacRipeMD160算法是RipeMD与MAC算法融合的产物。Tiger
:由Ross于1995年提出。Tiger号称是最快的Hash算法,专门为64位机器做了优化,其消息摘要长度为192位。
-Whirlpool
:Whirlpool已被列入ISO标准,由于它使用了与AES加密标准相同的转化技术,极大地提高了安全性,被称为最安全的摘要算法。Whirlpool在历史上共有3个版本,目前最新的版本是2003年颁布的,通常将其称为Whirlpool3.0,其消息摘要长度为512位。G0ST3411
:对于G0ST3411算法,未能获得更多的相关信息,只能得知该算法得到的摘要信息长度为256位。
算法 | 摘要长度 | 备注 |
---|---|---|
RipeMD128 | 128 | Bouncy Castle实现 |
RipeMD160 | 160 | Bouncy Castle实现 |
RipeMD256 | 256 | Bouncy Castle实现 |
RipeMD320 | 320 | Bouncy Castle实现 |
HmacRipeMD128 | 128 | Bouncy Castle实现 |
HmacRipeMD160 | 160 | Bouncy Castle实现 |
1)、 RipeMD系列算法实现
public class RipeMDCoder {
/**
* RipeMD128消息摘要
* @param data
* @return
* @throws Exception
*/
public static byte[] encodeRipeMD128(byte[] data) throws Exception {
Security.addProvider(new BouncyCastleProvider());
MessageDigest digest = MessageDigest.getInstance("RipeMD128");
return digest.digest(data);
}
/**
* RipeMD160消息摘要
* @param data
* @return
* @throws Exception
*/
public static byte[] encodeRipeMD160(byte[] data) throws Exception {
Security.addProvider(new BouncyCastleProvider());
MessageDigest digest = MessageDigest.getInstance("RipeMD160");
return digest.digest(data);
}
/**
* RipeMD256消息摘要
* @param data
* @return
* @throws Exception
*/
public static byte[] encodeRipeMD256(byte[] data) throws Exception {
Security.addProvider(new BouncyCastleProvider());
MessageDigest digest = MessageDigest.getInstance("RipeMD256");
return digest.digest(data);
}
/**
* RipeMD320消息摘要
* @param data
* @return
* @throws Exception
*/
public static byte[] encodeRipeMD320(byte[] data) throws Exception {
Security.addProvider(new BouncyCastleProvider());
MessageDigest digest = MessageDigest.getInstance("RipeMD320");
return digest.digest(data);
}
public static void main(String[] args) throws Exception{
String str = "hello world";
byte[] ripeMD128 = encodeRipeMD128(str.getBytes());
System.out.println(HexUtil.encodeHexStr(ripeMD128));
byte[] ripeMD160 = encodeRipeMD160(str.getBytes());
System.out.println(HexUtil.encodeHexStr(ripeMD160));
byte[] ripeMD256 = encodeRipeMD256(str.getBytes());
System.out.println(HexUtil.encodeHexStr(ripeMD256));
byte[] ripeMD320 = encodeRipeMD320(str.getBytes());
System.out.println(HexUtil.encodeHexStr(ripeMD320));
}
}
2)、 HmacRipeMD系列算法实现
public class HmacRipeMDCoder {
/**
* 初始化HmacRipeMD128密钥
* @return
* @throws Exception
*/
public static byte[] initHmacRipeMD128Key() throws Exception {
Security.addProvider(new BouncyCastleProvider());
KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacRipeMD128");
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* HmacRipeMD128消息摘要
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encodeHmacRipeMD128(byte[] data, byte[] key) throws Exception {
Security.addProvider(new BouncyCastleProvider());
SecretKey secretKey = new SecretKeySpec(key, "HmacRipeMD128");
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
mac.update(data);
return mac.doFinal();
}
/**
* 初始化HmacRipeMD160密钥
* @return
* @throws Exception
*/
public static byte[] initHmacRipeMD160Key() throws Exception {
Security.addProvider(new BouncyCastleProvider());
KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacRipeMD160");
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* HmacRipeMD160消息摘要
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encodeHmacRipeMD160(byte[] data, byte[] key) throws Exception {
Security.addProvider(new BouncyCastleProvider());
SecretKey secretKey = new SecretKeySpec(key, "HmacRipeMD160");
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
mac.update(data);
return mac.doFinal();
}
public static void main(String[] args) throws Exception {
String str = "hello world";
byte[] hmacRipeMD128Key = initHmacRipeMD128Key();
byte[] encodeHmacRipeMD128 = encodeHmacRipeMD128(str.getBytes(), hmacRipeMD128Key);
System.out.println(HexUtil.encodeHexStr(encodeHmacRipeMD128));
byte[] hmacRipeMD160Key = initHmacRipeMD160Key();
byte[] encodeHmacRipeMD160 = encodeHmacRipeMD160(str.getBytes(), hmacRipeMD160Key);
System.out.println(HexUtil.encodeHexStr(encodeHmacRipeMD160));
}
}
3.5、CRC(循环冗余校验算法)
1)、定义
CRC(Cyclic Redundancy Check,循环冗余校验)是可以根据数据产生简短固定位数的一种散列函数,主要用来检测或校验数据传输/保存后出现的错误。生成的散列值在传输或储存之前计算出来并且附加到数据后面。在使用数据之前,对数据的完整性做校验。一般来说,循环冗余校验的值都是32位的二进制数,以8位十六进制字符串形式表示。它是一类重要的线性分组码,编码和解码方法简单,检错和纠错能力强,在通信领域广泛地用于实现差错控制。
由上述内容分析,消息摘要算法与CRC算法同属散列函数,并且CRC算法很可能就是消息摘要算法的前身。
CRC算法历经了多个版本的演进,从最初的CRC-1算法至最终的CRC-160算法,为消息摘要算法奠定了基础。我们简单回顾一下CRC算法的发展历程。
CRC-1
:主要用于硬件,就是我们常说的奇偶校验码。CRC-32-IEEE802.3
:主要用于通信领域实现差错控制,也就是我们今天常说的CRC-32,IEEE802.3只是一种标准。CRC-32-Adler
:CRC-32的一个变种,可以称为“Adler–32”,与CRC-32一样可靠,但是速度更快。CRC-128
:演变为今天的MD算法。MD算法消息摘要值为128位二进制数。CRC-160
:演变为今天的SHA算法。SHA-1算法消息摘要值为160位二进制数。
至今,CRC-32算法仍是各种压缩算法中最为常用的数据完整性校验算法。而它的变种Adler-32普遍用于zlib压缩算法中的数据完整性校验。
2)、模型分析
以甲方乙方传递压缩数据模型为例:
3)、Java实现
在Java中,CRC-32算法由CRC32类来实现,Adler-32算法由Adler32类实现。两者都实现了Checksum接口。除此之外,还有CheckedInputStream和CheckedOutputStream两个类,可用于输入输出流的冗余校验处理。
public class CRCTest {
public static void main(String[] args) {
String str = "hello world";
//创建CRC32对象
CRC32 crc32 = new CRC32();
//使用指定的字节数组更新校验和,可以多次调用
crc32.update(str.getBytes());
//返回CRC32值
long value = crc32.getValue();
//d4a1185
System.out.println(Long.toHexString(value));
}
}
public class Adler32Test {
public static void main(String[] args) {
String str = "hello world";
Adler32 adler32 = new Adler32();
adler32.update(str.getBytes());
long value = adler32.getValue();
//1a0b045d
System.out.println(Long.toHexString(value));
}
}
public class CRCStreamTest {
public static void main(String[] args) throws Exception{
String str = "hello world";
//输入流
ByteArrayInputStream bis = new ByteArrayInputStream(str.getBytes());
CheckedInputStream cis = new CheckedInputStream(bis, new CRC32());
byte[] buf = new byte[1024];
while (-1 != cis.read(buf)) {
}
cis.close();
long value = cis.getChecksum().getValue();
System.out.println(Long.toHexString(value));
//输出流
FileOutputStream fos = new FileOutputStream("a.txt");
CheckedOutputStream cos = new CheckedOutputStream(fos, new CRC32());
cos.write(str.getBytes());
cos.flush();
cos.close();
long value1 = cos.getChecksum().getValue();
System.out.println(Long.toHexString(value1));
}
}
4、对称加密算法
对称加密就是加密与解密拥有相同的密钥,根据加密方式可分为密码和分组密码。分组密码工作模式可分为ECB、CBC、CFB、OFB和CTR等,密钥长度决定了加密算法的安全性。常见的加密算法有DES或3DES、AES和RC系列算法,除此之外,还有Blowfish、Twofish、Serpent、IDEA和PBE等。
4.1、对称密码体制
对称密码体制的保密通信模型如图所示。对称密码体制要求加密与解密使用同一个共享密钥,解密是加密的逆运算,由于通信双方共享同一个密钥,这就要求通信双方必须在通信前商定该密钥,并妥善保存该密钥。该密钥称为秘密密钥。秘密密钥的存在使得对称密码体制开放性变差。
对称密码体制分为两种:
- 一种是对明文的单个位(或字节)进行加密和解密,称为流密码,又称为序列密码;
- 另一种是把明文信息划分成不同的组(或块)结构,分别对每个组(或块)进行加密和解密,称为分组密码。
1)、流密码
流密码是军事、外交等机要部门中应用最为广泛的对称密码体制。同时,它也是手机应用平台最常用的加密手段。流密码实现较为简单,加密时将明文按字符(或字节)逐位进行加密,解密时将密文按字符(字节)逐位解密。加密、解密可以是简单的位运算,如模运算。明文加密后,生成的密文几乎和明文保持同样的长度。流密码加密与解密的流程如图所示。
同步流密码:
应用同步流密码,信息发送方和接收方在传递信息时,同步进行加密解密操作,明文与密文一一对应。密文的内容如果在传输过程中被篡改、删除或插入,可导致同步失效,以致密文解密失败,必须通过重新同步来实现解密、恢复密文。在密文传输过程中,如果一个密文位发生变化,那么该位的变化只影响该位的恢复,对后续密文位不影响,这是同步流密码的一个重要特点。但是,根据该特点主动攻击者可以有选择地对密文字符进行改动,并准确知道这些改动对明文的影响。因此,同步流密码具有同步性、无错误传递性及主动攻击性三种特性。同步流密码适用于为音频和视频数据提供版权保护。
自同步流密码:
与同步流密码相比,自同步流密码是一种有记忆变换的密码。每一个密钥与已产生的固定数量的密文位有关,密钥由已生成的密文决定。在密文传输过程中,如果一个密文位发生变化,那么该位的变化会影响到后续有限(如位)的密文位的正确解密。所以,自同步流密码有错误传递现象。但是,在接收位正确密文字符后,密码自身会实现重新同步。基于这一特点,如果主动攻击者对密文做了修改,接收方仍然不能检测出密文的完整性。与同步流密码相比,自同步流密码的密码分析更加困难,安全性更高因此,自同步流密码具有自同步性、错误传递有限性、主动攻击性及明文统计扩散性四种特性。
流密码具有实现简单、便于硬件计算、加密与解密处理速度快、错误传播率低等优点。但是,流密码对错误的产生不够敏感,这是流密码的缺点。为了弥补这一缺点,流密码通常配合其他技术验证信息的完整性。流密码涉及大量的理论知识,受限于应用场合(目前主要用于军事和外交等机要部门),许多研究成果并未完全公开。目前使用较多的流密码是自同步流密码。流密码的常用算法有RC4和SEAL等。
流密码的安全强度依赖于密钥流生成器所产生的密钥流序列的特征,关键在于密钥生成器的设计以及信息收发两端密钥流产生的同步技术。
2)、分组密码
分组密码多应用于网络加密,是对称密码体制中发展最为完善的密码体制。分组密码对固定长度的一组明文进行加密,这一固定长度称为分组长度。分组长度是分组密码的一个参数,它与分组算法的安全性成正比,其取值范围取决于实际应用的环境。为保证分组算法的安全性,分组长度越长越好,分组长度越长,密码分析越困难;为保证分组密码的实用性,分组长度越短越好,分组长度越短,越便于操作和运算。分组长度的设定需要权衡分组算法的安全性与实用性,一般设置为56位。但随着密码学的发展,分组长度只有56位的分组密码已经不能确保算法的安全性。目前,分组密码多选择128位作为算法的分组长度。
分组密码的加密过程是对一个分组长度为n
的明文分组进行加密操作,相应地产生一个n
位的密文分组,由此可见,不同的n
位明文分组共有2”
个。考虑到加密算法的可逆性(即保证解密过程的可行性),每一个不同的n
位明文分组都应该产生一个唯一的密文分组,加密过程对应的变换称为可逆变换或非奇异变换。所以,分组密码算法从本质上来说是定义了一种从分组的明文到相应的密文的可逆变换。
4.2、分组密码的工作模式
根据数据加密时每个加密区块间的关联方式,可以分为如下几种工作模式:
- 电子密码本模式(Electronic Code Book, ECB)
- 密文链接模式(Cipher Book Chaining, CBC)
- 密文反馈模式(Cipher Feed Back, CFB)
- 输出反馈模式(Output Feed Back, OFB)
- 计数器模式(Counter, CTR)
1)、电子密码本模式(ECB)
电子密码本模式如图所示,它是最基本、最易理解的工作模式。每次加密均产生独立的密文分组,每组的加密结果不会对其他分组产生影响,相同的明文加密后对应产生相同的密文,无初始化向量(也称为加密向量)
。可以认为有一个非常大的电码本,对任意一个可能的明文分组,电码本中都有一项对应于它的密文,这也是该模式名称的由来。
- 优点:易于理解且简单易行;便于实现并行操作;没有误差传递的问题。
- 缺点:不能隐藏明文的模式,如果明文重复,则对应的密文也会重复,密文内容很容易被替换、重排、删除、重放;对明文进行主动攻击的可能性较高。
- 用途:适合加密密钥、随机数等短数据。例如,安全地传递DES密钥,ECB是最合适的模式。
2)、密文链接模式(CBC,已丧失安全性)
密文链接模式如图所示,它是目前应用最广泛的工作模式。明文加密前需先与前面的密文进行异或运算(XOR)后再加密,因此只要选择不同的初始向量,相同的明文加密后产生不同的密文。
- 优点:密文链接模式加密后的密文上下文关联,即使在明文中出现重复的信息也不会产生相同的密文;密文内容如果被替换、重排、删除、重放或网络传输过程中发生错误,后续密文即被破坏,无法完成解密还原;对明文的主动攻击的可能性较低。
- 缺点:不利于并行计算,目前没有已知的并行运算算法;误差传递,如果在加密过程中发生错误,则错误将被无限放大,导致加密失败;
需要初始化向量。
- 用途:可加密任意长度的数据;适用于计算产生检测数据完整性的消息认证码Mac。
3)、密文反馈模式(CFB)
密文反馈模式如图所示,它类似于自同步流密码,分组加密后,按8位分组将密文和明文进行移位异或后得到输出同时反馈给移位寄存器。它的优点是可以按字节逐个进行加密解密,也可以按位字节处理。CFB是上下文相关的,明文的一个错误会影响后面的密文(错误扩散)。CFB需要一个初始化向量
,加密后与第一个分组进行异或运算产生第一组密文;然后,对第一组密文加密后再与第二个分组进行异或运算取得第二组密文;以此类推,直到加密完毕。
- 优点:隐藏了明文的模式,每一个分组的加密结果必受其前面所有分组内容的影响,即使出现多次相同的明文,也均产生不同的密文;分组密码转化为流模式,可产生密钥
流;可以及时加密传送小于分组的数据。 - 缺点:与CBC相类似。不利于并行计算,目前没有已知的并行运算算法;存在误差传送,一个单元损坏影响多个单元;需要初始化向量。
- 用途:因错误传播无界,可用于检查发现明文密文的篡改。
4)、输出反馈模式(OFB)
输出反馈模式如图所示,它将分组密码作为同步流密码运行,和CFB相似,不过OFB用的是前一个位密文输出分组反馈给移位寄存器,OFB没有错误扩散问题。该模式产生与明文异或运算的密钥流,从而产生密文,这一点与CFB大致相同,唯一的差异是与明文分组进行异或的输入部分是反复加密后得到的。
- 优点:隐藏了明文的模式;分组密码转化为流模式;无误差传送问题;可以及时加密传送小于分组的数据。
- 缺点:不利于并行计算;对明文的主动攻击是可能的,安全性较CFB差。
- 用途:适用于加密冗余性较大的数据,比如语音和图像数据。
5)、计数器模式(CTR)
计数器模式如图所示,它的特点是将计数器从初始值开始计数所得到的值发送给分组密码算法。随着计数器的增加,分组密码算法输出连续的分组来构成一个位串,该位串被用来与明文分组进行异或运算。计数器模式是用来提取分组密码的最大效能以实现保密性的。在AES的实际应用中,经常会选择CBC模式和CTR模式,但更多的是选择CTR模式。
- 优点:可并行计算;安全性至少与CBC模式一样好;加密与解密仅涉及密码算法的加密。
- 缺点:没有错误传播,因此不易确保数据完整性。
- 用途:适用于各种加密应用。
4.3、DES算法
1)、定义
DES(Data Encryption Standard)数据加密标准,密钥长度为56位,其衍生算法有DESede(3DES),由于安全性的原因AES逐步替代DES。
算法 | 密钥长度 | 密钥长度默认值 | 工作模式 | 填充方式 | 备注 |
---|---|---|---|---|---|
DES | 56 | 56 | ECB CBC PCBC CTR CTS CFB CFB8至CFB128 OFB OFB8至OFB128 | NoPadding PKCS5Padding ISO10126Padding | Java6实现 |
DES | 64 | 同上 | 同上 | PKCS7Padding ISO10126d2Padding X932Padding ISO7816d4Padding ZeroBytePadding | Bouncy Castle实现 |
分组对称加密算法有些运算模式要求明文数据的字节长度必须是其块大小的整倍数,因此在加密明文数据之前我们必须对明文数据进行填充。
常见填充模式:
None
:不填充PKCS7
:填充字符串由一个字节序列组成,每个字节填充该字节序列的长度Zeros
:填充字符串由设置为零的字节组成ANSIX923
:填充字符串由一个字节序列组成,此字节序列的最后一个字节填充字节序列的长度,其余字节均填充数字零ISO10126
:填充字符串由一个字节序列组成,此字节序列的最后一个字节填充字节序列的长度,其余字节填充随机数据
2)、模型分析
甲乙双方作为消息传递双方(甲方作为发送方,乙方作为接收方),我们假定甲乙双方在消息传递前已商定加密算法,欲完成一次消息传递需经过如下步骤:
- 由消息传递双方约定密钥,这里由甲方构建密钥
- 由密钥构建者公布密钥,这里由甲方将密钥公布给乙方
- 由消息发送方使用密钥对数据加密,这里由甲方将加密数据发送给乙方
- 由消息发送方将加密数据发送给消息接收者,这里由甲方将数据发送给乙方
- 由消息接收方使用密钥对加密数据解密,这里由乙方完成数据解密
3)、Java实现
Java7仅支持56位密钥长度。
public class DESCoder {
/**
* 密钥算法 <br>
* Java7只支持56bit密钥 <br>
*/
public static final String KEY_ALGORITHM = "DES";
/**
* 加密/解密算法 / 工作模式 / 填充方式
*/
public static final String ECB_CIPHER_ALGORITHM = "DES/ECB/PKCS5PADDING";
public static final String CBC_CIPHER_ALGORITHM = "DES/CBC/ISO10126PADDING";
/**
* 偏移变量,固定占8字节,CBC模式使用
*/
private static final String IV_PARAMETER = "12345678";
/**
* 生成密钥
* Java7 只支持56bit密钥
*
* @return byte[] 二进制密钥
* @throws NoSuchAlgorithmException
*/
public static byte[] initKey() throws NoSuchAlgorithmException {
/*
* 实例化密钥生成器
*/
KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
/*
* 初始化密钥生成器 若要使用64bit密钥注意替换 将下述代码kg.init(56); 替换为kg.init(64);
*/
keyGenerator.init(56, new SecureRandom());
// 生成秘密密钥
SecretKey secretKey = keyGenerator.generateKey();
// 获得密钥的二进制编码形式
return secretKey.getEncoded();
}
/**
* 转换密钥
* @param key 二进制密钥
* @return 密钥对象
* @throws Exception
*/
private static Key toKey(byte[] key) throws Exception {
// 实例化DES密钥规范
DESKeySpec dks = new DESKeySpec(key);
//实例化密钥工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
//生成密钥
SecretKey secretKey = keyFactory.generateSecret(dks);
return secretKey;
}
/**
* ecb模式加密
* @param data 待加密数据
* @param key 密钥
* @return 加密后的数据
* @throws Exception
*/
public static byte[] ecbEncrypt(byte[] data, byte[] key) throws Exception{
//还原密钥
Key k = toKey(key);
//实例化加密算法
Cipher cipher = Cipher.getInstance(ECB_CIPHER_ALGORITHM);
//初始化,设置为加密模式
cipher.init(Cipher.ENCRYPT_MODE, k);
//执行操作
return cipher.doFinal(data);
}
/**
* ecb解密
* @param data 待解密数据
* @param key 密钥
* @return 被解密的数据
* @throws Exception
*/
public static byte[] ecbDecrypt(byte[] data, byte[] key) throws Exception {
//还原密钥
Key k = toKey(key);
//实例化算法
Cipher cipher = Cipher.getInstance(ECB_CIPHER_ALGORITHM);
//初始化,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, k);
//执行操作
return cipher.doFinal(data);
}
/**
* cbc模式加密
* @param data 待加密数据
* @param key 密钥
* @return 加密后的数据
* @throws Exception
*/
public static byte[] cbcEncrypt(byte[] data, byte[] key) throws Exception{
//还原密钥
Key k = toKey(key);
//偏移参数
IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes());
//实例化加密算法
Cipher cipher = Cipher.getInstance(CBC_CIPHER_ALGORITHM);
//初始化,设置为加密模式
cipher.init(Cipher.ENCRYPT_MODE, k, iv);
//执行操作
return cipher.doFinal(data);
}
/**
* cbc解密
* @param data 待解密数据
* @param key 密钥
* @return 被解密的数据
* @throws Exception
*/
public static byte[] cbcDecrypt(byte[] data, byte[] key) throws Exception {
//还原密钥
Key k = toKey(key);
//偏移参数
IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes());
//实例化算法
Cipher cipher = Cipher.getInstance(CBC_CIPHER_ALGORITHM);
//初始化,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, k, iv);
//执行操作
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
System.out.println("ebc**************************************");
byte[] key = initKey();
System.out.println("key:" + HexUtil.encodeHexStr(key));
byte[] encrypt = ecbEncrypt(str.getBytes(), key);
System.out.println("encrypt:" + HexUtil.encodeHexStr(encrypt));
byte[] decrypt = ecbDecrypt(encrypt, key);
System.out.println("decrypt:" + new String(decrypt));
System.out.println("ebc**************************************");
key = initKey();
System.out.println("key:" + HexUtil.encodeHexStr(key));
encrypt = cbcEncrypt(str.getBytes(), key);
System.out.println("encrypt:" + HexUtil.encodeHexStr(encrypt));
decrypt = cbcDecrypt(encrypt, key);
System.out.println("decrypt:" + new String(decrypt));
}
}
4)、Boucy Calstle实现
Boucy Calstle实现提供64位密钥和PKCS7Padding等填充方式。
public class DESCoderByBC {
/**
* 密钥算法 <br>
*/
public static final String KEY_ALGORITHM = "DES";
/**
* 加密/解密算法 / 工作模式 / 填充方式
*/
public static final String CBC_CIPHER_ALGORITHM = "DES/CBC/PKCS7PADDING";
/**
* 偏移变量,固定占8字节,CBC模式使用
*/
private static final String IV_PARAMETER = "12345678";
/**
* 生成密钥
* @return byte[] 二进制密钥
* @throws NoSuchAlgorithmException
*/
public static byte[] initKey() throws NoSuchAlgorithmException {
Security.addProvider(new BouncyCastleProvider());
KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
//64位
keyGenerator.init(64, new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* 转换密钥
* @param key 二进制密钥
* @return 密钥对象
* @throws Exception
*/
private static Key toKey(byte[] key) throws Exception {
// 实例化DES密钥规范
DESKeySpec dks = new DESKeySpec(key);
//实例化密钥工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
//生成密钥
SecretKey secretKey = keyFactory.generateSecret(dks);
return secretKey;
}
/**
* cbc模式加密
* @param data 待加密数据
* @param key 密钥
* @return 加密后的数据
* @throws Exception
*/
public static byte[] cbcEncrypt(byte[] data, byte[] key) throws Exception{
Key k = toKey(key);
IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes());
Cipher cipher = Cipher.getInstance(CBC_CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, k, iv);
return cipher.doFinal(data);
}
/**
* cbc解密
* @param data 待解密数据
* @param key 密钥
* @return 被解密的数据
* @throws Exception
*/
public static byte[] cbcDecrypt(byte[] data, byte[] key) throws Exception {
Key k = toKey(key);
IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes());
Cipher cipher = Cipher.getInstance(CBC_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, k, iv);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
byte[] key = initKey();
System.out.println("key:" + HexUtil.encodeHexStr(key));
byte[] encrypt = cbcEncrypt(str.getBytes(), key);
System.out.println("encrypt:" + HexUtil.encodeHexStr(encrypt));
byte[] decrypt = cbcDecrypt(encrypt, key);
System.out.println("decrypt:" + new String(decrypt));
}
}
4.4、DESede算法(三重DES)
1)、定义
DES算法有3点安全隐患:密钥太短、迭代偏少和半公开性。这使得淘汰DES算法成为一种必然,但要淘汰DES算法必须找到合适的替代方案。
针对密钥太短和迭代偏少问题,有人提出了多重DES的方式来克服这些缺陷。比较典型的有两重DES(2DES)、三重DES(3DES)和四重DES(4DES)等几种形式,但在实际应用中一般采用3DES方案,它还有两个别名Triple DES和DESede。在Java中,我们通常称其为DESede算法。当然,其他两种名称在使用时同样可以获得支持。
DESede算法将密钥长度增至112位或168位,抗穷举攻击的的能力显著增强,但核心仍是DES算法,虽然通过增加迭代次数提高了安全性,但同时也造成处理速度较慢、密钥计算时间加长、加密效率不高的问题。
算法 | 密钥长度 | 密钥长度默认值 | 工作模式 | 填充方式 | 备注 |
---|---|---|---|---|---|
DESede(Triple DES,3DES) | 112、168 | 168 | ECB CBC PCBC CTR CTS CFB CFB8至CFB128 OFB OFB8至OFB128 | NoPadding PKCS5Padding ISO10126Padding | Java6实现 |
DESede(Triple DES,3DES) | 128、192 | 同上 | 同上 | PKCS7Padding ISO10126d2Padding X932Pading ISO7816d4Padding ZeroBytePadding | Bouncy Castle实现 |
2)、Java实现
public class DESedeCoder {
/**
* 密钥算法 Java支持密钥长度为112位或168位
*/
public static final String KEY_ALGORITHM = "DESede";
/**
* 算法/工作模式/填充方式
*/
public static final String CIPHER_ALGORITHM = "DESede/ECB/PKCS5Padding";
/**
* 生成密钥
* @return
* @throws Exception
*/
public static byte[] initKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
//java支持112和168位
keyGenerator.init(168);
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* 转换密钥
* @param key
* @return
* @throws Exception
*/
private static Key toKey(byte[] key) throws Exception {
DESedeKeySpec dks = new DESedeKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
return keyFactory.generateSecret(dks);
}
/**
* 加密
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
Key k = toKey(key);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, k);
return cipher.doFinal(data);
}
/**
* 解密
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
Key k = toKey(key);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, k);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception{
String str = "hello world";
byte[] key = initKey();
byte[] encrypt = encrypt(str.getBytes(), key);
System.out.println(HexUtil.encodeHexStr(encrypt));
byte[] decrypt = decrypt(encrypt, key);
System.out.println(new String(decrypt));
}
}
3)、Bouncy Castle实现
public class DESedeCoderByBC {
/**
* 密钥算法 BC支持密钥长度为128位或192位
*/
public static final String KEY_ALGORITHM = "DESede";
/**
* 算法/工作模式/填充方式
*/
public static final String CIPHER_ALGORITHM = "DESede/ECB/PKCS7Padding";
/**
* 生成密钥
* @return
* @throws Exception
*/
public static byte[] initKey() throws Exception {
Security.addProvider(new BouncyCastleProvider());
KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
keyGenerator.init(192);
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* 转换密钥
* @param key
* @return
* @throws Exception
*/
private static Key toKey(byte[] key) throws Exception {
DESedeKeySpec dks = new DESedeKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
return keyFactory.generateSecret(dks);
}
/**
* 加密
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
Key k = toKey(key);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, k);
return cipher.doFinal(data);
}
/**
* 解密
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
Key k = toKey(key);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, k);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception{
String str = "hello world";
byte[] key = initKey();
byte[] encrypt = encrypt(str.getBytes(), key);
System.out.println(HexUtil.encodeHexStr(encrypt));
byte[] decrypt = decrypt(encrypt, key);
System.out.println(new String(decrypt));
}
}
4.5、AES算法
1)、定义
AES(Advanced Encryption Standard)高级数据加密标准,作为DES算法的替代,支持128、192和256位密钥。AES算法具有密钥建立时间短、灵敏性好、内存需求低等优点,应用比较广泛。
算法 | 密钥长度 | 密钥长度默认值 | 工作模式 | 填充方式 | 备注 |
---|---|---|---|---|---|
AES | 128、192、256 | 128 | ECB CBC PCBC CTR CTS CFB CFB8至CFB128 OFB OFB8至OFB128 | NoPadding PKCS5Padding ISO10126Padding | Java6实现 若使用256位密钥需要获得无政策限制权限文件(Unlimited Strength Jurisdiction Policy Files) |
AES | 同上 | 同上 | PKS7Padding ZeroBytePadding | Bouncy Castle实现 |
2)、实现
public class AESCoder {
public static final String KEY_ALGORITHM = "AES";
/**
* 加密/解密算法 / 工作模式 / 填充方式 Java7支持PKCS5Padding填充方式
* Bouncy Castle支持PKCS7Padding填充方式
*/
public static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5PADDING";
/**
* 生成密钥
* @return byite[] 二进制密钥
* @throws Exception
*/
public static byte[] initKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
//AES要求密钥长度为128、192或256
keyGenerator.init(256);
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* 转换密钥
*
* @param key
* 二进制密钥
* @return Key 密钥
* @throws Exception
*/
private static Key toKey(byte[] key) throws Exception {
// 实例化AES密钥材料
SecretKey secretKey = new SecretKeySpec(key, KEY_ALGORITHM);
return secretKey;
}
/**
* 加密
* @param data 待加密数据
* @param key 密钥
* @return byte[] 加密后的数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
Key k = toKey(key);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, k);
return cipher.doFinal(data);
}
/**
* 解密
* @param data 待解密数据
* @param key 密钥
* @return byte[] 解密后的数据
* @throws Exception
*/
public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
Key k = toKey(key);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, k);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
System.out.println(HexUtil.encodeHexStr(str.getBytes()));
byte[] key = initKey();
System.out.println(HexUtil.encodeHexStr(key));
byte[] encrypt = encrypt(str.getBytes(), key);
System.out.println(HexUtil.encodeHexStr(encrypt));
byte[] decrypt = decrypt(encrypt, key);
System.out.println(new String(decrypt));
}
}
4.6、IDEA算法
1)、定义
IDEA(International Data Encryption Algorithm)国际数据加密算法是一种对称分组密码,其密钥长度为128位,数据块大小为64位,目前常用的场景是邮件加密算法。
算法 | 密钥长度 | 密钥长度默认值 | 工作模式 | 填充方式 | 备注 |
---|---|---|---|---|---|
IDEA | 128 | 128 | ECB | PKCS5Padding PKCS7Padding ISO10126Padding ZeroBytePadding | Bouncy Castle实现 |
2)、Bouncy Castle实现
public class IDEACoder {
public static final String KEY_ALGORITHM = "IDEA";
//加密/解密算法 / 工作模式 / 填充方式
public static final String CIPHER_ALGORITHM = "IDEA/ECB/PKCS5Padding";
/**
* 生成密钥
* @return
* @throws Exception
*/
public static byte[] initKey() throws Exception {
//加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
/**
* 转换Key
* @param key 二进制密钥
* @return Key 密钥对象
* @throws Exception
*/
private static Key toKey(byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, KEY_ALGORITHM);
return secretKey;
}
/**
* 加密
* @param data 待加密数据
* @param key 密钥
* @return byte[] 被加密的数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
Security.addProvider(new BouncyCastleProvider());
Key k = toKey(key);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, k);
return cipher.doFinal(data);
}
/**
* 解密
* @param data 待解密数据
* @param key 密钥
* @return 解密后的数据
* @throws Exception
*/
public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
Security.addProvider(new BouncyCastleProvider());
Key k = toKey(key);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, k);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception{
String str = "hello world";
System.out.println(HexUtil.encodeHexStr(str.getBytes()));
byte[] key = initKey();
System.out.println(HexUtil.encodeHexStr(key));
byte[] encrypt = encrypt(str.getBytes(), key);
System.out.println(HexUtil.encodeHexStr(encrypt));
byte[] decrypt = decrypt(encrypt, key);
System.out.println(new String(decrypt));
}
}
4.7、PBE算法(基于口令加密)
1)、定义
PBE(Password Based Encryption)基于密码加密算法是一种基于密码的加密算法,其特点是密码(password)由用户自己保管,采用随机数(加盐)杂凑多重加密等方法保证数据的安全性。PBE没有密钥,其密码(password)代替了密钥,为了增加密码的安全性,采用了加盐的处理方式。PBE是对称加密算法的综合性算法,常见的算法有PBEWithMD5AndDES,该算法是使用了MD5和DES构建PBE算法。
算法 | 密钥长度 | 密钥长度默认值 | 工作模式 | 填充方式 | 备注 |
---|---|---|---|---|---|
PBEWithMD5AndDES | 56 | 56 | CBC | PKCS5Padding | Java6实现 |
PBEWithMD5AndTripleDES | 112、168 | 168 | 同上 | 同上 | 同上 |
PBEWithSHA1AndDESede | 112、168 | 168 | 同上 | 同上 | 同上 |
PBEWithSHA1AndRC2_40 | 40至1024(8de倍数) | 128 | 同上 | 同上 | 同上 |
PBEWithMD5AndDES | 64 | 64 | CBC | PKCS5Padding PKCS7Padding ISO10126Padding ZeroBytePadding | Bouncy Castle实现 |
PBEWithMD5AndRC2 | 128 | 128 | 同上 | 同上 | 同上 |
PBEWithSHA1AndDES | 64 | 64 | 同上 | 同上 | 同上 |
PBEWithSHA1AndRC2 | 128 | 128 | 同上 | 同上 | 同上 |
PBEWithSHAAndIDEA-CBC | 128 | 128 | 同上 | 同上 | 同上 |
PBEWithSHAAnd2-KeyTripleDES-CBC | 128 | 128 | 同上 | 同上 | 同上 |
PBEWithSHAAnd3-KeyTripleDES-CBC | 192 | 192 | 同上 | 同上 | 同上 |
PBEWithSHAAnd128BitRC2-CBC | 128 | 128 | 同上 | 同上 | 同上 |
PBEWithSHAAnd40BitRC2-CBC | 40 | 40 | 同上 | 同上 | 同上 |
PBEWithSHAAnd128BitRC4 | 128 | 128 | 同上 | 同上 | 同上 |
PBEWithSHAAnd40BitRC4 | 40 | 40 | 同上 | 同上 | 同上 |
PBEWithSHAAndTwofish-CBC | 256 | 256 | 同上 | 同上 | 同上 |
2)、模型分析
基于PBE算法的消息传递模型与基于DES算法的消息传递模型还是有一定差别的,如图所示:
甲乙双方作为消息传递双方(甲方作为发送方,乙方作为接收方),我们假定甲乙双方在消息传递前已商定加密算法和迭代次数,欲完成一次消息传递需经过如下步骤:
- 由消息传递双方约定口令,这里由甲方构建口令。
- 由口令构建者公布口令,这里由甲方将口令公布给乙方。
- 由口令构建者构建本次消息传的使用的盐,这里由甲方构建盐。
- 由消息发送方使用口令、盐对数据加密,这里由甲方对数据加密。
- 由消息发送方将盐、加密数据发送给消息接收者,这里由甲方将盐、加密数据发送给乙方。
- 由消息接收方使用盐、口令对加密数据解密,这里由乙方完成数据解密。
3)、Java实现
public class PBECoder {
/**
* Java 6 支持以下任意一种算法
* PBEWithMD5AndDES
* PBEWithMD5AndTripleDES
* PBEWithSHA1AndDESede
* PBEWithSHA1AndRC2_40
*/
public static final String ALGORITHM = "PBEWithMD5AndTripleDES";
/**
* 盐初始化,长度必须为8字节
* @return byte[] 盐
* @throws Exception
*/
public static byte[] initSalt() throws Exception {
SecureRandom random = new SecureRandom();
return random.generateSeed(8);
}
/**
* 转换密钥
* @param password 密码
* @return Key 密钥
* @throws Exception
*/
private static Key toKey(String password) throws Exception {
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
SecretKey secretKey = keyFactory.generateSecret(keySpec);
return secretKey;
}
/**
* 加密
* @param data 待加密的数据
* @param password 密码
* @param salt 盐
* @return byte[] 加密后的数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, String password, byte[] salt) throws Exception {
Key key = toKey(password);
//实例化PBE参数
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 100);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
return cipher.doFinal(data);
}
/**
* 解密
* @param data 待解密的数据
* @param password 密码
* @param salt 盐
* @return byte[] 解密后的数据
* @throws Exception
*/
public static byte[] decrypt(byte[] data, String password, byte[] salt) throws Exception {
Key key = toKey(password);
//实例化PBE参数
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 100);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
String password = "123456";
byte[] salt = initSalt();
System.out.println(HexUtil.encodeHexStr(salt));
byte[] encrypt = encrypt(str.getBytes(), password, salt);
System.out.println(HexUtil.encodeHexStr(encrypt));
byte[] decrypt = decrypt(encrypt, password, salt);
System.out.println(new String(decrypt));
}
}
4)、Bouncy Castle实现
public class PBECoderByBC {
/**
* BC支持的算法
*/
public static final String ALGORITHM = "PBEWithSHAAnd128BitRC4";
/**
* 盐初始化,长度必须为8字节
* @return byte[] 盐
* @throws Exception
*/
public static byte[] initSalt() throws Exception {
SecureRandom random = new SecureRandom();
return random.generateSeed(8);
}
/**
* 转换密钥
* @param password 密码
* @return Key 密钥
* @throws Exception
*/
private static Key toKey(String password) throws Exception {
Security.addProvider(new BouncyCastleProvider());
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
SecretKey secretKey = keyFactory.generateSecret(keySpec);
return secretKey;
}
/**
* 加密
* @param data 待加密的数据
* @param password 密码
* @param salt 盐
* @return byte[] 加密后的数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, String password, byte[] salt) throws Exception {
Key key = toKey(password);
//实例化PBE参数
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 100);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
return cipher.doFinal(data);
}
/**
* 解密
* @param data 待解密的数据
* @param password 密码
* @param salt 盐
* @return byte[] 解密后的数据
* @throws Exception
*/
public static byte[] decrypt(byte[] data, String password, byte[] salt) throws Exception {
Key key = toKey(password);
//实例化PBE参数
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 100);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
String str = "hello world";
String password = "123456";
byte[] salt = initSalt();
System.out.println(HexUtil.encodeHexStr(salt));
byte[] encrypt = encrypt(str.getBytes(), password, salt);
System.out.println(HexUtil.encodeHexStr(encrypt));
byte[] decrypt = decrypt(encrypt, password, salt);
System.out.println(new String(decrypt));
}
}