Generating a CSR from a CngKey or X509Certificate(2) instance

1.7k views Asked by At

I need to generate the certificate signing request file, for submission to a third-party authority.

I am clearly missing a piece of knowledge that is preventing me making the cognitive leap from the CngKey and/or X509Certificate2 objects that I have successfully created, and the Base64-encoded ASN.1/DER certificate signing request.

All the searches I have done for c# generate csr (and equivalents) tell me how to create a self-signed certificate. However, I don't think this is exactly what I need (or is it?).

The CngKey object is created with the private key generated by a hardware security module, thusly:

var parameters = new CngKeyCreationParameters
{
    Provider = "The HSM's ECDSA Provider",
    ExportPolicy = CngExportPolicies.AllowArchiving,
    KeyCreationOptions = CngKeyCreationOptions.None,
    KeyUsage = CngKeyUsages.Signing,
    UIPolicy = new CngUIPolicy(CngUIProtectionLevels.ForceHighProtection)
};
var key = CngKey.Create(CngAlgorithm.ECDsaP256, keyName, parameters);

Using the System.Cryptography extensions from http://clrsecurity.codeplex.com/, I can build a self-signed certificate:

var dn = new X500DistinguishedName("CN=MyCompany;OU=MyOrgUnit;OID.2.25.4.45=MyUniqueID");

var certParams = new X509CertificateCreationParameters(dn)
{
    SignatureAlgorithm = X509CertificateSignatureAlgorithm.ECDsaSha256,
};

var cert = key.CreateSelfSignedCertificate(certParams);

I can even store it to the Certificate Enrollment Requests store:

var store = new X509Store("REQUEST", StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();

However, from there I am stuck.

I've tried Convert.ToBase64String(cert.Export(X509ContentType.Cert)) (and other values of X509ContentType) but these don't appear to be valid (my smoke tests with openssl req throw a bunch of ASN.1 errors).

Having got this far, I'd prefer not to have to rewrite with a third-party library like BouncyCastle or OpenSSL.NET, partly becuase of the lack of documentation, partly because of the interaction with the HSM. Similarly, I can't export the private key away from the HSM (because, what would be the point of the HSM if I could?).

I would just use the regular Windows Certificates MMC, if I could, but I need to provide a ASN.1 BIT STRING as part of the subject (I've excluded generation of this from the above, for brevity), and I can't seem to do that with the standard Create Custom Request dialog.

However, that dialog can create the desired CSR file format, so the functionality must exist somewhere, even if it is buried in the Win32 API.

So it's:

  1. Generate private key
  2. Generate certificate from key
  3. Store the certificate
  4. ???
  5. Send CSR to third-party
  6. Profit.

So what exactly is it that I am missing...?

1

There are 1 answers

0
Peter Dennis Bartok On

A CSR is a Certificate Signing Request. It contains the public key and whatever meta-data you would like your certificate to contain. The third-party (usually a Certificate Authority aka CA) then creates the certificate from that and provides the certificate to you. So your order is wrong.

  1. Generate the private key
  2. Generate the CSR
  3. Send CSR to third-party
  4. Receive Certificate from third-party

A CSR is typically provided in PKCS#10 format to the third-party/CA. Creating the ASN.1/DER for a CSR requires a lot of diligence if you want to really write that part yourself. RFC2986 provides you with the exact specification for creating a CSR.

Since your scenario involves a HSM, and the CSR must be signed, you will have to use your Cng provider to get the signature to include in the CSR.

There are no APIs in .Net (or Win32 crypto/bcrypt) that will allow you to "simply call a function" and get the CSR generated for you.

Also, note that a CSR contains what you are requesting to be in your certificate. The CA can chose to alter or skip any of the information you provide for inclusion in the certificate.