RSA 算法完整详解 + C# 原生实现(无第三方库)

非对称加密(公钥密码)详解


基本概念与工作原理

非对称加密(又称公钥密码学)是现代密码学的重要基石。区别于传统对称加密,该技术采用一对数学关联但功能不同的密钥:

公钥 (e, n)
  • 完全公开,可自由分发
  • 主要功能:
    • 加密:其他用户使用接收方公钥加密信息
    • 验证签名:确认消息来源的真实性
  • 典型应用:HTTPS连接建立时服务器向客户端发送公钥证书
私钥 (d, n)
  • 严格保密,仅所有者持有
  • 主要功能:
    • 解密:解密对应公钥加密的信息
    • 生成签名:证明消息的真实来源

加密解密机制

加密流程

明文 M → 公钥(e,n)加密 → 密文 C

数学表达式:

解密流程

密文 C → 私钥(d,n)解密 → 明文 M

数学表达式:

核心数学要素

素数选择
  • 两个不同的大素数 p 和 q(通常≥512位)
  • 示例:p=61,q=53(实际应用需更大)
关键计算
  • 模数:n = p×q
    示例:61×53=3233
  • 欧拉函数:φ(n)=(p-1)(q-1)
    示例:φ(3233)=60×52=3120
  • 公钥指数 e:
    • 满足 1<e<φ(n)且gcd(e,φ(n))=1
    • 常用值:e=65537(兼顾安全与效率)
    • 示例:e=17(与3120互质)
  • 私钥指数 d:
    • e的模逆元,满足 e×d ≡1 mod φ(n)
    • 通过扩展欧几里得算法计算
    • 示例:d=2753(17×2753 mod 3120=1)

RSA算法公式

cs 复制代码
加密:C = Mᵉ mod n  
解密:M = Cᵈ mod n
使用限制
  • 明文需满足 0<M<n
  • 长消息需分块处理(每块<n)

实际应用演示

参数设置:

  • p=61, q=53
  • n=3233
  • φ(n)=3120
  • e=17
  • d=2753

加密过程:

  1. 取明文 M=123
  2. 计算密文 C=123¹⁷ mod 3233=855

解密过程:

  1. 接收密文 C=855
  2. 计算明文 M=855²⁷⁵³ mod 3233=123

注:实际应用中需使用更大的数值(通常≥2048位)确保安全性。

历史背景与发展


公钥密码学的诞生

1976年,Whitfield Diffie和Martin Hellman两位密码学先驱在《密码学的新方向》论文中首次提出非对称加密概念,并设计了著名的Diffie-Hellman密钥交换协议。这一开创性工作成功解决了密钥分发这一长期难题,但当时仅提出了理论框架,尚未给出具体的可实施加密算法。

RSA算法的正式提出

1977年,麻省理工学院(MIT)的三位学者Ron Rivest、Adi Shamir和Leonard Adleman经过数月研究,首次构建出第一个实用的公钥加密系统------RSA算法。该算法以三人姓氏首字母命名,其安全性基于大整数分解难题,具体实现包含以下核心要素:

  • 密钥生成:选取两个大素数p和q,计算n=p×q
  • 公钥:(n,e)
  • 私钥:(n,d),其中e和d满足模反元素关系

鲜为人知的英国先例

历史档案显示,英国政府通信总部(GCHQ)数学家Clifford Cocks早在1973年就独立发明了类似的非对称加密算法。但由于情报机构的保密要求,这项成果被列为绝密(代号"非对称密码"),直到1997年才解密公开,比MIT团队的发明整整早了4年。

专利与普及历程

  • 1977年:在美国申请专利(US4405829)
  • 1983年:专利正式生效,由RSA Security公司维护
  • 2000年9月:专利到期后进入公共领域。专利期内RSA实验室通过授权费获取收益,专利到期后算法实现完全开源。

典型应用场景

作为最成熟的公钥算法,RSA已深度集成到各类安全协议中:

  • 网络通信:HTTPS握手过程中的密钥交换
  • 远程访问:SSH认证与加密通道建立
  • PKI体系:X.509数字证书的签名验证
  • 数据保护:PGP/GPG文件加密系统
  • 身份认证:电子签名标准(如ECDSA with RSA-PSS)

