RSA加密算法:非对称密码学的基石

一、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算法作为非对称密码学的基石,具有以下关键特点:

  1. 安全性高:基于大数分解难题
  2. 功能全面:支持加密、解密、数字签名
  3. 应用广泛:SSL/TLS、SSH、数字证书等核心协议

在实际应用中:

  • 优先使用Java安全库实现
  • 密钥长度至少2048位
  • 选择OAEP填充方案
  • 敏感数据采用混合加密系统

尽管量子计算带来新的挑战,RSA仍将在未来相当长的时间内继续发挥重要作用。理解RSA的原理和实现,是构建安全系统的必备知识。

相关推荐
我是唐青枫几秒前
C#.NET NLog 详解
开发语言·c#·.net
slowlybutsurely3 分钟前
Cursor快速入门
java·ai编程·cursor
天天摸鱼的java工程师9 分钟前
假设你在开发订单系统时遇到高并发下库存扣减出错,如何解决?由浅入深分析
java·后端·面试
都叫我大帅哥10 分钟前
Redis的ZSet:从“青铜”到“王者”的排序神器
java·redis
Mr_Xuhhh20 分钟前
网络基础(1)
c语言·开发语言·网络·c++·qt·算法
肖笙XiaoSheng21 分钟前
使用Gemini2.5 pro 优化我的定时任务(二)
java·后端·代码规范
小小霸王龙!23 分钟前
互联网大厂Java面试实录:Spring Boot与微服务在电商场景中的应用
java·spring boot·redis·微服务·电商
旺旺大力包24 分钟前
【JS笔记】JS 和 noodjs 的常见操作(十)
开发语言·javascript·node.js·ecmascript
深栈解码26 分钟前
JUC并发编程 CAS运行机制详解
java·后端
草履虫建模27 分钟前
Postman - API 调试与开发工具 - 标准使用流程
java·测试工具·spring·json·测试用例·postman·集成学习