前后端API交互数据加密——AES与RSA混合加密完整实例
前言
前段时间看到一篇文章讲如何保证API调用时数据的安全性(传送门:https://blog.csdn.net/ityouknow/article/details/80603617),文中讲到利用RSA来加密传输AES的秘钥,用AES来加密数据,并提供如下思路:
说人话就是前、后端各自生成自己的RSA秘钥对(公钥、私钥),然后交换公钥(后端给前端的是正常的明文公钥,前端给后端的是用后端公钥加密后的密文公钥;PS:其实我觉得直接交换两个明文公钥就行了),后端生成AES的明文key,用明文key进行AES加密得到密文数据,用前端的公钥进行RSA加密得到密文key,API交互时并将密文数据与密文key进行传输,前端用自己的私钥进行RAS解密的到明文key,用明文key进行AES解密得到明文数据;前端给后端发送数据时同理,这样一来,传输的数据都是密文,且只有秘钥才能解密
可惜这篇博客只提供了思路,但并没有具体的代码,我们在网上查找一下资料,开始生撸代码,实现一个前后端API交互数据加密——AES与RSA混合加密,并应用到项目中
后端加、解密
从网上查找工具类,再进行改造
先引入Base64工具类
<!-- Base64编码需要 --> <dependency> <groupId>org.apache.directory.studio</groupId> <artifactId>org.apache.commons.codec</artifactId> <version>1.8</version> </dependency>
AES
package cn.huanzi.ims.util; 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; /** * 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"; /** * 后端AES的key,由静态代码块赋值 */ public static String key; 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,new BouncyCastleProvider()); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), KEY_ALGORITHM)); //调用doFinal byte[] b = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)); // 转base64 return Base64.encodeBase64String(b); } /** * 解密 * * @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,new BouncyCastleProvider()); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), KEY_ALGORITHM)); //调用doFinal解密 byte[] decryptBytes = cipher.doFinal(decodeBase64); return new String(decryptBytes); } }
RSA
package cn.huanzi.ims.util; 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.Map; /** * RSA加、解密算法工具类 */ public class RsaUtil { /** * 加密算法AES */ private static final String KEY_ALGORITHM = "RSA"; /** * 算法名称/加密模式/数据填充方式 * 默认:RSA/ECB/PKCS1Padding */ private static final String ALGORITHMS = "RSA/ECB/PKCS1Padding"; /** * 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 位数 如果采用2048 上面最大加密和最大解密则须填写: 245 256 */ private static final int INITIALIZE_LENGTH = 1024; /** * 后端RSA的密钥对(公钥和私钥)Map,由静态代码块赋值 */ private static Map<String, Object> genKeyPair = new HashMap<>(); static { try { genKeyPair.putAll(genKeyPair()); } catch (Exception e) { e.printStackTrace(); } } /** * 生成密钥对(公钥和私钥) */ 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对象 byte[] keyBytes = Base64.decodeBase64(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); //设置加密、填充方式 /* 如需使用更多加密、填充方式,引入 <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对象 byte[] keyBytes = Base64.decodeBase64(publicKey); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key publicK = keyFactory.generatePublic(x509KeySpec); //设置加密、填充方式 /* 如需使用更多加密、填充方式,引入 <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; } byte[] toByteArray = out.toByteArray(); out.close(); return toByteArray; } }