Where are we going, and what's with the handbasket?

Creating a self-signed certificate in C#

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.

Published Tuesday, November 25, 2008 4:19 PM by dcook
Attachment(s): Certificate.cs

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

mohit said:

I have been through you code. Can you please give comment to what and why for the above bullet points and also when i created certificate using the given class i get following error:

Certificate status

This CA Root certificate is not trusted because it is not in the Trusted Root Certification Authorities store.

But being a selfsigned certificate the status should be ok. The same way when we create a certificate with IIS.

Please help

Thanks

mohit

mohit_raghav at hotmail dot com

January 22, 2009 3:37 PM
 

jclary said:

Great code and it works wonderfully but is there a way to set the key length?

January 22, 2009 6:30 PM
 

jclary said:

Being able to set the FriendlyName would be useful, too.

January 22, 2009 6:38 PM
 

jclary said:

I found a solution to setting the key length.  The third parameter to CryptKeyGen(), flags, needs to be bitwise OR'd (|) with 2048<<16 (0x080000000) to get a 2048bit key -- you end up passing in 0x08000001.

Check(NativeMethods.CryptGenKey(

                   providerContext,

                   1, // AT_KEYEXCHANGE

                   1 | (2048<<16), // CRYPT_EXPORTABLE | 2048bit

                   out cryptKey));

I still haven't found a way to add the FriendlyName parameter.

February 7, 2009 4:10 AM
 

sszelei said:

How Timely,  I was looking for this several weeks ago and had all but given up and started writing my own with much hardship.  Just happened to run accross this when looking up function calls.  This was great! Thank you! Thank You! Thank You! Tou saved me some gray hairs.

February 13, 2009 12:49 PM
 

kevindelafield said:

Did you get this to work?

When I use this routine to create a certficiate and try to store it in the cert store for My/CurrentUser,

and then extract the filename with 'FindPrivateKey' (from WCF examples), I get an error that the private key file is missing.

Are you sure this generates a private key?

Thanks,

Kevin

April 11, 2009 7:42 PM
 

kevindelafield said:

Also,

When I use the Certificates MMC plugin to view the certificate,

and I double click it, it says that there is a private key.

However, when I try to export it, it says the associated private key cannot be found.

Any ideas?

Thanks,

Kevin

April 11, 2009 7:51 PM
 

Jeremy Holovacs said:

For the FriendlyName and Description (and other useful information) it looks like these are extended properties added with CertSetCertificateContextProperty (CERT_DESCRIPTION_PROP_ID = 13, CERT_FRIENDLY_NAME_PROP_ID = 11) etc... I haven't figured out how to create the pointer context for the CRYPT_DATA_BLOB structure (trying to convert this in my head to VB.NET and this is well outside my comfort zone) but I think this'll work.

July 20, 2009 1:05 AM

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required
Submit

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker