使用.NET生成可被OpenSSL验证的CA签名证书方法
问题描述
参考相关技术回答提供的代码逻辑,将生成的parentCert根证书与cert终端证书保存为.crt格式文件后,尝试通过如下OpenSSL命令完成证书链验证:
openssl verify -CAfile parentCert.crt cert.crt
命令执行失败,返回如下错误信息:
Error loading file parentCert.crt 98580000:error:05800088:x509 certificate routines:X509_load_cert_crl_file_ex:no certificate or crl found:crypto\x509\by_file.c:251:
核心诉求为:如何在.NET环境中正确创建CA根证书(ca.crt)以及由该CA签名的终端证书(cert.crt),确保生成的证书可被OpenSSL正常完成验证。
测试用C#代码
本次测试使用的完整C#代码如下:
using (RSA parent = RSA.Create(4096)) using (RSA rsa = RSA.Create(2048)) { CertificateRequest parentReq = new CertificateRequest( "CN=Experimental Issuing Authority", parent, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); parentReq.CertificateExtensions.Add( new X509BasicConstraintsExtension(true, false, 0, true)); parentReq.CertificateExtensions.Add( new X509SubjectKeyIdentifierExtension(parentReq.PublicKey, false)); using (X509Certificate2 parentCert = parentReq.CreateSelfSigned( DateTimeOffset.UtcNow.AddDays(-45), DateTimeOffset.UtcNow.AddDays(365))) { CertificateRequest req = new CertificateRequest( "CN=Valid-Looking Timestamp Authority", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); req.CertificateExtensions.Add( new X509BasicConstraintsExtension(false, false, 0, false)); req.CertificateExtensions.Add( new X509KeyUsageExtension( X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation, false)); req.CertificateExtensions.Add( new X509EnhancedKeyUsageExtension( new OidCollection { new Oid("1.3.6.1.5.5.7.3.8") }, true)); req.CertificateExtensions.Add( new X509SubjectKeyIdentifierExtension(req.PublicKey, false)); using (X509Certificate2 cert = req.Create( parentCert, DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddDays(90), new byte[] { 1, 2, 3, 4 })) { // 可对生成的证书执行后续操作,例如导出为PFX格式、添加到X509Store证书存储等 string path = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..\\..\\..\\")); File.WriteAllBytes(Path.Combine(path, "parentCert.crt"), parentCert.Export(X509ContentType.Cert)); File.WriteAllBytes(Path.Combine(path, "cert.crt"), cert.Export(X509ContentType.Cert)); } } }
问题原因与修复方案
根因分析
现有证书生成逻辑本身的扩展配置没有问题:根证书正确开启了CA基础约束,终端证书关闭了CA标记,密钥用法、增强型密钥用法配置均符合规范。
报错的核心原因是证书文件编码格式不匹配:
- 调用
Export(X509ContentType.Cert)导出的是DER二进制编码格式的证书内容 - OpenSSL的
verify命令默认读取PEM格式(Base64编码,带-----BEGIN CERTIFICATE-----头尾标记)的证书文件,直接读取DER二进制文件就会抛出找不到证书/CRL的错误。
修复方式
推荐优先使用第一种修复方式,PEM是.crt格式的通用默认编码,跨平台、跨工具兼容性最好。
方式1:修改证书导出逻辑,保存为PEM格式文件
如果使用.NET 5及以上版本,可以直接用内置PemEncoding类生成PEM内容,替换原有文件写入逻辑即可:// 替换原有的File.WriteAllBytes导出代码 // 导出根证书为PEM格式 string parentCertPem = PemEncoding.Write("CERTIFICATE", parentCert.RawData); File.WriteAllText(Path.Combine(path, "parentCert.crt"), parentCertPem); // 导出终端证书为PEM格式 string certPem = PemEncoding.Write("CERTIFICATE", cert.RawData); File.WriteAllText(Path.Combine(path, "cert.crt"), certPem);如果使用.NET 5以下版本,可以手动拼接PEM格式内容:
string ConvertCertToPem(X509Certificate2 cert) { string base64Content = Convert.ToBase64String(cert.RawData, Base64FormattingOptions.InsertLineBreaks); return $"-----BEGIN CERTIFICATE-----\n{base64Content}\n-----END CERTIFICATE-----"; }修改后直接执行原OpenSSL验证命令,返回
cert.crt: OK即为验证通过。方式2:保留现有DER格式导出,修改OpenSSL命令指定文件编码
如果不需要调整文件导出逻辑,可以在OpenSSL命令中添加参数分别指定CA根证书、待验证终端证书的编码格式为DER:openssl verify -CAfile parentCert.crt -CAform DER cert.crt -inform DER
内容的提问来源于stack exchange,提问作者Yeray
相关产品推荐
相关产品推荐

