C#中如何用Portable.BouncyCastle提取X509证书CN及相关信息
基于Portable.BouncyCastle 1.8.8的规范实现
核心逻辑说明
- 无需手动拆解
DerSequence嵌套:certificate.SubjectDN返回的X509Name类型已封装了X.500 Distinguished Name的标准解析逻辑,可直接提取指定属性 - 证书有效期可通过内置属性直接获取,无需额外解析ASN.1结构
- 标准解析可正确处理CN中的转义特殊字符(如逗号、引号),避免正则匹配的截断错误
修正后的完整代码
using Org.BouncyCastle.X509; using Org.BouncyCastle.Asn1.X509; // 原有命名空间引用保持不变 using (var streamReader = new StreamReader("cert.pem")) { var pemReader = new PemReader(streamReader); while (true) { object pemObject = pemReader.ReadObject(); if (pemObject == null) break; switch (pemObject) { case RsaPrivateCrtKeyParameters privateKey: Console.WriteLine("Private key"); break; case X509Certificate certificate: Console.WriteLine("Certificate"); // 全量主题DN(保留用于调试) Console.WriteLine($" Full Subject DN: {certificate.SubjectDN}"); // 规范提取CN值(无需正则) string subjectCN = certificate.SubjectDN.GetValue(X509Name.CN).ToString(); Console.WriteLine($" Subject CN: {subjectCN}"); // 直接获取证书有效期 Console.WriteLine($" Valid From: {certificate.NotBefore:yyyy-MM-dd HH:mm:ss}"); Console.WriteLine($" Valid Until: {certificate.NotAfter:yyyy-MM-dd HH:mm:ss}"); // 规范提取SAN(替换原有硬拆ArrayList的逻辑) var sanExtension = certificate.GetExtension(X509Extensions.SubjectAlternativeName); if (sanExtension != null) { var sanGeneralNames = GeneralNames.GetInstance(sanExtension.GetParsedValue()); foreach (var name in sanGeneralNames.GetNames()) { Console.WriteLine($" Subject Alternative Name: {name.ToString()}"); } } break; } } }
.NET Core 3.1 / 5 内置库替代方案(无第三方依赖)
适用场景
如果你无需兼容旧版本.NET Framework,可完全移除Portable.BouncyCastle依赖,使用官方System.Security.Cryptography.X509Certificates类库实现,API更简洁、长期维护、跨平台兼容。
核心实现代码
using System.Security.Cryptography.X509Certificates; using System.Linq; // 直接从PEM文件加载证书(自动识别BEGIN CERTIFICATE块) using (var cert = X509Certificate2.CreateFromPemFile("cert.pem")) { Console.WriteLine("Certificate"); // 提取Subject CN(标准解析) string subjectCN = cert.GetNameInfo(X509NameType.SimpleName, forIssuer: false); Console.WriteLine($" Subject CN: {subjectCN}"); // 直接获取有效期 Console.WriteLine($" Valid From: {cert.NotBefore:yyyy-MM-dd HH:mm:ss}"); Console.WriteLine($" Valid Until: {cert.NotAfter:yyyy-MM-dd HH:mm:ss}"); // 提取SAN var sanExtension = cert.Extensions.FirstOrDefault(e => e.Oid.Value == "2.5.29.17"); if (sanExtension != null) { // .NET 5+ 可使用强类型扩展获取结构化SAN列表 // var sanExt = (X509SubjectAlternativeNamesExtension)sanExtension; // foreach (var name in sanExt.AlternativeNames) { ... } // .NET Core 3.1 可直接输出可读格式的SAN内容 Console.WriteLine($" Subject Alternative Names: {sanExtension.Format(false)}"); } }
注意:.NET Core 3.1+ 同样支持从PEM文件读取私钥,可通过
RSA.CreateFromPemFile("key.pem")等方法直接加载,无需额外处理PEM格式。若PEM文件同时包含证书和私钥,CreateFromPemFile会自动完成绑定。
方案对比
- Portable.BouncyCastle方案
- 优点:兼容.NET Framework 4.x等旧版框架,可处理冷门证书格式
- 缺点:需引入第三方NuGet包,API复杂度较高,更新频率较低
- .NET内置库方案
- 优点:无第三方依赖、API标准化、微软官方长期维护、性能更优
- 缺点:仅支持.NET Core 3.0+/.NET 5+,旧版.NET Framework不支持PEM直接解析API
内容的提问来源于stack exchange,提问作者ygoe
相关产品推荐
相关产品推荐

