.net 6+ 必须用 rsa.create(2048) 生成密钥对,导出私钥用 exportrsaprivatekey()(pkcs#8 格式),公钥用 exportrsapublickey()(subjectpublickeyinfo 格式),原生不支持 pem 封装需手动拼接头尾。

别用 RSACryptoServiceProvider,它在 .NET 6+ 已过时且跨平台直接报 PlatformNotSupportedException;所有新代码必须从 RSA.Create() 开始,密钥导出、加密、签名三者参数必须严格匹配填充方式与哈希算法,否则解密或验签必然失败。
怎么生成和导出密钥对(.NET 6+ 正确路径)
密钥生成必须调用 RSA.Create(2048)(或更高位数),不能 new RSACryptoServiceProvider(2048)。导出时注意格式语义:
-
ExportRSAPrivateKey()→ 返回 PKCS#8 格式私钥字节数组(OpenSSL 兼容,推荐) -
ExportRSAPublicKey()→ 返回 SubjectPublicKeyInfo 格式公钥字节数组(标准 PEM 公钥内容,对应-----BEGIN PUBLIC KEY-----) - .NET 原生不支持 PEM 封装,需手动拼接头尾:
"-----BEGIN RSA PRIVATE KEY-----\n" + Convert.ToBase64String(bytes, Base64FormattingOptions.InsertLineBreaks) + "\n-----END RSA PRIVATE KEY-----" - 硬编码
ExportParameters(true)是陷阱:返回的RSAParameters是结构体,无法跨平台序列化,也不含完整 PKCS#8 语义
CryptographicException: The parameter is incorrect. 的真实原因
这个错误 90% 不是密钥坏了,而是填充或长度不匹配:
- 加密时没显式指定
RSAEncryptionPadding.OaepSHA256,只写RSAEncryptionPadding.Oaep会退化为 SHA1(已不安全) - 明文超长:2048 位密钥 + OAEP-SHA256 下,
Encoding.UTF8.GetBytes(input).Length必须 ≤rsa.KeySize / 8 - 42(约 190 字节),不是字符数 - 加密用了私钥、解密用了公钥——这是签名逻辑,不是加解密流程;RSA 加解密必须是「公钥加密 → 私钥解密」
- 解密时没用同一私钥字节数组重新导入,
RSA对象不是长期可复用的“句柄”,每次操作建议新建并ImportRSAPrivateKey()
签名和验签必须用 SignData()/VerifyData(),别和 Encrypt() 混用
签名不是“用私钥加密原文”,而是对摘要做数学运算,接口完全独立:
- 签名:私钥调
SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)(OAEP 不适用于常规签名) - 验签:公钥调
VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1),参数顺序、哈希、填充必须完全一致 -
Encrypt()和Decrypt()只用于机密性;SignData()和VerifyData()只用于完整性与身份认证 - 把
SignData()结果当成“加密”再用公钥Encrypt()去“解密”,结果永远失败
为什么不能直接用 RSA 加密用户数据(比如 JSON 或文件)
RSA 本身不能加密任意长度原始数据,这是数学约束,不是实现缺陷:
- 2048 位密钥下,PKCS#1 最多加密 245 字节,OAEP-SHA256 最多约 190 字节;传入 300 字节 UTF8 字符串必抛
CryptographicException: Data too large for key size - 真实场景必须用混合加密:每次请求新生成 AES 密钥,用 AES 加密数据,再用 RSA 加密该 AES 密钥(即“信封加密”)
- 别自己
Array.Copy拼接字节数组;用BinaryWriter写入长度头,否则接收方无法拆分 AES 密文和 RSA 加密后的 key/iv - 私钥严禁以明文字符串形式存配置文件或日志;导出后应走安全存储(如 Azure Key Vault、Windows DPAPI、Linux keyring)
最易被忽略的点:填充方式和哈希算法必须在加密/解密、签名/验签两端**字面级一致**,哪怕只是 Oaep 和 OaepSHA256 的差别,也会导致静默失败;还有就是误把 ExportRSAPublicKey() 得到的 SubjectPublicKeyInfo 当成 PKCS#1 公钥传给 Python/Node.js,结果解密失败——它们默认期望的是 -----BEGIN RSA PUBLIC KEY-----,得换格式或换库。










