pem文件需先解析再加载:剥离头尾、base64解码,证书用x509certificate2构造函数,私钥用importpkcs8privatekey(.net 5+)或bouncycastle(加密/旧框架),公钥用importsubjectpublickeyinfo;推荐.net 6+的importfrompem。

PEM文件不是直接能用的证书对象,得先解析出密钥数据
PEM本质是Base64编码的文本,开头像 -----BEGIN RSA PRIVATE KEY----- 或 -----BEGIN CERTIFICATE-----,C# 的 X509Certificate2 构造函数只接受 DER 编码的二进制或 PFX/P12 文件,**不原生支持 PEM**。直接传入 .pem 路径会抛 CryptographicException: ASN.1 bad tag value met.。
所以必须手动剥离头尾、解码 Base64、再根据内容类型选择加载方式:
- 如果是
-----BEGIN CERTIFICATE-----→ 解码后用X509Certificate2构造函数(支持 DER 格式字节数组) - 如果是
-----BEGIN RSA PRIVATE KEY-----或-----BEGIN PRIVATE KEY-----→ 解码后需用RSA.ImportPkcs8PrivateKey()或RSA.ImportRSAPrivateKey()(.NET 5+) - 公钥同理,
-----BEGIN PUBLIC KEY-----可用RSA.ImportSubjectPublicKeyInfo()
.NET 5+ 推荐用内置 API 解析 PEM 密钥,避免第三方库
.NET 5 引入了 System.Security.Cryptography 下的 PEM 解析方法,比手动截取字符串更健壮。关键点:
-
RSA.ImportFromEncryptedPem()和RSA.ImportFromPem()只在 .NET 6+ 支持;.NET 5 需用ImportPkcs8PrivateKey()+ 手动提取 PKCS#8 区块 - 私钥若带密码(
-----BEGIN ENCRYPTED PRIVATE KEY-----),.NET 5 不支持解密,必须降级用 BouncyCastle 或升级到 .NET 6+ - 公钥加载最简单:
rsa.ImportSubjectPublicKeyInfo(pemBytes, out _),但注意传入的是「纯 Base64 解码后的字节」,不是原始 PEM 字符串
示例(加载无密码 PEM 公钥):
var pem = File.ReadAllText("public_key.pem");
var keyBytes = Convert.FromBase64String(ExtractPemBody(pem, "PUBLIC KEY"));
using var rsa = RSA.Create();
rsa.ImportSubjectPublicKeyInfo(keyBytes, out _); // 成功即表示加载完成
手写 ExtractPemBody 辅助函数,别依赖正则模糊匹配
很多人用 Regex.Match(..., @"-----BEGIN.*?-----([\s\S]*?)-----END"),但 PEM 块里可能含空行或空格,容易截断失败。稳妥做法是逐行扫描,严格匹配起始/结束标记:
- 跳过所有空白行和注释行(以 # 或 — 开头)
- 找到
-----BEGIN XXX-----后,收集后续非-----END XXX-----行的 Base64 内容 - 遇到
-----END立即停止,丢弃该行及之后内容 - 合并所有 Base64 行(去掉换行和空格)再
Convert.FromBase64String()
错误示例(常见坑):Convert.FromBase64String("MIIBIjANBgkqhkiG...") 若字符串含换行符会直接抛 FormatException。
BouncyCastle 是 .NET Framework 或带密码 PEM 的兜底方案
如果项目还在用 .NET Framework,或 PEM 私钥被密码保护(PKCS#8 Encrypted),BouncyCastle 是目前最稳定的选项。注意两点:
- 用
PemReader读取时,传入TextReader(如new StringReader(pem)),不要传文件路径字符串 - 私钥解密需提供密码:若
obj is AsymmetricCipherKeyPair,则obj.Private是私钥;若obj is RsaPrivateCrtKeyParameters,可转成RSAParameters再导入RSA实例 - nuget 包名是
Portable.BouncyCastle(.NET Standard 兼容),不是老版BC
这类场景下,硬啃 .NET 原生 API 反而容易卡在 ASN.1 解析异常上,不如明确走 BouncyCastle 路线。
真正麻烦的从来不是“怎么读”,而是 PEM 里混着多种格式(PKCS#1 / PKCS#8 / SEC1)、是否加密、换行符不统一——这些细节不验证清楚,代码跑不通时连错误都报得模棱两可。










