摘要算法
摘要算法主要包含三大类型:MD算法,SHA算法,已经基于KEY的MAC算法
MD算法
MD算法主要有MD2,MD3,MD4以及最常用的MD5。
SHA算法主要有SHA-1,SHA-224,SHA-256,SHA-384,SHA-512。
MAC算法是基于哈西散列算法(MD系列或者SHA系列等),在HASH散列进行KEY加密
JAVA密码体系对主流摘要算法的支持

MD5算法的调用示例
package com.rcl.platform.demo;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.Security;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class ProviderTest {
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update("我是MD".getBytes());
byte [] md5 = messageDigest.digest();
System.out.println(byte2HexStr(md5));
Security.addProvider(new BouncyCastleProvider());//注册BouncyCastleProvider
Provider provider = Security.getProvider("BC");
messageDigest = MessageDigest.getInstance("MD4", provider);
messageDigest.update("我是MD".getBytes());
byte [] md4 = messageDigest.digest();
System.out.println(byte2HexStr(md4));
messageDigest = MessageDigest.getInstance("SHA512");//调用SHA算法
messageDigest.update("我是SHA".getBytes());
byte [] sha512 = messageDigest.digest();
System.out.println(byte2HexStr(sha512));
SecretKey secretKey = new SecretKeySpec("woshikey".getBytes(), "HmacSHA512");//HMAC算法调用
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
byte [] hmacSHA224 = mac.doFinal("我是SHA".getBytes());
System.out.println(byte2HexStr(hmacSHA224));
}
public static String byte2HexStr(byte[] b) {
String stmp="";
StringBuilder sb = new StringBuilder("");
for (int n=0;n<b.length;n++) {
stmp = Integer.toHexString(b[n] & 0xFF);
sb.append((stmp.length()==1)? "0"+stmp : stmp);
sb.append("");
}
return sb.toString().toLowerCase().trim();
}
}
执行结果
f610c6a17a31eec9d36c5ef646149ea6
d40694ae1650fdb9bfe807833a3b6c89
e7046e9687398575eadf7eadf5854103b9a98fbedc1333cd553bc288b3518f997884cb7b827bc789b973d2dcf818253359133361602518d045d2cc2ff0eb0871
4e8f13266500448abf27d54aeaeb7825ed62c62d6c40633b3066faf10f6b3b9e933ec2e335b9ab5388da1c09161bf75a2e6fdac7f01ebeb1910f187824fc2562
对称加密
1、对称加密体制要求加密与解密使用同一个共享密钥,解密是加密的逆运算,对于共享密钥,要求通讯双方在通讯前协商共享密钥并妥善保存
2、对称加密体制分为两种:一种是对明文的单个位(或单个字节)进行加密,称为流密码;另一种是明文划分为不同的组(或块),分别对组加密,称为分组加密
3、常用的为分组加密,分组加密的安全性与分组长度有关,但是一般默认的长度为56位
4、对称加密有DES,DESede,AES,Blowfish,RC2,RC4,IDEA。相关概念可以百度百科
分组密码工作模式
1、电子密码本模式ECB:每次加密均产生独立的密文分组,每组加密的结果不会对其他组产生影响,无初始化向量
2、密文连接模式CBC:目前应用最广泛的工作模式,组明文加密前需先与前面的组密文进行异或运算(XOR)在加密
3、密文反馈模式CFB:类似于自同步流密码,分组加密后,按8位分组将密文和明文进行位移异或得到输出并同时反馈回位移寄存器
4、输出反馈模式OFB:和CFB类似,不过OFB用的是前一个n位密文输出分组反馈回位移寄存器
5、计数器模式CTR:将计数器从初始值开始计数,所得到的值发送给分组密码,随着计数器的增加,分组密码算法输出连续的分组来构成一个位串,该串用来与明文分组进行异或运算。在AES中,经常使用的是CBC和CTR,CTR用的比CBC更多些
填充模式
对于明文进行分组,最后一个分组长度不满足默认长度,称为短分组。对于短分组如何处理,需要使用到填充模式。
Java对对称加密算法的支持


Bouncy castle对于PBE的支持

