rsa不能加密任意长度数据,2048位密钥pkcs#1填充下明文上限为245字节,超长需用rsa+aes混合加密。

直接说结论:RSA在C#里不能用来加密任意长度的原始数据,RSACryptoServiceProvider或RSA.Create()对输入长度有严格限制(比如2048位密钥最多只能加密245字节的明文),强行传大字符串会抛CryptographicException: "The parameter is incorrect."。真正安全、可用的做法是——用RSA加密AES密钥,再用AES加密数据;签名则必须用SignData()/VerifyData(),别和加密接口混用。
为什么RSACryptoServiceProvider.Encrypt()总抛参数错误
这个异常几乎全是输入超长或填充方式不匹配导致的。2048位RSA密钥配合PKCS#1 v1.5填充时,最大明文长度是245字节;OAEP填充下是214字节。你传一个UTF-8编码后300字节的JSON字符串进去,必然失败。
- 检查明文长度:
Encoding.UTF8.GetBytes(input).Length必须 ≤ 密钥长度(bit)/ 8 − 11(PKCS#1)或 − 42(OAEP) - 别用
Encrypt()处理用户输入的任意文本,它不是为这个设计的 - 如果硬要加密短字符串(比如token ID),务必统一指定填充:
rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1)
SignData()和Encrypt()绝对不能互换
这是最常踩的坑:有人把私钥SignData()的结果当成“加密”,再用公钥Encrypt()去“解密”,结果永远失败。签名和加密是两套完全独立的数学过程,接口不可混用。
- 签名流程:私钥调
SignData(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1) - 验签流程:公钥调
VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1) - 加密流程:公钥调
Encrypt(plainBytes, RSAEncryptionPadding.OaepSHA256) - 解密流程:私钥调
Decrypt(cipherBytes, RSAEncryptionPadding.OaepSHA256) - 填充方式必须严格一致,
OaepSHA256比Pkcs1更安全,优先选前者
真实场景该怎么做:RSA + AES 混合加密
你要传一段用户敏感信息(比如身份证号+地址),正确链路是:
- 生成随机
Aes.Key和Aes.IV(每次请求都新生成) - 用AES-CBC或AES-GCM加密原始数据,得到
aesCipher - 用接收方公钥RSA加密
Aes.Key和Aes.IV(可拼成一个结构体再加密) - 把
aesCipher+ RSA加密后的key/iv一起发过去 - 对方先用私钥解出key/iv,再用它们解AES密文
注意:别自己Array.Copy拼接字节数组。用BinaryWriter写入长度头,否则对方收到后根本分不清哪段是AES密文、哪段是RSA密文。
密钥导出与加载容易忽略的细节
RSACryptoServiceProvider.ToXmlString(true)导出的是含私钥的XML,但.NET Core 3.0+默认用RSA.Create(),它不支持XML格式。迁移时必须改用ExportRSAPrivateKey()(返回PKCS#8)或ExportSubjectPublicKeyInfo()(公钥,X.509)。
- 旧代码依赖XML?加
using System.Security.Cryptography.Xml;并保持RSACryptoServiceProvider实例 - 新项目推荐
RSA.Create()+ImportRSAPrivateKey(),二进制更紧凑、无XML解析开销 - 私钥文件千万别硬编码进源码,也别放Git——哪怕只是测试密钥
- 生产环境用
CngKey或Azure Key Vault托管私钥,避免内存泄露风险
最关键的一点:RSA本身不处理哈希,签名前必须手动计算SHA256哈希值再喂给SignData();而加密前绝不能自行哈希——那会丢失原始数据。










