Decrypt my World

Cryptography, Security, Debugging and more!

How to create a certificate request with CertEnroll and .NET (C#)

How to create a certificate request with CertEnroll and .NET (C#)

Rate This
  • Comments 98

Hi all,

The following C# sample shows how to use CertEnroll COM component to create a certificate request, send the request to the CA, get the response from the CA, and install the new certificate in the machine:

(Note that this sample is a WinForms app with 3 buttons -createRequestButton, sendRequestButton, acceptPKCS7Button- and 2 textboxes -requestText & responseText-)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

//  Add the CertEnroll namespace
using CERTENROLLLib;
using CERTCLIENTLib;

namespace CATest
{
    public partial class Form1 : Form
    {
        private const int CC_DEFAULTCONFIG = 0;
        private const int CC_UIPICKCONFIG = 0x1;
        private const int CR_IN_BASE64 = 0x1;
        private const int CR_IN_FORMATANY = 0;
        private const int CR_IN_PKCS10 = 0x100;
        private const int CR_DISP_ISSUED = 0x3;
        private const int CR_DISP_UNDER_SUBMISSION = 0x5;
        private const int CR_OUT_BASE64 = 0x1;
        private const int CR_OUT_CHAIN = 0x100;

        public Form1()
        {
            InitializeComponent();
        }

        // Create request
        private void createRequestButton_Click(object sender, EventArgs e)
        {
            //  Create all the objects that will be required
            CX509CertificateRequestPkcs10 objPkcs10 = new CX509CertificateRequestPkcs10Class();
            CX509PrivateKey objPrivateKey = new CX509PrivateKeyClass();
            CCspInformation objCSP = new CCspInformationClass();
            CCspInformations objCSPs = new CCspInformationsClass();
            CX500DistinguishedName objDN = new CX500DistinguishedNameClass();
            CX509Enrollment objEnroll = new CX509EnrollmentClass();
            CObjectIds objObjectIds = new CObjectIdsClass();
            CObjectId objObjectId = new CObjectIdClass();
            CX509ExtensionKeyUsage objExtensionKeyUsage = new CX509ExtensionKeyUsageClass(); 
            CX509ExtensionEnhancedKeyUsage objX509ExtensionEnhancedKeyUsage = new CX509ExtensionEnhancedKeyUsageClass();
            string strRequest;

            try
            {
                requestText.Text = "";

                //  Initialize the csp object using the desired Cryptograhic Service Provider (CSP)
                objCSP.InitializeFromName(
                    "Microsoft Enhanced Cryptographic Provider v1.0"
                );

                //  Add this CSP object to the CSP collection object
                objCSPs.Add(
                    objCSP
                );

                //  Provide key container name, key length and key spec to the private key object
                //objPrivateKey.ContainerName = "AlejaCMa";
                objPrivateKey.Length = 1024;
                objPrivateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE;
                objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES; 
                objPrivateKey.MachineContext = false;

                //  Provide the CSP collection object (in this case containing only 1 CSP object)
                //  to the private key object
                objPrivateKey.CspInformations = objCSPs;

                //  Create the actual key pair
                objPrivateKey.Create();

                //  Initialize the PKCS#10 certificate request object based on the private key.
                //  Using the context, indicate that this is a user certificate request and don't
                //  provide a template name
                objPkcs10.InitializeFromPrivateKey(
                    X509CertificateEnrollmentContext.ContextUser, 
                    objPrivateKey, 
                    ""
                );

                // 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(
                    "CN=AlejaCMa",
                    X500NameFlags.XCN_CERT_NAME_STR_NONE
                );

                //  Assing the subject name by using the Distinguished Name object initialized above
                objPkcs10.Subject = objDN;

                // Create enrollment request
                objEnroll.InitializeFromRequest(objPkcs10);
                strRequest = objEnroll.CreateRequest(
                    EncodingType.XCN_CRYPT_STRING_BASE64
                );

                requestText.Text = strRequest;

            } catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        // Submit request to CA and get response 
        private void sendRequestButton_Click(object sender, EventArgs e)
        {
            //  Create all the objects that will be required
            CCertConfig objCertConfig = new CCertConfigClass();
            CCertRequest objCertRequest = new CCertRequestClass();
            string strCAConfig;
            string strRequest;
            int iDisposition;
            string strDisposition;
            string strCert;

            try
            {
                strRequest = requestText.Text;

                // Get CA config from UI
                //strCAConfig = objCertConfig.GetConfig(CC_DEFAULTCONFIG);
                strCAConfig = objCertConfig.GetConfig(CC_UIPICKCONFIG);                

                // Submit the request
                iDisposition = objCertRequest.Submit(
                    CR_IN_BASE64 | CR_IN_FORMATANY,
                    strRequest,
                    null,
                    strCAConfig
                );

                // Check the submission status
                if (CR_DISP_ISSUED != iDisposition) // Not enrolled
                {
                    strDisposition = objCertRequest.GetDispositionMessage();

                    if (CR_DISP_UNDER_SUBMISSION == iDisposition) // Pending
                    {
                        MessageBox.Show("The submission is pending: " + strDisposition);
                        return;
                    }
                    else // Failed
                    {
                        MessageBox.Show("The submission failed: " + strDisposition);
                        MessageBox.Show("Last status: " + objCertRequest.GetLastStatus().ToString());
                        return;
                    }
                }

                // Get the certificate
                strCert = objCertRequest.GetCertificate(
                    CR_OUT_BASE64 | CR_OUT_CHAIN
                );

                responseText.Text = strCert;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        // Install response from CA
        private void acceptPKCS7Button_Click(object sender, EventArgs e)
        {
            //  Create all the objects that will be required
            CX509Enrollment objEnroll = new CX509EnrollmentClass();
            string strCert;
            
            try
            {
                strCert = responseText.Text;

                // Install the certificate
                objEnroll.Initialize(X509CertificateEnrollmentContext.ContextUser);
                objEnroll.InstallResponse(
                    InstallResponseRestrictionFlags.AllowUntrustedRoot,
                    strCert,
                    EncodingType.XCN_CRYPT_STRING_BASE64,
                    null
                );

                MessageBox.Show("Certificate installed!");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }      
    }
}

I hope this helps.

Cheers,

 

Alex (Alejandro Campos Magencio)

  • The objCertConfig.GetConfig(CC_UIPICKCONFIG); call throws "CCertConfig::GetConfig: No more data is available. 0x80070103 (WIN32/HTTP: 259)"

    I'm assuming this is because I'm not a member of the domain.  Can I get a cert from a domain CA on a system that is not a member of the domain?

  • Hi Alex,

    If I need to create .p7b file, from your code do I simply filestream the strCert string obtained via GetCertificate(CR_OUT_BASE 64 | CR_OUT_CHAIN) with the file extension .p7b?  Please help!

    Thanks,

    VR

  • Hi VR,

    Some info on how to export the cert with its private key and its cert chain here:

    How to export our enrolled certificates programmatically

    blogs.msdn.com/.../how-to-export-our-enrolled-certificates-programmatically.aspx

    You may use this flag to export the entire cert chain:

    PFXExportOptions enumeration

    msdn.microsoft.com/.../aa379073(v=vs.85).aspx

    "

    PFXExportChainWithRoot

    Includes the entire certificate chain, including the root certification authority certificate.

    "

    Regards,

    Alex

  • Hi VR,

    If you don't need the private key, but only the public part of the cert and its cert chain, this method that you mentioned should do the work according to our docs:

    ICertRequest3::GetCertificate method

    msdn.microsoft.com/.../aa385046(v=vs.85).aspx

    "

    The GetCertificate method returns the certificate issued for the request as an X.509 certificate, or optionally packaged in a Public Key Cryptography Standards (PKCS) #7 message that contains the complete certificate chain for the Certificate Services server.

    "

    Regards,

    Alex

  • Hi Alex,

    Thank you for your response. I am trying to create a .p7b file as follows:

                               string BinaryCert = cCert.GetCertificate(CR_OUT_BINARY | CR_OUT_CHAIN);

                               byte[] strBytes = System.Text.Encoding.Unicode.GetBytes(BinaryCert);

                               File.WriteAllBytes(@"D:\Test.p7b", strBytes);

    When I attempt to right click the file and install the certificate with the above Test.p7b I receive the following error:

    "This file is invalid as the use as the following : PKCS #7 certificates"  [Invalid Public Key Security Object File}

    But if i export the file in .p7b format from within CA, it installs the certificate.  Also, if I GetCertificate(CR_OUT_BASE64 | CR_OUT_CHAIN) and save it with extension .p7b it seem to install the certificate fine. But I need the .p7b file with binary format. Please advise me on what I am doing wrong ! How do I really create a .p7b file via code just like the way it is exported from the Certificate Authority.

    Thanks for your help!

    VR

  • Hi VR,

    If you get a valid Base64 string, converting it to a binary array will be very easy with Convert.FromBase64String.

    If you get binary data in a string, converting it to a binary array won't be so easy. You shouldn't use Unicode.GetBytes in the first place. Some of the bytes in the array may form invalid Unicode characters, because you are not dealing with a Unicode string. If I recall well, by default GetBytes strips all invalid Unicode characters from the string and only return valid chars.

    I hope this helps.

    Regards,

    Alex

  • Thank you so much Alex. You answered my question, I got it working.

    Thanks again for all your help. I really appreciate it.

    Regards,

    VR

  • Hi Ales, i have a problem with your code and the error is in the sendRequest:

    Denied by Policy Module  0x80094801, The request does not contain a certificate template extension or the CertificateTemplate request attribute.

    Thanks for your help!

    DM

  • Hi Alex, i have a problem with your code and the error is in the sendRequest:

    Denied by Policy Module  0x80094801, The request does not contain a certificate template extension or the CertificateTemplate request attribute.

    Thanks for your help!

    DM

  • Hi DM,

    If I recall well, this sample doesn't use cert templates, so the error is to be expected if your CA requires them. This other sample shows how to use cert templates:

    How to request an smartcard logon cert programmatically (C#)

    blogs.msdn.com/.../how-to-request-an-smartcard-logon-cert-programmatically-c.aspx

    Regards,

    Alex

  • Hi Alejandro,

    I'm using windows XP and my application is hosted on windows 2003 where i don't have CerEnroll COM component, Please help me with sample code for this case.

    Thanks

    RA

  • Hi RA,

    On WinXP/Server 2003 you need to use XEnroll instead of CertEnroll. Unfortunatelly I don't have any sample for XEnroll, mainly because it has been deprecated for years.If you need help with XEnroll, I suggest you open a case with Microsoft Technical Support.

    Regards,

    Alex

  • Hi Alex,

    Thanks for your reply i got the code for XEnroll .

    My CA server is configured on windows 2008 and for development we are using windows xp .

    my biggest worry is i'm generating my certificate request using XEnroll and sending to CA for signing who have CertEnroll.

    will this scenario work? or do i need to use windows 2008 for development where I can find CertEnroll dll.

    Please reply

    Thanks

    RA

  • Hi All/Alex

    Please reply...

    Can I generate certificate using windows 2003/XP(where i have XEnroll ) and send to CA server which is into windows server 2008(where we have CertEnroll). Will this work or give error....

    Please help

    Thanks

    in advance

    RA

  • Hi RAfsar,

    Here you have the answer to your question:

    support.microsoft.com/.../922706

    Basically you can use XEnroll to request certs on WinServer 2008, but with reduced functionality (cannot tell which functionality you will be missing exactly, though).

    Regards,

    Alex

Page 4 of 7 (98 items) «23456»
Leave a Comment
  • Please add 6 and 1 and type the answer here:
  • Post
Translate This Page