Shaun Xu

The Sheep-Pen of the Shaun


News

logo

Shaun, the author of this blog is a semi-geek, clumsy developer, passionate speaker and incapable architect with about 10 years experience in .NET. He hopes to prove that software development is art rather than manufacturing. He's into cloud computing platform and technologies (Windows Azure, Aliyun) as well as WCF and ASP.NET MVC. Recently he's falling in love with JavaScript and Node.js.

Currently Shaun is working at IGT Technology Development (Beijing) Co., Ltd. as the architect responsible for product framework design and development.

MVP

My Stats

  • Posts - 97
  • Comments - 346
  • Trackbacks - 0

Tag Cloud


Recent Comments


Recent Posts


Archives


Post Categories



As many of you may already know that, I'm working at a global gaming and entertainment company taking the responsible for design and implement the next generation platform which will be running on the cloud, and also design the cloud platform as well. Currently one of the goal is to replace the active directory integrated security and identity solution with certificate-based solution in our product. In short, we need to work with Active Directory Certificate Service to request and issue the certificates for vary clients, so that they can use these certificates to connect our services from any devices, such as PC, smart phone, and pad, etc., securely.

Since what we need to implement first is a certificate service that can be used by any clients. This service will be talking to the Windows Active Directory Certificate Service (AD CS) through C#.

There are many articles for IT Pro on how to install and configure the AD CS, but very few on how to communicate with AD CS by C#. In this post I will describe and demonstrate how to work with the AD CS via C#. So I will not talk much about the theory of digital certificate, public key infrastructure and certificate authority, but will focus on how to use them.

 

Basic Knowledge of Certificate

Certificate, also known as public key certificate or digital certificate, is an electronic document which uses a digital signature to bind a public key with an identity. The identity could be anything. For example it could be represent a user, a device, a service or even a few lines of code.

The certificate can be used to sign the identity and could be verified by others. For example a message being signed by a certificate could be verified by the receiver, so that it will be able to know whether the message is the original one or had been modified by someone else. The certificate can also be used to encrypt and decrypt. This is the reason why we can bind a certificate on a website so that the data between the browser and server would be secured, since they are been encrypted and signed by the certificate.

The certificate authority (CA) takes the responsible to issue the certificates. In Unix people can use OpenSSL's ca command or SuSE's gensslcert to issue certificate. In Windows we can use the Active Directory Certificate Service.

In an enterprise there might be more than one CAs and normally they will be organized hierarchically. The top level would be the Root CA, which have a certificate signed by itself. All subordinate CAs’ certificate should be requested to and signed by the root CA.

image

Each CA can receive the certificate request from the client and issue them. Normally, the root CA would not be reachable by the clients since it holds the root CA certificate which is very important. Clients may send the certificate request to some subordinate CAs and get the certificate installed.

image

The certificate contains a key pair, which includes a private key and a public key. In order to make the private key secured, when requesting and installing the certificate, the private key should never be passed out of the client. Certificate request could be in PKCS #10 or CMC format, sent from the client to the CA. The subordinate CA received the request, and based on the request handling and policies, it will mark the request as pending status and let the administrator issue or deny manually, or automatically issue them. The certificate response would be in PKCS #7 format, signed by the CA certificate. Then the client will verify the response and combine it with the original private key to a full certificate.

image

So when we need to create a certificate, what we need to do is to

  • Generate the key pair and some other stuff in order to send to the CA.
  • Generate the certificate request in PKCS #10 or CMC format, and submit to CA.
  • Download the CA response.
  • Combine and install the full certificate on client based on the local key pair and the CA response.

 

In Windows Server 2008 R2, the AD CS introduced a new component named CES and CEP, which are Certificate Enrollment Service and Certificate Enrollment Policy. The client can communicate with CA through these web services. But in the prior version we have to use two COM: CertCli and CertEnroll.

CertCli component takes the responsible for connecting the CA server to submit the certificate request, certificate renew request and look for the request ID that CA server has. When connect from other machine the CertCli utilizes DCOM technology to invoke the CA functionality. This means, the CertCli cannot be used out of the domain or between the firewall.

CertEnroll component takes the responsible for generate the PKCS message, install and export the certificate. It doesn’t need to communicate to the CA directly.

Since in .NET we can wrap the COM and use it in managed code, we should be able to communicate with the CA by using them.

 

Generate Certificate Request Message to Standalone CA

There are two types of CA: enterprise CA and standalone CA. There are many differences between them. But to be simplified, the standalone CA cannot use the certificate template. In this post I will firstly demonstrate how to request the certificate to a standalone CA.

It’s only two steps to request a certificate, first one is to generate the request message, and then send the message to CA. To generate a valid certificate request message we need to use the CertEnroll COM, to send the request to need to use the CertCli COM. So let’s create a new console application and added these 2 COM components into the references.

image

To make sure the code runs successfully, it’s recommended to execute the sample code on the CA server, or at least the server on the same domain with the CA server. I will explain more about this later.

There are many information we need to specify or provide in order to build the request message. The first one is to select the valid cryptographic service provider (CSP). There are many CSPs built in the Windows. We can choose one of them, or we can just let the operation system retrieve all valid CSPs to us to use.

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using CERTENROLLLib;
   6: using CERTENROLLLib;
   7:  
   8: namespace ShaunXu.ADCSviaCSharp
   9: {
  10:     class Program
  11:     {
  12:         private string CreateCertRequestMessage()
  13:         {
  14:             var objCSPs = new CCspInformations();
  15:             objCSPs.AddAvailableCsps();
  16:         }
  17:  
  18:         static void Main(string[] args)
  19:         {
  20:         }
  21:     }
  22: }

Then we will create the key pair of the certificate. In this step we need to specify information below:

  • Length: The key length of the private key. Normally the key length should NOT less than 1024 for security consideration.
  • Key Spec: Define how this key pair, and the certificate will be used. For example digital signature or key exchange.
  • Key Usage: The key usage value will be upgrade based on the Key Spec we defined.
  • Machine Context: Specify whether the certificate will be used for current user and machine.
  • Export Policy: Specify whether the private key can be exported or not from this machine.
  • CSP Information: The valid CSPs for this key pair.

