Condividi tramite


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.

Certificate.cs

Comments

  • Anonymous
    January 22, 2009
    The comment has been removed

  • Anonymous
    January 22, 2009
    Great code and it works wonderfully but is there a way to set the key length?

  • Anonymous
    January 22, 2009
    Being able to set the FriendlyName would be useful, too.

  • Anonymous
    February 06, 2009
    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.

  • Anonymous
    February 13, 2009
    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.

  • Anonymous
    April 11, 2009
    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

  • Anonymous
    April 11, 2009
    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

  • Anonymous
    July 19, 2009
    The comment has been removed

  • Anonymous
    February 19, 2010
    What is the string parameter x500  in CreateSelfSignCertificatePfx(..) used for? I get an exception if I set it to anything but an empty string. I can create a certificate (if x500 is an empty string) and I can also import it with certmgr, but the "friendly name", "issued by" and "issued to" are missing. Anyone knows how the certificate can be added to the certificate store from C# without using certmgr?

  • Anonymous
    February 21, 2010
    Ok. it is pretty simple to add a certificate to the certificate store:            X509Certificate2 certFile = new X509Certificate2(certificatePath);            certFile.FriendlyName = "My Certificate";            X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);                                                           // or StoreName.Root and/or StoreLocation.CurrentUser            store.Open(OpenFlags.ReadWrite); //OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);            store.Add(certFile);            store.Close();

  • Anonymous
    February 21, 2010
    Info on x500 parameter can be found here : http://msdn.microsoft.com/en-us/library/aa377160(VS.85).aspx

  • Anonymous
    February 23, 2010
    If you want to play with certificates on windowws, you should try using powershell: my posts on the topic: http://ig2600.blogspot.com/2009/10/do-your-certificate-management-in.html http://ig2600.blogspot.com/2010/02/better-certificate-management-in.html

  • Anonymous
    March 27, 2010
    Please someone better explain x500 parameter, with samples and description. PLEASEEE

  • Anonymous
    March 30, 2010
    I'm really not an expert, but here's what I understand about it. x500 is the certificate's "distinguished name", which can have a bunch of different sections. It includes things like the friendly name and the organization name. An example is CN="My Certificate"; C="USA" See the documentation for CertStrToName on MSDN.

  • Anonymous
    April 19, 2010
    This code just works! Awesome! One question though - I want to generate the certificate with subset of Intended Purposes. I thought that PROV_RSA_FULL manages it but was wrong. Does anyone know how to set intended purposes to client authentication only?

  • Anonymous
    October 19, 2010
    If you get 'the associated private key cannot be found.' or similar private key problems when storing the key using X509Store. Make sure you have certificate flag X509KeyStorageFlags.PersistKeySet set as per support.microsoft.com/.../950090 HTH This took me ages to work out.

  • Anonymous
    October 21, 2010
    The comment has been removed

  • Anonymous
    November 16, 2010
    " .. What is the string parameter x500  in CreateSelfSignCertificatePfx(..) used for? I get an exception if I set it to anything but an empty string.". I got this working if I set x500 to a string that starts with "CN=". Great work with the supplied code ..much appreciated.

  • Anonymous
    August 15, 2011
    You are mega cool great guy who helped to implement such functionality! Awesome, Wish you luck, you bring the light to the encryption in ,net!

  • Anonymous
    September 07, 2011
    Used your code in our project, Thank a ton! Cheers!!

  • Anonymous
    December 04, 2011
    The returned certificate does not have a key usage. How can I give it one?

  • Anonymous
    January 15, 2012
    Hello, thanks for the Code it works fine. But can you tell me how to add certificate feature that it can grant the Identity of a Remote Computer. I think it has to do with the "1.3.6.1.5.5.7.3.1" as extended key Property or Keyusage in SSL Certificate Details. But I don't know how and where to add this feature in your Code. thx much SquadWuschel

  • Anonymous
    April 03, 2012
    Doug this code is great, helped me out of a tight jam. I'm trying to re-create an SSL Certificate we have been generating previously with OpenSSL, and I've noticed that there is one small difference between your certificates and the ones generated by OpenSSL. I don't get any 'Subject Key Identifier', 'Authority Key Identifier' or 'Basic Constraints'. Any idea how to get this code to include those attributes? Cheers.

  • Anonymous
    August 12, 2013
    I want to store my private key on different location rather than into store..... Can any one tel me it is possible???  

  • Anonymous
    December 17, 2013
    how put the "issued by" and "issued to" ??

  • Anonymous
    December 25, 2013
    I'll try the code, create a certificate from code it's really obscure in .net

  • Anonymous
    January 23, 2014
    what willl be output after run this code ... plz rply fst its urgent .. ??

  • Anonymous
    May 16, 2014
    I tried using this with the sample for SslStream, but I get the error "System.NotSupportedException: The server mode SSL must use a certificate with the associated private key." Are you sure this code actually exports the private key?

  • Anonymous
    May 16, 2014
    Addendum: I guess it IS exported after all: Instantiating the certificate as X502Certificate2 instead in the server sample program allowed it to use its private key.

  • Anonymous
    September 22, 2014
    If you modify this code to install the generated certificate directly in a persistent store (e.g., the My store), beware of a pitfall. Although the finally block mostly just frees handles, it does one extra thing: after closing the key store handle, it then re-opens that key store with CRYPT_DELETEKEYSET, thus destroying the store. This is the right thing to do in the context of the example - the certificate itself isn't stored in any local cert store, so you don't need to keep hold of the key - the only persistent record of the key is in the PFX that this code exports. (And if you import the certificate from the PFX, that will have the side effect of importing the key too.) But if you modify the code to import the generated certificate into a local store, you must remove that code that deletes the key set! It's obvious with hindsight, but it took me an age to notice because I didn't initially realise that the finally block was doing anything more than freeing up open handles.

  • Anonymous
    March 16, 2015
    Hey, I am able to generate a certificate with your code but how do i set the Issued To and Issued By fields in the certificate. Currently it also gives me error "This CA Root certificate is not trusted." Appreciate your help. Thanks !

  • Anonymous
    December 28, 2015
    Using this code is nice, however I don't exactly know why I can't send It and then use It at client side. stackoverflow.com/.../34506470

  • Anonymous
    February 11, 2016
    A friendly name can be set on the certificate by including the following before the call to CertOpenStore: dataHandle = GCHandle.Alloc(friendlyName, GCHandleType.Pinned); string friendlyName = "Hello"; CryptDataBlob friendlyNameBlob = new CryptDataBlob(); friendlyNameBlob.DataLength = (friendlyName.Length + 1)*2; friendlyNameBlob.Data = dataHandle.AddrOfPinnedObject(); bool result = NativeMethods.CertSetCertificateContextProperty(  certContext,  11, // CERT_FRIENDLY_NAME_PROP_ID  0,  ref friendlyNameBlob); Check(result); dataHandle.Free(); Add the following definition for CryptDataBlob, as well: [StructLayout(LayoutKind.Sequential)] internal struct CryptDataBlob {  public int DataLength;  public IntPtr Data;  public CryptDataBlob(int dataLength, IntPtr data)  {    DataLength = dataLength;    Data = data;  } }