一、RSA算法核心概述
RSA是目前应用最广泛的非对称加密算法 ,核心特征是使用一对密钥(公钥、私钥)完成数据交互,两个密钥数学关联但无法由其中一个推导出另一个,彻底解决对称加密密钥传输的安全难题。
1.1 核心特性
-
公钥(Public Key):公开透明,可对外分发,主要用于加密数据、验证签名
-
私钥(Private Key):绝对保密,仅持有者留存,主要用于解密数据、生成签名
-
核心逻辑:公钥加密、私钥解密(保证数据保密性);私钥加签、公钥验签(保证数据完整性、真实性、不可抵赖性)
1.2 基础数学原理(极简易懂)
RSA的安全性基于大整数质因数分解难题,核心数学流程如下:
-
随机选取两个不相等的大质数p、q;
-
计算模数 N = p * q;
-
计算欧拉函数 φ(N) = (p-1)*(q-1);
-
选取公钥指数e(常用65537,满足1<e<φ(N)且与φ(N)互质);
-
计算私钥指数d(e关于φ(N)的模逆元,满足 e*d ≡ 1 (mod φ(N)));
-
最终:公钥=(N,e),私钥=(N,d)。
加密公式:C = M\^e \\bmod N (M为明文,C为密文)
解密公式:M = C\^d \\bmod N
二、RSA核心功能场景区分
很多开发者容易混淆加密解密、加签验签,两者的使用场景、密钥用法、核心目的完全不同:
| 功能 | 密钥使用规则 | 核心目的 | 适用场景 |
|---|---|---|---|
| 加密/解密 | 公钥加密,私钥解密 | 保证数据保密性,数据仅私钥持有者可读取 | 传输隐私数据(密码、手机号、密钥) |
| 加签/验签 | 私钥加签,公钥验签 | 保证数据完整性、真实性、不可抵赖 | 接口请求、文件校验、交易确认 |
三、Java实现RSA完整流程
Java通过java.security、javax.crypto原生包实现RSA,无需引入第三方依赖,本文实现:密钥对生成、公钥加密、私钥解密、私钥加签、公钥验签全流程,并逐行解析原理。
3.1 前置工具说明
-
密钥算法:RSA
-
加密填充方式:RSA/ECB/PKCS1Padding(通用标准,兼容绝大多数场景)
-
签名算法:SHA256withRSA(SHA256哈希+RSA加密签名,安全性高于MD5)
-
密钥长度:2048位(1024位已不安全,4096位性能较低,2048位为最优选择)
3.2 完整可运行代码
java
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* RSA非对称加密工具类
* 功能:密钥对生成、公钥加密、私钥解密、私钥加签、公钥验签
*/
public class RSAUtil {
// 密钥算法
private static final String RSA_ALGORITHM = "RSA";
// 加密算法+填充模式
private static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";
// 签名算法
private static final String SIGN_ALGORITHM = "SHA256withRSA";
// 密钥长度(2048位)
private static final int KEY_SIZE = 2048;
/**
* 1. 生成RSA公私钥对
*/
public static KeyPair generateRSAKeyPair() throws Exception {
// 获取密钥对生成器实例
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
// 初始化密钥长度
keyPairGenerator.initialize(KEY_SIZE);
// 生成密钥对
return keyPairGenerator.generateKeyPair();
}
/**
* 2. 公钥加密
* @param data 原始明文数据
* @param publicKey 公钥
* @return Base64编码后的密文
*/
public static String encrypt(String data, PublicKey publicKey) throws Exception {
// 获取加密器实例
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 初始化加密器为加密模式
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 执行加密
byte[] encryptBytes = cipher.doFinal(data.getBytes());
// 密文Base64编码(方便传输存储)
return Base64.getEncoder().encodeToString(encryptBytes);
}
/**
* 3. 私钥解密
* @param encryptData Base64编码的密文
* @param privateKey 私钥
* @return 解密后的明文
*/
public static String decrypt(String encryptData, PrivateKey privateKey) throws Exception {
// 获取解密器实例
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 初始化解密器为解密模式
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// Base64解码密文
byte[] encryptBytes = Base64.getDecoder().decode(encryptData);
// 执行解密
byte[] decryptBytes = cipher.doFinal(encryptBytes);
return new String(decryptBytes);
}
/**
* 4. 私钥加签(生成数字签名)
* @param data 原始数据
* @param privateKey 私钥
* @return Base64编码的签名
*/
public static String sign(String data, PrivateKey privateKey) throws Exception {
// 获取签名对象
Signature signature = Signature.getInstance(SIGN_ALGORITHM);
// 初始化签名对象(私钥签名)
signature.initSign(privateKey);
// 传入待签名数据
signature.update(data.getBytes());
// 生成签名并Base64编码
byte[] signBytes = signature.sign();
return Base64.getEncoder().encodeToString(signBytes);
}
/**
* 5. 公钥验签(验证数据合法性)
* @param data 原始数据
* @param signStr 签名字符串
* @param publicKey 公钥
* @return 验签结果 true=合法 false=篡改/伪造
*/
public static boolean verify(String data, String signStr, PublicKey publicKey) throws Exception {
// 获取签名验证对象
Signature signature = Signature.getInstance(SIGN_ALGORITHM);
// 初始化验签对象(公钥验签)
signature.initVerify(publicKey);
// 传入待验证原始数据
signature.update(data.getBytes());
// Base64解码签名
byte[] signBytes = Base64.getDecoder().decode(signStr);
// 执行验签
return signature.verify(signBytes);
}
// 测试主方法
public static void main(String[] args) throws Exception {
// 1. 生成密钥对
KeyPair keyPair = generateRSAKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
System.out.println("公钥:" + Base64.getEncoder().encodeToString(publicKey.getEncoded()));
System.out.println("私钥:" + Base64.getEncoder().encodeToString(privateKey.getEncoded()));
// 待测试明文
String plainText = "Java RSA 加密解密加签验签测试!123456";
System.out.println("\n原始明文:" + plainText);
// 2. 公钥加密
String encryptStr = encrypt(plainText, publicKey);
System.out.println("加密密文:" + encryptStr);
// 3. 私钥解密
String decryptStr = decrypt(encryptStr, privateKey);
System.out.println("解密明文:" + decryptStr);
// 4. 私钥加签
String signStr = sign(plainText, privateKey);
System.out.println("数据签名:" + signStr);
// 5. 公钥验签(正常数据)
boolean verifyResult1 = verify(plainText, signStr, publicKey);
System.out.println("正常数据验签结果:" + verifyResult1);
// 6. 篡改数据后验签(模拟数据被篡改)
String errorText = "篡改后的RSA测试数据";
boolean verifyResult2 = verify(errorText, signStr, publicKey);
System.out.println("篡改数据验签结果:" + verifyResult2);
}
}
四、逐步骤原理与作用详解
4.1 RSA密钥对生成(核心基础)
执行代码
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
keyPairGenerator.initialize(KEY_SIZE);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
原理与作用
-
获取密钥生成器:通过Java安全工厂获取RSA算法专用密钥生成器,绑定底层加密算法实现;
-
初始化密钥长度:2048位指定了模数N的长度,长度越长,破解难度越高,但加密解密速度越慢,2048位是安全与性能的平衡点;
-
生成密钥对:程序自动执行前文数学逻辑(选取大质数、计算N、φ(N)、e、d),生成配对的公钥和私钥,两个密钥绑定唯一数学关系。
关键特点:每次生成的密钥对均不重复,私钥绝对无法通过公钥逆向推导。
4.2 公钥加密(保障数据保密性)
核心流程
获取加密器 → 初始化为加密模式 → 明文转字节数组 → 执行RSA加密 → Base64编码输出
原理与作用
-
Cipher加密器 :Java加密核心类,
RSA/ECB/PKCS1Padding是标准填充模式,解决原始RSA无法加密短数据、数据对齐问题,兼容所有主流系统; -
加密模式初始化 :
Cipher.ENCRYPT_MODE绑定公钥,确定加密逻辑为「公钥加密」; -
加密运算:底层执行 C = M\^e \\bmod N 公式,将明文转为不可直接读取的密文;
-
Base64编码:加密后的密文是二进制字节数组,Base64编码后转为字符串,方便HTTP传输、日志打印、数据库存储。
使用意义:任何人都可以用公钥加密数据,但只有对应的私钥能解密,彻底防止传输过程中数据泄露。
4.3 私钥解密(还原原始数据)
核心流程
获取解密器 → 初始化为解密模式 → Base64解码密文 → 执行解密 → 转明文字符串
原理与作用
-
解密绑定私钥 :
Cipher.DECRYPT_MODE绑定私钥,只有持有对应私钥的服务,才能触发解密逻辑; -
解密运算:底层执行 M = C\^d \\bmod N 公式,通过私钥指数d还原明文;
-
数据还原:将解密后的字节数组转为字符串,得到原始业务数据。
核心约束 :公钥加密的密文,只能由对应私钥解密,其他私钥无法破解,保障数据专属保密性。
4.4 私钥加签(生成数字签名)
核心流程
获取签名工具 → 私钥初始化签名器 → 传入原始数据 → 生成签名 → Base64编码
原理与作用
签名的本质是:对数据的哈希值进行私钥加密,区别于直接加密原始数据。
-
SHA256哈希:先对原始数据进行SHA256哈希运算,生成固定长度的哈希值,数据哪怕修改1个字符,哈希值会完全改变,保障数据完整性;
-
私钥加密哈希值 :用私钥对哈希值加密生成签名,因为私钥唯一,签名具有唯一性、不可伪造性;
-
签名作用:证明数据由私钥持有者发送,且数据未被篡改、不可抵赖。
4.5 公钥验签(校验数据合法性)
核心流程
获取验签工具 → 公钥初始化验签器 → 传入原始数据 → 解码签名 → 校验匹配性
原理与作用
-
公钥解密签名:用公钥解密签名,得到原始数据的哈希值;
-
二次哈希比对:对接收的原始数据重新计算SHA256哈希值,与解密出的哈希值对比;
-
校验结果判定:一致则说明数据未篡改、签名合法;不一致则说明数据被修改或签名伪造。
核心价值:解决数据篡改、身份伪造、交易抵赖问题,是接口鉴权、数据校验的核心方案。
五、常见问题与注意事项
5.1 加密数据长度限制
RSA单次加密数据长度有限制:密钥长度/8 - 11(2048位密钥最大加密245字节数据)。如需加密大文本,需使用「RSA+AES」混合加密:RSA加密AES密钥,AES加密原始数据。
5.2 密钥格式规范
-
公钥默认X509编码格式,对外分发;
-
私钥默认PKCS8编码格式,严禁对外泄露;
-
存储传输密钥必须Base64编码,避免格式错乱。
5.3 算法安全性
-
禁止使用1024位密钥(已被暴力破解);
-
禁止使用MD5withRSA签名(MD5哈希碰撞漏洞),统一使用SHA256withRSA;
-
必须携带填充模式,避免使用无填充原始RSA。
六、总结
-
RSA密钥对基于大整数分解难题生成,公钥公开、私钥保密,是所有功能的基础;
-
加密解密 :公钥加密、私钥解密,核心解决数据保密性,防止数据泄露;
-
加签验签 :私钥加签、公钥验签,核心解决数据完整性、真实性、不可抵赖性;
-
Java原生API可完美实现RSA全功能,无需第三方依赖,2048位密钥+SHA256withRSA是生产环境最优组合。