C# 仅使用.Net生成证书请求并提交给CA

C# 仅使用.Net生成证书请求并提交给CA,c#,.net-core,certificate,pki,C#,.net Core,Certificate,Pki,我正在尝试仅使用.Net代码创建证书请求,并将请求提交给我们的内部Active Directory PKI证书颁发机构,然后获取证书。我有一个已经工作了几年的解决方案,但它使用了CERTCLILib和CERTENROLLLib,我想摆脱这些依赖关系,并将此代码移植到.NET5 然后将这些证书导入Yubikey设备。我们在Yubikey上生成密钥对,然后将公钥与CSR一起使用 这个问题对于获得一个DER编码的CSR非常有帮助,但我仍然有一些问题没有弄清楚 如何指定要在CertificateRequ

我正在尝试仅使用.Net代码创建证书请求,并将请求提交给我们的内部Active Directory PKI证书颁发机构,然后获取证书。我有一个已经工作了几年的解决方案,但它使用了CERTCLILib和CERTENROLLLib,我想摆脱这些依赖关系,并将此代码移植到.NET5

然后将这些证书导入Yubikey设备。我们在Yubikey上生成密钥对,然后将公钥与CSR一起使用

这个问题对于获得一个DER编码的CSR非常有帮助,但我仍然有一些问题没有弄清楚

  • 如何指定要在
    CertificateRequest
    对象中使用的CA和模板
  • 我有一个公钥,它是一个
    rsapameters
    对象。如何将其放入
    RSA
    对象中,以便与
    certificateRequest
    构造函数一起使用
  • 一旦我有了DER编码的CSR,我如何将其提交给CA?我在
    System.Security.Cryptography.X509Certificates
    命名空间中找不到任何实现该功能的类或方法
  • 这是我当前正在工作的代码,我想将其移植到.NET5。请注意,
    DeviceDetails
    包含关于Yubikey设备以及CA和模板的属性。此代码是一个更大的应用程序的一部分,该应用程序提供Yubikey设备

        {
            //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}");
                }
            }
    
    

    多部分问题很难回答,因为它们需要多部分答案。以下是我可以回答的部分:

    如何指定要在CertificateRequest对象中使用的CA和模板

    您不能,但这没关系,因为您也不在CertEnroll代码中。CertificateRequest对象相当于您的
    objPkcs10
    ,CA和模板用于处理
    CreateSigningRequest
    输出

    我有一个公钥,它是一个对象。如何将其放入RSA对象以与CertificateRequest构造函数一起使用

    一旦我有了DER编码的CSR,我如何将其提交给CA?我在System.Security.Cryptography.X509Certificates命名空间中找不到实现此功能的任何类或方法


    这个盒子里没有直接的东西。基于
    CX509CertificateRequest
    类名,它似乎正在使用,但仍有身份验证和请求提交部分需要解决。

    我用以下行指定模板名称:cmcReq.initializeFrominerRequestTemplateName(objPkcs10,deviceDetails.CertificateDetails.TemplateName);因此,似乎(到目前为止)没有一种方法(仅使用dotnet)来实现这一点。@Dbloom好吧,它没有一条简单的路线,没有。但一旦您能够找到如何发现AD CS服务器并与之对话,可能所有的部分都存在。
    using (RSA key = RSA.Create())
    {
        key.ImportParameters(rsaParameters);
        ...
    }