应使用 rsa.create() 创建 rsa 实例,避免过时的 rsacryptoserviceprovider;密钥导出用 exportpkcs8privatekey/exportsubjectpublickeyinfo;加密须用 oaep-sha256 且校验明文长度;签名用 signdata/verifydata 并指定 sha256 和 pkcs1;私钥严禁明文存储。

用 RSA.Create() 创建实例,别碰过时的 RSACryptoServiceProvider
新版 .NET(.NET Core 2.0+ / .NET 5+)里 RSACryptoServiceProvider 已标记为过时,它依赖 Windows CryptoAPI,跨平台不兼容,且默认密钥大小受限(比如老版本只支持最多 16384 位但实际常卡在 4096)。直接用 RSA.Create() 是唯一推荐路径——它自动适配当前运行时的最优实现(Windows 上走 CNG,Linux/macOS 走 OpenSSL)。
常见错误:硬编码 new RSACryptoServiceProvider(2048),跑在 Linux 容器里直接抛 PlatformNotSupportedException。
-
RSA.Create(2048)创建新密钥对;需要持久化时调用ExportPkcs8PrivateKey()(私钥)或ExportSubjectPublicKeyInfo()(公钥),二进制字节流,不是 PEM 字符串 - 如果必须读 PEM 格式(如 OpenSSL 生成的
id_rsa.pub),得先用第三方库(如BouncyCastle)转,.NET 原生不解析 PEM 封装 - 密钥长度选 2048 足够多数场景;3072 更稳妥;4096 增加开销,加密/解密耗时明显上升(尤其服务端高频解密时)
公钥加密必须用 Encrypt() + OAEP SHA256,别用 PKCS1
PKCS1 填充模式已被证明存在 Bleichenbacher 攻击风险,.NET 默认仍保留但它不该出现在新代码里。OAEP 是唯一安全选择,且必须显式指定哈希算法——new RSAEncryptionPadding.OaepSHA256(),不能只写 RSAEncryptionPadding.Oaep(会默认用 SHA1,而 SHA1 已不安全)。
典型翻车现场:传入 Encoding.UTF8.GetBytes("hello") 直接加密,结果解密失败——RSA 对明文长度敏感,2048 位密钥下 OAEP-SHA256 最多只能加密 190 字节左右数据。超长内容得先用 AES 加密,再用 RSA 加密 AES 密钥(即混合加密)。
- 加密前务必检查明文长度:
rsa.KeySize / 8 - 42(OAEP-SHA256)或rsa.KeySize / 8 - 11(PKCS1,不推荐) - 公钥加密仅适合小数据(如会话密钥、token ID),绝不要拿来加密用户上传的 PDF 或 JSON
- 解密方必须用同一私钥 + 同样填充方式,否则抛
CryptographicException:“The parameter is incorrect.”
签名用 SignData(),验签用 VerifyData(),别混淆加密和签名接口
RSA 签名不是“用私钥加密”,而是基于摘要的数学运算。SignData() 内部自动做哈希(如 SHA256)再签名,VerifyData() 自动复现同样哈希过程。手动先哈希再调 SignHash() 容易出错,且失去 .NET 对哈希算法绑定的保护。
容易被忽略的坑:签名时没指定 HashAlgorithmName.SHA256 和 RSAEncryptionPadding.Pkcs1(签名不用 OAEP),导致和 Java/OpenSSL 互操作失败——对方可能默认用 SHA256+PKCS1,而你用了 SHA1。
- 签名输入是原始数据(
byte[]),不是字符串;字符编码由调用方决定(通常 UTF8) - 验签失败常见原因:数据被意外修改(换行符、BOM、空格)、编码不一致、或对方用的是不同哈希算法
- 签名值本身是固定长度(如 2048 位密钥 → 256 字节),可 Base64 存储或传输,但别当成加密结果去“解密”
密钥导出和存储:私钥绝不存明文,公钥可自由分发
导出的私钥字节流(ExportPkcs8PrivateKey())不含密码保护,写到磁盘等于裸奔。生产环境必须用 ProtectedData(Windows DPAPI)或 KeyVault(Azure/AWS)封装,或者干脆让 RSA 实例托管在 CngKey 中由系统保护。
公钥看似无害,但若被篡改(如中间人替换),后续所有签名验证和加密都失效。所以分发公钥必须带完整性校验(如发布时附 SHA256 校验和)或走可信渠道(HTTPS 下载、证书链验证)。
- 开发调试阶段可用
Convert.ToBase64String(rsa.ExportSubjectPublicKeyInfo())快速看公钥内容,但别把这串 Base64 当成“加密格式” - 别把私钥塞进 appsettings.json 或环境变量——哪怕加了注释“请勿泄露”,也早晚会进 Git 历史
- 容器化部署时,私钥应通过 secret mount 注入,而非构建进镜像
密钥生命周期管理比算法调用复杂得多,尤其是私钥如何安全生成、存储、轮换和销毁,这些地方没有银弹,但每一步漏掉都会让前面所有加密逻辑形同虚设。