示例代码
package com.rcl.platform.demo;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public abstract class DESCoder {
public static void main(String[] args) throws Exception {
String inputStr = "我是加密内容";
byte[] inputData = inputStr.getBytes();
byte[] key = "key123321".getBytes();
inputData = encrypt(inputData, key);
byte[] outputData = decrypt(inputData, key);
System.out.println("解密后:\t" + new String(outputData));
byte [] iv = "11111111".getBytes();
inputData = encryptCBC(inputStr.getBytes(), key, iv);
outputData = decryptCBC(inputData, key, iv);
System.out.println("解密后 CBC模式:\t" + new String(outputData));
byte[] data = encrypt(inputStr.getBytes(), "snowolf1", "11111111".getBytes());//SALT必须为8字节
byte[] output = decrypt(data, "snowolf1", "11111111".getBytes());
System.out.println("PBE解密后:\t" + new String(output));
}
private static Key toKey(byte[] key) throws Exception {
// 实例化DES密钥材料
DESKeySpec dks = new DESKeySpec(key);
// 实例化秘密密钥工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
// 生成秘密密钥
SecretKey secretKey = keyFactory.generateSecret(dks);
return secretKey;
}
/**
* 转换密钥
*
* @param key
* 二进制密钥
* @return Key 密钥
* @throws Exception
*/
private static Key toKeyDESede(byte[] key) throws Exception {
// 实例化DES密钥材料
DESedeKeySpec dks = new DESedeKeySpec(key);
// 实例化秘密密钥工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
// 生成秘密密钥
SecretKey secretKey = keyFactory.generateSecret(dks);
return secretKey;
}
/**
* 转换密钥
*
* @param key
* 二进制密钥
* @return Key 密钥
* @throws Exception
*/
private static Key toKeyAESOrIDEA(byte[] key) throws Exception {
// 实例化AES密钥材料
SecretKey secretKey = new SecretKeySpec(key, "AES");
// SecretKey secretKey = new SecretKeySpec(key, "IDEA");
return secretKey;
}
/**
* 转换密钥
*
* @param password
* 密码
* @return Key 密钥
* @throws Exception
*/
private static Key toKeyPBE(String password) throws Exception {
// 密钥材料转换
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
// 实例化
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndTripleDES");
// 生成密钥
SecretKey secretKey = keyFactory.generateSecret(keySpec);
return secretKey;
}
/**
* 解密
*
* @param data
* 待解密数据
* @param key
* 密钥
* @param ivParameterSpec
* 加密向量
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decryptCBC(byte[] data, byte[] key, byte [] ivParameterSpec) throws Exception {
// 还原密钥
Key k = toKey(key);
// 实例化
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
//初始化加密向量,CBC模式必须参数,参数最少8位
IvParameterSpec param = new IvParameterSpec(ivParameterSpec);//CBC
// 初始化,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, k, param);
// 执行操作
return cipher.doFinal(data);
}
/**
* 加密
*
* @param data
* 待加密数据
* @param key
* 密钥
* @param ivParameterSpec
* 加密向量
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encryptCBC(byte[] data, byte[] key, byte [] ivParameterSpec) throws Exception {
// 还原密钥
Key k = toKey(key);
// 实例化
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
IvParameterSpec param = new IvParameterSpec(ivParameterSpec);//CBC
// 初始化,设置为加密模式
cipher.init(Cipher.ENCRYPT_MODE, k, param);
// 执行操作
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("DES/ECB/PKCS5PADDING");
// 初始化,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, k);
// 执行操作
return cipher.doFinal(data);
}
/**
* 加密
*
* @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("DES/ECB/PKCS5PADDING");
// 初始化,设置为加密模式
cipher.init(Cipher.ENCRYPT_MODE, k);
// 执行操作
return cipher.doFinal(data);
}
/**
* 加密
*
* @param data
* 数据
* @param password
* 密码
* @param salt
* 盐
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, String password, byte[] salt)
throws Exception {
// 转换密钥
Key key = toKeyPBE(password);
// 实例化PBE参数材料
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
// 实例化
Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
// 初始化
cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
// 执行操作
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 = toKeyPBE(password);
// 实例化PBE参数材料
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
// 实例化
Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
// 初始化
cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
// 执行操作
return cipher.doFinal(data);
}
}
执行结果
解密后: 我是加密内容
解密后 CBC模式: 我是加密内容
PBE解密后: 我是加密内容
非对称加密
DH:密钥交换算法
DH算法的目的是解决非对称加密解密性能问题,通过协商的方式产生对称密钥。生产对称密钥的步骤大致如下
1、甲方通过DH算法生成甲方的公私钥对
2、甲方加自己的公钥发送给乙方
3、乙方根据甲方的公钥产生自己的公私钥对
4、甲乙双产生协商的好的对应加密算法的对称密钥。甲方生成是通过甲方私钥+乙方公钥使用DH提供的对称密钥的生成方式产生对称密钥;乙方生成是通过乙方私钥+甲方公钥使用DH提供的对称密钥的生成方式产生对称密钥
关键点:甲方需要公布自己的公钥给乙方,乙方生成需要使用此公钥;无法抵挡中间人攻击
密钥长度:512-1024位
密钥默认长度:1024位
工作模式:无
填充方式:无
JDK6.0提供了实现
package test;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public abstract class DHCoder {
/**
* 非对称加密密钥算法
*/
public static final String KEY_ALGORITHM = "DH";
/**
* 本地密钥算法,即对称加密密钥算法,可选DES、DESede和AES算法
*/
public static final String SECRET_KEY_ALGORITHM = "AES";
/**
* 默认密钥长度
*
* DH算法默认密钥长度为1024 密钥长度必须是64的倍数,其范围在512到1024位之间。
*/
private static final int KEY_SIZE = 512;
/**
* 公钥
*/
private static final String PUBLIC_KEY = "DHPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "DHPrivateKey";
/**
* 初始化甲方密钥
*
* @return Map 甲方密钥Map
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
// 实例化密钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
// 初始化密钥对生成器
keyPairGenerator.initialize(KEY_SIZE);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 甲方公钥
DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
// 甲方私钥
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
// 将密钥对存储在Map中
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 初始化乙方密钥
*
* @param key
* 甲方公钥
* @return Map 乙方密钥Map
* @throws Exception
*/
public static Map<String, Object> initKey(byte[] key) throws Exception {
// 解析甲方公钥
// 转换公钥材料
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 产生公钥
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
// 由甲方公钥构建乙方密钥
DHParameterSpec dhParamSpec = ((DHPublicKey) pubKey).getParams();
// 实例化密钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());
// 初始化密钥对生成器
keyPairGenerator.initialize(dhParamSpec);
// 产生密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 乙方公钥
DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
// 乙方私钥
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
// 将密钥对存储在Map中
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 构建密钥
*
* @param publicKey
* 公钥
* @param privateKey
* 私钥
* @return byte[] 本地密钥
* @throws Exception
*/
public static byte[] getSecretKey(byte[] publicKey, byte[] privateKey)
throws Exception {
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 初始化公钥
// 密钥材料转换
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKey);
// 产生公钥
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
// 初始化私钥
// 密钥材料转换
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
// 产生私钥
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 实例化
KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory
.getAlgorithm());
// 初始化
keyAgree.init(priKey);
keyAgree.doPhase(pubKey, true);
// 生成本地密钥
SecretKey secretKey = keyAgree.generateSecret(SECRET_KEY_ALGORITHM);
return secretKey.getEncoded();
}
}
非对称加密至RSA
JDK6.0提供了RSA加密算法的实现
在RSA算法中,公钥既可以用于解密,也可以用于加密,私钥也是如此。但是公钥加密只能私钥解密,反正私钥加密只能公钥解密。
对于JDK与 Bouncy Castle的实现如下

