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


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)

Comments (102)

  1. Anonymous says:

    Very good article, it helps me a lot ๐Ÿ™‚

  2. Anonymous says:

    I got the error:

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

    How can I specify a certificate template extension?

  3. Anonymous says:

    Hi,

    can you explain a little how to do the same with the "on behalf" feature ?

    I imagine, you need to sign the request before submiting ? how ?

    thks

    Anthony

  4. Anonymous says:

    Hello

    Thank you for your sample code

    But I got an Error

    using CERTENROLLLib;

    using CERTCLIENTLib;

    the above using **** has error..

    Dose it must add some reference in this project?

    thank you very much

  5. Yes, you have to add references to the CertEnroll & CertCli COM objects (Project > Add Reference > COM).

  6. Anonymous says:

    how does one compile this code?

    csc /t:library /r:cercli.dll does not work. Thanks.

  7. Henok, take into account this is just a sample, and it’s just a file of a complete project. This is part of a form part of a very basic WinForms project created with VS2008.

  8. Anonymous says:

    While trying to run the given example I am getting the error : CCertRequest::Submit: The RPC server is unavailable. 0x800706ba (WIN32: 1722).

    I am using Vista Ultimate as the client to run this application and submitting request to Standalone CA hosted on W2003 Enterprise edition server with Service Pack 2 and Hotfix for CA services for supporting Vista and 2008 Clients . This server is not part of any domain server. Just connceted to my companies intranet.I am able to get the certificate from this Vista machine through web enrollment module.

    I have checked that CA server and RPC server are running.

    Please give any pointer to what could be the problem.

    Thanks in advance.

    Rajendra

  9. Anonymous says:

    Thanks for the code, it is rare to find a similar one.

    I have 2 questions:

    1. If I have already a public key, how do I add it to the request.
    2. How to specify the CA to which I wil submit the request.

    Thanks in advance

  10. Anonymous says:

    Thanks for the sample code.

    But i now i am facing 2 problems when i want to generate key from hardware token and download cert from CA to token:

    1) When i call the CreateRequest(1) during create csr, it generate private and public key pair and immediately store a certificate into my token. If I just want to store the key pair only, what should i set

    2) When i call InstallResponse(4,strCert,0,"") to install the cert to token is shows error "Data is invalid. 0x8007000d"

  11. I don’t the answers on the top of my head, and I would need time to reproduce and investigate. If you need help with this I suggest you open a case with us, Microsoft Technical Support.

    Regards,

    Alex

  12. Anonymous says:

    How I can specify CA?

    when I put

    strCAConfig = "somecertserver.com/certserver" (Windows 2003 CA Authority) getting

    {"CCertRequest::Submit: The RPC server is unavailable. 0x800706ba (WIN32: 1722)"}

  13. Check the following MSDN article for details on how to specify a CA in the strCAConfig variable:

    ICertRequest2::Submit Method

    http://msdn.microsoft.com/en-us/library/aa385054(VS.85).aspx

    "

    strConfig [in]

    Represents a valid configuration string for the Certificate Services server in the form COMPUTERNAMECANAME, where COMPUTERNAME is the server’s network name, and CANAME is the common name of the certification authority, as entered during Certificate Services setup. For information about the configuration string name, see ICertConfig.

    "

    I hope this helps.

    Regards,

    Alex

  14. Anonymous says:

    finally get the certificate!!! How would I create pfx file out of it.

    Here are my steps (according to your post):

    1. objEnroll.Initialize
    2. objEnroll.InstallResponse

    3. objEnroll.CreatePFX which return me a string

    How I can create pfx file?

  15. Anonymous says:

    Figured it out:

    string ress = objEnroll.CreatePFX(password, PFXExportOptions.PFXExportEEOnly,                                                  EncodingType.XCN_CRYPT_STRING_BASE64);

    var fs = new FileStream(exportPath, FileMode.Create);

                   fs.Write(Convert.FromBase64String(ress), 0, Convert.FromBase64String(ress).Length);

                   fs.Close();

  16. Anonymous says:

    Is there a way to get pfx blob without installing response to local certificate store?

    Something like:

    1. objEnroll.Initialize

    2. something i am not aware of ๐Ÿ™

    3. objEnroll.CreatePFX which return me a string

  17. Don’t know a way. Don’t think it’s possible, because when we make a request, a partial cert with public/private key pair gets created in the profile of the user/machine. Then we send the request to the CA and its response gets combined to that partial cert to create the definite signed cert. That process requires the cert to be in the profile. You may get the PFX and delete the cert afterwards…

  18. Anonymous says:

    Thanks for explanation! I will go with a way you suggested.

  19. Mike-D says:

    Is it possible to emulate the function "Automatically Enroll Certificates" using CAPICOM or CertEnroll on XP to force auto enrollment ?

    Via MMC-Certificates – right-click on cert store – All Tasks – Automatically Enroll Certificates

    Thanks – Mike

  20. Paul_Kudryavtsev says:

    Hi!

    Can you suggest some links or some code snippets for enrollment certificate for template "SmartcardLogon"? What should I change in your code to enroll certificate for smartcard?

    Thanks

  21. CarlCioffi says:

    I was wondering if there was ever a response to a question above because I have the same question but I can’t find an answer.

    I have 2 questions:

    1. If I have already a public key, how do I add it to the request. (need this answer)
    2. How to specify the CA to which I wil submit the request. (don’t need this answer)

    Thanks in advance

  22. Anonymous says:

    I create my own web site to request a certificate.  Everything works fine and I’m able to get the certificate from the CA into a string (strCert).

    Now, I need to send it to the user via the browser and install it on the user’s PC.  

    I work with C# (VS2008) and .NET 3.5 inside a Web Application.

    Any thoughts?

  23. scof_mon says:

    Hello,

    I also have the exact same situation as Rene. I have written the code on windows server 2008 using asp.net C# to request for certificate. As if I call it from the client machine (another machin within LAN using http://ipaddress/ApplicationName) it works fine. But installResponse only installs it on the server machine. How can I send the certificate response to the Client machine so that it can install it within both IE and Firefox? Please help.

    Thank you in advance.

  24. Anonymous says:

    Is this solution good for the site that’s hosted on Win2003 server?

    What would be the equivalent CertEnroll.dll COM in 2003?

  25. Anonymous says:

    Thanks for this sample!

    When I tried to initialize the IX509Enrollment object from template (InitializeFromTemplateName) – it failed when the minimum supported CA of the template was Windows Server 2008. It worked fine with the same template which its minimum supported CA is Windows Server 2003. (My CA is on Windows Server 2008×64, the code was invoked on the same computer).

    It fails in the CreateRequest stage, with the following exception:

    CertEnroll::CX509Enrollment::p_CreateRequest: Key does not exist. 0x8009000d (-2146893811)

    Any idea?

    Thanks,

    D.

  26. sambeauvois says:

    hi

    I had a problem with the InitializeFromTemplateName method.

    I used it with succes in dev environment, but when I moved my application in stagin environment, it failed with the error :

    "CertEnroll::CX509CertificateRequestPkcs10::InitializeFromTemplateName: The requested certificate template is not supported by this CA. 0×80094800 (-2146875392)"

    So I tried to change the security but it wasn’t the error source

    in fact, InitializeFromTemplateName can take the certificate template object identifier as parameter

    So I used it and it worked

    I described that here :

    http://www.sambeauvois.be/blog/2010/04/the-ix509certificaterequestpkcs10-initializefromtemplatename-adventure/

  27. Anonymous says:

    The InitializeFromTemplateName method does work. Make sure you are using the template name opposed to it's display name, if you have spaces in the name, you are using the display name!! ๐Ÿ™‚

  28. Jawcontact says:

    Hi,

    I'm programing an application that request a certificate from Microsoft Enterprise CA on behalf users. Actually, I can request a certificate (with Certenroll) by using a template. the application is hosted on IIS on the enterprise CA server. But the certificate I request belong to the computer account. My problem is that I don't know request a certificate for a specific user on AD. I have no idea how to map my certificate request to a user! Do you (or anyone) know how? with attribute or methods i should add to your code to do so?

    Thanks,

  29. Hi Jawcontact,

    If I understood correctly, you are using server side code to request certificates on behalf of users. If so, the typical solution is to create a PFX file in server side, and send it to user afterwards. So you could implement a solution like the following:

    1. Have server code to request and install the certificate in a  machine certificate store.
    2. The server will export the certificate + private key in PFX format from that cert store, and save it to some database or folder.

    3. User downloads the PFX file to local machine.

    4. Client side code installs the PFX certificate in the userโ€™s certificate store.

    Regards,

    Alex

  30. Jawcontact says:

    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?

  31. Anonymous says:

    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

  32. Anonymous says:

    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

  33. Anonymous says:

    Hi Alejandro,

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

    Thanks in advance, Pablo

  34. Anonymous says:

    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.

  35. Anonymous says:

    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.

  36. 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

  37. Anonymous says:

    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

  38. 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

  39. Anonymous says:

    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

  40. 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

  41. Anonymous says:

    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.

  42. Anonymous says:

    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)

    '

    '————————————————————————

  43. 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

  44. Anonymous says:

    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";

  45. Raymond Rogers says:

    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?

  46. Anonymous says:

    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

  47. 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

  48. 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

  49. Anonymous says:

    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

  50. 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

  51. Anonymous says:

    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

  52. Anonymous says:

    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

  53. Anonymous says:

    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

  54. 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

  55. Anonymous says:

    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

  56. 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

  57. 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

  58. 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

  59. 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

  60. Thank you very much Alex………..

  61. Anonymous says:

    Hi All,

    When i'm submitting my request to CA my request is going into pending folder .

    iDisposition = objCertRequest.Submit(

                       CR_IN_BASE64 | CR_IN_FORMATANY,

                       strRequest,

                       null,

                       strCAConfig

                   );

    I'm getting iDisposition =5 which is pending .

    Am i doing any thin wrong how to issue the certificate directly i mean not to pending stage.

    Thanks

    Rizwan

  62. Hi Rizwan,

    That should be something to configure on the server. CAs can issue certs directly or wait for an admin's approval.

    Regards,

    Alex

  63. Anonymous says:

    Thanks Alex for your quick help,

    Once it is pending and approved by Admin then how to fetch the same request which is now signed by CA server?

    I must be very much thank full to you for your blog this has helped me a lot.

    Thanks

    Rizwan

  64. Anonymous says:

    We have resolved this issue you were right we need to make some changes into the config on CA and it started working.

    Thanks

    Rizwan

  65. Anonymous says:

    Hi Alex,

    Now i've generated CSR and got it signed back from CA server.

    When I'm trying to convert into Xenroll.CreatePFXfile("Password",certPath) i get error that this is not possible with current key.

    I'm able to successfully import that certificate into certificate store.When i go to certificate store and try to export certificate private key manually so i find that option grayed out.

    Any idea for this issue.

    Because i wanted to extract my private key from signed certificate and make it password protected and send to mail.

    Thanks

    Rizwan

  66. Hi Rizwan,

    You need to mark your key as exportable when creating the request. There are flags for that as you can see here:

    blogs.msdn.com/…/certenroll-cx509enrollment-p-createrequest-returns-error-0x8009000b.aspx

    Regards,

    Alex

  67. Anonymous says:

    Thanks Alex for all your help, it worked and i'm able to generate certificate and convert to .pfx file.

    Any Idea how to revoke the certificate which CA has issued?

    I was looking and  found the command through certutill.exe ,but same i wanted to do through my c# code.

    Please guide.

    Thanks in advance for all your help so far.

    Rizwan

  68. Hi Rizwan, I don't have any sample to revoke certs, but I guess you can use this method:

    ICertAdmin2::RevokeCertificate method

    msdn.microsoft.com/…/aa383251(VS.85).aspx

    Here you have a sample that shows how to use certadmin in .NET, in case it helps as a base:

    blogs.msdn.com/…/how-to-export-issued-certificates-from-a-ca-programatically-c.aspx

  69. Anonymous says:

    Thanks Alex for all your help and quick response for my queries…

    I'm creating .PFX file using xenroll.createpfxfile which accepts password and file path.

    I've a requirement to createpfx file without password.

    Any idea how to do that using xenroll.dll

    Thanks

    Rizwan

  70. Hi Rizwan,

    I don't remember much about xenroll, as it has been deprecated for years, but I would have assumed that passing "" or null as the password would do…

  71. Anonymous says:

    If i pass it null or "" then also it ask for password and without entering password able to proceed further, but i just wanted to get rid of password box, so it should't even show me the  password option …

    Thanks

    Rizwan

  72. Anonymous says:

    Hi Alex

    If i pass it null or "" then also it ask for password and without entering password able to proceed further, but i just wanted to get rid of password box, so it should't even show me the  password option …

    Or do we have any other COM component available who does this?

    Thanks

    Rizwan

  73. Anonymous says:

    Thank you very much for your blog and your quick response ,this blog actually helped me a lot.

    You are really great.

    Thanks

    Rizwan

  74. Anonymous says:

    certEnroll.createPKCS10(dn, ClientAuthentication)

    method is working fine when called using console app but on IIS server its not working the moment its coming here and throwing exception

    Exception : The system cannot find the file specified. (Exception from HRESULT: 0x80070002)

    Any help please

    Thanks

    Rizwan

  75. Anonymous says:

    I'm facing all new type of issue which is getting logged into System Event when i'm hosting my code to one of the environment which is as follows.

    Description:

    Application popup: Security Warning : You are about to install a certificate from a certification authority (CA) claiming to represent:

    My CA Name

    Windows cannot validate that the certificate is actually from "My CA Name". You should confirm its origin by contacting "My CA Name". The following number will assist you in this process:

    Thumbprint (sha1):

    Warning:

    If you install this root certificate, Windows will automatically trust any certificate issued by this CA. Installing a certificate with an unconfirmed thumbprint is a security risk. If you click "Yes" you acknowledge this risk.

    Do you want to install this certificate?

    For more information, see Help and Support Center at go.microsoft.com/…/events.asp.

    Any idea?

    Please help me .

    Thanks

    Rizwan

  76. Anonymous says:

    My app  is hosted on IIS and on webpage i get waiting message and after some time it gives me session time out error(which is normal behaviour of any asp.net app),it seems that it displayes some popup window and that is hidden some where which is not visible to us.

    Thanks

    Rizwan

  77. Anonymous says:

    We have resolved this issue by installing CA root certificate into machine where my app is hosted under local trust store so that it works for all the account on that system.

    Thanks

    Rizwan

  78. Anonymous says:

    Hi Alex,  I have installed enterprise CA and also trying to request certificate as mentioned in your blog. while executing the code mentioned above I am getting the following error:

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

    So I added the template in the code objPkcs10.InitializeFromPrivateKey( X509CertificateEnrollmentContext.ContextUser, objPrivateKey,  "") but now I am getting the following error: Provider could not perform the action since the  context was acquired as silent.

    Can you provide some help. We are not using MMC to request certificate but trying to generate programmatically.

  79. Hi Ashish,

    Here you have a sample that requests a cert with a certificate template:

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

  80. Anonymous says:

    Thanks Alex.. it worked.. so basically I added the following piece of code: CX509ExtensionTemplateName objExtensionTemplate = new CX509ExtensionTemplateName();

    objExtensionTemplate.InitializeEncode("User1");

    objPkcs10.X509Extensions.Add((CX509Extension)objExtensionTemplate);

    P.S Do you have some similar example to request user cert on behalf by enrollment agent

  81. Hi Ashish,

    I'm glad it worked. And regarding the other example you comment, I do. If it is not in my blog, I don't have it. And as I don't work on this subject any longer and I discontinued this blog, I won't have it. If you need further help, please contact Microsoft Technical Support.

    Cheers

  82. Anonymous says:

    Because I've been trying to figure out how to add "Request Email Address", I thought I'd reply here. It actually goes in the subject. It wold be Subject = "E=my@email.com, CN=MyCN". Hopefully this helps someone else.

  83. Anonymous says:

    Hi Alex, I am trying to submit certificate request for on behalf of other user to CA on Window Server 2003. Code in C# is like:

    using CERTCLIENTLib;

    using XENROLLLib;

    private const string ProviderName = "Microsoft Enhanced Cryptographic Provider v1.0";

    private const string EmailCertOID = "1.3.6.1.5.5.7.3.4";

    private const int PROV_RSA_FULL = 1;

    private const int XECR_CMC = 3;

    private const int CR_IN_ENCODEANY = 0xff;

    CEnroll certEnroll = new CEnroll();

    certEnroll.ProviderName = ProviderName;

    certEnroll.addCertTypeToRequest(TemplateName);

    certEnroll.ProviderType = PROV_RSA_FULL;

    certEnroll.KeySpec = AT_KEYEXCHANGE;

    certEnroll.GenKeyFlags = CRYPT_EXPORTABLE;

    certEnroll.EnableSMIMECapabilities = 1;

    certEnroll.SignerCertificate = Convert.ToBase64String(EnrollmentAgentCertificate.GetRawCertData());

    certEnroll.addNameValuePairToSignature("RequesterName", DomainName + @"" + UserName);

    string subjectCN = "CN=" + UserName;

    string certRequest = certEnroll.createRequest(XECR_CMC, subjectCN, EmailCertOID);

    CCertRequest objCertRequest = new CCertRequest();

    int iDisposition = objCertRequest.Submit(CR_IN_ENCODEANY | CR_IN_FORMATANY, certRequest, string.Empty, CAAddress);

    on objCertRequest.Submit() I am getting below error:

    "Unable to cast COM object of type CertClientLib.CCertRequestClass to interface type CertClientLib.ICertRequest3. This operation failed because the QueryInterface call on the COM component for the interface with IID'{AFC8F92B-33A2-4861-BF36-2933B7CD67B3}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002(E_NOINTERFACE))"

    What can be the issue ?

  84. Anonymous says:

    hi,

    i using windows vista … i am getting exception  Unable to cast COM object of type CERTENROLLLib.CX509CertificateRequestPKcs10Class to interface type CERTENROLLLib.IX509CErtificateRequestPkcs10V2. – Interface not found exception…

    I found IX509CertificateRequestPkcs10V2 is not supported in vista… and tried  IX509CertificateRequestPkcs10 objPkcs10 = (IX509CertificateRequestPkcs10)Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX509CertificateRequestPkcs10"));

    still having exception..

  85. Anonymous says:

    Hi there,

    I can get the resulting client certificate request signed, but after successfully installing the signed certificate, it's missing the private key. Any ideas as to why this happens?

    My code is pretty much the same as the example, with the exception that I submit the request to the CA manually in base64 format, and the user is given the option to generate the private key under machine context, not just currentuser.

    Thanks.

    Ray

  86. Anonymous says:

    Hi there,

    I'm having a issue with the class CX509Enrollment when I execute my code in Windows Vista. The code below works very well in Windows 7 and I can generate a CSR signed with SHA256 but when I execute the same code in Windows Vista, is generated a CSR and a Digital Certificate self-signed. After this, is impossible to publish the digital certificate generated by CA.

                   String providerName = "SafeSign Standard Cryptographic Service Provider";

                   objCSP.InitializeFromName("SafeSign Standard Cryptographic Service Provider");

                   objCSPs.Add(objCSP);

                   String date = DateTime.Now.ToString("MMddHHmmss") + DateTime.Now.Millisecond.ToString();

                   objPrivateKey.ProviderName = providerName;

                   objPrivateKey.ContainerName = "SERASA-" + date;

                   objPrivateKey.Length = 2048;

                   objPrivateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE;

                   objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_SIGNING_FLAG|X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_KEY_AGREEMENT_FLAG;

                   objPrivateKey.MachineContext = false;

                   objPrivateKey.CspInformations = objCSPs;

                   objPrivateKey.Create();

                   objPkcs10.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, objPrivateKey, "");

                   CObjectId hashobj = new CObjectId();

                   hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID, ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlags.AlgorithmFlagsNone, "SHA256");

                   objPkcs10.HashAlgorithm = hashobj;

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

                   );

                   objPkcs10.X509Extensions.Add((CX509Extension)objExtensionKeyUsage);

                   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);

                   objDN.Encode("CN=Eder Souza, O=e-Safer, OU=Consultoria, OU=ICP-Brasl", X500NameFlags.XCN_CERT_NAME_STR_NONE);

                   objPkcs10.Subject = objDN;

                   objEnroll.InitializeFromRequest(objPkcs10);

                   strRequest = objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);

                   txtCSR.Text = strRequest;

    I will appreciate any help.

    Thank you.

    Eder

  87. Anonymous says:

    How to add a new property in ICertProperties. Can you please reply its very urgent.

    Thanks in advance.

  88. Anonymous says:

    In the link below there is a related code which works like a charm!!… I used this in a Windows Server 2008 R2 installation. Hope this help to others as an alternative to the code posted here ๐Ÿ™‚

    http://sharetext.org/Xird

  89. Anonymous says:

    Thanks for your amazing and unique blog post about this subject. I successfuly implemented your code. But still have a little but disturbing problem.

    I just want to create CSR and Private Key. I won't use our CA servers to generate and install certificate. CSR created successfuly but private key didn't. I asked my question at Stackoverflow, i hope you can help me. stackoverflow.com/…/generating-csr-and-exporting-private-key-with-c-sharp

  90. Anonymous says:

    The intent of the CspInformations collection property on the private key class is to include all CSPs installed on the system, and thatโ€™s merely an optimization to prevent multiple CSP enumerations when enrolling for multiple certs.

    Since the above sample targets one CSP, set the private key class ProviderName string property to the CSP name and avoid creating the CspInformation object and collection object.

    This directly tells the private key class to use that CSP.

    Specifying a collection of CSPs tells the Pkcs10 request class to first sort the CSPs in the collection, assign priorities, enumerate in priority order, then pick the first one that can successfully create a new key.

    Thatโ€™s a lot of work when you already know which CSP to use.

  91. Anonymous says:

    After calling objCertRequest.Submit:

    1) use objCertRequest.GetLastStatus to get an HRESULT error code from the CA.  If not S_OK, this HRESULT will help explain why the request was denied.  Pass it to objCertRequest.GetErrorMessageText to obtain localized error message text for the HRESULT.

    2) use objCertRequest.GetDispositionMessage to obtain server supplied informational/explanatory message text that may be present even when the cert was issued.

  92. Anonymous says:

    I am trying to generate certificate using <CertEnroll in Asp.net

    While calling CreateRequest(), I am getting the below error every time.

    var enroll = new CX509Enrollment();

    //Code to Initialize and assign certificate name

    string csr = enroll.CreateRequest();

    Error:

    CertEnroll::CX509Enrollment::_CreateRequest: Access is denied. 0x80070005 (WIN32: 5 ERROR_ACCESS_DENIED)

    Please help as I am stuck at this point.

  93. Anonymous says:

    I am trying to add SAN values to a CSR provided by the user, I am passing the san values in the strAttributes parameter on objCertRequest.Submit . but after the cer file is generated the san are not being displayed on the certificate generated.

    Any help will be appreciated .

    Thanks

  94. Anonymous says:

    Hello,

    I am trying to export private key via code like this:

    objCSP.InitializeFromName(cryptograficProvider);

                   //  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 = "CertEnrolment API test 1";

                   objPrivateKey.Length = 2048;

                   objPrivateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE;

                   objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;

                   objPrivateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;

                   objPrivateKey.MachineContext = true;

                   //  Provide the CSP collection object (in this case containing only 1 CSP object)

                   //  to the private key object

                   objPrivateKey.CspInformations = objCSPs;

                   //objPrivateKey.SecurityDescriptor = "Administrator";

                   //  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.ContextAdministratorForceMachine,

                       objPrivateKey,

                       "TestUser"

                   );

                   objPkcs10.PrivateKey.Export("BCRYPT_PUBLIC_KEY_BLOB");

    But still i am getting error:CertEnroll::CX509PrivateKey::Export: This function is not supported on this system. 0x80070078 (WIN32/HTTP: 120 ERROR_CALL_NOT_IMPLEMENTED

    Have any idea what is wrong?

    BR,

    Lubomir

  95. Anonymous says:

    After some fighting I found the problem. In source code bcrypt.h is BCRYPT_PUBLIC_KEY_BLOB defined as

    #define BCRYPT_PRIVATE_KEY_BLOB      L"PRIVATEBLOB"

    Correct call is:     objPkcs10.PrivateKey.Export("PRIVATEBLOB");

    then private key is exported ๐Ÿ™‚

  96. Anonymous says:

    The code sample doesn't compile in VS2013 ("The type 'CERTENROLLLib.CX509CertificateRequestPkcs10Class' has no constructors defined" and "Interop type 'CERTENROLLLib.CX509CertificateRequestPkcs10Class' cannot be embedded. Use the applicable interface instead.")

  97. Anonymous says:

    Hey Alex,

    Can we update any certificate request from pending to Issued using C#.

    If yes then how?

    Thanks

  98. reidca says:

    Anyone have an idea why taking the code here and specifying the private key algorithm as shown in the example on

    CObjectId algobj = new CObjectId();

    algobj.InitializeFromAlgorithmName( ObjectIdGroupId.XCN_CRYPT_ANY_GROUP_ID, ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlags.AlgorithmFlagsNone, "ECDSA_P521");

    objPrivateKey.Algorithm = algobj;

    Each time the result is "Cannot find object or property. 0x80092004 (-2146885628)"

    This is on windows 7 64-bit.

    Thanks

  99. Anonymous says:

    Hi Alex, hi VR,

    how do I set the RequesterName or any other cert properties such as

    Request.GiveName, Request.SurName, RequestEmailAddress etc..

    Here is the reference: msdn.microsoft.com/…/cc226759.aspx

    Request_Email: Column name "Request.EMail". The EmailAddress attribute of the DN from the Subject of the certificate request.

    So just provide the values in the DN of the certificate request.