面临的量子威胁

随着量子计算的发展,Peter Shor于1994年提出的量子算法能在多项式时间内解决大整数分解问题。具体威胁包括:

  • 2048位RSA可能被4000+量子比特计算机破解
  • NIST预计2024年完成后量子密码标准化(PQC)
  • 过渡方案采用混合加密(如RSA+格密码)

当前业界正逐步采用抗量子算法(如CRYSTALS-Kyber)替代RSA,各大厂商已开始支持新标准。根据NIST路线图,RSA-2048预计将在2030年前逐步淘汰。

核心原理


数学基础

RSA算法的核心数学基础之一是欧拉定理。该定理表明:对于任意两个互质的正整数M和n(即gcd(M,n)=1),存在以下关系:

其中,是欧拉函数,表示小于n且与n互质的正整数的个数。当n为两个素数p和q的乘积时,

RSA算法的正确性推导如下:

  • 密钥生成条件,即存在整数k满足:

  • 加密过程 :密文

  • 解密过程 :计算,展开后为:

根据欧拉定理,当gcd(M,n)=1时:

因此:

这证明了解密过程能够准确还原原始明文。

在实际应用中,即使M与n不互质(即gcd(M,n)≠1),通过中国剩余定理仍可证明RSA算法的有效性。

安全根基

RSA算法的安全性依赖于大整数质因数分解问题的计算困难性,具体表现为:

  • 正向计算容易:给定两个大素数p和q,计算其乘积n=p×q可在多项式时间内完成。
  • 逆向计算困难 :仅给定n时,分解出p和q在经典计算机上需要亚指数级时间复杂度。目前最优的通用数域筛法(GNFS)的时间复杂度约为:

    其中,常数c≈1.92。

实际安全限制

  • 历史发展
    • 1999年:512位RSA被破解(约155十进制位)
    • 2009年:768位RSA被破解(232十进制位)
  • 当前推荐
    • 常规用途:至少2048位(617十进制位)
    • 高安全性需求(如CA证书、长期加密):4096位(1234十进制位)
  • 未来展望:量子计算机的Shor算法可在多项式时间内分解大整数,因此后量子密码学研究正在进行。

典型攻击方式包括:

  • 旁路攻击(如计时攻击、功耗分析)
  • 选择密文攻击
  • 共模攻击(当多个用户使用相同n时)

这些攻击需要通过填充方案(如OAEP)和防护措施进行防范。

RSA加密算法执行流程详解


RSA加密算法的完整流程包含三个核心模块:密钥生成→加密→解密。以下是各模块的详细实现步骤:

密钥生成模块

选择质数
  • 随机选取两个不同的大质数p和q
  • 实际应用中推荐使用1024位或2048位长度的质数
  • 示例:p=61,q=53(仅为演示,实际应用需更大)
计算模数
  • 计算n = p × q
  • 该模数n将用于公钥和私钥
  • 示例:n = 61×53 = 3233
计算欧拉函数
  • 计算φ(n) = (p-1)(q-1)
  • 表示小于n且与n互质的正整数数量
  • 示例:φ(3233) = 60×52 = 3120
选择公钥指数e
  • 通常采用固定值65537(0x10001)
  • 必须满足1 < e < φ(n)且gcd(e, φ(n)) = 1
  • 示例:选择e=17(与3120互质)
计算私钥指数d
  • d是e关于模φ(n)的模反元素
  • 满足e×d ≡ 1 mod φ(n)
  • 可通过扩展欧几里得算法求解
  • 示例:d=2753(17×2753 mod 3120=1)
密钥输出
  • 公钥:(e, n) → 示例:(17, 3233)
  • 私钥:(d, n) → 示例:(2753, 3233)

加密模块

明文预处理
  • 将明文转换为整数M(使用ASCII/Unicode编码)
  • 需满足0 < M < n(否则需分段处理)
  • 示例:加密"A"(ASCII 65),65 < 3233可直接加密
加密计算
  • 使用公钥(e,n)计算密文C = M^e mod n
  • 采用快速幂算法提升计算效率
  • 示例:

解密模块

解密计算
  • 使用私钥(d,n)计算明文M = C^d mod n
  • 同样使用快速幂算法优化
  • 示例:
后处理
  • 将整数M还原为原始格式
  • 示例:65 → "A"

实际应用注意事项

长文本处理
  • 原生RSA不能直接加密长文本(需M < n)
  • 工程解决方案:
    • 采用RSA+AES混合加密
    • RSA加密随机AES密钥
    • AES加密实际数据
填充方案
  • 必须使用标准填充方案(如OAEP或PKCS#1 v1.5)
  • 无填充的教科书式RSA存在安全隐患:
    • 易受低加密指数攻击
    • 易受选择明文攻击
    • 可能遭受共模攻击
性能考虑
  • RSA运算效率较低,不适合大数据量加密
  • 实际应用通常仅用于密钥交换或数字签名

注:本文示例为无填充的教科书式RSA,仅供学习算法原理使用,实际应用必须遵循安全标准实现。

算法性能分析


运算瓶颈:模幂运算

RSA加解密的计算核心在于大数模幂运算(即计算 ),其中:

  • 加密过程:使用公钥指数 ( e )(通常取65537这个小素数以优化计算速度)
  • 解密过程:由于私钥指数 ( d ) 通常与模数 ( n ) 位数相当,解密速度显著慢于加密。例如2048位RSA密钥解密需数毫秒,而加密仅需几十微秒
优化方法
  • 快速幂算法 :通过二进制分解指数,将时间复杂度从 ( O(k) ) 降至(( k ) 为指数位数)。例如:

  • 中国剩余定理(CRT):私钥解密时将大模数分解为两个小模数计算,可提升解密速度3-4倍

密钥长度与安全强度

RSA的安全性基于大整数分解难度,其密钥长度与对称加密算法的安全强度对应关系如下:

RSA密钥长度 等效对称密钥强度 对应算法示例
2048位 112位 3DES-112
3072位 128位 AES-128
4096位 192位 AES-192
应用场景
  • 2048位RSA:常规安全需求(如TLS证书)
  • 4096位RSA:高安全要求场景(如长期存储的敏感数据)

时间复杂度分析

  • 模幂运算 :采用快速幂算法时,计算 的时间复杂度为 。对于2048位的 ( d ),约需2048次模乘运算
  • 素性检测
    • Miller-Rabin测试 :概率性素数检测,20次迭代可将误判率降至
    • 实际应用:生成RSA密钥时先使用小素数筛法初筛,再用Miller-Rabin验证

局限性及应用场景

性能局限
  • 计算密集:不适合直接加密大数据(如1GB文件需分段处理,效率极低)
  • 数据限制:明文长度受模数 ( n ) 限制(2048位RSA最多加密245字节)
典型应用
  • 密钥交换:与对称加密配合使用(如TLS中用RSA加密AES密钥)
  • 数字签名:对哈希值(如SHA-256)签名而非原始数据
应用示例
  • HTTPS通信:客户端用RSA加密随机AES密钥,后续通信切换至AES
  • PGP签名:对文件SHA-256哈希值进行RSA签名,接收方用公钥验证

完整原生实现


特性:

  • 不调用System.Security.Cryptography内置 RSA
  • 使用System.Numerics.BigInteger处理超大整数(.NET 内置,不属于第三方加密库)
  • 手动实现:MillerRabin 素性测试、扩展欧几里得算法、模逆元、快速模幂、密钥生成、教科书式 RSA 加解密
  • ⚠️【教科书 RSA,无填充!禁止生产使用】
cs 复制代码
using System;
using System.Numerics;
using System.Random;

namespace RSAStudy
{
    /// <summary>
    /// 纯学习用途:无填充教科书RSA实现
    /// 生产环境严禁使用!缺少填充、无安全防护
    /// </summary>
    public static class SimpleRSA
    {
        private static readonly Random Rand = new Random();

        #region 基础数学工具
        /// <summary>
        /// 快速模幂:(baseNum ^ exponent) % mod
        /// </summary>
        public static BigInteger ModPow(BigInteger baseNum, BigInteger exponent, BigInteger mod)
        {
            BigInteger result = 1;
            baseNum %= mod;
            while (exponent > 0)
            {
                if ((exponent & 1) == 1)
                    result = (result * baseNum) % mod;
                baseNum = (baseNum * baseNum) % mod;
                exponent >>= 1;
            }
            return result;
        }

        /// <summary>
        /// 扩展欧几里得算法  ax + by = gcd(a,b)
        /// </summary>
        private static BigInteger ExtGcd(BigInteger a, BigInteger b, out BigInteger x, out BigInteger y)
        {
            if (b == 0)
            {
                x = 1;
                y = 0;
                return a;
            }
            BigInteger g = ExtGcd(b, a % b, out BigInteger x1, out BigInteger y1);
            x = y1;
            y = x1 - (a / b) * y1;
            return g;
        }

        /// <summary>
        /// 求模逆元 a^(-1) mod m,要求a与m互质
        /// </summary>
        public static BigInteger ModInverse(BigInteger a, BigInteger m)
        {
            BigInteger g = ExtGcd(a, m, out BigInteger x, out _);
            if (g != 1)
                throw new Exception("不存在模逆元,两个数字不互质");
            BigInteger res = (x % m + m) % m;
            return res;
        }

        /// <summary>
        /// 生成指定范围内随机BigInteger
        /// </summary>
        private static BigInteger RandomBigInteger(BigInteger min, BigInteger max)
        {
            if (min >= max) throw new ArgumentException("范围错误");
            byte[] buffer = new byte[max.ToByteArray().Length];
            Rand.NextBytes(buffer);
            BigInteger r = new BigInteger(buffer);
            r = BigInteger.Abs(r);
            return min + r % (max - min);
        }
        #endregion

        #region Miller-Rabin 概率素性检测
        /// <summary>
        /// MillerRabin素数测试
        /// </summary>
        public static bool IsPrime(BigInteger n, int rounds = 10)
        {
            if (n <= 1) return false;
            if (n <= 3) return true;
            if (n % 2 == 0) return false;

            BigInteger d = n - 1;
            int s = 0;
            while (d % 2 == 0)
            {
                d /= 2;
                s++;
            }

            for (int r = 0; r < rounds; r++)
            {
                BigInteger a = RandomBigInteger(2, n - 1);
                BigInteger x = ModPow(a, d, n);
                if (x == 1 || x == n - 1)
                    continue;

                bool composite = true;
                for (int i = 0; i < s - 1; i++)
                {
                    x = ModPow(x, 2, n);
                    if (x == n - 1)
                    {
                        composite = false;
                        break;
                    }
                }
                if (composite)
                    return false;
            }
            return true;
        }

        /// <summary>
        /// 寻找指定位数随机素数
        /// </summary>
        public static BigInteger GeneratePrime(int bitLength)
        {
            if (bitLength < 8) throw new ArgumentException("位数过小");
            BigInteger min = BigInteger.Pow(2, bitLength - 1);
            BigInteger max = BigInteger.Pow(2, bitLength) - 1;

            while (true)
            {
                BigInteger num = RandomBigInteger(min, max);
                if (num % 2 == 0) num++;
                if (IsPrime(num))
                    return num;
            }
        }
        #endregion

        #region RSA 密钥结构
        public struct RsaKeyPair
        {
            public BigInteger E; //公钥指数
            public BigInteger D; //私钥指数
            public BigInteger N; //模数
        }
        #endregion

        /// <summary>
        /// 生成RSA密钥对
        /// bit: 总模数长度,示例使用256bit用于快速测试,正式至少2048
        /// </summary>
        public static RsaKeyPair GenerateRsaKeys(int totalBit = 256)
        {
            int primeBit = totalBit / 2;
            BigInteger p = GeneratePrime(primeBit);
            BigInteger q = GeneratePrime(primeBit);
            while (p == q)
                q = GeneratePrime(primeBit);

            BigInteger n = p * q;
            BigInteger phi = (p - 1) * (q - 1);
            BigInteger e = 65537; //标准公钥指数

            //确保e和phi互质
            while (BigInteger.GreatestCommonDivisor(e, phi) != 1)
            {
                e += 2;
            }

            BigInteger d = ModInverse(e, phi);
            return new RsaKeyPair { E = e, D = d, N = n };
        }

        /// <summary>
        /// 教科书RSA加密 数字M
        /// </summary>
        public static BigInteger Encrypt(BigInteger M, BigInteger e, BigInteger n)
        {
            if (M >= n || M <= 0)
                throw new Exception("明文数值必须满足 0<M<N");
            return ModPow(M, e, n);
        }

        /// <summary>
        /// 教科书RSA解密
        /// </summary>
        public static BigInteger Decrypt(BigInteger C, BigInteger d, BigInteger n)
        {
            return ModPow(C, d, n);
        }

        /// <summary>
        /// 字符串辅助:文本转大整数(简单编码测试用)
        /// </summary>
        public static BigInteger StringToBigInt(string text)
        {
            byte[] data = System.Text.Encoding.UTF8.GetBytes(text);
            return new BigInteger(data, isUnsigned: true);
        }

        public static string BigIntToString(BigInteger num)
        {
            byte[] data = num.ToByteArray(isUnsigned: true);
            return System.Text.Encoding.UTF8.GetString(data);
        }

        //测试入口
        public static void Main()
        {
            Console.WriteLine("===== 简易RSA测试(学习使用)=====");
            //生成256位RSA密钥,测试用;生产替换为2048
            var keys = GenerateRsaKeys(256);
            Console.WriteLine($"公钥 E={keys.E}");
            Console.WriteLine($"模数 N={keys.N}");
            Console.WriteLine($"私钥 D={keys.D}");

            string originText = "Hello RSA 学习测试";
            Console.WriteLine($"\n原始明文:{originText}");

            BigInteger M = StringToBigInt(originText);
            Console.WriteLine($"明文数值M:{M}");

            //加密
            BigInteger C = Encrypt(M, keys.E, keys.N);
            Console.WriteLine($"密文C:{C}");

            //解密
            BigInteger recoverM = Decrypt(C, keys.D, keys.N);
            string recoverText = BigIntToString(recoverM);
            Console.WriteLine($"还原明文:{recoverText}");
        }
    }
}

运行说明

  • 项目需要启用 System.Numerics
  • 示例中使用 256 位密钥,生成速度快,仅用于演示;真实场景使用≥2048bit;
  • 限制:字符串不能太长!转换后的大整数 M 必须小于 N,长文本直接加密会报错,印证前面理论:RSA 不适合加密大量数据。

RSA 优缺点


优点

解决密钥分发难题
  • 采用非对称加密机制,公钥可公开传输(如通过证书或密钥服务器分发),私钥始终由持有者保密。
  • 相比对称加密(如 AES),无需预先协商共享密钥,避免了密钥交换过程中的安全风险(如中间人攻击)。
  • 典型应用:HTTPS 握手时通过 RSA 交换会话密钥、SSH 公钥认证。
支持数字签名
  • 私钥可对数据哈希值加密生成签名,公钥用于验证签名,实现身份认证(确认发送者)和完整性保护(防篡改)。
  • 示例:软件发布者用 RSA 私钥签名安装包,用户通过公钥验证来源可信性。
标准成熟,兼容性强
  • PKCS#1、RFC 8017 等标准明确定义算法细节,硬件/软件生态支持完善。
  • 所有主流操作系统(Windows/Linux/macOS)、浏览器(Chrome/Firefox)及协议(SSL/TLS、SSH、S/MIME)均原生集成 RSA。
加密与签名一体化
  • 同一套密钥对既可加密(公钥加密、私钥解密),又可签名(私钥签名、公钥验签),简化系统设计。
  • 对比:某些算法(如 ECC)需区分加密密钥对和签名密钥对。

缺点

性能瓶颈显著
  • 计算复杂度高,加密/解密速度比对称加密慢 1000 倍以上,仅适合加密短数据(如会话密钥)。
  • 实际应用中通常采用混合加密:RSA 传递 AES 密钥,再用 AES 加密大文件。
密钥长度影响效率
  • 安全性依赖大数分解难度,密钥长度每增加一倍,运算耗时呈指数增长(如 4096 位密钥比 2048 位慢 6-8 倍)。
原生填充漏洞
  • 无填充的"裸 RSA"易受重放攻击、选择明文攻击(如通过数学性质推测明文)。
  • 必须使用 OAEP(加密)或 PSS(签名)等填充方案,但错误实现仍可能导致漏洞(如 PKCS#1 v1.5 填充的 Bleichenbacher 攻击)。
抗量子攻击能力弱
  • Shor 算法可在量子计算机上高效分解大整数,2048 位 RSA 可能被量子计算机迅速破解。
  • 后量子密码学替代方案(如格密码)正在标准化(NIST PQC 项目)。
实现门槛高
  • 密钥生成需高质量随机数,且需严格防护侧信道攻击(如时序攻击、功耗分析)。
  • 历史案例:OpenSSL 的"Heartbleed"漏洞因内存处理不当引发。
密钥尺寸庞大
  • 2048 位公钥占用 256 字节,比 ECC 密钥(256 位仅 32 字节)大 8 倍,影响存储和传输效率。
  • 在物联网(IoT)等资源受限场景中问题尤为突出。

适用场景详解


典型应用场景

HTTPS/TLS 安全通信
  • 服务器 RSA 加密预主密钥:在 TLS 握手过程中,客户端使用服务器的 RSA 公钥加密预主密钥(46字节随机数),服务器用私钥解密获取。现代协议(TLS 1.3)已逐步替换为前向安全的ECDHE密钥交换。
  • 示例:当用户访问https://example.com时,浏览器与服务器建立TLS连接,RSA用于初始密钥交换阶段。
X.509 数字证书体系
  • 网站SSL证书:证书颁发机构(CA)使用RSA私钥对网站公钥进行签名,浏览器用CA公钥验证证书真实性。
  • 代码签名证书:软件开发者使用RSA私钥对程序哈希值签名,用户验证签名确保软件未被篡改。
  • 典型参数:当前主流采用RSA-2048或RSA-3072,证书有效期通常为1-2年。
SSH 安全认证
  • 早期身份认证:SSHv1使用RSA进行主机认证和用户认证,现已被更安全的ECDSA/Ed25519替代。
  • 典型实现:用户生成RSA密钥对(~/.ssh/id_rsa),将公钥上传至服务器实现免密登录。
数字签名应用
  • 电子签章系统:对PDF/合同文件的哈希值进行RSA签名,提供法律效力的电子签名。
  • 示例流程
    • 计算文档SHA-256哈希值
    • 用发送方私钥加密哈希值生成签名
    • 接收方用公钥解密验证签名真伪
混合加密方案
  • AES+RSA组合加密
    • 生成随机AES-256密钥(32字节)
    • 用接收方RSA公钥加密该对称密钥
    • 用AES加密实际文件数据
    • 将加密后的密钥和数据一起传输
硬件安全设备
  • 智能卡/U盾应用
    • 银行U盾存储RSA私钥,交易时进行签名
    • 企业VPN令牌使用RSA进行身份认证
    • 典型芯片:飞思卡尔/NXP的智能卡芯片,支持RSA-2048运算

不适用场景

大文件加密
  • 性能限制 :加密1GB文件需要:
    • RSA-2048:约30秒(单线程)
    • AES-256:仅需约0.5秒
  • 内存消耗:RSA加密数据长度受密钥长度限制(2048位密钥最多加密245字节)
实时流媒体
  • 延迟问题
    • RSA加密延迟:约10ms/次(2048位)
    • 直播视频需要<100ms端到端延迟
  • 解决方案:使用ECDH密钥交换建立会话后切换AES加密
长期保密通信
  • 量子计算威胁
    • 理论上量子计算机可破解RSA的整数分解问题
    • NIST建议2030年后停用RSA-2048
  • 替代方案:部署抗量子密码如CRYSTALS-Kyber(已纳入NIST PQC标准)

总结


  • RSA 是最经典的非对称加密算法,依托大数分解难题,使用一对公私钥分离实现加密与认证;
  • 数学核心:模幂运算、欧拉定理、模逆元;完整流程:素数选取→密钥生成→加密→解密;
  • 教科书原始 RSA 仅适合理论学习,工程使用必须:2048bit 以上密钥 + OAEP 填充 + 使用官方标准密码库;禁止手写实现上线;
  • 工程最佳实践:RSA 只加密 AES 等对称密钥,大量业务数据交给高速对称加密;
  • 长期视角:随着量子计算发展,RSA 未来会逐步被 NIST 标准化的后量子密码算法替代。