RSA 明明是非对称加密,为什么只能加密 245 字节?

在这里插入图片描述

前言

欢迎关注dotnet研习社,今天我们讨论的主题是“非对称加密(RSA)的一个245异常解决方案”。

很多刚接触非对称加密(RSA)的同学,都踩过一个坑:

我用 RSA 加密一段数据,却抛出异常:
System.Security.Cryptography.CryptographicException: The message exceeds the maximum allowable length for the chosen options (245).
在这里插入图片描述
疑问立刻就来了:

  • RSA 是非对称加密,号称安全性高,那为什么不能随便加密大数据?
  • 245 这个数字又是怎么来的?
  • 想要加密大文件或长文本怎么办?

别急,这篇文章一次性帮你彻底解惑。


一、RSA 是什么?

RSA 是世界上最经典、应用最广泛的非对称加密算法之一。
它最大的特点是:

  • 用一对密钥:公钥私钥
  • 加密和解密使用不同的密钥
  • 一个加密后,只能用另一个解密

因为这种「非对称性」,RSA 成为 HTTPS、SSL/TLS、数字签名、区块链等场景的基石。

二、RSA 能加密多少字节?

很多人以为:

RSA 是加密算法,那我可以直接用它加密 1MB 的文件。

实际上这是完全错误的!

RSA 的原理是大数分解,本质上是对一个大整数做模运算。所以:

  • 加密时输入的「明文」也会被看作一个大整数
  • 这个整数必须小于模数(n)
  • 而模数的大小就是你密钥长度决定的

