为什么直接用 RSA.Create()
生成的密钥不能跨平台解密
因为 .NET 默认使用的是 Windows CNG(Cryptography Next Generation)实现,生成的密钥默认是
RSACng类型,私钥导出格式为
PKCS#8(带密码保护时可能加密),而公钥是
SubjectPublicKeyInfo格式。但很多其他语言(如 Python 的
cryptography、Node.js 的
crypto)默认期望 PEM 封装的
PKCS#1格式公钥/私钥(即以
-----BEGIN RSA PUBLIC KEY-----开头)。不转换格式就传给对方,必然解密失败。
实操建议:
导出公钥时,优先用ExportSubjectPublicKeyInfo()(对应 PEM 中
-----BEGIN PUBLIC KEY-----),这是跨语言兼容性最好的选择; 导出私钥时,若需给其他语言用,避免用
ExportPkcs8PrivateKey()(.NET 6+ 默认),改用
ExportRSAPrivateKey()得到 PKCS#1 格式(
-----BEGIN RSA PRIVATE KEY-----),但注意:该方法导出的是未加密私钥,务必确保传输和存储安全; 如果对方坚持要 PKCS#8 无密码私钥(比如 Java 的
PKCS8EncodedKeySpec),可用
ExportPkcs8PrivateKey(),再用 OpenSSL 转换:
openssl pkcs8 -topk8 -nocrypt -in key.pk8 -out key.pem。
RSA.Encrypt()
报错“Data too large for key size”怎么处理
RSA 本身不能直接加密长数据,它有严格的明文长度限制:对 2048 位密钥,PKCS#1 v1.5 填充下最多加密 245 字节;OAEP 填充下约 190 字节。超过就会抛出
CryptographicException: Data too large for key size。
这不是 bug,是 RSA 的数学约束。真实场景中必须分层处理:
用 RSA 加密一个随机生成的 256 位 AES 密钥(即“信封加密”); 用这个 AES 密钥 +AesGcm或
AesCbc加密实际数据; 把加密后的 AES 密钥和 AES 密文一起发送; 接收方先用 RSA 解密出 AES 密钥,再用它解密数据。
别试图手动分块加密——RSA 分块不仅低效,还破坏语义安全性,且不同填充方式无法简单拼接。
如何安全地序列化和反序列化 RSA 密钥(尤其在 ASP.NET Core 配置中)
密钥不能硬编码,也不该以明文形式存进
appsettings.json。推荐做法是分离存储: 私钥走操作系统级保护:Windows 用
CngKey.Import()+ DPAPI,Linux/macOS 用
System.Security.Cryptography.X509Certificates加载 PFX 并设
X509KeyStorageFlags.EphemeralKeySet; 公钥可安全暴露:用
ExportSubjectPublicKeyInfo()得到字节数组,再转 Base64 存配置,加载时用
ImportSubjectPublicKeyInfo()还原; 如果必须存私钥字符串(如容器环境),至少用环境变量 + AES-GCM 加密后再存,启动时用 KMS(如 Azure Key Vault、AWS KMS)解密密钥本身; 绝对不要用
ExportParameters(true)输出
RSAPrivateCrtKey结构体——它包含所有素数分量,一旦泄露等于私钥彻底暴露。
为什么用 RSASignaturePadding.Pkcs1
签名后,OpenSSL 验证失败
签名算法 ≠ 哈希算法。.NET 的
SignData()默认只做签名运算,不自动哈希;而 OpenSSL 的
openssl dgst -sha256 -sign是先哈希再签名。两者行为不一致就会验签失败。
正确对齐方式:
如果对方用openssl dgst -sha256 -sign key.pem,你必须在 C# 中先算 SHA256 哈希,再调用
SignHash(),并指定
RSASignaturePadding.Pkcs1; 更推荐统一用
SignData()+
RSASignaturePadding.Pss(带盐值),它内部自动哈希(默认 SHA256),且 PSS 是现代标准,OpenSSL 也支持(
openssl pkeyutl -sign -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pss); 验证时务必用匹配的填充和哈希:PSS 签名必须用
VerifyData()+
RSASignaturePadding.Pss,不能混用 Pkcs1。
密钥长度、填充方式、哈希算法这三项必须全部对齐,少一个都会验证失败——而且错误信息往往只报“验证失败”,不会告诉你哪一项不匹配。
