# 前言
在我们平时项目开发过程中,经常会遇到各种各样的数据安全问题,为了防止我们的业务数据不被泄露,提高我们系统的安全性。在数据传输的时候我们对传输的数据进行加密,这里采用了 RSA (非对称) + AES (对称加密)的方式。
<ul>
<li>RSA:非对称加密,公钥(Public Key)与私钥(Private Key)是通过一种算法得到的一个密钥对(即一个公钥和一个私钥),公钥是密钥对中公开的部分,私钥则是非公开的部分。公钥通常用于加密会话密钥、验证数字签名,或加密可以用相应的私钥解密的数据。</li>
<li>AES:对称加密,对称加密是最快速、最简单的一种加密方式,加密(encryption)与解密(decryption)用的是同样的密钥(secret key)。对称加密通常使用的是相对较小的密钥,一般小于256 bit。因为密钥越大,加密越强,但加密与解密的过程越慢。</li>
</ul>
之所以用 AES 加密数据是因为效率高,RSA 运行速度慢,我们可以采用这两种算法互补,来保证安全性,用 RSA 来加密传输 AES 的秘钥,用 AES 来加密数据,两者相互结合,优势互补。
# 大致思路
<ol>
<li>客户端启动,发送请求到服务端,服务端用 RSA 算法生成一对公钥和私钥,我们简称为publickey1,privatekey2,将公钥 publickey1 返回给客户端。</li>
<li>客户端拿到服务端返回的公钥 publickey1 后,自己用 RSA 算法生成一对公钥和私钥,我们简称为publickey2,privatekey2,并将公钥 publickey2通过公钥 publickey1加密,加密之后传输给服务端。</li>
<li>此时服务端收到客户端传输的密文,用私钥 privatekey1 进行解密,因为数据是用公钥 publickey1 加密的,通过解密就可以得到客户端生成的公钥 publickey2 。</li>
<li>然后自己在生成对称加密,也就是我们的 AES,其实也就是相对于我们配置中的那个 16 的长度的加密 key,生成了这个 key 之后我们就用公钥 publickey2 进行加密,返回给客户端,因为只有客户端有publickey2 对应的私钥 privatekey2,只有客户端才能解密。</li>
<li>客户端得到数据之后,用 privatekey2 进行解密操作,得到 AES 的加密 key,最后就用加密 key 进行数据传输的加密,至此整个流程结束。</li>
</ol>
这里只是实现的大致流程,最终还是根据自己的业务需要来实现。
这里主要对后端的实现,前端的话大家可以自己去了解一下,这里不过多的介绍了!
# 工具类
## AES工具类 AESUtil
```c
//日志
private static final Logger logger = LoggerFactory.getLogger(AESUtil.class);
/**
* 加密算法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 int KEY_LENGTH = 16 * 8;
/**
* 算法名称/加密模式/数据填充方式
* 默认:AES/ECB/PKCS5Padding
*/
private static final String ALGORITHMS = "AES/ECB/PKCS5Padding";
/**
* 后端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() {
int length = KEY_LENGTH / 8;
StringBuilder uid = new StringBuilder(length);
//产生16位的强随机数
Random rd = new SecureRandom();
for (int i = 0; i < length; i++) {
//产生0-2的3位随机数
switch (rd.nextInt(3)) {
case 0:
//0-9的随机数
uid.append(rd.nextInt(10));
break;
case 1:
//ASCII在65-90之间为大写,获取大写随机
uid.append((char) (rd.nextInt(26) + 65));
break;
case 2:
//ASCII在97-122之间为小写,获取小写随机
uid.append((char) (rd.nextInt(26) + 97));
break;
default:
break;
}
}
return uid.toString();
}
/**
* AES 加密
*
* @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)));
}
/**
* AES 解密
*
* @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));
}
AES 测试类
public static void main(String[] args) throws Exception {
//获取 AES 的 key
String key = getKey();
String encrypt = encrypt("你们好啊",key);
System.out.println("加密之后"+key);
System.out.println("***********************");
String decrypt = decrypt(encrypt,key);
System.out.println("解密之后"+decrypt);
}
RSA 工具类 RSAUtil
//日志
private static final Logger logger = LoggerFactory.getLogger(RSAUtil.class);
static{
try{
Security.addProvider(new BouncyCastleProvider());
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 加密算法RSA
*/
private static final String KEY_ALGORITHM = "RSA";
/**
* 算法名称/加密模式/数据填充方式
* 默认:RSA/ECB/PKCS1Padding
*/
private static final String ALGORITHMS = "RSA/ECB/PKCS1Padding";
/**
* RSA最大加密明文大小
*/
private static final int MAX_ENCRYPT_BLOCK = 245;
/**
* RSA最大解密密文大小
*/
private static final int MAX_DECRYPT_BLOCK = 256;
/**
* RSA 位数 如果采用2048 上面最大加密和最大解密则须填写: 245 256
*/
private static final int INITIALIZE_LENGTH = 2048;
/**
* 后端RSA的密钥对(公钥和私钥)Map,由静态代码块赋值
*/
private static Map<String, Object> map = new LinkedHashMap<>(2);
/**
* 生成密钥对(公钥和私钥)
*/
public static Map<String,String> 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();
// 得到公钥字符串
String publicKeyString = Base64.encodeBase64String(publicKey.getEncoded());
// 得到私钥字符串
String privateKeyString = Base64.encodeBase64String((privateKey.getEncoded()));
map.put("publicKey",publicKeyString);
map.put("privateKey",privateKeyString);
return map;
}
/**
* RSA私钥解密
* @param data BASE64编码过的密文
* @param privateKey 私钥(BASE64编码)
* @return utf-8编码的明文
*/
public static byte[] decryptByPrivateKey(byte[] data, String privateKey) throws Exception {
//base64格式的key字符串转Key对象
Key privateK = KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.DECRYPT_MODE, privateK);
//分段进行解密操作
return encryptAndDecryptOfSubsection(data, cipher, MAX_DECRYPT_BLOCK);
}
/**
* RSA公钥加密
* @param data BASE64编码过的密文
* @param publicKey 公钥(BASE64编码)
* @return utf-8编码的明文
*/
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)));
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.ENCRYPT_MODE, publicK);
//分段进行加密操作
return encryptAndDecryptOfSubsection(data, cipher, MAX_ENCRYPT_BLOCK);
}
/**
* RSA公钥解密
* @param data BASE64编码过的密文
* @param publicKey RSA公钥
* @return utf-8编码的明文
*/
public static byte[] pubKeyDec(byte[] data, String publicKey) throws Exception {
//base64格式的key字符串转Key对象
Key privateK = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey)));
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.DECRYPT_MODE, privateK);
//分段进行解密操作
return encryptAndDecryptOfSubsection(data, cipher, MAX_DECRYPT_BLOCK);
}
/**
* RSA私钥加密
* @param data 待加密的明文
* @param privateKey RSA私钥
* @return 经BASE64编码后的密文
*/
public static byte[] privKeyEnc(byte[] data, String privateKey) throws Exception {
//base64格式的key字符串转Key对象
Key publicK = KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.ENCRYPT_MODE, publicK);
//分段进行加密操作
return encryptAndDecryptOfSubsection(data, cipher, MAX_ENCRYPT_BLOCK);
}
/**
* 分段进行加密、解密操作
*/
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();
}
RSA + AES 测试类
//随机获取AES的key,加密data数据
String key = AESUtil.getKey();
//生成公钥和私钥
genKeyPair();
//获取公钥和私钥
String publicKey = map.get("publicKey");
String privateKey = map.get("privateKey");
System.out.println("=====================公钥加密,私钥解密==================");
//随机AES的key加密后的密文
String data = AESUtil.encrypt("你们好啊", key);
System.out.println("AES 加密之后:");
System.out.println(data);
//用公钥来加密AES的key,并转成Base64
String aesKey = Base64.encodeBase64String(encryptByPublicKey(data.getBytes(),publicKey));
System.out.println("RSA 加密之后:");
System.out.println(aesKey);
//用私钥解密到AES的key
byte[] plaintext = decryptByPrivateKey(Base64.decodeBase64(aesKey),privateKey);
aesKey = new String(plaintext);
System.out.println("RSA 解密之后:");
System.out.println(aesKey);
//AES解密得到明文data数据
String result = AESUtil.decrypt(aesKey, key);
System.out.println("AES 解密之后:");
System.out.println(result);
System.out.println("=====================私钥加密,公钥解密==================");
//随机AES的key加密后的密文
String data1 = AESUtil.encrypt("你们好啊", key);
System.out.println("AES 加密之后:");
System.out.println(data1);
//用公钥来加密AES的key,并转成Base64
String aesKey1 = Base64.encodeBase64String(privKeyEnc(data1.getBytes(),privateKey));
System.out.println("RSA 加密之后:");
System.out.println(aesKey1);
//用私钥解密到AES的key
byte[] plaintext1 = pubKeyDec(Base64.decodeBase64(aesKey1),publicKey);
aesKey1 = new String(plaintext1);
System.out.println("RSA 解密之后:");
System.out.println(aesKey1);
//AES解密得到明文data数据
String result1 = AESUtil.decrypt(aesKey1, key);
System.out.println("AES 解密之后:");
System.out.println(result1);
到这里后端配置完成