举个例子:

  • 常用 RSA 密钥长度是 2048 位 = 256 字节
  • 加密时还要预留填充(Padding)用于增强安全性(比如 PKCS#1 v1.5 填充至少要 11 字节)

所以:

可加密最大明文 ≈ 256 字节 - 11 字节 = 245 字节

这就是那个神秘的 245 的来历。


三、为什么不能直接分块?

有的同学会问:

那我把文件拆成 245 字节一块一块 RSA 加密可不可以?

理论上可行,实际上极不推荐!

原因:

  1. 性能差:RSA 加密本身速度慢,大文件分块会极大增加 CPU 消耗。
  2. 安全性弱:RSA 的分块加密容易被重放或分块攻击,不安全。
  3. 没有意义:工业标准里从来不是这么做的。

四、正确姿势:混合加密

那么怎么安全且高效地加密大数据?

正确方法是混合加密

  1. 用 RSA 保护对称加密的密钥(如 AES Key)
  2. 用 AES 之类的对称加密算法处理大数据

核心思路:

  • 对称加密快,适合大数据
  • 非对称加密安全,适合保护密钥

两者组合,完美取长补短。

五、示例流程

  • RSA 公钥加密 AES Key
  • AES 加密大数据
  • RSA 私钥解密 AES Key
  • AES 解密还原大数据

完整示例:RSA + AES 混合加密

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace HybridEncryptionDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("=== Hybrid Encryption Demo ===");

            // ===== 1. 生成 RSA 密钥对 =====
            using RSA rsa = RSA.Create(2048);
            var publicKey = rsa.ExportParameters(false);
            var privateKey = rsa.ExportParameters(true);

            // ===== 2. 原文数据 =====
            string plainText = "";
            plainText += "这是要加密的一段很长很长的数据,用于演示超过 RSA 245 字节限制,看看是否正常。X1 \r\n";
            plainText += "这是要加密的一段很长很长的数据,用于演示超过 RSA 245 字节限制,看看是否正常。X2 \r\n";
            plainText += "这是要加密的一段很长很长的数据,用于演示超过 RSA 245 字节限制,看看是否正常。X3 \r\n";
            plainText += "这是要加密的一段很长很长的数据,用于演示超过 RSA 245 字节限制,看看是否正常。X4 \r\n";
            plainText += "这是要加密的一段很长很长的数据,用于演示超过 RSA 245 字节限制,看看是否正常。X5 \r\n";
            plainText += "这是要加密的一段很长很长的数据,用于演示超过 RSA 245 字节限制,看看是否正常。X6 \r\n";
            plainText += "这是要加密的一段很长很长的数据,用于演示超过 RSA 245 字节限制,看看是否正常。X7 \r\n";
            plainText += "这是要加密的一段很长很长的数据,用于演示超过 RSA 245 字节限制,看看是否正常。X8 \r\n";
            plainText += "这是要加密的一段很长很长的数据,用于演示超过 RSA 245 字节限制,看看是否正常。X9 \r\n";

            Console.WriteLine($"原文: {plainText},长度:{plainText.Length}");

            // ===== 3. 生成 AES 密钥和 IV =====
            using Aes aes = Aes.Create();
            aes.KeySize = 256;
            aes.GenerateKey();
            aes.GenerateIV();

            byte[] aesKey = aes.Key;
            byte[] aesIV = aes.IV;

            // ===== 4. 用 AES 加密大数据 =====
            byte[] encryptedData = AESEncrypt(plainText, aesKey, aesIV);
            Console.WriteLine($"AES 加密后: {Convert.ToBase64String(encryptedData)}");

            // ===== 5. 用 RSA 公钥加密 AES Key + IV =====
            using RSA rsaEncryptor = RSA.Create();
            rsaEncryptor.ImportParameters(publicKey);

            byte[] encryptedAesKey = rsaEncryptor.Encrypt(aesKey, RSAEncryptionPadding.Pkcs1);
            byte[] encryptedAesIV = rsaEncryptor.Encrypt(aesIV, RSAEncryptionPadding.Pkcs1);

            Console.WriteLine($"RSA 加密的 AES Key: {Convert.ToBase64String(encryptedAesKey)}");
            Console.WriteLine($"RSA 加密的 AES IV:  {Convert.ToBase64String(encryptedAesIV)}");

            // ===== 6. 用 RSA 私钥解密 AES Key + IV =====
            using RSA rsaDecryptor = RSA.Create();
            rsaDecryptor.ImportParameters(privateKey);

            byte[] decryptedAesKey = rsaDecryptor.Decrypt(encryptedAesKey, RSAEncryptionPadding.Pkcs1);
            byte[] decryptedAesIV = rsaDecryptor.Decrypt(encryptedAesIV, RSAEncryptionPadding.Pkcs1);

            // ===== 7. 用解密后的 AES Key 解密数据 =====
            string decryptedText = AESDecrypt(encryptedData, decryptedAesKey, decryptedAesIV);
            Console.WriteLine($"最终解密还原: {decryptedText}");
        }

        static byte[] AESEncrypt(string plainText, byte[] key, byte[] iv)
        {
            using Aes aesAlg = Aes.Create();
            aesAlg.Key = key;
            aesAlg.IV = iv;

            using MemoryStream msEncrypt = new MemoryStream();
            using CryptoStream csEncrypt = new CryptoStream(msEncrypt, aesAlg.CreateEncryptor(), CryptoStreamMode.Write);
            using StreamWriter swEncrypt = new StreamWriter(csEncrypt);

            swEncrypt.Write(plainText);
            swEncrypt.Close();

            return msEncrypt.ToArray();
        }

        static string AESDecrypt(byte[] cipherText, byte[] key, byte[] iv)
        {
            using Aes aesAlg = Aes.Create();
            aesAlg.Key = key;
            aesAlg.IV = iv;

            using MemoryStream msDecrypt = new MemoryStream(cipherText);
            using CryptoStream csDecrypt = new CryptoStream(msDecrypt, aesAlg.CreateDecryptor(), CryptoStreamMode.Read);
            using StreamReader srDecrypt = new StreamReader(csDecrypt);

            return srDecrypt.ReadToEnd();
        }
    }
}

运行效果

执行后,输出结果:
在这里插入图片描述

核心点

✅ 大数据用 AES 加密
✅ 用 RSA 公钥只加密 AES Key/IV
✅ 私钥解密后还原 Key/IV,再解密大数据

六、为什么要填充(Padding)?

还有人疑惑:

填充是干什么的?为什么要减掉 11 字节?

这是为了防止已知明文攻击。RSA 原生是纯数学运算,如果没有填充,同一个明文每次加密结果都一样,极易被穷举。
Padding(如 PKCS#1、OAEP)可以引入随机性,让同一个明文每次加密结果都不同,大大提升安全性。

七、一句话总结

RSA 是用来加密小块数据的(比如对称密钥、签名、哈希)

大文件要用对称加密(AES、DES)

正确做法是混合加密

关于RSA算法,有一句快速记忆的口诀:

加密用公钥,解密用私钥

签名用私钥,验签用公钥

结语

当你再次看到:

The message exceeds the maximum allowable length for the chosen options (245).

不要惊慌,别想着把密钥换得更大,更不要分块暴力加密。

正确解法就是:
RSA + AES 混合加密
这才是安全通信的业界标配。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dotnet研习社

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值