For a personal project involving SSL, I wanted to create some certificates that could be used to authenticate the client and server to each other. Nothing fancy - self-signed is perfectly fine in this case since the client would have an actual copy of the server cert to use when validating the server, and having the cert on the filesystem is secure enough for the task. In any case, I was disappointed to find out that even with all of the other crypto and certificate support, .NET lacks support for this. I was also disappointed by how difficult it was to figure out how to do this.

CertCreateSelfSignCertificate sounds promising, but it ends up not being quite enough. It turns out that you have to do the following (as simple as I know how to make it, anyway):

  1. CryptAcquireContext(out providerContext, randomContainerName, null, PROV_RSA_FULL, CRYPT_NEWKEYSET)
  2. CryptGenKey(providerContext, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, out cryptKey)
  3. CertStrToName(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, name, CERT_X500_NAME_STR, 0, dataBuffer, ref dataLength, 0)
  4. cert = CreateSelfSignCertificate(providerContext, blob(dataBuffer, dataLength), 0, KeyProviderInfo(randomContainerName, PROV_RSA_FULL, AT_KEYEXCHANGE), 0, startTime, endTime, 0)
  5. certificateStore = CertOpenStore("Memory", 0, 0, CERT_STORE_CREATE_NEW_FLAG, 0)
  6. CertAddCertificateContextToStore(certificateStore, cert, CERT_STORE_ADD_NEW, out storeCert)
  7. CertSetCertificateContextProperty(storeCert, CERT_KEY_PROV_INFO_PROP_ID, 0, KeyProviderInfo(randomContainerName, PROV_RSA_FULL, AT_KEYEXCHANGE))
  8. PFXExportCertStoreEx(certificateStore, pfxBlob, password, 0, EXPORT_PRIVATE_KEYS)
  9. Free everything.

In case anybody is interested, source code is attached and is free for use by anybody as long as you don't hold me or Microsoft liable for it -- I have no idea whether this is actually the right or best way to do this. Give it the X500 distinguished name, validity start and end dates, and an optional password for encrypting the key data, and it will give you the PFX file data. Let me know if you find any bugs or have any suggestions.