一、RSA算法概述
RSA(Rivest-Shamir-Adleman)是1977年由Ron Rivest、Adi Shamir和Leonard Adleman提出的非对称加密算法,它基于大数分解的数学难题,是当今应用最广泛的公钥密码系统。RSA的核心思想是使用一对密钥(公钥和私钥)进行加密和解密操作,解决了对称加密中的密钥分发问题。
核心特性
特性 | 描述 |
---|---|
非对称性 | 加密密钥与解密密钥不同 |
数学基础 | 基于大整数分解难题 |
密钥长度 | 通常1024-4096位 |
应用场景 | 数字签名、安全通信、密钥交换 |
公钥加密 私钥解密 私钥签名 公钥验证 明文 RSA加密 密文 明文 数字签名 签名验证
二、RSA算法原理
1. 密钥生成过程
1. 选择两个大素数 p 和 q(保密)
2. 计算 n = p × q(公开)
3. 计算欧拉函数 φ(n) = (p-1)(q-1)(保密)
4. 选择整数 e 满足 1 < e < φ(n) 且 gcd(e, φ(n)) = 1(公开)
5. 计算 d 满足 d × e ≡ 1 mod φ(n)(保密)
2. 加密与解密
- 加密 : C = M e m o d n C = M^e \mod n C=Memodn
- 解密 : M = C d m o d n M = C^d \mod n M=Cdmodn
3. 数字签名
- 签名 : S = M d m o d n S = M^d \mod n S=Mdmodn
- 验证 : M = S e m o d n M = S^e \mod n M=Semodn
三、Java实现(完整注释版)
java
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
public class RSAAlgorithm {
// 密钥长度
private static final int KEY_SIZE = 2048;
private static final int DEFAULT_PUBLIC_EXPONENT = 65537;
private static final int MAX_BLOCK_SIZE = KEY_SIZE / 8 - 11; // PKCS#1 v1.5填充需要11字节
// 素数p和q
private BigInteger p;
private BigInteger q;
// 模数n
private BigInteger n;
// 欧拉函数
private BigInteger phi;
// 公钥指数e
private BigInteger e;
// 私钥指数d
private BigInteger d;
/**
* 生成RSA密钥对
*/
public void generateKeys() {
SecureRandom random = new SecureRandom();
// 生成两个大素数p和q
p = BigInteger.probablePrime(KEY_SIZE / 2, random);
q = BigInteger.probablePrime(KEY_SIZE / 2, random);
// 计算模数n = p * q
n = p.multiply(q);
// 计算欧拉函数φ(n) = (p-1)*(q-1)
phi = p.subtract(BigInteger.ONE)
.multiply(q.subtract(BigInteger.ONE));
// 选择公钥指数e
e = BigInteger.valueOf(DEFAULT_PUBLIC_EXPONENT);
// 确保e与φ(n)互质
while (phi.gcd(e).compareTo(BigInteger.ONE) > 0 && e.compareTo(phi) < 0) {
e = e.add(BigInteger.ONE);
}
// 计算私钥指数d
d = e.modInverse(phi);
}
/**
* 加密消息
* @param message 明文消息
* @return 密文字节数组
*/
public byte[] encrypt(byte[] message) {
// 计算最大分组大小
int maxBlockSize = getMaxEncryptBlockSize();
// 如果消息长度小于等于最大分组大小,直接加密
if (message.length <= maxBlockSize) {
return encryptBlock(pkcs1Pad(message, maxBlockSize + 11));
}
// 分组加密
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
int offset = 0;
while (offset < message.length) {
int blockSize = Math.min(maxBlockSize, message.length - offset);
byte[] block = Arrays.copyOfRange(message, offset, offset + blockSize);
// 填充并加密当前分组
byte[] paddedBlock = pkcs1Pad(block, maxBlockSize + 11);
byte[] encryptedBlock = encryptBlock(paddedBlock);
outputStream.write(encryptedBlock, 0, encryptedBlock.length);
offset += blockSize;
}
return outputStream.toByteArray();
}
/**
* 加密单个分组
*/
private byte[] encryptBlock(byte[] block) {
BigInteger m = new BigInteger(1, block);
if (m.compareTo(n) >= 0) {
throw new IllegalArgumentException("分组过大,无法加密");
}
return m.modPow(e, n).toByteArray();
}
/**
* 解密消息(
* @param ciphertext 密文字节数组
* @return 明文字节数组
*/
public byte[] decrypt(byte[] ciphertext) {
// 计算加密后的分组大小
int encryptedBlockSize = n.bitLength() / 8 + (n.bitLength() % 8 == 0 ? 0 : 1);
// 如果密文长度小于等于加密分组大小,直接解密
if (ciphertext.length <= encryptedBlockSize) {
return pkcs1Unpad(decryptBlock(ciphertext));
}
// 分组解密
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
int offset = 0;
while (offset < ciphertext.length) {
int blockSize = Math.min(encryptedBlockSize, ciphertext.length - offset);
byte[] block = Arrays.copyOfRange(ciphertext, offset, offset + blockSize);
// 解密当前分组并去除填充
byte[] decryptedBlock = pkcs1Unpad(decryptBlock(block));
outputStream.write(decryptedBlock, 0, decryptedBlock.length);
offset += blockSize;
}
return outputStream.toByteArray();
}
/**
* 解密单个分组
*/
private byte[] decryptBlock(byte[] block) {
BigInteger c = new BigInteger(1, block);
return c.modPow(d, n).toByteArray();
}
/**
* PKCS#1 v1.5填充
*/
private byte[] pkcs1Pad(byte[] data, int blockSize) {
if (data.length > blockSize - 11) {
throw new IllegalArgumentException("数据过长,无法填充");
}
byte[] padded = new byte[blockSize];
SecureRandom random = new SecureRandom();
// 填充格式: 0x00 0x02 [随机非零字节] 0x00 [原始数据]
padded[0] = 0x00;
padded[1] = 0x02;
// 填充随机非零字节
int paddingLength = blockSize - data.length - 3;
for (int i = 2; i < 2 + paddingLength; i++) {
byte r;
do {
r = (byte) random.nextInt();
} while (r == 0);
padded[i] = r;
}
padded[2 + paddingLength] = 0x00;
System.arraycopy(data, 0, padded, 3 + paddingLength, data.length);
return padded;
}
/**
* 去除PKCS#1 v1.5填充
*/
private byte[] pkcs1Unpad(byte[] data) {
try {
// 检查最小长度和起始字节
if (data.length < 2 + 8 + 1) { // 至少需要: 0x00 0x02 + 8随机字节 + 0x00
throw new IllegalArgumentException("数据过短,无效填充");
}
// 查找分隔符0x00
int separator = 2;
while (separator < data.length && data[separator] != 0x00) {
separator++;
}
// 检查是否找到分隔符
if (separator >= data.length - 1) {
throw new IllegalArgumentException("未找到填充分隔符");
}
// 检查随机填充长度是否足够(至少8字节)
if (separator - 2 < 8) {
throw new IllegalArgumentException("随机填充长度不足");
}
return Arrays.copyOfRange(data, separator + 1, data.length);
} catch (Exception e) {
throw new IllegalArgumentException("无效的PKCS#1填充: " + e.getMessage());
}
}
/**
* 获取最大加密分组大小
*/
public int getMaxEncryptBlockSize() {
return MAX_BLOCK_SIZE;
}
/**
* 获取最大解密分组大小
*/
public int getMaxDecryptBlockSize() {
return KEY_SIZE / 8;
}
/**
* 获取Base64编码的公钥
* @return 公钥字符串(n,e)
*/
public String getPublicKey() {
return "n=" + Base64.getEncoder().encodeToString(n.toByteArray()) +
"&e=" + Base64.getEncoder().encodeToString(e.toByteArray());
}
/**
* 获取Base64编码的私钥
* @return 私钥字符串(n,d)
*/
public String getPrivateKey() {
return "n=" + Base64.getEncoder().encodeToString(n.toByteArray()) +
"&d=" + Base64.getEncoder().encodeToString(d.toByteArray());
}
public static void main(String[] args) {
RSAAlgorithm rsa = new RSAAlgorithm();
// 1. 生成密钥对
rsa.generateKeys();
System.out.println("公钥: " + rsa.getPublicKey());
System.out.println("私钥: " + rsa.getPrivateKey());
// 2. 加密解密测试
String originalMessage = "RSA算法Java实现演示";
System.out.println("\n原始消息: " + originalMessage);
byte[] encrypted = rsa.encrypt(originalMessage.getBytes());
System.out.println("加密结果 (Base64): " + Base64.getEncoder().encodeToString(encrypted));
byte[] decrypted = rsa.decrypt(encrypted);
System.out.println("解密消息: " + new String(decrypted));
}
}

四、RSA实际应用
1. 安全通信协议
java
// 模拟TLS密钥交换
public class SecureCommunication {
public void simulateTLS() {
// 客户端生成临时RSA密钥对
RSAEncryption clientRSA = new RSAEncryption();
clientRSA.generateKeys();
// 服务器生成对称密钥
byte[] sessionKey = generateSessionKey();
// 客户端发送公钥给服务器
String publicKey = clientRSA.getPublicKey();
// 服务器用客户端公钥加密会话密钥
RSAEncryption serverRSA = new RSAEncryption();
serverRSA.setPublicKey(publicKey); // 解析公钥的方法需实现
byte[] encryptedKey = serverRSA.encrypt(sessionKey);
// 客户端用私钥解密会话密钥
byte[] decryptedKey = clientRSA.decrypt(encryptedKey);
// 验证密钥是否相同
System.out.println("密钥交换: " +
(Arrays.equals(sessionKey, decryptedKey) ? "成功" : "失败"));
}
private byte[] generateSessionKey() {
SecureRandom random = new SecureRandom();
byte[] key = new byte[32]; // AES-256密钥
random.nextBytes(key);
return key;
}
}
2. 数字签名系统
java
public class DigitalSignatureSystem {
public void signDocument() {
// 生成RSA密钥对
RSAEncryption rsa = new RSAEncryption();
rsa.generateKeys();
// 准备文档
String document = "重要合同内容";
// 创建文档哈希
byte[] hash = sha256(document.getBytes());
// 使用私钥签名
byte[] signature = rsa.sign(hash);
// 验证签名
boolean isValid = rsa.verify(hash, signature);
System.out.println("签名验证结果: " + isValid);
}
private byte[] sha256(byte[] input) {
try {
java.security.MessageDigest md =
java.security.MessageDigest.getInstance("SHA-256");
return md.digest(input);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
五、RSA安全性分析
1. 安全威胁与防护
威胁类型 | 防护措施 |
---|---|
因式分解攻击 | 使用2048位以上密钥 |
计时攻击 | 恒定时间实现 |
填充预言攻击 | 使用OAEP填充 |
量子计算威胁 | 迁移到后量子密码 |
2. 最佳实践
java
public class RSASecurityBestPractices {
// 推荐的密钥长度
public static final int MIN_KEY_SIZE = 2048;
public static final int RECOMMENDED_KEY_SIZE = 3072;
public static final int HIGH_SECURITY_KEY_SIZE = 4096;
// 安全的填充方案
public enum PaddingScheme {
PKCS1_V1_5("RSA/ECB/PKCS1Padding"),
OAEP_SHA1("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"),
OAEP_SHA256("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
private final String transformation;
PaddingScheme(String transformation) {
this.transformation = transformation;
}
public String getTransformation() {
return transformation;
}
}
// 使用Java加密体系实现
public static byte[] encryptWithJCE(byte[] data, PublicKey publicKey, PaddingScheme scheme) {
try {
Cipher cipher = Cipher.getInstance(scheme.getTransformation());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
} catch (Exception e) {
throw new RuntimeException("加密失败", e);
}
}
}
六、Java标准库实现
java
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class RSAWithJavaSecurity {
/**
* 生成RSA密钥对
* @param keySize 密钥长度
* @return 密钥对
*/
public static KeyPair generateKeyPair(int keySize) throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(keySize);
return keyGen.generateKeyPair();
}
/**
* 使用公钥加密
* @param publicKey 公钥
* @param data 待加密数据
* @return 加密结果
*/
public static byte[] encrypt(PublicKey publicKey, byte[] data) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 使用私钥解密
* @param privateKey 私钥
* @param encrypted 加密数据
* @return 解密结果
*/
public static byte[] decrypt(PrivateKey privateKey, byte[] encrypted) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(encrypted);
}
/**
* 使用私钥签名
* @param privateKey 私钥
* @param data 原始数据
* @return 签名
*/
public static byte[] sign(PrivateKey privateKey, byte[] data) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
/**
* 使用公钥验证签名
* @param publicKey 公钥
* @param data 原始数据
* @param signature 签名
* @return 验证是否成功
*/
public static boolean verify(PublicKey publicKey, byte[] data, byte[] signatureBytes) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(signatureBytes);
}
/**
* 将公钥转换为Base64字符串
*/
public static String publicKeyToString(PublicKey publicKey) {
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
}
/**
* 将Base64字符串转换为公钥
*/
public static PublicKey stringToPublicKey(String keyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
public static void main(String[] args) throws Exception {
// 1. 生成密钥对
KeyPair keyPair = generateKeyPair(2048);
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// 2. 加密解密演示
String message = "使用Java安全库实现RSA";
byte[] encrypted = encrypt(publicKey, message.getBytes());
byte[] decrypted = decrypt(privateKey, encrypted);
System.out.println("解密结果: " + new String(decrypted));
// 3. 数字签名演示
byte[] signature = sign(privateKey, message.getBytes());
boolean isValid = verify(publicKey, message.getBytes(), signature);
System.out.println("签名验证: " + (isValid ? "成功" : "失败"));
// 4. 密钥序列化
String pubKeyStr = publicKeyToString(publicKey);
System.out.println("\n序列化公钥: " + pubKeyStr);
PublicKey restoredKey = stringToPublicKey(pubKeyStr);
System.out.println("恢复公钥验证: " +
publicKeyToString(restoredKey).equals(pubKeyStr));
}
}

七、RSA的未来发展
1. 后量子密码学
随着量子计算机的发展,RSA面临重大挑战:
- Shor算法:可在多项式时间内破解RSA
- 迁移方案 :
RSA 基于格的密码 多元密码 哈希签名
2. 性能优化方向
优化技术 | 效果 | 实现方式 |
---|---|---|
硬件加速 | 10-100倍性能提升 | 专用密码处理器 |
多素数RSA | 提高解密效率 | 使用3个以上素数 |
批处理 | 提高吞吐量 | 同时处理多个消息 |
总结
RSA算法作为非对称密码学的基石,具有以下关键特点:
- 安全性高:基于大数分解难题
- 功能全面:支持加密、解密、数字签名
- 应用广泛:SSL/TLS、SSH、数字证书等核心协议
在实际应用中:
- 优先使用Java安全库实现
- 密钥长度至少2048位
- 选择OAEP填充方案
- 敏感数据采用混合加密系统
尽管量子计算带来新的挑战,RSA仍将在未来相当长的时间内继续发挥重要作用。理解RSA的原理和实现,是构建安全系统的必备知识。