【问题标题】:Generate a certificate request and submit to a CA using only .Net仅使用 .Net 生成证书请求并提交给 CA
【发布时间】:2021-07-04 15:06:51
【问题描述】:

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

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

Generate and Sign Certificate Request using pure .net Framework 此处的这个问题对获得 DER 编码的 CSR 非常有帮助,但我仍有一些问题无法弄清楚。

  1. 如何指定 CA 和要在 CertificateRequest 对象中使用的模板?
  2. 我有一个公共密钥,它是一个RSAParameters 对象。如何将其放入 RSA 对象中以与 CertificateRequst 构造函数一起使用?
  3. 获得 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}");
        }
    }
}

【问题讨论】:

    标签: c# .net-core certificate pki


    【解决方案1】:

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

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

    您不能,但没关系,因为您也不在 CertEnroll 代码中。 CertificateRequest 对象等同于您的objPkcs10,CA 和模板用于您对CreateSigningRequest 输出所做的操作。

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

    using (RSA key = RSA.Create())
    {
        key.ImportParameters(rsaParameters);
        ...
    }
    

    获得 DER 编码的 CSR 后,如何将其提交给 CA?我在 System.Security.Cryptography.X509Certificates 命名空间中找不到任何可以实现此目的的类或方法。

    盒子里没有直接的东西。从CX509CertificateRequest类名看,好像是用Certificate Management over CMS (CMC),不过后面还有认证和请求提交部分要解决。

    【讨论】:

    • 我用这一行指定模板名称:cmcReq.InitializeFromInnerRequestTemplateName(objPkcs10, deviceDetails.CertificateDetails.TemplateName);
    • 所以似乎(到目前为止)没有办法仅使用 dotnet 来完成此任务。
    • @Dbloom 好吧,没有单行本,不。但是,一旦您找到如何发现 AD CS 服务器并与之对话,可能所有部分都已存在。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-20
    • 1970-01-01
    • 2014-07-23
    • 1970-01-01
    • 2020-01-17
    相关资源
    最近更新 更多