RSA 加密算法详解
RSA 是一种非对称加密算法(Asymmetric Cryptography),由 Ron Rivest、Adi Shamir 和 Leonard Adleman 于 1977 年提出,其核心基于「大数分解难题」(大质数相乘容易,分解乘积为两个质数极难),是目前工业界应用最广泛的加密算法之一,也是 Java 后端开发中处理敏感数据、接口验签、密钥交换的核心技术。
一、核心特性(与开发直接相关)
1. 非对称密钥体系
- 公钥(Public Key) :公开分发,用于加密数据 或验证签名,任何人都可获取。
- 私钥(Private Key) :严格保密,用于解密数据 或生成签名,仅持有者可访问。
- 核心逻辑:公钥加密的数据只能用对应私钥解密,私钥签名的数据只能用对应公钥验证。
2. 双重功能
- 加密解密:保护数据机密性(如用户手机号、身份证号加密存储)。
- 签名验签:保证数据完整性(防篡改)和身份真实性(防伪造),是 API 接口验签的核心方案。
3. 性能特点
- 优点:安全性高(密钥长度足够时,目前无有效破解方法)、无需安全信道分发密钥。
- 缺点:运算速度慢(比 AES 等对称加密慢 100-1000 倍),不适合大数据量加密(如文件、流数据)。
二、底层原理
RSA 原理基于数论,核心是「公钥加密、私钥解密」的数学不可逆性,简化步骤如下:
1. 密钥生成(核心步骤)
- 选择两个大质数
p和q(实际开发中需 1024 位以上,推荐 2048/4096 位)。 - 计算乘积
n = p * q(n是公钥和私钥的共同模数,决定密钥长度)。 - 计算欧拉函数
φ(n) = (p-1) * (q-1)(用于后续计算私钥)。 - 选择公钥指数
e:满足1 < e < φ(n)且e与φ(n)互质(常用固定值65537,兼顾安全性和性能)。 - 计算私钥指数
d:满足(e * d) mod φ(n) = 1(d是e在φ(n)下的模逆元)。 - 最终密钥对:
- 公钥:
(e, n)(通常编码为 X.509 格式,Base64 字符串存储)。 - 私钥:
(d, n)(通常编码为 PKCS#8 格式,加密存储在密钥库或配置中心)。
- 公钥:
2. 加密 / 解密过程
- 加密 (公钥加密):明文
m→ 密文c = m^e mod n(m必须小于n,否则需分段)。 - 解密 (私钥解密):密文
c→ 明文m = c^d mod n。
3. 签名 / 验签过程(API 验签核心)
- 签名 (私钥签名):对数据
data计算哈希值hash(data)→ 用私钥加密哈希值得到签名sig = hash(data)^d mod n。 - 验签 (公钥验签):接收方用公钥解密签名
hash'(data) = sig^e mod n→ 对比hash'(data)与自身计算的hash(data),一致则数据未篡改且身份合法。
三、实战部分
- 签名 / 验签过程
- 加密 / 解密过程
签名- 验签过程
java
package com.fuiou.service.utils;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* @Author LQ
* @Date 2025/12/25 19:23
* @Description:
*/
public class RsaSignVerify {
/**
* RSA算法标识(JDK原生固定值)
*/
private static final String RSA_ALGORITHM = "RSA";
/**
* 签名算法:SHA256withRSA(JDK8+原生支持,安全级别高)
*/
private static final String SIGN_ALGORITHM = "SHA256withRSA";
/**
* RSA密钥长度:2048位(平衡安全性与性能,JDK原生支持)
*/
private static final int RSA_KEY_SIZE = 2048;
/**
* Base64编码器(JDK8+内置,避免第三方依赖)
*/
private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
/**
* Base64解码器
*/
private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
// ====================== 密钥对生成(纯原生API) ======================
/**
* 生成RSA密钥对(公钥+私钥)
* @return KeyPair 密钥对对象
* @throws NoSuchAlgorithmException 算法不支持(JDK8+必支持RSA,此异常仅兜底)
*/
public static KeyPair generateRsaKeyPair() throws NoSuchAlgorithmException {
// 初始化RSA密钥生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
// 设置密钥长度和随机源(增强随机性)
keyPairGenerator.initialize(RSA_KEY_SIZE, new SecureRandom());
// 生成密钥对
return keyPairGenerator.generateKeyPair();
}
// ====================== 密钥与Base64互转(原生解析) ======================
/**
* 公钥转Base64字符串(公钥默认采用X.509编码)
* @param publicKey 公钥对象
* @return Base64编码的公钥字符串
*/
public static String publicKeyToBase64(PublicKey publicKey) {
if (publicKey == null) {
throw new IllegalArgumentException("公钥对象不能为空");
}
return BASE64_ENCODER.encodeToString(publicKey.getEncoded());
}
/**
* 私钥转Base64字符串(私钥默认采用PKCS#8编码)
* @param privateKey 私钥对象
* @return Base64编码的私钥字符串
*/
public static String privateKeyToBase64(PrivateKey privateKey) {
if (privateKey == null) {
throw new IllegalArgumentException("私钥对象不能为空");
}
return BASE64_ENCODER.encodeToString(privateKey.getEncoded());
}
/**
* Base64公钥字符串解析为PublicKey对象(X.509格式)
* @param base64PublicKey Base64编码的公钥字符串
* @return PublicKey 公钥对象
* @throws NoSuchAlgorithmException 算法不支持
* @throws InvalidKeySpecException 密钥格式错误
*/
public static PublicKey base64ToPublicKey(String base64PublicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
if (base64PublicKey == null || base64PublicKey.trim().isEmpty()) {
throw new IllegalArgumentException("Base64公钥字符串不能为空");
}
// 解码Base64为字节数组
byte[] publicKeyBytes = BASE64_DECODER.decode(base64PublicKey.trim());
// 初始化X.509公钥规范
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
// 生成公钥对象
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return keyFactory.generatePublic(keySpec);
}
/**
* Base64私钥字符串解析为PrivateKey对象(PKCS#8格式)
* @param base64PrivateKey Base64编码的私钥字符串
* @return PrivateKey 私钥对象
* @throws NoSuchAlgorithmException 算法不支持
* @throws InvalidKeySpecException 密钥格式错误
*/
public static PrivateKey base64ToPrivateKey(String base64PrivateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
if (base64PrivateKey == null || base64PrivateKey.trim().isEmpty()) {
throw new IllegalArgumentException("Base64私钥字符串不能为空");
}
// 解码Base64为字节数组
byte[] privateKeyBytes = BASE64_DECODER.decode(base64PrivateKey.trim());
// 初始化PKCS#8私钥规范
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
// 生成私钥对象
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return keyFactory.generatePrivate(keySpec);
}
// ====================== 私钥加签(原生Signature API) ======================
/**
* 私钥签名(SHA256withRSA)
* @param data 待签名的原始数据(UTF-8编码)
* @param privateKey 私钥对象
* @return Base64编码的签名字符串
* @throws NoSuchAlgorithmException 算法不支持
* @throws InvalidKeyException 私钥无效
* @throws SignatureException 签名过程异常
*/
public static String sign(String data, PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
if (data == null || data.trim().isEmpty()) {
throw new IllegalArgumentException("待签名数据不能为空");
}
if (privateKey == null) {
throw new IllegalArgumentException("私钥对象不能为空");
}
// 初始化签名对象(SHA256withRSA算法)
Signature signature = Signature.getInstance(SIGN_ALGORITHM);
// 初始化签名模式(私钥签名)
signature.initSign(privateKey);
// 更新待签名数据(UTF-8编码,避免乱码)
signature.update(data.getBytes(StandardCharsets.UTF_8));
// 生成签名字节数组并转Base64
byte[] signBytes = signature.sign();
return BASE64_ENCODER.encodeToString(signBytes);
}
/**
* 重载:Base64私钥字符串签名
* @param data 待签名数据
* @param base64PrivateKey Base64编码的私钥字符串
* @return Base64编码的签名字符串
* @throws NoSuchAlgorithmException 算法不支持
* @throws InvalidKeySpecException 密钥格式错误
* @throws InvalidKeyException 私钥无效
* @throws SignatureException 签名过程异常
*/
public static String sign(String data, String base64PrivateKey) {
try {
PrivateKey privateKey = base64ToPrivateKey(base64PrivateKey);
return sign(data, privateKey);
} catch (Exception e) {
return null;
}
}
// ====================== 公钥验签(核心方法) ======================
/**
* 公钥验签(验证签名是否合法)
* @param data 原始数据(UTF-8编码,需与签名时一致)
* @param sign Base64编码的签名字符串
* @param publicKey 公钥对象
* @return true=验签通过,false=验签失败
* @throws NoSuchAlgorithmException 算法不支持
* @throws InvalidKeyException 公钥无效
* @throws SignatureException 验签过程异常
*/
public static boolean verify(String data, String sign, PublicKey publicKey){
try {
if (data == null || data.trim().isEmpty()) {
throw new IllegalArgumentException("原始数据不能为空");
}
if (sign == null || sign.trim().isEmpty()) {
throw new IllegalArgumentException("签名字符串不能为空");
}
if (publicKey == null) {
throw new IllegalArgumentException("公钥对象不能为空");
}
// 初始化签名对象(与签名算法一致)
Signature signature = Signature.getInstance(SIGN_ALGORITHM);
// 初始化验签模式(公钥验签)
signature.initVerify(publicKey);
// 更新原始数据(UTF-8编码)
signature.update(data.getBytes(StandardCharsets.UTF_8));
// 解码签名字符串并验签
byte[] signBytes = BASE64_DECODER.decode(sign.trim());
return signature.verify(signBytes);
} catch (Exception e) {
return false;
}
}
/**
* 重载:Base64公钥字符串验签
* @param data 原始数据
* @param sign Base64编码的签名字符串
* @param base64PublicKey Base64编码的公钥字符串
* @return true=验签通过,false=验签失败
* @throws NoSuchAlgorithmException 算法不支持
* @throws InvalidKeySpecException 密钥格式错误
* @throws InvalidKeyException 公钥无效
* @throws SignatureException 验签过程异常
*/
public static boolean verify(String data, String sign, String base64PublicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
PublicKey publicKey = base64ToPublicKey(base64PublicKey);
boolean result = verify(data, sign, publicKey);
return result;
}
// ====================== 测试示例 ======================
public static void main(String[] args) {
try {
// 1. 生成密钥对
KeyPair keyPair = generateRsaKeyPair();
String base64PublicKey = publicKeyToBase64(keyPair.getPublic());
String base64PrivateKey = privateKeyToBase64(keyPair.getPrivate());
System.out.println("原生生成Base64公钥:\n" + base64PublicKey);
System.out.println("原生生成Base64私钥:\n" + base64PrivateKey);
// 2. 待签名原始数据
String originalData = "Pure Java Native RSA Sign & Verify Test";
// 3. 私钥加签
String signResult = sign(originalData, base64PrivateKey);
System.out.println("签名结果(Base64):\n" + signResult);
// 4. 公钥验签(正常场景,预期通过)
boolean verifySuccess = verify(originalData, signResult, base64PublicKey);
System.out.println("正常数据验签结果:" + (verifySuccess ? "通过" : "失败"));
// 5. 篡改数据验签(预期失败)
String tamperedData = originalData + "_hack";
boolean verifyFail = verify(tamperedData, signResult, base64PublicKey);
System.out.println("篡改数据验签结果:" + (verifyFail ? "通过" : "失败"));
} catch (Exception e) {
e.printStackTrace();
}
}
}

加密 / 解密过程
java
public class RsaEncryptUtil {
// RSA加密算法(指定PKCS1Padding填充,避免无填充的安全问题)
private static final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding";
// RSA密钥长度(2048位为安全基线,1024位已不安全)
private static final int KEY_SIZE = 2048;
// 2048位RSA+PKCS1Padding的最大明文长度(2048/8 - 11 = 245 - 11 = 234?修正:2048位密钥的模长是256字节,PKCS1Padding占11字节,所以最大明文长度=256-11=245字节)
private static final int MAX_ENCRYPT_BLOCK = 245;
// 2048位RSA解密的最大块长度(=密钥模长=256字节)
private static final int MAX_DECRYPT_BLOCK = 256;
/**
* Base64编码器(JDK8+内置,避免第三方依赖)
*/
private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
/**
* Base64解码器
*/
private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
/**
* 生成RSA密钥对(公钥+私钥)
*/
public static KeyPair generateRsaKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(KEY_SIZE);
return keyPairGenerator.generateKeyPair();
}
/**
* 公钥加密:明文→Base64编码的密文(方便传输/存储)
* @param plainText 明文字符串
* @param publicKey RSA公钥
* @return Base64编码的密文
* @throws Exception 加密异常
*/
public static String encryptByPublicKey(String plainText, PublicKey publicKey) throws Exception {
byte[] plainBytes = plainText.getBytes(StandardCharsets.UTF_8);
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
// 初始化加密模式(公钥)
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 分段加密(解决RSA单次加密长度限制)
int inputLen = plainBytes.length;
byte[] encryptedBytes = new byte[0];
int offSet = 0;
byte[] cache;
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(plainBytes, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(plainBytes, offSet, inputLen - offSet);
}
// 拼接分段加密结果
byte[] temp = new byte[encryptedBytes.length + cache.length];
System.arraycopy(encryptedBytes, 0, temp, 0, encryptedBytes.length);
System.arraycopy(cache, 0, temp, encryptedBytes.length, cache.length);
encryptedBytes = temp;
offSet += MAX_ENCRYPT_BLOCK;
}
// 加密后的二进制转Base64明文(密文的可读形式)
return Base64.getEncoder().encodeToString(encryptedBytes);
}
/**
* 私钥解密:Base64编码的密文→明文
* @param cipherTextBase64 Base64编码的密文
* @param privateKey RSA私钥
* @return 明文字符串
* @throws Exception 解密异常
*/
public static String decryptByPrivateKey(String cipherTextBase64, PrivateKey privateKey) throws Exception {
// 1. Base64密文转回二进制字节数组
byte[] cipherBytes = Base64.getDecoder().decode(cipherTextBase64);
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
// 2. 初始化解密模式(私钥)
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 3. 分段解密(对应分段加密)
int inputLen = cipherBytes.length;
byte[] decryptedBytes = new byte[0];
int offSet = 0;
byte[] cache;
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(cipherBytes, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(cipherBytes, offSet, inputLen - offSet);
}
// 拼接分段解密结果
byte[] temp = new byte[decryptedBytes.length + cache.length];
System.arraycopy(decryptedBytes, 0, temp, 0, decryptedBytes.length);
System.arraycopy(cache, 0, temp, decryptedBytes.length, cache.length);
decryptedBytes = temp;
offSet += MAX_DECRYPT_BLOCK;
}
// 4. 解密后的字节转明文字符串
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
// -------------- 复用:密钥与Base64字符串互转 --------------
public static String publicKeyToBase64(PublicKey publicKey) {
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
}
public static PublicKey base64ToPublicKey(String pubKeyBase64) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(pubKeyBase64);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
public static String privateKeyToBase64(PrivateKey privateKey) {
return Base64.getEncoder().encodeToString(privateKey.getEncoded());
}
public static PrivateKey base64ToPrivateKey(String priKeyBase64) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(priKeyBase64);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
// 测试主方法
public static void main(String[] args) throws Exception {
// ========== 1. 生成RSA密钥对 ==========
KeyPair keyPair = generateRsaKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
System.out.println("公钥(Base64):" + publicKeyToBase64(publicKey));
System.out.println("私钥(Base64):" + privateKeyToBase64(privateKey));
System.out.println("----------------------------------------");
// ========== 2. 待加密的明文(支持长文本,分段加密) ==========
String plainText = "RSA解密测试:这是一段需要加密的长文本,RSA非对称加密的特点是公钥加密、私钥解密,且单次加密长度有限,因此需要分段处理!RSA解密测试:这是一段需要加密的长文本,RSA非对称加密的特点是公钥加密、私钥解密,且单次加密长度有限,因此需要分段处理!";
System.out.println("原始明文:" + plainText);
System.out.println("----------------------------------------");
// ========== 3. 公钥加密 ==========
String cipherText = encryptByPublicKey(plainText, publicKey);
System.out.println("加密后密文(Base64):" + cipherText);
System.out.println("----------------------------------------");
// ========== 4. 私钥解密 ==========
String decryptedText = decryptByPrivateKey(cipherText, privateKey);
System.out.println("解密后明文:" + decryptedText);
System.out.println("----------------------------------------");
// 验证解密结果是否与原始明文一致
System.out.println("解密结果验证:" + plainText.equals(decryptedText));
}
}

