I have a code which generates key pair, generates CSR(PKCS#10) then sends to MSCA, accordingly MSCA issues certificates. This code works fine in Windows Server 2012, it takes few seconds to issue a certificate. However, in Windows Server 2016 or 2019 it takes about 100 seconds. I got logs and noticed that in two places it takes 49 seconds to process:
- objPkcs10.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, objPrivateKey, null);
- string base64p10 = objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);
How to optimize this code or should I configure somewhere to process it faster like in Windows Server 2012 ?
CODE:
// Create all the objects that will be required
IX509CertificateRequestPkcs10 objPkcs10 = new CX509CertificateRequestPkcs10Class();
IX509PrivateKey objPrivateKey = new CX509PrivateKeyClass();
CX500DistinguishedName objDN = new CX500DistinguishedNameClass();
CObjectId objECC = new CObjectIdClass();
CObjectId objHASH = new CObjectId();
IX509Enrollment2 objEnroll = new CX509EnrollmentClass();
IX509ExtensionKeyUsage objExtensionKeyUsage = new CX509ExtensionKeyUsageClass();
IX509ExtensionEnhancedKeyUsage objX509ExtensionEnhancedKeyUsage = new CX509ExtensionEnhancedKeyUsageClass();
CObjectIds objObjectIds = new CObjectIdsClass();
CObjectId objObjectId = new CObjectIdClass();
// Initialize the object ID class for the ECC algorithm
objECC.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_PUBKEY_ALG_OID_GROUP_ID, ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlags.AlgorithmFlagsNone, "TEST_ALG");
// Initialize the object ID class for the hashing algorithm
objHASH.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID, ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlags.AlgorithmFlagsNone, "TEST_ALG_2");
// Provide provider name and the object id to the private key object
objPrivateKey.ProviderName = "Test Software Key Storage Provider";
objPrivateKey.Algorithm = objECC;
objPrivateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE;
objPrivateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;
objPrivateKey.ContainerName = privContName;
objPrivateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_EXPORT_FLAG;
// Create the actual key pair
objPrivateKey.Create();
// Encode the name in using the Distinguished Name object
objDN.Encode(_SOAPRequest.subject, X500NameFlags.XCN_CERT_NAME_STR_NONE);
// 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, null);
// The newly created certificate request object will contain some default extensions.
// Suppress these defaults by setting the SuppressDefaults flag
objPkcs10.SuppressDefaults = true;
objPkcs10.HashAlgorithm = objHASH;
// Key Usage Extension
objExtensionKeyUsage.InitializeEncode(
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_DIGITAL_SIGNATURE_KEY_USAGE |
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_NON_REPUDIATION_KEY_USAGE |
CERTENROLLLib.X509KeyUsageFlags.XCN_CERT_KEY_ENCIPHERMENT_KEY_USAGE |
CERTENROLLLib.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.4"); // OID for Client Authentication usage
objObjectIds.Add(objObjectId);
objX509ExtensionEnhancedKeyUsage.InitializeEncode(objObjectIds);
objPkcs10.X509Extensions.Add((CX509Extension)objX509ExtensionEnhancedKeyUsage);
// SMIME Capabilities - add all supported by the default RSA cryptographic provider
CSmimeCapabilitiesClass smimes = new CSmimeCapabilitiesClass();
smimes.AddAvailableSmimeCapabilities(true);
CX509ExtensionSmimeCapabilitiesClass smimeExt = new CX509ExtensionSmimeCapabilitiesClass();
smimeExt.InitializeEncode(smimes);
objPkcs10.X509Extensions.Add((CX509Extension)smimeExt);
// Assing the subject name by using the Distinguished Name object initialized above
objPkcs10.Subject = objDN;
objEnroll.InitializeFromRequest(objPkcs10);
string base64p10 = objEnroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64);
///////////////////////////SEND REQUEST/////////////////////////////
//Create all the objects that will be required
int iDisposition;
string strCAConfig;
string strDisposition;
CCertConfig objCertConfig = new CCertConfigClass();
CCertRequest objCertRequest = new CCertRequestClass();
// Get CA config from UI
strCAConfig = @"localhost\test-CA";
// Submit the request
iDisposition = objCertRequest.Submit(CR_IN_BASE64 | CR_IN_FORMATANY, base64p10, null, strCAConfig);
// Check the submission status
if (CR_DISP_ISSUED != iDisposition) // Not enrolled
{
strDisposition = objCertRequest.GetDispositionMessage();
if (CR_DISP_UNDER_SUBMISSION == iDisposition) // Pending
{
return "The submission is pending: " + strDisposition;
}
else // Failed
{
return "error";
}
}
// Get the certificate
strCert = objCertRequest.GetCertificate(CR_OUT_BASE64);