When we finished to define all information listed above we can just invoke CX509PrivateKey.Create to let the operation system generate a key pair for us. It will be stored in the machine in a “magic” folder.

   1: var objPrivateKey = new CX509PrivateKey();
   2: objPrivateKey.Length = 2048;
   3: objPrivateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE;
   4: objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;
   5: objPrivateKey.MachineContext = false;
   6: objPrivateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG;
   7: objPrivateKey.CspInformations = objCSPs;
   8: objPrivateKey.Create();

Next step, initialize the PKCS #10 object from the private we had just created. We need to specify whether the certificate should be used for current user or machine, which must be as same as the value of Machine Context that we defined in previous step. Since we will send the request to a standalone CA we will not specify the template here.

   1: var objPkcs10 = new CX509CertificateRequestPkcs10();
   2: objPkcs10.InitializeFromPrivateKey(
   3:     X509CertificateEnrollmentContext.ContextUser,
   4:     objPrivateKey,
   5:     string.Empty);

Next, specify some extension information to the certificate. I will not deep into these extensions. Just one thing, all extensions in certificate will be defined by an identity named Object ID (OID). So if we want to add some extensions to the certificate we need to specify the OID rather than the name. For example, in the code below I added “Client Authentication” enhanced key usage extension to the certificate by specifying its OID “1.3.6.1.5.5.7.3.2”.

   1: var objExtensionKeyUsage = new CX509ExtensionKeyUsage();
   2: objExtensionKeyUsage.InitializeEncode(
   3:     CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
   4:     CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_NON_REPUDIATION_KEY_USAGE |
   5:     CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE |
   6:     CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DATA_ENCIPHERMENT_KEY_USAGE);
   7: objPkcs10.X509Extensions.Add((CX509Extension)objExtensionKeyUsage);
   8:  
   9: var objObjectId = new CObjectId();
  10: var objObjectIds = new CObjectIds();
  11: var objX509ExtensionEnhancedKeyUsage = new CX509ExtensionEnhancedKeyUsage();
  12: objObjectId.InitializeFromValue("1.3.6.1.5.5.7.3.2");
  13: objObjectIds.Add(objObjectId);
  14: objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds);
  15: objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage);

Next, we will specify the subject of the certificate. As I mentioned earlier, a certificate can represent anything. So the subject will take the information of what is being identified y this certificate. There are some fields in subject:

  • CN: Common Name
  • C: Country (Must be 2 letter.)
  • S: State
  • L: Locality
  • O: Organization
  • OU: Organization Unit
  • E: Email

We can define one or more fields when request the certificate and it will combine in the format like this.

   1: [Field_Name_1] = [Field_Value_1], [Field_Name_2] = [Field_Value_2], [Field_Name_3] = [Field_Value_3]

For example this is a valid subject with the CN, C, S, L, O and OU defined.

   1: CN = UIX, OU = NAS, O = IGT, L = Reno, S = Nevada, C = US

To specify the subject in C# we also need to provide them into the same format, and set into the PKCS #10 object.

   1: var objDN = new CX500DistinguishedName();
   2: var subjectName = "CN = shaunxu.me, OU = ADCS, O = Blog, L = Beijng, S = Beijing, C = CN";
   3: objDN.Encode(subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE);
   4: objPkcs10.Subject = objDN;

Finally we initialize the CertEnroll COM object by passing the PKCS #10 in and invoke its CreateRequest method to generate the certificate request in base64 format.

   1: var objEnroll = new CX509Enrollment();
   2: objEnroll.InitializeFromRequest(objPkcs10);
   3: var strRequest = objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);

So the full code for certificate request generation would be like this.

   1: private string CreateCertRequestMessage()
   2: {
   3:     var objCSPs = new CCspInformations();
   4:     objCSPs.AddAvailableCsps();
   5:  
   6:     var objPrivateKey = new CX509PrivateKey();
   7:     objPrivateKey.Length = 2048;
   8:     objPrivateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE;
   9:     objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;
  10:     objPrivateKey.MachineContext = false;
  11:     objPrivateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG;
  12:     objPrivateKey.CspInformations = objCSPs;
  13:     objPrivateKey.Create();
  14:  
  15:     var objPkcs10 = new CX509CertificateRequestPkcs10();
  16:     objPkcs10.InitializeFromPrivateKey(
  17:         X509CertificateEnrollmentContext.ContextUser,
  18:         objPrivateKey,
  19:         string.Empty);
  20:  
  21:     var objExtensionKeyUsage = new CX509ExtensionKeyUsage();
  22:     objExtensionKeyUsage.InitializeEncode(
  23:         CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
  24:         CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_NON_REPUDIATION_KEY_USAGE |
  25:         CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE |
  26:         CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DATA_ENCIPHERMENT_KEY_USAGE);
  27:     objPkcs10.X509Extensions.Add((CX509Extension)objExtensionKeyUsage);
  28:  
  29:     var objObjectId = new CObjectId();
  30:     var objObjectIds = new CObjectIds();
  31:     var objX509ExtensionEnhancedKeyUsage = new CX509ExtensionEnhancedKeyUsage();
  32:     objObjectId.InitializeFromValue("1.3.6.1.5.5.7.3.2");
  33:     objObjectIds.Add(objObjectId);
  34:     objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds);
  35:     objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage);
  36:  
  37:     var objDN = new CX500DistinguishedName();
  38:     var subjectName = "CN = shaunxu.me, OU = ADCS, O = Blog, L = Beijng, S = Beijing, C = CN";
  39:     objDN.Encode(subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE);
  40:     objPkcs10.Subject = objDN;
  41:  
  42:     var objEnroll = new CX509Enrollment();
  43:     objEnroll.InitializeFromRequest(objPkcs10);
  44:     var strRequest = objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);
  45:     return strRequest;
  46: }

 

Send Certificate Request to CA

Send the certificate request message we had just generated to a CA would be easy. In fact we can save the message into a text file and copy to the CA server, request the certificate by using the CA manage portal. But if we are going to use C# then we will need to use CertCli COM to send the message, and verify the status by retrieving the disposition status and request ID.

First we will create the object of CertCli and invoke its Submit method by passing the certificate request message and CA address. The CA address should be in the format:

  • [CA_SERVER_IP]\[CA_NAME]
  • [CA_SERVER_NAME]\[CA_NAME]

