参考:https://www.cnblogs.com/huanzi-qch/p/10913636.html
这里只讨论java代码,详情请看:前后端API交互数据加密——AES与RSA混合加密完整实例
AES简介
高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的)。对称加密算法也就是加密和解密用相同的密钥,具体的加密流程如下图:
RSA加密
RSA公钥加密算法是1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的。RSA取名来自开发他们三者的名字。RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
RSA公开密钥密码体制。所谓的公开密钥密码体制就是使用不同的加密密钥与解密密钥,是一种“由已知加密密钥推导出解密密钥在计算上是不可行的”密码体制。
AES+RSA=数据加密方案:
流程:
接收方创建RSA秘钥对,
发送RSA公钥给发送方,自己保留RSA私钥
发送方创建AES密钥,加密待传送的明文,之后用RSA公钥加密该密钥,
RSA公钥加密AES的密钥+AES密钥加密明文的密文----通过Internet发给---->接收方
接收方用RSA私钥解密加密的密钥,之后再用解密后的AES密钥解密数据密文,得到明文。
AES和RSAjava实现代码实例
pom:
<!-- Base64编码需要 -->
<dependency>
<groupId>org.apache.directory.studio</groupId>
<artifactId>org.apache.commons.codec</artifactId>
<version>1.8</version>
</dependency>
AESUtil:
package com.example.utils.jiamiUtil.encrypt.utils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Random;
//参考:https://www.cnblogs.com/huanzi-qch/p/10913636.html
/**
* AES加、解密算法工具类
*/
public class AesUtil {
/**
* 加密算法AES
*/
private static final String KEY_ALGORITHM = "AES";
/**
* key的长度,Wrong key size: must be equal to 128, 192 or 256
* 传入时需要16、24、36
*/
private static final Integer KEY_LENGTH = 16 * 8;
/**
* 算法名称/加密模式/数据填充方式
* 默认:AES/ECB/PKCS5Padding
*/
private static final String ALGORITHMS = "AES/ECB/PKCS5Padding";
// private static final String ALGORITHMS = "AES/ECB/PKCS7Padding";
/**
* 后端AES的key,由静态代码块赋值
*/
public static String key;
/**
* 不能在代码中创建
* JceSecurity.getVerificationResult 会将其put进 private static final Map<Provider,Object>中,导致内存缓便被耗尽
*/
private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();
static {
key = getKey();
}
/**
* 获取key
*/
public static String getKey() {
StringBuilder uid = new StringBuilder();
//产生16位的强随机数
Random rd = new SecureRandom();
for (int i = 0; i < KEY_LENGTH / 8; i++) {
//产生0-2的3位随机数
int type = rd.nextInt(3);
switch (type) {
case 0:
//0-9的随机数
uid.append(rd.nextInt(10));
break;
case 1:
//ASCII在65-90之间为大写,获取大写随机
uid.append((char) (rd.nextInt(25) + 65));
break;
case 2:
//ASCII在97-122之间为小写,获取小写随机
uid.append((char) (rd.nextInt(25) + 97));
break;
default:
break;
}
}
return uid.toString();
}
/**
* 加密
*
* @param content 加密的字符串
* @param encryptKey key值
*/
public static String encrypt(String content, String encryptKey) throws Exception {
//设置Cipher对象
Cipher cipher = Cipher.getInstance(ALGORITHMS, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), KEY_ALGORITHM));
//调用doFinal
// 转base64
return Base64.encodeBase64String(cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)));
}
/**
* 解密
*
* @param encryptStr 解密的字符串
* @param decryptKey 解密的key值
*/
public static String decrypt(String encryptStr, String decryptKey) throws Exception {
//base64格式的key字符串转byte
byte[] decodeBase64 = Base64.decodeBase64(encryptStr);
//设置Cipher对象
Cipher cipher = Cipher.getInstance(ALGORITHMS,PROVIDER);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), KEY_ALGORITHM));
//调用doFinal解密
return new String(cipher.doFinal(decodeBase64));
}
}
RSAUtil:
package com.example.utils.jiamiUtil.encrypt.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
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.LinkedHashMap;
import java.util.Map;
/**
* RSA加、解密算法工具类
*/
@Slf4j
public class RsaUtil {
/**
* 加密算法AES
*/
private static final String KEY_ALGORITHM = "RSA";
/**
* RSA 位数 如果采用2048 上面最大加密和最大解密则须填写: 245 256
*/
private static final int INITIALIZE_LENGTH = 1024;
/**
* 算法名称/加密模式/数据填充方式
* 默认:RSA/ECB/PKCS1Padding
*/
private static final String ALGORITHMS = "RSA/ECB/PKCS1Padding";
// private static final String ALGORITHMS = "RSA/ECB/PKCS7Padding";
/**
* Map获取公钥的key
*/
private static final String PUBLIC_KEY = "publicKey";
/**
* Map获取私钥的key
*/
private static final String PRIVATE_KEY = "privateKey";
/**
* RSA最大加密明文大小
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/**
* RSA最大解密密文大小
*/
private static final int MAX_DECRYPT_BLOCK = 128;
/**
* 后端RSA的密钥对(公钥和私钥)Map,由静态代码块赋值
*/
private static Map<String, Object> genKeyPair = new LinkedHashMap<>();
static {
try {
genKeyPair.putAll(genKeyPair());
} catch (Exception e) {
//输出到日志文件中
log.error(ErrorUtil.errorInfoToString(e));
}
}
/**
* 生成密钥对(公钥和私钥)
*/
private static Map<String, Object> genKeyPair() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(INITIALIZE_LENGTH);
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;
}
/**
* 私钥解密
*
* @param encryptedData 已加密数据
* @param privateKey 私钥(BASE64编码)
*/
public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
//base64格式的key字符串转Key对象
Key privateK = KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
//设置加密、填充方式
/*
如需使用更多加密、填充方式,引入
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
</dependency>
并改成
Cipher cipher = Cipher.getInstance(ALGORITHMS ,new BouncyCastleProvider());
*/
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.DECRYPT_MODE, privateK);
//分段进行解密操作
return encryptAndDecryptOfSubsection(encryptedData, cipher, MAX_DECRYPT_BLOCK);
}
/**
* 公钥加密
*
* @param data 源数据
* @param publicKey 公钥(BASE64编码)
*/
public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
//base64格式的key字符串转Key对象
Key publicK = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey)));
//设置加密、填充方式
/*
如需使用更多加密、填充方式,引入
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
</dependency>
并改成
Cipher cipher = Cipher.getInstance(ALGORITHMS ,new BouncyCastleProvider());
*/
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.ENCRYPT_MODE, publicK);
//分段进行加密操作
return encryptAndDecryptOfSubsection(data, cipher, MAX_ENCRYPT_BLOCK);
}
/**
* 获取私钥
*/
public static String getPrivateKey() {
Key key = (Key) genKeyPair.get(PRIVATE_KEY);
return Base64.encodeBase64String(key.getEncoded());
}
/**
* 获取公钥
*/
public static String getPublicKey() {
Key key = (Key) genKeyPair.get(PUBLIC_KEY);
return Base64.encodeBase64String(key.getEncoded());
}
/**
* 分段进行加密、解密操作
*/
private static byte[] encryptAndDecryptOfSubsection(byte[] data, Cipher cipher, int encryptBlock) throws Exception {
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > encryptBlock) {
cache = cipher.doFinal(data, offSet, encryptBlock);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * encryptBlock;
}
out.close();
return out.toByteArray();
}
}
简单测试
AES对称加密、解密简单测试
1、字符串
package com.example.utils.jiamiUtil.encrypt.test.aestest;
import com.example.utils.jiamiUtil.encrypt.utils.AesUtil;
public class AestestString {
public static void main(String[] args) {
//16位
// String key = "MIGfMA0GCSqGSIb3";
String key = AesUtil.getKey();
System.out.println("key == "+key);
//字符串
String str = "huanzi.qch@qq.com:欢子";
try {
//加密
String encrypt = AesUtil.encrypt(str, key);
//解密
String decrypt = AesUtil.decrypt(encrypt, key);
System.out.println("加密前:" + str);
System.out.println("加密后:" + encrypt);
System.out.println("解密后:" + decrypt);
} catch (Exception e) {
e.printStackTrace();
}
}
}
2、复杂对象
package com.example.utils.jiamiUtil.encrypt.test.aestest;
import com.example.utils.jiamiUtil.encrypt.test.vo.ImsUserVo;
import com.example.utils.jiamiUtil.encrypt.utils.AesUtil;
public class AestestObject {
public static void main(String[] args) {
//16位 只要是16位就可以
// String key = "MIGfMA0GCSqGSIb3";
String key = "MIGfMA0GCSqGSIb1";
//复杂对象
ImsUserVo userVo = new ImsUserVo();
userVo.setUserName("123456");
userVo.setPassword("111111");
try {
//加密
String encrypt = AesUtil.encrypt(userVo.toString(), key);
//解密
String decrypt = AesUtil.decrypt(encrypt, key);
System.out.println("加密前:" + userVo.toString());
System.out.println("加密后:" + encrypt);
System.out.println("解密后:" + decrypt);
} catch (Exception e) {
e.printStackTrace();
}
}
}
RAS非对称加密、解密简单测试
1、字符串的RSA公钥加密、私钥解密
package com.example.utils.jiamiUtil.encrypt.test.rastest;
import com.example.utils.jiamiUtil.encrypt.utils.RsaUtil;
import org.apache.commons.codec.binary.Base64;
public class RASTestString {
public static void main(String[] args) {
//字符串
String str = "huanzi.qch@qq.com:欢子";
try {
System.out.println("私钥:" + RsaUtil.getPrivateKey());
System.out.println("公钥:" + RsaUtil.getPublicKey());
//公钥加密
byte[] ciphertext = RsaUtil.encryptByPublicKey(str.getBytes(), RsaUtil.getPublicKey());
//私钥解密
byte[] plaintext = RsaUtil.decryptByPrivateKey(ciphertext, RsaUtil.getPrivateKey());
System.out.println("公钥加密前:" + str);
System.out.println("公钥加密后:" + Base64.encodeBase64String(ciphertext));
System.out.println("私钥解密后:" + new String(plaintext));
} catch (Exception e) {
e.printStackTrace();
}
}
}
2、复杂对象的RSA公钥加密、私钥解密
package com.example.utils.jiamiUtil.encrypt.test.rastest;
import com.example.utils.jiamiUtil.encrypt.test.vo.ImsUserVo;
import com.example.utils.jiamiUtil.encrypt.utils.RsaUtil;
import org.apache.commons.codec.binary.Base64;
public class RASTestObject {
public static void main(String[] args) {
//复杂对象
ImsUserVo userVo = new ImsUserVo();
userVo.setUserName("12345");
userVo.setPassword("11111");
try {
System.out.println("私钥:" + RsaUtil.getPrivateKey());
System.out.println("公钥:" + RsaUtil.getPublicKey());
//公钥加密
byte[] ciphertext = RsaUtil.encryptByPublicKey(userVo.toString().getBytes(), RsaUtil.getPublicKey());
//私钥解密
byte[] plaintext = RsaUtil.decryptByPrivateKey(ciphertext, RsaUtil.getPrivateKey());
System.out.println("公钥加密前:" + userVo.toString());
System.out.println("公钥加密后:" + Base64.encodeBase64String(ciphertext));
System.out.println("私钥解密后:" + new String(plaintext));
} catch (Exception e) {
e.printStackTrace();
}
}
}