How to request an smartcard logon cert programmatically (C#)
Hi all,
The other day I created this C# sample which shows how to request an smartcard logon cert to a CA. It is based on this other sample: How to create a certificate request with CertEnroll and .NET (C#).
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();
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();
CX509ExtensionTemplateName objExtensionTemplate = new CX509ExtensionTemplateName();
string strRequest;
try
{
requestText.Text = "";
// Get all available CSPs
objCSPs.AddAvailableCsps();
// Provide key info
objPrivateKey.ContainerName = "Alex";
objPrivateKey.ProviderName = "eToken Base Cryptographic Provider";
objPrivateKey.ProviderType = X509ProviderType.XCN_PROV_RSA_FULL;
objPrivateKey.Length = 1024;
objPrivateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE;
objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;
objPrivateKey.MachineContext = false;
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.4.1.311.20.2.2"); // OID for Smartcard logon
objObjectIds.Add(objObjectId);
objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds);
objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage);
// Template Extension
objExtensionTemplate.InitializeEncode("SmartcardLogon");
objPkcs10.X509Extensions.Add((CX509Extension)objExtensionTemplate);
// 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.
Regards,
Alex (Alejandro Campos Magencio)
Comments
- Anonymous
November 12, 2010
Hi Alex,I have a customer who's using my implementation of your code (you remember, I'm sure). During the process, my program receives an error 0x80004001 when executing the call CspInformations::AddAvailableCsps. Do you have any idea what this means?Thanks in advance. If you like you can mail me at robert.collins@datev.deBest Regards! - Anonymous
April 22, 2011
I am trying to use your code, but instead of calling the enterprise CA I am making a call to a standalone CA. So basically I am not performing the following// Template Extension objExtensionTemplate.InitializeEncode("SmartcardLogon"); objPkcs10.X509Extensions.Add((CX509Extension)objExtensionTemplate);Everything seems fine and am able to issue the certificate.The problem I run into is that if I try to issue another cert, I get an error that the card is being used by another process, when I try to create a privateKey by calling thisobjPrivateKey.Create();The only way around it is to manually delete the old certificate adn the associated keys.Any idea why that would be happening? - Anonymous
April 22, 2011
duh, i wasnt changing the container name :)