spring boot 实现 RSA + AES 混合加解密案例

# 前言
在我们平时项目开发过程中,经常会遇到各种各样的数据安全问题,为了防止我们的业务数据不被泄露,提高我们系统的安全性。在数据传输的时候我们对传输的数据进行加密,这里采用了 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);

到这里后端配置完成

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值