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)

  • Hi Alejacma,

    Actually we would use a server side to request certificate on behalf users but my web application cannot. Here is the code that I use to request a certificat:

           private const int CR_IN_BASE64 = 0x1;

           private const int CR_IN_FORMATANY = 0;

           private const string CertificateAuthorityAddress = "localhost\\PKI Test Entreprise Issuing CA";

           private const string distinguishedName = "CN=User Name1,OU=client1,DC=myDomain";

               //  Create all the objects that will be required

               CX509CertificateRequestPkcs10 objPkcs10 = new CX509CertificateRequestPkcs10Class();        

               CX500DistinguishedName objDN = new CX500DistinguishedNameClass();

               CX509Enrollment objEnroll = new CX509EnrollmentClass();

               string strRequest;

               objDN.Encode(

                   distinguishedName,

                   X500NameFlags.XCN_CERT_NAME_STR_NONE

               );

               objPkcs10.InitializeFromTemplateName(X509CertificateEnrollmentContext.ContextUser, "PKITUser");

               // 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

               );

               sendRequest(strRequest);

           // Submit request to CA and get response  

           private void sendRequest(string strRequest)

           {

               //  Create all the objects that will be required

               CCertConfig objCertConfig = new CCertConfigClass();

               CCertRequest objCertRequest = new CCertRequestClass();

               string strCAConfig;

               int iDisposition;

               string strDisposition;

               string strCert;

               // Submit the request

               iDisposition = objCertRequest.Submit(

                   CR_IN_BASE64 | CR_IN_FORMATANY,

                   strRequest,

                   null,

                   CertificateAuthorityAddress

               );

    The problem is in the CA even if I've specified the user's Distinguish Name, the certificate that is issued belong to the local computer instead to belong to the user (in this exemple User Name1). How can I request a certificate for User Name1 and be stored in the ldap for this user and not for the computer account?

  • The exemple ist good. I  need to find an example of creating a certificate on-behalf-off, one where a different CSP is used (a smartcard csp) and the private key is stored on the card. I've run into a dead end with the enrollEOBOCMC example from the SDK because there is no way to influnce the private key object using the IX509CertificateRequestCmc interface. I tried GetInnerRequest, but that doesn't work.

    Thanks in advance

    Robert

  • Hi!

    I have a strange error when i try to run this example on my windows 2008 standard server. When i klick on CreateRequestButton i get an exception: "Unable to cast COM object of type CERTENROLLLib.CX509CertificateRequestPKcs10Class to interface type CERTENROLLLib.IX509CErtificateRequestPkcs10V2. This operation failed besouse the QueryInterface call on the COM component for the interface with IID'{278AB35B-217D-11DA-B2A4-000E7BBB2B09}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002(E_NOINTERFACE)))"

    I can run the app from my windows 7 and a windows 2008 R2 computer without problems but not a windows 2008 standard. The only thing i can see its different is that on win 7 and R2 computers the certmgr have version number 6.1.0..... and win 2008 standard have version number 6.0. ( I have tried 3-4 windows 2008 standard somputers and all throws the same error )  Tryed to google it but no result. Anyone have a solution or maby know how can i update my certmgr to 6.1 ?

    regards

    Adam

  • Hi Alejandro,

    Do you know how to do a PKCS #10 in client side with mozilla firefox compatible?

    Thanks in advance, Pablo

  • I am able create and publish the certificate request to my CA. I am also able to pull the certificate after it is created. But where is the certificate being installed? I cannot find it anywhere in the certificates MMC snap-in.

  • This code produces CSRs that after installing the certificate do not allow SSL 3.0, TLS 1.1 or 1.2 to work.

    When generating the CSR through the IIS7 manager SSL 3.0, TLS 1.1 and 1.2 work.

  • You are just right echoDreamz. This is just a sample that shows how to create a cert request. If you need your cert to have specific extensions and properties (like the usage of the cert), you will need to modify the sample to fit your needs.

    Regards,

    Alex

  • Hi,

    Thanks for the article. It helped me a lot!. Can you please tell me how I can update some of the certificate properties / request properties such as FirstName, LAstName, mainily  "RequesterName" and "Request Email Address" properties of the request while submitting a request to the Enterprise Issuing CA.

    Thanks,

    VR

  • Hi VR,

    Check this property of the request:

    IX509CertificateRequestPkcs7::RequesterName property

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

    For Request Email Address I would need to investigate. And for that I would need a Technical Support case, as I don't have much free time to check this out.

    Regards,

    Alex

  • Hi Alex,

    Thank you for your quick response. If I use XEnroll.dll(the one supported on Server2003 as opposed to CEnroll.dll) and I am not creating the PKCS#10, instead obtaining an already created CSR via IIS , submit to CA via the CCertRequest.Submit() method, then how do I set the RequesterName or any other cert properties such as Request.GiveName, Request.SurName, RequestEmailAddress etc..

    Please let me know your findings with your technical support ticket on methods to set the request/certificate properties in CA considering it all running on a Server2003 environment.Your help is greatly appreciated!

    Thanks,

    VR

  • Hi VR,

    "

    Please let me know your findings with your technical support ticket

    "

    I don't think you understood me well. I don't have any support ticket and I cannot create one. My customers are the ones who create the tickets for me or my colleagues to work on the issues.

    Do you need help right now?

    blogs.msdn.com/.../do-you-need-help-right-now.aspx

    I won't be able to take on new work until Jan 2nd 2012, but my colleagues may be able to assist too. Just note that XEnroll is obsolete and support to Windows Server 2003 is very, very limited.

    Regards,

    Alex

  • In unmanaged version CX509Enrollment:Install:InstallResponse() returns different Return Codes that can differentiate potential errors.

    Managed version doesn't return any feedback.

    Are there specific exceptions I can catch and what they indicate?

    Thank you in advance.

  • The appended code snippet throws error:

    CertEnroll::CX509PrivateKey::Export: This function is not supported on this system. 0x80070078 (WIN3Unhandled Exception: System.Runtime.InteropServices.COMException: CertEnroll::CX509PrivateKey::Export: This function is not supported on this system. 0x80070

    078 (WIN32/HTTP: 120)2/HTTP: 120)

    on all of 32- & 64-bit 2008 and 2008R2 ... is this a technical issue (i.e., function is not implemented) or a bureaucratic issue ("you're not allowed to do this").

    '------------------------------------------------------------------------

    '

    ' vbc /t:exe /r:System.dll,certenrollinterop.dll /verbose pkcs10.vb

    '

    Module PKCS10

    Sub Main()

     Dim pkcs10 As New certenrollinterop.CX509CertificateRequestPkcs10()

     Dim prvkey As New certenrollinterop.CX509PrivateKey()

     prvkey.ExportPolicy = 3

     prvkey.KeyUsage     = &Hffffff

     prvkey.ProviderType = 24

     ' Aiming for "Microsoft Enhanced RSA and AES Cryptographic Provider (Signature)"

     pkcs10.InitializeFromPrivateKey(certenrollinterop.X509CertificateEnrollmentContext.ContextUser, prvkey, "")

     pkcs10.Subject = New certenrollinterop.CX500DistinguishedName()

     pkcs10.Subject.encode("CN=Me!", certenrollinterop.X500NameFlags.XCN_CERT_X500_NAME_STR)

     pkcs10.encode

     System.Console.WriteLine(pkcs10.Subject.Name)

     System.Console.WriteLine(pkcs10.PrivateKey.CspStatus.DisplayName)

     System.Console.WriteLine(pkcs10.PrivateKey.Export("BCRYPT_PRIVATE_KEY_BLOB", certenrollinterop.EncodingType.XCN_CRYPT_STRING_BASE64))

    End Sub ' Main

    End Module ' PKCS10

    '

    ' Whether compiled using 32-bit .NET 2.0, or 64-bit .NET 4.0, that last line

    ' consistently throws (on all of 32- and 64-bit 2008, as well as 2008R2):

    '

    ' CertEnroll::CX509PrivateKey::Export: This function is not supported on this system. 0x80070078 (WIN32/HTTP: 120)

    '

    '------------------------------------------------------------------------

  • Hi James,

    I would need to debug the issue to figure out what is going on exactly. Please feel free to open a case with MS Technical Support so we are able to assist you properly.

    Thx!

    Alex

  • The following sequence is not recommended:

    >objCSP.InitializeFromName("Microsoft Enhanced Cryptographic Provider v1.0");

    >objCSPs.Add(objCSP);

    >objPrivateKey.CspInformations = objCSPs

    The CspInformations collection is expected to be empty or to contain all CSPs available on the machine.

    The proper way to target a specific CSP is:

    >objPrivateKey.ProviderName = "Microsoft Enhanced Cryptographic Provider v1.0";

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