The CA name can be found by logging on the CA server and navigate to the Active Directory Certificate Service node in Server Manager window. Right click the CA node and select Properties.

image

The code would be like this.

   1: private static int SendCertificateRequest(string message)
   2: {
   3:     var objCertRequest = new CCertRequest();
   4:     var iDisposition = objCertRequest.Submit(
   5:             CR_IN_BASE64 | CR_IN_FORMATANY,
   6:             message,
   7:             string.Empty,
   8:             @"192.168.56.101\pal-CPAL-CA");
   9: }

The return value of the Submit method indicates the status of the certificate request, normally it would be in the statuses below.

  • 0x03: Issued. This means the certificate had been issued by the CA that we can download and install to the local machine.
  • 0x05: Under submission. This means the request was in pending status, the certificate administrator need to issue it manually.
  • Failed due to some reason. We can use CCertRequest.GetDispositionMessage method to retrieve the failure reason.
   1: switch(iDisposition)
   2: {
   3:     case CR_DISP_ISSUED:
   4:         Console.WriteLine("The certificate had been issued.");
   5:         break;
   6:     case CR_DISP_UNDER_SUBMISSION:
   7:         Console.WriteLine("The certificate is still pending.");
   8:         break;
   9:     default:
  10:         Console.WriteLine("The submission failed: " + objCertRequest.GetDispositionMessage());
  11:         Console.WriteLine("Last status: " + objCertRequest.GetLastStatus().ToString());
  12:         break;
  13: }
  14: return objCertRequest.GetRequestId();

Download and Install Certificate

Once the certificate request had been sent, it will be processed by CA request handling module. By default, for standalone CA all certificate requests will be at pending status and wait for the administrator to issue manually. The administrator should go to the CA portal and select the Pending Requests node, right click on the item and click Issue. (The administrator can click Deny if he/she don’t want to send this certificate.)

image

Then go to the Issued Certificates node we can see the issued certificate available. The certificate couldn’t be downloaded and installed into the machine where it requested unless in this status.

image

Back to the source to implement the code to download and install the full certificate. First of all, we will utilize the CCertRequest.RetrievePending method to detect the status of our certificate request had sent. If it’s issued then we will download the response, which is in PKCS #7 format, to the local machine by using the method CCertRequest.GetCertificate.

   1: private static void DownloadAndInstallCert(int requestId)
   2: {
   3:     var objCertRequest = new CCertRequest();
   4:     var iDisposition = objCertRequest.RetrievePending(requestId, @"192.168.56.101\pal-CPAL-CA");
   5:  
   6:     if (iDisposition == CR_DISP_ISSUED)
   7:     {
   8:         var cert = objCertRequest.GetCertificate(CR_OUT_BASE64 | CR_OUT_CHAIN);
   9:     }
  10: }

Then initialize the CertEnroll object from context user certificate store, install the response that we had just retrieved.

   1: var objEnroll = new CX509Enrollment();
   2: objEnroll.Initialize(X509CertificateEnrollmentContext.ContextUser);
   3: objEnroll.InstallResponse(
   4:     InstallResponseRestrictionFlags.AllowUntrustedRoot,
   5:     cert,
   6:     EncodingType.XCN_CRYPT_STRING_BASE64,
   7:     null);
   8: Console.WriteLine("The certificate had been installed successfully.");

After it downloaded and installed the certificate we can check it’s in the current user certificate store. And from now on, since the certificate was in the store, we can use X509Store and X509Certificate2 class to export and view the attributes such as subject, thumbprint, etc..

image

The full code is listed below.

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using CERTENROLLLib;
   6: using CERTCLIENTLib;
   7:  
   8: namespace ShaunXu.ADCSviaCSharp
   9: {
  10:     class Program
  11:     {
  12:         private static string CreateCertRequestMessage()
  13:         {
  14:             var objCSPs = new CCspInformations();
  15:             objCSPs.AddAvailableCsps();
  16:  
  17:             var objPrivateKey = new CX509PrivateKey();
  18:             objPrivateKey.Length = 2048;
  19:             objPrivateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE;
  20:             objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;
  21:             objPrivateKey.MachineContext = false;
  22:             objPrivateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG;
  23:             objPrivateKey.CspInformations = objCSPs;
  24:             objPrivateKey.Create();
  25:  
  26:             var objPkcs10 = new CX509CertificateRequestPkcs10();
  27:             objPkcs10.InitializeFromPrivateKey(
  28:                 X509CertificateEnrollmentContext.ContextUser,
  29:                 objPrivateKey,
  30:                 string.Empty);
  31:  
  32:             var objExtensionKeyUsage = new CX509ExtensionKeyUsage();
  33:             objExtensionKeyUsage.InitializeEncode(
  34:                 CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
  35:                 CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_NON_REPUDIATION_KEY_USAGE |
  36:                 CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE |
  37:                 CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DATA_ENCIPHERMENT_KEY_USAGE);
  38:             objPkcs10.X509Extensions.Add((CX509Extension)objExtensionKeyUsage);
  39:  
  40:             var objObjectId = new CObjectId();
  41:             var objObjectIds = new CObjectIds();
  42:             var objX509ExtensionEnhancedKeyUsage = new CX509ExtensionEnhancedKeyUsage();
  43:             objObjectId.InitializeFromValue("1.3.6.1.5.5.7.3.2");
  44:             objObjectIds.Add(objObjectId);
  45:             objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds);
  46:             objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage);
  47:  
  48:             var objDN = new CX500DistinguishedName();
  49:             var subjectName = "CN = shaunxu.me, OU = ADCS, O = Blog, L = Beijng, S = Beijing, C = CN";
  50:             objDN.Encode(subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE);
  51:             objPkcs10.Subject = objDN;
  52:  
  53:             var objEnroll = new CX509Enrollment();
  54:             objEnroll.InitializeFromRequest(objPkcs10);
  55:             var strRequest = objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);
  56:             return strRequest;
  57:         }
  58:  
  59:         private const int CC_DEFAULTCONFIG = 0;
  60:         private const int CC_UIPICKCONFIG = 0x1;
  61:         private const int CR_IN_BASE64 = 0x1;
  62:         private const int CR_IN_FORMATANY = 0;
  63:         private const int CR_IN_PKCS10 = 0x100;
  64:         private const int CR_DISP_ISSUED = 0x3;
  65:         private const int CR_DISP_UNDER_SUBMISSION = 0x5;
  66:         private const int CR_OUT_BASE64 = 0x1;
  67:         private const int CR_OUT_CHAIN = 0x100;
  68:  
  69:         private static int SendCertificateRequest(string message)
  70:         {
  71:             var objCertRequest = new CCertRequest();
  72:             var iDisposition = objCertRequest.Submit(
  73:                     CR_IN_BASE64 | CR_IN_FORMATANY,
  74:                     message,
  75:                     string.Empty,
  76:                     @"192.168.56.101\pal-CPAL-CA");
  77:  
  78:             switch(iDisposition)
  79:             {
  80:                 case CR_DISP_ISSUED:
  81:                     Console.WriteLine("The certificate had been issued.");
  82:                     break;
  83:                 case CR_DISP_UNDER_SUBMISSION:
  84:                     Console.WriteLine("The certificate is still pending.");
  85:                     break;
  86:                 default:
  87:                     Console.WriteLine("The submission failed: " + objCertRequest.GetDispositionMessage());
  88:                     Console.WriteLine("Last status: " + objCertRequest.GetLastStatus().ToString());
  89:                     break;
  90:             }
  91:             return objCertRequest.GetRequestId();
  92:         }
  93:  
  94:         private static void DownloadAndInstallCert(int requestId)
  95:         {
  96:             var objCertRequest = new CCertRequest();
  97:             var iDisposition = objCertRequest.RetrievePending(requestId, @"192.168.56.101\pal-CPAL-CA");
  98:  
  99:             if (iDisposition == CR_DISP_ISSUED)
 100:             {
 101:                 var cert = objCertRequest.GetCertificate(CR_OUT_BASE64 | CR_OUT_CHAIN);
 102:                 var objEnroll = new CX509Enrollment();
 103:                 objEnroll.Initialize(X509CertificateEnrollmentContext.ContextUser);
 104:                 objEnroll.InstallResponse(
 105:                     InstallResponseRestrictionFlags.AllowUntrustedRoot,
 106:                     cert,
 107:                     EncodingType.XCN_CRYPT_STRING_BASE64,
 108:                     null);
 109:                 Console.WriteLine("The certificate had been installed successfully.");
 110:             }
 111:         }
 112:  
 113:         static void Main(string[] args)
 114:         {
 115:             Console.WriteLine("Request a new certificate? (y|n)");
 116:             if (Console.ReadLine() == "y")
 117:             {
 118:                 var request = CreateCertRequestMessage();
 119:                 var id = SendCertificateRequest(request);
 120:                 Console.WriteLine("Request ID: " + id.ToString());
 121:             }
 122:  
 123:             Console.WriteLine("Download & install certificate? (y|n)");
 124:             if (Console.ReadLine() == "y")
 125:             {
 126:                 Console.WriteLine("Request ID?");
 127:                 var id = int.Parse(Console.ReadLine());
 128:                 DownloadAndInstallCert(id);
 129:             }            
 130:         }
 131:     }
 132: }

 

Enterprise CA and Certificate Template

In the section above we discussed on how to use C# to communicate with AD CS, that is a standalone CA. A standalone CA has some limitation comparing with the enterprise CA. The biggest difference is that, the standalone CA cannot use the certificate templates.

When we implement the certificate request function, we specified everything the certificate needs. And for a CA there’s no way to define what kind of information can be set by request, what policy should the request follow. And there’s no way to define how long the certificate will be valid, which is the validity period, as well. All certificates issued by a standalone CA will have the same validity period, which is defined at the register in CA server. But if we are using enterprise CA, we can define vary rules and validity period in each template.

The enterprise templates are stored in the active directory, which means all CAs in the AD can select which templates they can use. This is a good way to control the certificate issuing permission.

You can verify if a CA is enterprise or not by opening the CA portal. If there’s a sub folder named Certificate Templates it means this is an enterprise CA.

image

Let’s create a template and specify some rules. Click the Certificate Templates node which under the Active Directory Certificate Service node and select a template named Computer. Right click the template and click Duplicate Template.

You can not create a brand new template. Instead you have to duplicate an existing template.

image

Select Windows Server 2008 Enterprise version on the popping up windows and specify a template name. In the template properties window we can see that it’s possible to define the validity period of it. All certificates that requested and issued on this template will have the same validity period.

image

And there are many items we can define as well. For example we can have the “Client Authenticate” in the application policies extension, which we had specified in code in previous sample.

image

And we can define what kind of value can be set to the subject in certificates. This provides a good way for certificate administrator to control the value of the certificates. For example, if the certificate is to represent a domain user, the subject must be a valid AD user. But in this case we will let the request supply the subject which means no control on CA side.

image

Once we created the template we also need to issue this template to this CA server, which means it can be received and issued by this CA. Right click the Certificate Templates node and select New > Certificate Template to Issue, and select the template we have just created.

image

Now the template is ready for use. Then we will change our code to send request to enterprise CA with template specified.

 

Send Request to Enterprise CA with Template

Send the certificate request to an enterprise CA would be very similar as what we did on a standalone CA. Previously when we generated the key pair we used an empty string on the template name parameter. So now for enterprise CA we will specify which template we are going to use.

   1: var objPkcs10 = new CX509CertificateRequestPkcs10();
   2: objPkcs10.InitializeFromPrivateKey(
   3:     X509CertificateEnrollmentContext.ContextUser,
   4:     objPrivateKey,
   5:     "ShaunXu");

It’s not allowed to request a certificate without template specified on an enterprise CA. This means we have to set a template. On the other hand, standalone CA does not allow the request related with a template.

Seems that we finished, but if we just execute it will throw an exception to us, said that the file exists when adding some extensions.

image

The exception message could be a little bit confusing. In fact this is because we defined something which had been defined in the certificate template. If we dig into the source code we can see that the exception occurred when we added the key usage extension.

image

And if we get back to the CA server and open the template we are using, we can find that the key usage had been defined in the template. This means in the code, or in the certificate request we should not specify it again.

image

