如何在 .NET Core 中生成对 CSR 的响应(即编写 CSR 签名服务器)?
How to generate a response to a CSR in .NET Core (i.e. to write a CSR signing server)?
我正在通过 Nuget 包学习证书签名请求和签名服务器 System.Security.Cryptography.Cng
。还有什么比尝试重新创建一个更好的方法。目前似乎有一个我没有解决的问题,那就是服务器签名方,即在下面的代码中,我在 using 子句中的 request.Create(
处得到 System.InvalidOperationException: 'An X509Extension with OID '2.5.29.37' has already been specified.'
。我在 http://oid-info.com/get/2.5.29.37 看到它是关于扩展密钥使用的。
问题:
MakeLocalhostCert
可能是错误的,应该如何更改才能使其成为签署 CSR 的证书?
- 是否可以add/remove extensions/OIDs返回CSR?我相信是的,但不知何故这部分目前让我望而却步。
我使用了 https://whosebug.com/users/6535399/bartonjs at and at 的出色答案才走到这一步。 :)
private static void CsrSigningTest()
{
//Both ECDSA and RSA included here, though ECDSA is probably better.
using(ECDsa privateClientEcdsaKey = ECDsa.Create(ECCurve.NamedCurves.nistP256))
//using(RSA privateClientRsaKey = RSA.Create(2048))
{
//A client creates a certificate signing request.
CertificateRequest request = new CertificateRequest(
new X500DistinguishedName("CN=example.com, O=Example Ltd, OU=AllOver, L=Sacremento, ST=\"California\", C=US, E=some@example.com"),
privateClientEcdsaKey,
HashAlgorithmName.SHA256);
/*CertificateRequest request = new CertificateRequest(
new X500DistinguishedName("CN=example.com, O=Example Ltd, OU=AllOver, L=Sacremento, ST=\"California\", C=US, E=some@example.com"),
privateClientRsaKey,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);*/
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddDnsName("example.com");
request.CertificateExtensions.Add(sanBuilder.Build());
//Not a CA, a server certificate.
request.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.8") }, false));
byte[] derEncodedCsr = request.CreateSigningRequest();
var csrSb = new StringBuilder();
csrSb.AppendLine("-----BEGIN CERTIFICATE REQUEST-----");
csrSb.AppendLine(Convert.ToBase64String(derEncodedCsr));
csrSb.AppendLine("-----END CERTIFICATE REQUEST-----");
//Thus far OK, this csr seems to be working when using an online checker.
var csr = csrSb.ToString();
//Now, sending this to a server... How does the server function:
//1) Read the CSR to be processed?
//2) How does this CSR get signed?
//In the following, can the signing cert be self-signed could be had from
//
byte[] serial = new byte[16];
using(var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(serial);
}
DateTimeOffset notBefore = DateTimeOffset.UtcNow;
DateTimeOffset notAfter = notBefore.AddYears(1);
var issuerCertificate = MakeLocalhostCert();
//For the part 1) there, this doesn't seem to work, likely since CSR isn't a X509 certificate.
//But then again, there doesn't seem to be anything in CertificateRequest to read this.
//In reality in the server the prologue and epilogue strings should be removed and the string read.
//var testRequest = new X509Certificate2(derEncodedCsr);
using(X509Certificate2 responseToCsr = request.Create(issuerCertificate, notBefore, notAfter, serial))
{
//How to add extensions here?
var csrResSb = new StringBuilder();
csrResSb.AppendLine("-----BEGIN CERTIFICATE-----");
csrResSb.AppendLine(Convert.ToBase64String(responseToCsr.GetRawCertData()));
csrResSb.AppendLine("-----END CERTIFICATE-----");
var signedCert = csrResSb.ToString();
}
}
}
private static X509Certificate2 MakeLocalhostCert()
{
using(ECDsa key = ECDsa.Create(ECCurve.NamedCurves.nistP384))
{
var request = new CertificateRequest(
"CN=localhost",
key,
HashAlgorithmName.SHA384);
request.CertificateExtensions.Add(
new X509BasicConstraintsExtension(true, false, 0, true));
const X509KeyUsageFlags endEntityTypicalUsages =
X509KeyUsageFlags.DataEncipherment |
X509KeyUsageFlags.KeyEncipherment |
X509KeyUsageFlags.DigitalSignature |
X509KeyUsageFlags.NonRepudiation |
X509KeyUsageFlags.KeyCertSign;
request.CertificateExtensions.Add(
new X509KeyUsageExtension(endEntityTypicalUsages, true));
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddDnsName("localhost");
sanBuilder.AddIpAddress(IPAddress.Loopback);
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
request.CertificateExtensions.Add(sanBuilder.Build());
/*request.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection
{
// server and client authentication
new Oid("1.3.6.1.5.5.7.3.1"),
new Oid("1.3.6.1.5.5.7.3.2")
},
false));*/
DateTimeOffset now = DateTimeOffset.UtcNow.AddMinutes(-1);
return request.CreateSelfSigned(now, now.AddYears(2));
}
}
一旦我应用了关于 OID 的修复并将最后一位更改为
using(X509Certificate2 responseToCsr = request.Create(issuerCertificate, notBefore, notAfter, serial))
{
request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(issuerCertificate.PublicKey, false));
var csrResSb = new StringBuilder();
csrResSb.AppendLine("-----BEGIN CERTIFICATE-----");
csrResSb.AppendLine(Convert.ToBase64String(responseToCsr.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));
csrResSb.AppendLine("-----END CERTIFICATE-----");
var signedCert = csrResSb.ToString();
}
我在 signedCert
中取回了一份证书,看起来像已签署的 CSR。如果从实际的 CSR 文件中读取,则缺少的部分是构建 CSR。
CoreFx GH 中存在一个问题,跟踪此处涉及的一些问题:Security crypto - Roadmap。
很高兴知道:, How to convert a CSR text file into .NET Core/ Standard CertificateRequest for Signing?, How to convert a CSR text file into .NET Core/ Standard CertificateRequest for Signing? 有关更多信息,请参见 .NET5/6.
要修复您的异常,您希望您的代码设置一个具有两个目的 OID 的 EKU 扩展,而不是两个各有一个的扩展。
// Defined two EKU extensions
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.8") }, false));
至
// One extension carrying multiple purpose OIDs
request.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection
{
new Oid("1.3.6.1.5.5.7.3.1"),
new Oid("1.3.6.1.5.5.7.3.8"),
},
false));
那么你的 title/question 中还有一些其他问题:
- "How to generate a response to a CSR in .NET Core (i.e. to write a CSR signing server)?"
- 此代码未读取 CSR,因此未对 CSR 做出响应。
- class 的目的是满足单元测试和其他开发环境需求,并能够生成 CSR 以发送到真正的 CA 产品。
- .NET Core 甚至无法读取 CSR,只能编写它们。
- "I'm learning about certificate signing requests and signing servers via Nuget package System.Security.Cryptography.Cng"
- None 你的代码似乎使用了 Cng 类型(这很好,你应该很少关心)。
CertificateRequest
是 System.Security.Cryptography.X509Certificates.dll 的一部分,通过 Microsoft.NETCore.App
公开
- "Is it possible to add/remove extensions/OIDs to the CSR being returned?"
- 是的,您在调用
CreateSigningRequest
之前将它们添加到 CertificateExtensions 属性。
- (隐含)"Is it possible to add/remove extensions/OIDs to the certificate being returned?"
- 是的,您在调用
Create
或 CreateSelfSigned
之前将它们添加到 CertificateExtensions 属性。
我正在通过 Nuget 包学习证书签名请求和签名服务器 System.Security.Cryptography.Cng
。还有什么比尝试重新创建一个更好的方法。目前似乎有一个我没有解决的问题,那就是服务器签名方,即在下面的代码中,我在 using 子句中的 request.Create(
处得到 System.InvalidOperationException: 'An X509Extension with OID '2.5.29.37' has already been specified.'
。我在 http://oid-info.com/get/2.5.29.37 看到它是关于扩展密钥使用的。
问题:
MakeLocalhostCert
可能是错误的,应该如何更改才能使其成为签署 CSR 的证书?- 是否可以add/remove extensions/OIDs返回CSR?我相信是的,但不知何故这部分目前让我望而却步。
我使用了 https://whosebug.com/users/6535399/bartonjs at
private static void CsrSigningTest()
{
//Both ECDSA and RSA included here, though ECDSA is probably better.
using(ECDsa privateClientEcdsaKey = ECDsa.Create(ECCurve.NamedCurves.nistP256))
//using(RSA privateClientRsaKey = RSA.Create(2048))
{
//A client creates a certificate signing request.
CertificateRequest request = new CertificateRequest(
new X500DistinguishedName("CN=example.com, O=Example Ltd, OU=AllOver, L=Sacremento, ST=\"California\", C=US, E=some@example.com"),
privateClientEcdsaKey,
HashAlgorithmName.SHA256);
/*CertificateRequest request = new CertificateRequest(
new X500DistinguishedName("CN=example.com, O=Example Ltd, OU=AllOver, L=Sacremento, ST=\"California\", C=US, E=some@example.com"),
privateClientRsaKey,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);*/
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddDnsName("example.com");
request.CertificateExtensions.Add(sanBuilder.Build());
//Not a CA, a server certificate.
request.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.8") }, false));
byte[] derEncodedCsr = request.CreateSigningRequest();
var csrSb = new StringBuilder();
csrSb.AppendLine("-----BEGIN CERTIFICATE REQUEST-----");
csrSb.AppendLine(Convert.ToBase64String(derEncodedCsr));
csrSb.AppendLine("-----END CERTIFICATE REQUEST-----");
//Thus far OK, this csr seems to be working when using an online checker.
var csr = csrSb.ToString();
//Now, sending this to a server... How does the server function:
//1) Read the CSR to be processed?
//2) How does this CSR get signed?
//In the following, can the signing cert be self-signed could be had from
//
byte[] serial = new byte[16];
using(var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(serial);
}
DateTimeOffset notBefore = DateTimeOffset.UtcNow;
DateTimeOffset notAfter = notBefore.AddYears(1);
var issuerCertificate = MakeLocalhostCert();
//For the part 1) there, this doesn't seem to work, likely since CSR isn't a X509 certificate.
//But then again, there doesn't seem to be anything in CertificateRequest to read this.
//In reality in the server the prologue and epilogue strings should be removed and the string read.
//var testRequest = new X509Certificate2(derEncodedCsr);
using(X509Certificate2 responseToCsr = request.Create(issuerCertificate, notBefore, notAfter, serial))
{
//How to add extensions here?
var csrResSb = new StringBuilder();
csrResSb.AppendLine("-----BEGIN CERTIFICATE-----");
csrResSb.AppendLine(Convert.ToBase64String(responseToCsr.GetRawCertData()));
csrResSb.AppendLine("-----END CERTIFICATE-----");
var signedCert = csrResSb.ToString();
}
}
}
private static X509Certificate2 MakeLocalhostCert()
{
using(ECDsa key = ECDsa.Create(ECCurve.NamedCurves.nistP384))
{
var request = new CertificateRequest(
"CN=localhost",
key,
HashAlgorithmName.SHA384);
request.CertificateExtensions.Add(
new X509BasicConstraintsExtension(true, false, 0, true));
const X509KeyUsageFlags endEntityTypicalUsages =
X509KeyUsageFlags.DataEncipherment |
X509KeyUsageFlags.KeyEncipherment |
X509KeyUsageFlags.DigitalSignature |
X509KeyUsageFlags.NonRepudiation |
X509KeyUsageFlags.KeyCertSign;
request.CertificateExtensions.Add(
new X509KeyUsageExtension(endEntityTypicalUsages, true));
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddDnsName("localhost");
sanBuilder.AddIpAddress(IPAddress.Loopback);
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
request.CertificateExtensions.Add(sanBuilder.Build());
/*request.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection
{
// server and client authentication
new Oid("1.3.6.1.5.5.7.3.1"),
new Oid("1.3.6.1.5.5.7.3.2")
},
false));*/
DateTimeOffset now = DateTimeOffset.UtcNow.AddMinutes(-1);
return request.CreateSelfSigned(now, now.AddYears(2));
}
}
using(X509Certificate2 responseToCsr = request.Create(issuerCertificate, notBefore, notAfter, serial))
{
request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(issuerCertificate.PublicKey, false));
var csrResSb = new StringBuilder();
csrResSb.AppendLine("-----BEGIN CERTIFICATE-----");
csrResSb.AppendLine(Convert.ToBase64String(responseToCsr.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));
csrResSb.AppendLine("-----END CERTIFICATE-----");
var signedCert = csrResSb.ToString();
}
我在 signedCert
中取回了一份证书,看起来像已签署的 CSR。如果从实际的 CSR 文件中读取,则缺少的部分是构建 CSR。
要修复您的异常,您希望您的代码设置一个具有两个目的 OID 的 EKU 扩展,而不是两个各有一个的扩展。
// Defined two EKU extensions
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.8") }, false));
至
// One extension carrying multiple purpose OIDs
request.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection
{
new Oid("1.3.6.1.5.5.7.3.1"),
new Oid("1.3.6.1.5.5.7.3.8"),
},
false));
那么你的 title/question 中还有一些其他问题:
- "How to generate a response to a CSR in .NET Core (i.e. to write a CSR signing server)?"
- 此代码未读取 CSR,因此未对 CSR 做出响应。
- class 的目的是满足单元测试和其他开发环境需求,并能够生成 CSR 以发送到真正的 CA 产品。
- .NET Core 甚至无法读取 CSR,只能编写它们。
- 此代码未读取 CSR,因此未对 CSR 做出响应。
- "I'm learning about certificate signing requests and signing servers via Nuget package System.Security.Cryptography.Cng"
- None 你的代码似乎使用了 Cng 类型(这很好,你应该很少关心)。
CertificateRequest
是 System.Security.Cryptography.X509Certificates.dll 的一部分,通过Microsoft.NETCore.App
公开
- None 你的代码似乎使用了 Cng 类型(这很好,你应该很少关心)。
- "Is it possible to add/remove extensions/OIDs to the CSR being returned?"
- 是的,您在调用
CreateSigningRequest
之前将它们添加到 CertificateExtensions 属性。
- 是的,您在调用
- (隐含)"Is it possible to add/remove extensions/OIDs to the certificate being returned?"
- 是的,您在调用
Create
或CreateSelfSigned
之前将它们添加到 CertificateExtensions 属性。
- 是的,您在调用