Handling certificates for testing without storing them (and with private key!)


Just a reminder to myself and you…

I do most of my unit testing involving certificates using Bouncy Castle, which is amazing.

Sometimes I just want to keep my tests simpler and when I’m not working on very complex requirements I use regular self-signed certificates. I have a couple dummy ones and I like handling test certificates on its byte representation to avoid pulling them from the store.

What about those times when the private key is strictly required? (Such as this time when I was constructing SAML tokens). I keep forgetting that simple certificate byte raw data does not contain its private key! It is at the end, just the certificate.

I don’t use implicitly typed local variables (var) in these examples

so it is clear what is each type. Also note that the Dump( ) method is

an extension used by LINQPad. I’m aware of the unnecessary conversion to Base64 string but it is how I store these certificates in my tests.

Wrong Implementation

using(X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
    // Getting original certificate and its representation. Using RawData
    store.Open(OpenFlags.ReadOnly);
    X509Certificate2Collection foundCertificates = store.Certificates.Find(X509FindType.FindByThumbprint, "{ your lame-ass thumbprint XD LOL }", false);
    X509Certificate2 originalCertificate =  foundCertificates[0];
    string base64Representation =  Convert.ToBase64String(originalCertificate.RawData);

    // Creating certificate from this representation
    byte[] restoredCertificateBytes = Convert.FromBase64String(base64Representation);
    X509Certificate2 restoredCertificate = new X509Certificate2(restoredCertificateBytes);
    restoredCertificate.HasPrivateKey.Dump(); // Results in false.
}

So this results in the X509 certificate object not containing its private key. The certificate is just that, the certificate. To have a certificate and private key pair, you must use another format. The most common of course being the PKCS#12 format, best known as your ol’ pal the PFX file. You obtain this not through the RawData property but using the Export method, which also returns a

Correct implementation

using(X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
    // Getting original certificate and its representation. Using the Export method
    store.Open(OpenFlags.ReadOnly);
    X509Certificate2Collection foundCertificates = store.Certificates.Find(X509FindType.FindByThumbprint, "{ your lame-ass thumbprint XD LOL }", false);
    X509Certificate2 originalCertificate =  foundCertificates[0];
    string base64Representation =  Convert.ToBase64String(originalCertificate.Export(X509ContentType.Pfx, "Your (not necesarily for testing) strong password"));

    // Creating certificate from this representation
    byte[] restoredCertificateBytes = Convert.FromBase64String(base64Representation);
    X509Certificate2 restoredCertificate = new X509Certificate2(restoredCertificateBytes, "Your (not necesarily for testing) strong password");
    restoredCertificate.HasPrivateKey.Dump(); // Results in true!.
}

Happy coding, happy drinking.

Comments (0)

Skip to main content