我使用的是Windows 11,我使用适当的keyUsage、basicConstraints和SubjectKeyIdentifier创建了一个pfx格式的自签名证书,以便制作CA证书。我使用 certmgr 将此证书添加到受信任的 CA 列表中。我从这个 CA 证书制作了一个客户端证书,以便测试我自己用 C# 编码的 ocsp 服务器。客户端证书具有指向 URL 的 OCSP 的 AIA 扩展:
http://localhost:8080/ocsp/
为了签署 OCSP 请求,我创建了一个带有 ocspSigning 扩展的指定证书。 OCSP 服务器似乎工作正常,它可以接收和发送响应。我使用 Bouncycastle 测试了服务器的一些响应,一切似乎都很好,但是当我使用 X509Chain.Build() 时,它无法构建链:
X509Certificate2 certificate = new X509Certificate2("client.pfx", "password");
X509Chain ch = new X509Chain();
X509ChainPolicy pol = new X509ChainPolicy();
pol.RevocationMode = X509RevocationMode.Online;
ch.ChainPolicy = pol;
ch.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
Console.WriteLine(ch.Build(certificate));
Console.WriteLine("Chain Information");
Console.WriteLine("Chain revocation flag: {0}", ch.ChainPolicy.RevocationFlag);
Console.WriteLine("Chain revocation mode: {0}", ch.ChainPolicy.RevocationMode);
Console.WriteLine("Chain verification flag: {0}", ch.ChainPolicy.VerificationFlags);
Console.WriteLine("Chain verification time: {0}", ch.ChainPolicy.VerificationTime);
Console.WriteLine("Chain status length: {0}", ch.ChainStatus.Length);
Console.WriteLine("Chain application policy count: {0}", ch.ChainPolicy.ApplicationPolicy.Count);
Console.WriteLine("Chain certificate policy count: {0} {1}", ch.ChainPolicy.CertificatePolicy.Count, Environment.NewLine);
//Output chain element information.
Console.WriteLine("Chain Element Information");
Console.WriteLine("Number of chain elements: {0}", ch.ChainElements.Count);
Console.WriteLine("Chain elements synchronized? {0} {1}", ch.ChainElements.IsSynchronized, Environment.NewLine);
foreach (X509ChainElement element in ch.ChainElements)
{
Console.WriteLine("Element issuer name: {0}", element.Certificate.Issuer);
Console.WriteLine("Element subject name: {0}", element.Certificate.Subject);
Console.WriteLine("Element certificate valid until: {0}", element.Certificate.NotAfter);
Console.WriteLine("Element certificate is valid: {0}", element.Certificate.Verify());
Console.WriteLine("Element error status length: {0}", element.ChainElementStatus.Length);
foreach(var crs in element.ChainElementStatus)
{
Console.WriteLine(crs.Status);
Console.WriteLine(crs.StatusInformation);
}
Console.WriteLine("Element information: {0}", element.Information);
Console.WriteLine("Number of element extensions: {0}{1}", element.Certificate.Extensions.Count, Environment.NewLine);
if (ch.ChainStatus.Length > 1)
{
for (int index = 0; index < element.ChainElementStatus.Length; index++)
{
Console.WriteLine(element.ChainElementStatus[index].Status);
Console.WriteLine(element.ChainElementStatus[index].StatusInformation);
}
}
结果:
Chain Information
Chain revocation flag: EntireChain
Chain revocation mode: Online
Chain verification flag: NoFlag
Chain verification time: 08/10/2024 07:25:52
Chain status length: 1
Chain application policy count: 0
Chain certificate policy count: 0
Chain Element Information
Number of chain elements: 2
Chain elements synchronized? False
Element issuer name: C=FR, S=Nouvelle-Aquitaine, L=Limoges, O=Universal Administration, CN=Universal Administration
Element subject name: C=FR, S=Nouvelle-Aquitaine, L=Limoges, O=Universal Administration, OU=Universal Administration Video Converting Client 1, CN=Universal Administration Dataclient
Element certificate valid until: 07/10/2029 22:43:08
Element certificate is valid: False
Element error status length: 1
RevocationStatusUnknown
Element information:
Number of element extensions: 1
Element issuer name: C=FR, S=Nouvelle-Aquitaine, L=Limoges, O=Universal Administration, CN=Universal Administration
Element issuer name: C=FR, S=Nouvelle-Aquitaine, L=Limoges, O=Universal Administration, CN=Universal Administration
Element certificate valid until: 07/10/2124 14:28:15
Element certificate is valid: True
Element error status length: 0
Element information:
Number of element extensions: 4
False
我不明白为什么 X509Chain.Build() 无法检查吊销状态,如果有帮助,我们将不胜感激
这是我的 ocsp 服务器的 base64 编码响应:
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
您可以使用在线 ASN.1 Javascript Decoder 进行检查
这是我用来创建 CA 证书的代码:
static void CreateBaseCertificate()
{
using (RSA rsa = RSA.Create(2048))
{
var request = new CertificateRequest("C=FR, ST=Nouvelle-Aquitaine, L=Limoges, O=Universal Administration, CN=Universal Administration", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(
new X509BasicConstraintsExtension(true, false, 0, false));
request.CertificateExtensions.Add(
new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
request.CertificateExtensions.Add(
new X509KeyUsageExtension(X509KeyUsageFlags.KeyCertSign, false));
request.CertificateExtensions.Add(CreateOcspAiaExtension("http://localhost:8080/ocsp/"));
X509Certificate2 certificate = request.CreateSelfSigned(DateTimeOffset.Now.AddDays(-1), DateTimeOffset.Now.AddYears(100));
var pfxPath = @"ca.pfx";
File.WriteAllBytes(pfxPath, certificate.Export(X509ContentType.Pfx, "password"));
File.WriteAllText("ca.key", rsa.ExportRSAPrivateKeyPem());
Console.WriteLine($"Certificat X.509 généré et sauvegardé en tant que PFX à : {pfxPath}");
}
}
这是创建客户端证书的代码:
static void CreateDataclientCertificate(string countrycode, string state, string city, string unit)
{
X509Certificate2 cacert = new X509Certificate2("ca.pfx", "password");
using (RSA rsa = RSA.Create(2048))
{
var request = new CertificateRequest("C=" + countrycode + ", ST=" + state + ", L=" + city + ", O=Universal Administration, OU=" + unit + ", CN=Universal Administration Dataclient", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(CreateOcspAiaExtension("http://localhost:8080/ocsp/"));
X509Certificate2 certificate = request.Create(cacert, DateTimeOffset.Now.AddDays(-1), DateTimeOffset.Now.AddYears(5), new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
certificate = certificate.CopyWithPrivateKey(rsa);
var pfxPath = @"client.pfx";
File.WriteAllBytes(pfxPath, certificate.Export(X509ContentType.Pfx, "password"));
Console.WriteLine($"X509 Certificate generated and saved as pfx at : {pfxPath}");
}
}
这是 OCSP 签名证书的代码:
static void CreateOCSPCertificate(string countrycode, string state, string city, string unit)
{
X509Certificate2 cacert = new X509Certificate2("ca.pfx", "password");
using (RSA rsa = RSA.Create(2048))
{
var request = new CertificateRequest("C=" + countrycode + ", ST=" + state + ", L=" + city + ", O=Universal Administration, OU=" + unit + ", CN=OCSP signing certificate", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(
new X509BasicConstraintsExtension(false, false, 0, true));
request.CertificateExtensions.Add(
new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
request.CertificateExtensions.Add(
new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, true));
Oid ocspSigningOid = new Oid("1.3.6.1.5.5.7.3.9");
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(
new OidCollection { ocspSigningOid }, true));
request.CertificateExtensions.Add(CreateOcspAiaExtension("http://localhost:8080/ocsp/"));
X509Certificate2 certificate = request.Create(cacert, DateTimeOffset.Now.AddDays(-1), DateTimeOffset.Now.AddYears(5), new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 });
certificate = certificate.CopyWithPrivateKey(rsa);
var pfxPath = @"ocsp.pfx";
File.WriteAllBytes(pfxPath, certificate.Export(X509ContentType.Pfx, "password"));
File.WriteAllText("ocsp.key", rsa.ExportRSAPrivateKeyPem());
Console.WriteLine($"X509 Certificate generated and saved as pfx at: {pfxPath}");
}
}
static System.Security.Cryptography.X509Certificates.X509Extension CreateOcspAiaExtension(string ocspUrl)
{
Oid ocspOid = new Oid("1.3.6.1.5.5.7.48.1");
AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
writer.PushSequence();
writer.PushSequence();
writer.WriteObjectIdentifier(ocspOid.Value, Asn1Tag.ObjectIdentifier);
var uriTag = new Asn1Tag(TagClass.ContextSpecific, 6);
using(writer.PushSequence(uriTag))
writer.WriteCharacterString(UniversalTagNumber.IA5String, ocspUrl);
writer.PopSequence();
writer.PopSequence();
return new System.Security.Cryptography.X509Certificates.X509Extension(new Oid("1.3.6.1.5.5.7.1.1"), writer.Encode(), false);
}
问题解决了。该问题源于 OCSP 签名证书 ASN.1 数据在 OCSP 响应中写入的方式。我更正了它,现在 X509Chain 正在构建链并正确检查撤销。