四、处理大数据量加密
RSA 加密的明文长度限制为「密钥长度 - 填充长度」
-
2048 位密钥 + OAEP 填充:最大明文长度 = 2048/8 - 42 = 214 字节。
-
超过限制需
分段加密或「RSA + AES 混合加密」(推荐):
- 用 AES 加密大数据(如文件、长文本),AES 密钥随机生成。
- 用 RSA 加密 AES 密钥。
- 传输时发送「AES 加密后的密文 + RSA 加密后的 AES 密钥」。
五、springboot中拦截器的运用
java
@Component
public class SignInterceptor implements HandlerInterceptor {
@Value("${rsa.public-key}")
private String publicKeyStr;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 获取请求参数和签名
String requestParams = getRequestParams(request); // 拼接所有请求参数(含 GET/POST)
String sign = request.getHeader("X-Sign"); // 从请求头获取签名
// 2. 验签
PublicKey publicKey = RSAUtils.getPublicKey(publicKeyStr);
boolean verifyResult = RSAUtils.verify(requestParams, sign, publicKey);
if (!verifyResult) {
response.setStatus(HttpStatus.FORBIDDEN.value());
response.getWriter().write("Sign verification failed");
return false;
}
return true;
}
// 拼接请求参数(按字典序排序,避免参数顺序导致验签失败)
private String getRequestParams(HttpServletRequest request) {
Map<String, String[]> paramMap = request.getParameterMap();
return paramMap.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(entry -> entry.getKey() + "=" + String.join(",", entry.getValue()))
.collect(Collectors.joining("&"));
}
}
六、安全注意事项
- 密钥长度:至少 2048 位(1024 位已被破解),金融级场景推荐 4096 位。
- 加密模式 :禁用
RSA/ECB/PKCS1Padding(ECB 模式无随机性,易被攻击),使用OAEPWithSHA-256AndMGF1Padding。 - 签名算法 :禁用
MD5withRSA(MD5 哈希易碰撞),使用SHA256withRSA或SHA512withRSA。 - 私钥安全:绝对禁止泄露私钥,定期轮换密钥(如每 6 个月)。
- 公钥分发:确保公钥来源可信(如通过 HTTPS 下载、配置中心分发),避免公钥被篡改。
- 随机数 :密钥生成时使用
SecureRandom(而非Random),确保密钥随机性。
七、与对称加密(AES)的对比及组合使用
| 特性 | RSA(非对称) | AES(对称) |
|---|---|---|
| 密钥数量 | 公钥 + 私钥 | 单密钥 |
| 运算速度 | 慢(适合小数据) | 快(适合大数据) |
| 密钥分发 | 公钥可公开(无需安全信道) | 需安全信道分发密钥 |
| 适用场景 | 加密密钥、签名验签 | 加密数据(文件、长文本) |
组合使用方案(推荐)
- 用 AES 加密业务数据(如订单信息、用户详情)。
- 用 RSA 加密 AES 的随机密钥。
- 传输 / 存储时,将「AES 密文 + RSA 加密后的 AES 密钥」一起发送 / 存储。
- 接收方用 RSA 私钥解密得到 AES 密钥,再用 AES 解密业务数据。