仅使用 .Net 生成证书请求并提交给 CA
Generate a certificate request and submit to a CA using only .Net
我正在尝试仅使用 .Net 代码来创建证书请求并将请求提交给我们的本地 Active Directory PKI 证书颁发机构,然后取回证书。我有一个已经工作了几年的解决方案,但它使用了 CERTCLILib 和 CERTENROLLLib,我想摆脱这些依赖并将此代码移植到 .Net 5。
然后将这些证书导入 Yubikey 设备。我们在 Yubikey 上生成密钥对,然后将 public 密钥与 CSR 一起使用。
这个问题对获得DER编码的CSR很有帮助,但我还有几个问题没弄清楚。
- 如何指定要在
CertificateRequest
对象中使用的 CA 和模板?
- 我有一个 public 键,它是一个
RSAParameters
对象。我如何才能将其放入 RSA
对象以与 CertificateRequst
构造函数一起使用?
- 一旦我有了 DER 编码的 CSR,我该如何将它提交给 CA?我在
System.Security.Cryptography.X509Certificates
命名空间中找不到任何 类 或方法来实现这一点。
这是我当前正在运行的代码,我想将其移植到 .NET 5。请注意,DeviceDetails
包含有关 Yubikey 设备以及 CA 和模板的属性。此代码是配置 Yubikey 设备的更大应用程序的一部分。
public static class CertificateUtilities
{
//private const int CC_UIPICKCONFIG = 0x1;
private const int CR_IN_BASE64 = 0x1;
private const int CR_IN_FORMATANY = 0;
private const int CR_DISP_ISSUED = 0x3;
private const int CR_DISP_UNDER_SUBMISSION = 0x5;
private const int CR_OUT_BASE64 = 0x1;
public static string GenerateRequest(DeviceDetails deviceDetails)
{
// Create all the objects that will be required
var objPkcs10 = new CX509CertificateRequestPkcs10();
var objDN = new CX500DistinguishedName();
var objObjectIds = new CObjectIds();
var objObjectId = new CObjectId();
var objExtensionKeyUsage = new CX509ExtensionKeyUsage();
var objX509ExtensionEnhancedKeyUsage = new CX509ExtensionEnhancedKeyUsage();
var objPublicKey = new CX509PublicKey();
try
{
var publicKey = Utilities.ExportPublicKeyToPEMFormat(deviceDetails.PublicKey);
publicKey = string.Join("", publicKey.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries).Where(s => !s.StartsWith("--")));
objPublicKey.InitializeFromEncodedPublicKeyInfo(publicKey, EncodingType.XCN_CRYPT_STRING_BASE64);
var sha512 = new CObjectId();
sha512.InitializeFromValue("2.16.840.1.101.3.4.2.3");
// Initialize the PKCS#10 certificate request object based on the public key.
objPkcs10.InitializeFromPublicKey(X509CertificateEnrollmentContext.ContextUser, objPublicKey, "");
objPkcs10.HashAlgorithm = sha512;
//Key Usage Extension
objExtensionKeyUsage.InitializeEncode(
X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
X509KeyUsageFlags.XCN_CERT_NON_REPUDIATION_KEY_USAGE |
X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE |
X509KeyUsageFlags.XCN_CERT_DATA_ENCIPHERMENT_KEY_USAGE
);
objPkcs10.X509Extensions.Add((CX509Extension)objExtensionKeyUsage);
// Enhanced Key Usage Extension
objObjectId.InitializeFromValue("1.3.6.1.5.5.7.3.2"); // OID for Client Authentication usage
objObjectIds.Add(objObjectId);
objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds);
objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage);
// Encode the name in using the Distinguished Name object
objDN.Encode(
deviceDetails.CertificateDetails.Subject,
X500NameFlags.XCN_CERT_NAME_STR_NONE
);
// Adding the subject name by using the Distinguished Name object initialized above
objPkcs10.Subject = objDN;
// Adding the Subject Alternate Names
var strRfc822Name = deviceDetails.UserDetails.OUN + "@corp.com";
var strUpn = deviceDetails.UserDetails.OUN + "@corp.com";
var objRfc822Name = new CAlternativeName();
var objUserPrincipalName = new CAlternativeName();
var objAlternativeNames = new CAlternativeNames();
var objExtensionAlternativeNames = new CX509ExtensionAlternativeNames();
// Set Alternative RFC822 Name
objRfc822Name.InitializeFromString(AlternativeNameType.XCN_CERT_ALT_NAME_RFC822_NAME, strRfc822Name);
// Set Alternative UPN
objUserPrincipalName.InitializeFromString(AlternativeNameType.XCN_CERT_ALT_NAME_USER_PRINCIPLE_NAME, strUpn);
// Set Alternative Names
objAlternativeNames.Add(objRfc822Name);
objAlternativeNames.Add(objUserPrincipalName);
objExtensionAlternativeNames.InitializeEncode(objAlternativeNames);
objPkcs10.X509Extensions.Add((CX509Extension)objExtensionAlternativeNames);
// Create a CMC outer request and initialize
var cmcReq = new CX509CertificateRequestCmc();
cmcReq.InitializeFromInnerRequestTemplateName(objPkcs10, deviceDetails.CertificateDetails.TemplateName);
// encode the request
cmcReq.Encode();
var strRequest = cmcReq.RawData[EncodingType.XCN_CRYPT_STRING_BASE64];
return strRequest;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
// Submit request to CA and get response
public static X509Certificate2 SendRequest(DeviceDetails deviceDetails, string request, out string error)
{
error = "";
// Create all the objects that will be required
//var objCertConfig = new CCertConfig();
var objCertRequest = new CCertRequest();
try
{
// Submit the request
var iDisposition = objCertRequest.Submit(
CR_IN_BASE64 | CR_IN_FORMATANY,
request,
null,
deviceDetails.CertificateDetails.IssuingCa
);
// Check the submission status
if (CR_DISP_ISSUED != iDisposition) // Not enrolled
{
var strDisposition = objCertRequest.GetDispositionMessage();
if (CR_DISP_UNDER_SUBMISSION == iDisposition) // Pending
{
error = "The submission is pending: " + strDisposition;
return null;
}
else // Failed
{
error = $"The submission failed: {strDisposition}. Last status: {objCertRequest.GetLastStatus()}";
return null;
}
}
// Get the certificate
var strCert = objCertRequest.GetCertificate(CR_OUT_BASE64);
var rawCert = Convert.FromBase64String(strCert);
return new X509Certificate2(rawCert);
}
catch (Exception ex)
{
throw new Exception($"Error sending the request. {ex.Message}");
}
}
}
多部分问题很难,因为它们需要多部分答案。以下是我可以回答的部分:
How do I specify the CA and the template to use in the CertificateRequest object?
你不能,但没关系,因为你也不在 CertEnroll 代码中。 CertificateRequest 对象等同于您的 objPkcs10
,CA 和模板用于您对 CreateSigningRequest
输出所做的操作。
I have a public key that is a RSAParameters object. How can I get that into an RSA object to use with the CertificateRequst constructor?
using (RSA key = RSA.Create())
{
key.ImportParameters(rsaParameters);
...
}
Once I have the DER encoded CSR, how do I submit that to the CA? I can't find any classes or methods in the System.Security.Cryptography.X509Certificates namespace that accomplishes that.
框中没有直接用于此的内容。根据CX509CertificateRequest
class的名字,好像是在用Certificate Management over CMS (CMC),但是后面还有认证和请求提交的部分要解决。
我正在尝试仅使用 .Net 代码来创建证书请求并将请求提交给我们的本地 Active Directory PKI 证书颁发机构,然后取回证书。我有一个已经工作了几年的解决方案,但它使用了 CERTCLILib 和 CERTENROLLLib,我想摆脱这些依赖并将此代码移植到 .Net 5。
然后将这些证书导入 Yubikey 设备。我们在 Yubikey 上生成密钥对,然后将 public 密钥与 CSR 一起使用。
这个问题
- 如何指定要在
CertificateRequest
对象中使用的 CA 和模板? - 我有一个 public 键,它是一个
RSAParameters
对象。我如何才能将其放入RSA
对象以与CertificateRequst
构造函数一起使用? - 一旦我有了 DER 编码的 CSR,我该如何将它提交给 CA?我在
System.Security.Cryptography.X509Certificates
命名空间中找不到任何 类 或方法来实现这一点。
这是我当前正在运行的代码,我想将其移植到 .NET 5。请注意,DeviceDetails
包含有关 Yubikey 设备以及 CA 和模板的属性。此代码是配置 Yubikey 设备的更大应用程序的一部分。
public static class CertificateUtilities
{
//private const int CC_UIPICKCONFIG = 0x1;
private const int CR_IN_BASE64 = 0x1;
private const int CR_IN_FORMATANY = 0;
private const int CR_DISP_ISSUED = 0x3;
private const int CR_DISP_UNDER_SUBMISSION = 0x5;
private const int CR_OUT_BASE64 = 0x1;
public static string GenerateRequest(DeviceDetails deviceDetails)
{
// Create all the objects that will be required
var objPkcs10 = new CX509CertificateRequestPkcs10();
var objDN = new CX500DistinguishedName();
var objObjectIds = new CObjectIds();
var objObjectId = new CObjectId();
var objExtensionKeyUsage = new CX509ExtensionKeyUsage();
var objX509ExtensionEnhancedKeyUsage = new CX509ExtensionEnhancedKeyUsage();
var objPublicKey = new CX509PublicKey();
try
{
var publicKey = Utilities.ExportPublicKeyToPEMFormat(deviceDetails.PublicKey);
publicKey = string.Join("", publicKey.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries).Where(s => !s.StartsWith("--")));
objPublicKey.InitializeFromEncodedPublicKeyInfo(publicKey, EncodingType.XCN_CRYPT_STRING_BASE64);
var sha512 = new CObjectId();
sha512.InitializeFromValue("2.16.840.1.101.3.4.2.3");
// Initialize the PKCS#10 certificate request object based on the public key.
objPkcs10.InitializeFromPublicKey(X509CertificateEnrollmentContext.ContextUser, objPublicKey, "");
objPkcs10.HashAlgorithm = sha512;
//Key Usage Extension
objExtensionKeyUsage.InitializeEncode(
X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
X509KeyUsageFlags.XCN_CERT_NON_REPUDIATION_KEY_USAGE |
X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE |
X509KeyUsageFlags.XCN_CERT_DATA_ENCIPHERMENT_KEY_USAGE
);
objPkcs10.X509Extensions.Add((CX509Extension)objExtensionKeyUsage);
// Enhanced Key Usage Extension
objObjectId.InitializeFromValue("1.3.6.1.5.5.7.3.2"); // OID for Client Authentication usage
objObjectIds.Add(objObjectId);
objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds);
objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage);
// Encode the name in using the Distinguished Name object
objDN.Encode(
deviceDetails.CertificateDetails.Subject,
X500NameFlags.XCN_CERT_NAME_STR_NONE
);
// Adding the subject name by using the Distinguished Name object initialized above
objPkcs10.Subject = objDN;
// Adding the Subject Alternate Names
var strRfc822Name = deviceDetails.UserDetails.OUN + "@corp.com";
var strUpn = deviceDetails.UserDetails.OUN + "@corp.com";
var objRfc822Name = new CAlternativeName();
var objUserPrincipalName = new CAlternativeName();
var objAlternativeNames = new CAlternativeNames();
var objExtensionAlternativeNames = new CX509ExtensionAlternativeNames();
// Set Alternative RFC822 Name
objRfc822Name.InitializeFromString(AlternativeNameType.XCN_CERT_ALT_NAME_RFC822_NAME, strRfc822Name);
// Set Alternative UPN
objUserPrincipalName.InitializeFromString(AlternativeNameType.XCN_CERT_ALT_NAME_USER_PRINCIPLE_NAME, strUpn);
// Set Alternative Names
objAlternativeNames.Add(objRfc822Name);
objAlternativeNames.Add(objUserPrincipalName);
objExtensionAlternativeNames.InitializeEncode(objAlternativeNames);
objPkcs10.X509Extensions.Add((CX509Extension)objExtensionAlternativeNames);
// Create a CMC outer request and initialize
var cmcReq = new CX509CertificateRequestCmc();
cmcReq.InitializeFromInnerRequestTemplateName(objPkcs10, deviceDetails.CertificateDetails.TemplateName);
// encode the request
cmcReq.Encode();
var strRequest = cmcReq.RawData[EncodingType.XCN_CRYPT_STRING_BASE64];
return strRequest;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
// Submit request to CA and get response
public static X509Certificate2 SendRequest(DeviceDetails deviceDetails, string request, out string error)
{
error = "";
// Create all the objects that will be required
//var objCertConfig = new CCertConfig();
var objCertRequest = new CCertRequest();
try
{
// Submit the request
var iDisposition = objCertRequest.Submit(
CR_IN_BASE64 | CR_IN_FORMATANY,
request,
null,
deviceDetails.CertificateDetails.IssuingCa
);
// Check the submission status
if (CR_DISP_ISSUED != iDisposition) // Not enrolled
{
var strDisposition = objCertRequest.GetDispositionMessage();
if (CR_DISP_UNDER_SUBMISSION == iDisposition) // Pending
{
error = "The submission is pending: " + strDisposition;
return null;
}
else // Failed
{
error = $"The submission failed: {strDisposition}. Last status: {objCertRequest.GetLastStatus()}";
return null;
}
}
// Get the certificate
var strCert = objCertRequest.GetCertificate(CR_OUT_BASE64);
var rawCert = Convert.FromBase64String(strCert);
return new X509Certificate2(rawCert);
}
catch (Exception ex)
{
throw new Exception($"Error sending the request. {ex.Message}");
}
}
}
多部分问题很难,因为它们需要多部分答案。以下是我可以回答的部分:
How do I specify the CA and the template to use in the CertificateRequest object?
你不能,但没关系,因为你也不在 CertEnroll 代码中。 CertificateRequest 对象等同于您的 objPkcs10
,CA 和模板用于您对 CreateSigningRequest
输出所做的操作。
I have a public key that is a RSAParameters object. How can I get that into an RSA object to use with the CertificateRequst constructor?
using (RSA key = RSA.Create())
{
key.ImportParameters(rsaParameters);
...
}
Once I have the DER encoded CSR, how do I submit that to the CA? I can't find any classes or methods in the System.Security.Cryptography.X509Certificates namespace that accomplishes that.
框中没有直接用于此的内容。根据CX509CertificateRequest
class的名字,好像是在用Certificate Management over CMS (CMC),但是后面还有认证和请求提交的部分要解决。