Hence we need to comment the code for adding the key usage, also we need to comment the enhanced key usage part since it had been defined in the template, too. Because we let the request supply the subject name so here we can still specify the subject information in the request. The method for generating request message would be like this.

   1: private static string CreateCertRequestMessage()
   2: {
   3:     var objCSPs = new CCspInformations();
   4:     objCSPs.AddAvailableCsps();
   5:  
   6:     var objPrivateKey = new CX509PrivateKey();
   7:     objPrivateKey.Length = 2048;
   8:     objPrivateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE;
   9:     objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;
  10:     objPrivateKey.MachineContext = false;
  11:     objPrivateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG;
  12:     objPrivateKey.CspInformations = objCSPs;
  13:     objPrivateKey.Create();
  14:  
  15:     var objPkcs10 = new CX509CertificateRequestPkcs10();
  16:     objPkcs10.InitializeFromPrivateKey(
  17:         X509CertificateEnrollmentContext.ContextUser,
  18:         objPrivateKey,
  19:         "ShaunXu");
  20:  
  21:     //var objExtensionKeyUsage = new CX509ExtensionKeyUsage();
  22:     //objExtensionKeyUsage.InitializeEncode(
  23:     //    CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
  24:     //    CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_NON_REPUDIATION_KEY_USAGE |
  25:     //    CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE |
  26:     //    CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DATA_ENCIPHERMENT_KEY_USAGE);
  27:     //objPkcs10.X509Extensions.Add((CX509Extension)objExtensionKeyUsage);
  28:  
  29:     //var objObjectId = new CObjectId();
  30:     //var objObjectIds = new CObjectIds();
  31:     //var objX509ExtensionEnhancedKeyUsage = new CX509ExtensionEnhancedKeyUsage();
  32:     //objObjectId.InitializeFromValue("1.3.6.1.5.5.7.3.2");
  33:     //objObjectIds.Add(objObjectId);
  34:     //objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds);
  35:     //objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage);
  36:  
  37:     var objDN = new CX500DistinguishedName();
  38:     var subjectName = "CN = entprise.shaunxu.me, OU = ADCS, O = Blog, L = Beijng, S = Beijing, C = CN";
  39:     objDN.Encode(subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE);
  40:     objPkcs10.Subject = objDN;
  41:  
  42:     var objEnroll = new CX509Enrollment();
  43:     objEnroll.InitializeFromRequest(objPkcs10);
  44:     var strRequest = objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);
  45:     return strRequest;
  46: }

It works well this time and installed the certificate successfully. If we open the certificate store in MMC we can see the new one with the template displayed.

image

 

Certificate Renewal

A certificate must have a limited validity period. For example the certificate we had just request before is valid through 2012-01-13 07:21:48 to 20124-01-12 07:21:48.

image

When the certificate is going to be expired the operation system will send the renew request to the CA server automatically to attempt renew it. But we can ask to renew it by our code.

To send a certificate renewal message we must have this certificate installed in the certificate store. It could be in local machine or current user store. The first step is to find it by using the X509Store.Certificates.Find.

   1: private static int Renew()
   2: {
   3:     X509Certificate2 certificate = null;
   4:     X509Store store = new X509Store(StoreLocation.CurrentUser);
   5:     try
   6:     {
   7:         store.Open(OpenFlags.ReadWrite);
   8:         certificate = store.Certificates.Find(X509FindType.FindByThumbprint, "c1555218deed2c6dbe5101178617ef7628388a85", false)[0];
   9:     }
  10:     catch (Exception ex)
  11:     {
  12:         Console.WriteLine(ex.ToString());
  13:     }
  14:     finally
  15:     {
  16:         store.Close();
  17:     }
  18: }

The certificate renew request is in PKCS #7 format. So in the next step, we will create an object of PKCS #7 and initialize it from the certificate we had just found from the certificate store. When initializing we’d specify that this is a renew request in the parameter. We also need to specify that the new certificate will inherit the validity period and the key pair from the existing one.

   1: var objPkcs7 = new CX509CertificateRequestPkcs7();
   2: objPkcs7.InitializeFromCertificate(
   3:     X509CertificateEnrollmentContext.ContextUser,
   4:     true,
   5:     Convert.ToBase64String(certificate.RawData),
   6:     EncodingType.XCN_CRYPT_STRING_BASE64,
   7:     X509RequestInheritOptions.InheritPrivateKey & X509RequestInheritOptions.InheritValidityPeriodFlag);

Then the following code would be very similar with what we did to send the new request before. Using the CertEnroll to generate the request message and send it out by CertCli, and check the disposition status.

   1: var objEnroll = new CX509Enrollment();
   2: objEnroll.InitializeFromRequest(objPkcs7);
   3: var message = objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);
   4:  
   5: var objCertRequest = new CCertRequest();
   6: var iDisposition = objCertRequest.Submit(
   7:         CR_IN_BASE64 | CR_IN_FORMATANY,
   8:         message,
   9:         string.Empty,
  10:         @"192.168.56.101\pal-CPAL-CA");
  11:  
  12: switch (iDisposition)
  13: {
  14:     case CR_DISP_ISSUED:
  15:         Console.WriteLine("The certificate had been issued.");
  16:         break;
  17:     case CR_DISP_UNDER_SUBMISSION:
  18:         Console.WriteLine("The certificate is still pending.");
  19:         break;
  20:     default:
  21:         Console.WriteLine("The submission failed: " + objCertRequest.GetDispositionMessage());
  22:         Console.WriteLine("Last status: " + objCertRequest.GetLastStatus().ToString());
  23:         break;
  24: }
  25: return objCertRequest.GetRequestId();

When the request had been sent to the CA, based on the request handling policy it will be issued automatically or manually by the administrator. To download and install the renewed certificate would be the same like what we did before, so just use the method that download the new certificate should be fine.

The full code would be like this. Just note that I hard-coded my certificate thumbprint in the code.

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using CERTENROLLLib;
   6: using CERTCLIENTLib;
   7: using System.Security.Cryptography.X509Certificates;
   8:  
   9: namespace ShaunXu.ADCSviaCSharp
  10: {
  11:     class Program
  12:     {
  13:         private static string CreateCertRequestMessage()
  14:         {
  15:             var objCSPs = new CCspInformations();
  16:             objCSPs.AddAvailableCsps();
  17:  
  18:             var objPrivateKey = new CX509PrivateKey();
  19:             objPrivateKey.Length = 2048;
  20:             objPrivateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE;
  21:             objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;
  22:             objPrivateKey.MachineContext = false;
  23:             objPrivateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG;
  24:             objPrivateKey.CspInformations = objCSPs;
  25:             objPrivateKey.Create();
  26:  
  27:             var objPkcs10 = new CX509CertificateRequestPkcs10();
  28:             objPkcs10.InitializeFromPrivateKey(
  29:                 X509CertificateEnrollmentContext.ContextUser,
  30:                 objPrivateKey,
  31:                 "ShaunXu");
  32:  
  33:             //var objExtensionKeyUsage = new CX509ExtensionKeyUsage();
  34:             //objExtensionKeyUsage.InitializeEncode(
  35:             //    CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
  36:             //    CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_NON_REPUDIATION_KEY_USAGE |
  37:             //    CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE |
  38:             //    CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DATA_ENCIPHERMENT_KEY_USAGE);
  39:             //objPkcs10.X509Extensions.Add((CX509Extension)objExtensionKeyUsage);
  40:  
  41:             //var objObjectId = new CObjectId();
  42:             //var objObjectIds = new CObjectIds();
  43:             //var objX509ExtensionEnhancedKeyUsage = new CX509ExtensionEnhancedKeyUsage();
  44:             //objObjectId.InitializeFromValue("1.3.6.1.5.5.7.3.2");
  45:             //objObjectIds.Add(objObjectId);
  46:             //objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds);
  47:             //objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage);
  48:  
  49:             var objDN = new CX500DistinguishedName();
  50:             var subjectName = "CN = entprise.shaunxu.me, OU = ADCS, O = Blog, L = Beijng, S = Beijing, C = CN";
  51:             objDN.Encode(subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE);
  52:             objPkcs10.Subject = objDN;
  53:  
  54:             var objEnroll = new CX509Enrollment();
  55:             objEnroll.InitializeFromRequest(objPkcs10);
  56:             var strRequest = objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);
  57:             return strRequest;
  58:         }
  59:  
  60:         private const int CC_DEFAULTCONFIG = 0;
  61:         private const int CC_UIPICKCONFIG = 0x1;
  62:         private const int CR_IN_BASE64 = 0x1;
  63:         private const int CR_IN_FORMATANY = 0;
  64:         private const int CR_IN_PKCS10 = 0x100;
  65:         private const int CR_DISP_ISSUED = 0x3;
  66:         private const int CR_DISP_UNDER_SUBMISSION = 0x5;
  67:         private const int CR_OUT_BASE64 = 0x1;
  68:         private const int CR_OUT_CHAIN = 0x100;
  69:  
  70:         private static int SendCertificateRequest(string message)
  71:         {
  72:             var objCertRequest = new CCertRequest();
  73:             var iDisposition = objCertRequest.Submit(
  74:                     CR_IN_BASE64 | CR_IN_FORMATANY,
  75:                     message,
  76:                     string.Empty,
  77:                     @"192.168.56.101\pal-CPAL-CA");
  78:  
  79:             switch(iDisposition)
  80:             {
  81:                 case CR_DISP_ISSUED:
  82:                     Console.WriteLine("The certificate had been issued.");
  83:                     break;
  84:                 case CR_DISP_UNDER_SUBMISSION:
  85:                     Console.WriteLine("The certificate is still pending.");
  86:                     break;
  87:                 default:
  88:                     Console.WriteLine("The submission failed: " + objCertRequest.GetDispositionMessage());
  89:                     Console.WriteLine("Last status: " + objCertRequest.GetLastStatus().ToString());
  90:                     break;
  91:             }
  92:             return objCertRequest.GetRequestId();
  93:         }
  94:  
  95:         private static void DownloadAndInstallCert(int requestId)
  96:         {
  97:             var objCertRequest = new CCertRequest();
  98:             var iDisposition = objCertRequest.RetrievePending(requestId, @"192.168.56.101\pal-CPAL-CA");
  99:  
 100:             if (iDisposition == CR_DISP_ISSUED)
 101:             {
 102:                 var cert = objCertRequest.GetCertificate(CR_OUT_BASE64 | CR_OUT_CHAIN);
 103:                 var objEnroll = new CX509Enrollment();
 104:                 objEnroll.Initialize(X509CertificateEnrollmentContext.ContextUser);
 105:                 objEnroll.InstallResponse(
 106:                     InstallResponseRestrictionFlags.AllowUntrustedRoot,
 107:                     cert,
 108:                     EncodingType.XCN_CRYPT_STRING_BASE64,
 109:                     null);
 110:                 Console.WriteLine("The certificate had been installed successfully.");
 111:             }
 112:         }
 113:  
 114:         private static int Renew()
 115:         {
 116:             X509Certificate2 certificate = null;
 117:             X509Store store = new X509Store(StoreLocation.CurrentUser);
 118:             try
 119:             {
 120:                 store.Open(OpenFlags.ReadWrite);
 121:                 certificate = store.Certificates.Find(X509FindType.FindByThumbprint, "c1555218deed2c6dbe5101178617ef7628388a85", false)[0];
 122:             }
 123:             catch (Exception ex)
 124:             {
 125:                 Console.WriteLine(ex.ToString());
 126:             }
 127:             finally
 128:             {
 129:                 store.Close();
 130:             }
 131:  
 132:             var objPkcs7 = new CX509CertificateRequestPkcs7();
 133:             objPkcs7.InitializeFromCertificate(
 134:                 X509CertificateEnrollmentContext.ContextUser,
 135:                 true,
 136:                 Convert.ToBase64String(certificate.RawData),
 137:                 EncodingType.XCN_CRYPT_STRING_BASE64,
 138:                 X509RequestInheritOptions.InheritPrivateKey & X509RequestInheritOptions.InheritValidityPeriodFlag);
 139:  
 140:             var objEnroll = new CX509Enrollment();
 141:             objEnroll.InitializeFromRequest(objPkcs7);
 142:             var message = objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);
 143:  
 144:             var objCertRequest = new CCertRequest();
 145:             var iDisposition = objCertRequest.Submit(
 146:                     CR_IN_BASE64 | CR_IN_FORMATANY,
 147:                     message,
 148:                     string.Empty,
 149:                     @"192.168.56.101\pal-CPAL-CA");
 150:  
 151:             switch (iDisposition)
 152:             {
 153:                 case CR_DISP_ISSUED:
 154:                     Console.WriteLine("The certificate had been issued.");
 155:                     break;
 156:                 case CR_DISP_UNDER_SUBMISSION:
 157:                     Console.WriteLine("The certificate is still pending.");
 158:                     break;
 159:                 default:
 160:                     Console.WriteLine("The submission failed: " + objCertRequest.GetDispositionMessage());
 161:                     Console.WriteLine("Last status: " + objCertRequest.GetLastStatus().ToString());
 162:                     break;
 163:             }
 164:             return objCertRequest.GetRequestId();
 165:         }
 166:  
 167:         static void Main(string[] args)
 168:         {
 169:             Console.WriteLine("Request a new certificate? (y|n)");
 170:             if (Console.ReadLine() == "y")
 171:             {
 172:                 var request = CreateCertRequestMessage();
 173:                 var id = SendCertificateRequest(request);
 174:                 Console.WriteLine("Request ID: " + id.ToString());
 175:             }
 176:  
 177:             Console.WriteLine("Download & install certificate? (y|n)");
 178:             if (Console.ReadLine() == "y")
 179:             {
 180:                 Console.WriteLine("Request ID?");
 181:                 var id = int.Parse(Console.ReadLine());
 182:                 DownloadAndInstallCert(id);
 183:             }
 184:  
 185:             Console.WriteLine("Renew an existing certificate? (y|n)");
 186:             if (Console.ReadLine() == "y")
 187:             {
 188:                 var id = Renew();
 189:                 Console.WriteLine("Request ID: " + id.ToString());
 190:             }
 191:  
 192:             Console.WriteLine("Download & install renewed certificate? (y|n)");
 193:             if (Console.ReadLine() == "y")
 194:             {
 195:                 Console.WriteLine("Request ID?");
 196:                 var id = int.Parse(Console.ReadLine());
 197:                 DownloadAndInstallCert(id);
 198:             }
 199:         }
 200:     }
 201: }