package test;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
public abstract class RSACoder {
/**
* 非对称加密密钥算法
*/
public static final String KEY_ALGORITHM = "RSA";
/**
* 公钥
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* RSA密钥长度
* 默认1024位,
* 密钥长度必须是64的倍数,
* 范围在512至65536位之间。
*/
private static final int KEY_SIZE = 512;
/**
* 私钥解密
*
* @param data
* 待解密数据
* @param key
* 私钥
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data, byte[] key)
throws Exception {
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 生成私钥
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公钥解密
*
* @param data
* 待解密数据
* @param key
* 公钥
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, byte[] key)
throws Exception {
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 生成公钥
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 公钥加密
*
* @param data
* 待加密数据
* @param key
* 公钥
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, byte[] key)
throws Exception {
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 私钥加密
*
* @param data
* 待加密数据
* @param key
* 私钥
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, byte[] key)
throws Exception {
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 生成私钥
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 取得私钥
*
* @param keyMap
* 密钥Map
* @return byte[] 私钥
* @throws Exception
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap)
throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
*
* @param keyMap
* 密钥Map
* @return byte[] 公钥
* @throws Exception
*/
public static byte[] getPublicKey(Map<String, Object> keyMap)
throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 初始化密钥
*
* @return Map 密钥Map
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
// 实例化密钥对生成器
KeyPairGenerator keyPairGen = KeyPairGenerator
.getInstance(KEY_ALGORITHM);
// 初始化密钥对生成器
keyPairGen.initialize(KEY_SIZE);
// 生成密钥对
KeyPair keyPair = keyPairGen.generateKeyPair();
// 公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 封装密钥
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
}
https://www.zhihu.com/question/35137387