一、背景介绍
RSA是一种非对称加密算法,该加密算法的原理就是对一极大整数做因数分解的困难性来保证安全性。通常个人保存私钥,公钥是公开的(可能同时多人持有)。
二、RSA算法工具类
package com.hl.rsademo.util; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import javax.crypto.Cipher; /** * RSA算法工具类 */ public class RSAUtil { /** * RSA最大加密明文大小 */ private static final int MAX_ENCRYPT_BLOCK = 117; /** * RSA最大解密密文大小 */ private static final int MAX_DECRYPT_BLOCK = 128; /** * 获取密钥对 * * @return 密钥对 */ public static KeyPair getKeyPair() throws Exception { KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); generator.initialize(1024); return generator.generateKeyPair(); } /** * 获取私钥 * * @param privateKey 私钥字符串 * @return 私钥对象 */ public static PrivateKey getPrivateKey(String privateKey) throws Exception { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); byte[] decodedKey = Base64.getDecoder().decode(privateKey.getBytes()); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey); return keyFactory.generatePrivate(keySpec); } /** * 获取公钥 * * @param publicKey 公钥字符串 * @return 公钥对象 */ public static PublicKey getPublicKey(String publicKey) throws Exception { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); byte[] decodedKey = Base64.getDecoder().decode(publicKey.getBytes()); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey); return keyFactory.generatePublic(keySpec); } /** * RSA加密 * * @param data 待加密数据 * @param publicKey 公钥 * @return 加密字符串 */ public static String encrypt(String data, PublicKey publicKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); int inputLen = data.getBytes().length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offset = 0; byte[] cache; int i = 0; // 对数据分段加密 while (inputLen - offset > 0) { if (inputLen - offset > MAX_ENCRYPT_BLOCK) { cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK); } else { cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset); } out.write(cache, 0, cache.length); i++; offset = i * MAX_ENCRYPT_BLOCK; } byte[] encryptedData = out.toByteArray(); out.close(); // 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串 return Base64.getEncoder().encodeToString(encryptedData); } /** * RSA解密 * * @param data 待解密数据 * @param privateKey 私钥 * @return 解密字符串 */ public static String decrypt(String data, PrivateKey privateKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] dataBytes = Base64.getDecoder().decode(data); int inputLen = dataBytes.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offset = 0; byte[] cache; int i = 0; // 对数据分段解密 while (inputLen - offset > 0) { if (inputLen - offset > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(dataBytes, offset, inputLen - offset); } out.write(cache, 0, cache.length); i++; offset = i * MAX_DECRYPT_BLOCK; } out.close(); // 解密后的内容 return out.toString(StandardCharsets.UTF_8); } /** * 签名 * * @param data 待签名数据 * @param privateKey 私钥 * @return 签名 */ public static String sign(String data, PrivateKey privateKey) throws Exception { byte[] keyBytes = privateKey.getEncoded(); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey key = keyFactory.generatePrivate(keySpec); Signature signature = Signature.getInstance("MD5withRSA"); signature.initSign(key); signature.update(data.getBytes()); return Base64.getEncoder().encodeToString(signature.sign()); } /** * 验签 * * @param srcData 原始字符串 * @param publicKey 公钥 * @param sign 签名 * @return 是否验签通过 */ public static boolean verify(String srcData, PublicKey publicKey, String sign) throws Exception { byte[] keyBytes = publicKey.getEncoded(); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey key = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance("MD5withRSA"); signature.initVerify(key); signature.update(srcData.getBytes()); return signature.verify(Base64.getDecoder().decode(sign.getBytes())); } }
三、案例测试
package com.hl.rsademo; import com.hl.rsademo.util.RSAUtil; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import java.security.KeyPair; import java.util.Base64; @SpringBootTest class RsADemoApplicationTests { @Test public void test() throws Exception { // 生成密钥对 KeyPair keyPair = RSAUtil.getKeyPair(); String privateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()); String publicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()); System.out.println("私钥:" + privateKey); System.out.println("公钥:" + publicKey); // RSA加密 String data = "hello java"; System.out.println("原始字符串:" + data); String encryptData = RSAUtil.encrypt(data, RSAUtil.getPublicKey(publicKey)); System.out.println("加密数据:" + encryptData); // RSA签名 String sign = RSAUtil.sign(data, RSAUtil.getPrivateKey(privateKey)); System.out.println("数据签名:" + sign); System.out.println(); // RSA验签 boolean result = RSAUtil.verify(data, RSAUtil.getPublicKey(publicKey), sign); System.out.println("数据验签:" + result); // RSA解密 String decryptData = RSAUtil.decrypt(encryptData, RSAUtil.getPrivateKey(privateKey)); System.out.println("解密数据:" + decryptData); } }
测试结果如下:
使用流程一般是我们和第三方系统对接时,公私钥都由我们自己生成
私钥和公钥都给到对方,我们作为数据推送方,先用公钥加密,私钥签名
对方接收数据后使用公钥验签,验签通过确认数据没有被篡改后,再用私钥解密