After executed and back to the certificate store we can see the certificate renewed by the CA, which its validity period had been changed from 2012-01-13 07:21:48 - 20124-01-12 7:21:48 to 2012-01-13 07:52:36 - 20124-01-12 07:52:36. The old certificate had been archived by the operation system automatically.

image

 

Request Certificate Out of Domain

As I mentioned before, the sample code in this post should be executed in the same server of CA, or at least a server in the CA’s domain. This is the limitation when using CertCli and CertEnroll to communication with CA.

First of all, CA integrated with active directory. By default, only the authenticated user can request certificate. Secondly, if we are using enterprise CA, all templates are being stored in the AD. When the client request a new certificate with template specified, it will try to retrieve the template information from AD.

image

So before Windows Server 2008 R2 it would be very difficult to communicate to the CA from the client that out of the domain. This is why, in the beginning of this post I mentioned, that I’m working on a WCF web service working as a proxy to let the client (PC, laptop and mobile) connect and request certificates out side from the domain.

image

But if we have the Windows Server 2008 R2, it introduced a new component of AD CS which called Certificate Enrollment Web Services. Basically it includes two web services that wraps the LDAP invoke and DCOM invoke, so that the client can communicate with them through HTTPS with WS-Trust.

image

 

Summary

When I was beginning to work on this task I found there is very little information on the internet about how to communicate with the CA by C#, or even by code. I think this is because, CA is something related with IT Pro that more focused on how to install and configure. IT Pro doesn’t care about the code. Communicating from C# is more related with development but developer doesn’t care about the CA since it’s something about IT infrastructure. So this topic is in the middle of the two worlds - IT and development.

But I think when we move to the cloud computing, the enterprise application, most of them we need to migration the existing AD integrated architecture to certificate-based architecture, which need to replace the existing security, authentication, identification parts.

In this post I introduced a little bit background knowledge about CA, especially the AD CS. I demonstrated how to request, install and renew a certificate to a standalone and an enterprise CA by C# through COM. I also mentioned a little bit about the new Certificate Enrollment Web Service. Thanks to the great post and articles I  referred recently, this and this.

There are still some topics I didn’t cover. For example the online revocation list, SCEP, OCSP, etc.. We need them if we need to build a fully, robust, online certificate solution.

 

Hope this helps,

Shaun

All documents and related graphics, codes are provided "AS IS" without warranty of any kind.
Copyright © Shaun Ziyan Xu. This work is licensed under the Creative Commons License.

Comments

Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Tomo london on 1/13/2012 4:37 PM
Thanks for this Shaun - makes life a lot easier when someone explains it like this. Coming to certs as a newbie.
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Lilia Roum on 1/18/2012 5:38 AM
Forst of all it's a great posting.
I have a question:
- I create a csr from C# as you described;
- Uploaded a request to CA, downloaded the cert (PEM format);
- I installed a cert to amachine from C#, I am able to export it to PFX format from C#, but when I load this cert.PFX file into X509Certificate2 object it doesn't have Private Key.

My question is how to combine downloaded cert file with a private key from C#?

Thank you in advance.
Lilia
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Shaun on 1/18/2012 10:18 AM
@Lilia Roum
I'm not sure if you sent the certificate request to CA by C# or manually. If you follow my post it talked about how to combine the certificate response with local private key and generate the full certificate.

1: var objEnroll = new CX509Enrollment();
2: objEnroll.Initialize(X509CertificateEnrollmentContext.ContextUser);
3: objEnroll.InstallResponse(
4: InstallResponseRestrictionFlags.AllowUntrustedRoot,
5: cert,
6: EncodingType.XCN_CRYPT_STRING_BASE64,
7: null);
8: Console.WriteLine("The certificate had been installed successfully.");

But you must execute the code on the machine which sent the request as the private key had been saved in the system in a 'magic' folder.

