RSA加密算法:非对称密码学的基石
一、RSA算法概述
RSA(Rivest-Shamir-Adleman)是1977年由Ron Rivest、Adi Shamir和Leonard Adleman提出的非对称加密算法,它基于大数分解的数学难题,是当今应用最广泛的公钥密码系统。RSA的核心思想是使用一对密钥(公钥和私钥)进行加密和解密操作,解决了对称加密中的密钥分发问题。
核心特性
特性 | 描述 |
---|---|
非对称性 | 加密密钥与解密密钥不同 |
数学基础 | 基于大整数分解难题 |
密钥长度 | 通常1024-4096位 |
应用场景 | 数字签名、安全通信、密钥交换 |
二、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实现(完整注释版)
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和qprivate BigInteger p;private BigInteger q;// 模数nprivate BigInteger n;// 欧拉函数private BigInteger phi;// 公钥指数eprivate BigInteger e;// 私钥指数dprivate BigInteger d;/*** 生成RSA密钥对*/public void generateKeys() {SecureRandom random = new SecureRandom();// 生成两个大素数p和qp = BigInteger.probablePrime(KEY_SIZE / 2, random);q = BigInteger.probablePrime(KEY_SIZE / 2, random);// 计算模数n = p * qn = p.multiply(q);// 计算欧拉函数φ(n) = (p-1)*(q-1)phi = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));// 选择公钥指数ee = BigInteger.valueOf(DEFAULT_PUBLIC_EXPONENT);// 确保e与φ(n)互质while (phi.gcd(e).compareTo(BigInteger.ONE) > 0 && e.compareTo(phi) < 0) {e = e.add(BigInteger.ONE);}// 计算私钥指数dd = 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随机字节 + 0x00throw new IllegalArgumentException("数据过短,无效填充");}// 查找分隔符0x00int 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. 安全通信协议
// 模拟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. 数字签名系统
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. 最佳实践
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标准库实现
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
- 迁移方案:
2. 性能优化方向
优化技术 | 效果 | 实现方式 |
---|---|---|
硬件加速 | 10-100倍性能提升 | 专用密码处理器 |
多素数RSA | 提高解密效率 | 使用3个以上素数 |
批处理 | 提高吞吐量 | 同时处理多个消息 |
总结
RSA算法作为非对称密码学的基石,具有以下关键特点:
- 安全性高:基于大数分解难题
- 功能全面:支持加密、解密、数字签名
- 应用广泛:SSL/TLS、SSH、数字证书等核心协议
在实际应用中:
- 优先使用Java安全库实现
- 密钥长度至少2048位
- 选择OAEP填充方案
- 敏感数据采用混合加密系统
尽管量子计算带来新的挑战,RSA仍将在未来相当长的时间内继续发挥重要作用。理解RSA的原理和实现,是构建安全系统的必备知识。