After you issued the certificate on CA it is NOT the full certificate. You have to use the code above to install it on the local machine so that you will have the private key.
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by craps tables on 1/18/2012 8:38 PM
Nice crucial article.
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Americo Neto on 1/19/2012 1:36 AM
Thank you very much!
Excellent article!
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Lilia Roum on 2/10/2012 5:51 AM
Hi Shawn,
Everywhere it is specified that that CertEnroll.dll is introduced from Windows Vista and for servers from Windows 2008, it is not specified that it is from 2008 R2.
But when I run code for CSR creation from C# on Windows 2008 (NOT R2) it throws exception:

Unable to cast COM object of type 'System.__ComObject' to interface type 'CERTENROLLLib.CX509CertificateRequestPkcs10'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{728AB35B-217D-11DA-B2A4-000E7BBB2B09}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

Any idea?
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Shaun on 2/10/2012 10:41 AM
@Lilia

Sorry I didn't met this exception on my Win08R2 machine and cannot reproduce it. The CertEnroll was introduced from Vista and Win08, so it would be in Win7 and 08R2 as well.
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by albert on 10/19/2012 3:03 AM
Hello Shawn
Do you have any solution for Windows Server 2003?
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Shaun on 10/19/2012 1:55 PM
@albert
Sorry I didn't test on 2003. As far as I know in 2003 there's no CertEnroll and CertCli. You might need use XEnroll. But the API should be totally different.
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by ashish on 1/29/2013 7:26 PM
Hi Shaun, thanks for the post.. its quite interesting and useful...did you figure out how to use the CEP and CES components to issue certs for a nondomain member
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Shaun Xu on 1/30/2013 9:50 AM
@ashish, No I didn't have a chance to play with SCE and SCP. But you can use another ADCS feature named SCEP, which can be used to issue certificate for a non-domain member.
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by ashish on 1/31/2013 1:37 PM
hi Shaun, yesterday I tried to generate the cert but I are getting error when I add the cert template in the following function call..
objPkcs10.InitializeFromPrivateKey(
X509CertificateEnrollmentContext.ContextUser,
objPrivateKey,"User1")..
Error: "the submission failed. denied by policy module. the request does not contain template extension or the cerificatetemplate request attribute"
1) If I pass null string for template then it works, but I have deployed enterprise CA and also created the certificate using that template manually..
2)Also the csr is generated on the client side so how can the client use the CA cert template information at that instance..
3) IS there any function where we can pass the tempalate information when we request cert from CA and not in the CSR.
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by ashish on 1/31/2013 5:23 PM
sorry ... I have mixed two things errors.. when I execute the code I get the following error:

Provider could not perform the action since the context was acquired as silent.




Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by harshit jain on 2/7/2013 12:00 PM
Hi,

Can you pls tell that how we can install a third party certificate to all users in a particular domain in Active Directory?
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by sitaramen on 5/20/2013 12:28 PM
Thanks for the post....
Does CX509CertificateRequestPkcs10 work for windows vista?
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by sh on 6/3/2013 6:45 PM
i am getting the error as:
Could not load type 'CERTCLIENTLib.ICertRequest3' from assembly 'Interop.CERTCLIENTLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by sh on 6/3/2013 6:47 PM
build with windows7 and deployed in server 2008 R2

how to resolve this error

Could not load type 'CERTENROLLLib.IX509Enrollment2' from assembly 'Interop.CERTENROLLLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.


Unable to cast COM object of type 'System.__ComObject' to interface type 'CERTENROLLLib.CX509CertificateRequestPkcs10'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{728AB35B-217D-11DA-B2A4-000E7BBB2B09}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).


Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by sh on 6/3/2013 6:49 PM
how to install PKI (CA ) Server in windows 7
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Sahana on 8/23/2013 6:42 PM
Hi Shaun, thanks for the post.. Do you have the c# code for certificate creation using SCEP?
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Viji on 10/16/2013 1:21 PM
Excellent post.. helped me understand the whole cert generation flow.. Thanks a lot
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Raman on 12/18/2013 1:01 PM
thanks for such a nice article. but i am getting one problem.

when i generate a new certificate it is working fine it allow me to export certificate ... but as i renew the certificate it update the validity or extend the validity but it restrict me to export certificate.
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Tamil on 6/18/2014 12:45 AM
HOW TO GET DEFAULT TEMPLATE NAME FROM ACTIVE CERTIFICATE DIRECTORY USING C#?
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Priya on 6/18/2014 12:49 AM
HOW TO GET DEFAULT TEMPLATE NAME FROM ACTIVE CERTIFICATE DIRECTORY USING C#? When I try to get it, it says invalid host name. But while creating certificate i am using the same Host name.. Please help me to get Template name from Active certificate directory via c#?
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Tamil on 6/18/2014 1:03 AM
While submit certificate request, it throws the following error. Directory does not contain a certificate template extension or certificate template request attribute.

Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Shaun Xu on 6/18/2014 8:52 AM
@Tamil
First please verify you have enterprise CA configured. Standalone CA doesn't support template. But I don't think there's "default" template name. You have to specify which template you want when sending request.
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Tamil on 6/18/2014 12:41 PM
I have to get the list of template names from the active directory. I should display that template names in drop down. User can select any one of the template from the drop down. Dont know the how to get that?
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Shaun Xu on 6/18/2014 12:46 PM
@Tamil, Sorry I don't how to retrieve the template list.
Gravatar # How to remove expired certificates from Active directory. Not from stors via c#
Posted by Tamil on 8/6/2014 1:50 PM
I have to remove/delete expired certificates from Active directory c#. Not from stors. From my server I dont have permission to hit the store. But i should delete the unwanted certificate from AD CA.
Gravatar # re: Working with Active Directory Certificate Service via C#
Posted by Tamil on 8/11/2014 9:50 PM
Without install the certificate into AD server, how to generate pfx certificate.


Should skip the following steps:

var cert = objCertRequest.GetCertificate(CR_OUT_BASE64 | CR_OUT_CHAIN); 103: var objEnroll = new CX509Enrollment); objEnroll.Initialize(X509CertificateEnrollmentContext.ContextUser); objEnroll.InstallResponse( InstallResponseRestrictionFlags.AllowUntrustedRoot, cert, EncodingType.XCN_CRYPT_STRING_BASE64, null);
Post A Comment
Title:
Name:
Email:
Comment:
Verification: