2

I have a C# unit test project where I'd like to test some encryption extensions that I've written. I would like to use a dummy cert for testing, but I have the following restrictions:

  • The build and test runs are done remotely on a machine that I do not have access to. Thus, I cannot install the certificate on the build machine.
  • I cannot include secrets (such as a .pfx file) as part of the git repository or build, as doing so would violate security protocols. Thus, I cannot read the cert from a file included in the project.

Since I'll be doing encryption and decryption, I need to have the private key information. How can I create this cert programmatically within these restraints?

wlyles
  • 2,236
  • 1
  • 20
  • 38

1 Answers1

4

One way to do this is to create a certificate for testing, convert it to a base 64 string, and then read the certificate from this string in the code. This requires four steps:

1. Create the .cer and .pvk files

To do this, one can use the MakeCert.exe tool (note that this tool is deprecated, and Microsoft recommends that you use the New-SelfSignedCertificate PowerShell cmdlet. I haven't tried this, but presumably either method would work.). This is a .NET Framework tool, and is included as part of the Windows SDK. I happened to have the Windows 7 SDK installed on my machine, so for me this exe was located under C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\, but your results may vary. This tool does exactly what we want! From the documentation:

The Certificate Creation tool generates X.509 certificates for testing purposes only. It creates a public and private key pair for digital signatures and stores it in a certificate file. This tool also associates the key pair with a specified publisher's name and creates an X.509 certificate that binds a user-specified name to the public part of the key pair.

For the sake of this example, let's call the cert TestCert. To create the .cer and .pvk files, run the following command:

MakeCert.exe -sv TestCert.pvk -n "cn=Test Certificate" TestCert.cer -r

The -sv flag indicates the name of the .pvk file to create, the -n flag is the X.500-compliant name for our certificate (it's easiest to use the "cn=CertName" format like above), and the -r flag indicates that it will be self-signed. If you want to specify the beginning and end dates for your certificate, use the -b and -e flags, and format the dates as mm/dd/yyyy (by default, the certificate is valid from the day of creation until 2039). If your certificate will be used for encryption and decryption (like mine), you must specify the -sky Exchange and -pe flags. You'll be prompted to create a password for the certificate during the process.

2. Create the .pfx file

This can be done with the pvk2pfx.exe tool, which should be in the same place as MakeCert.exe. This tool converts a .pvk and .cer file into a .pfx file. To use this tool, run the following command:

pvk2pfx.exe -pvk TestCert.pvk -spc TestCert.cer -pfx TestCert.pfx

The -pvk flag is the file name of the .pvk file to use (created in step 1), the -spc flag is the file name of the .cer file to use (created in step 1), and the -pfx flag is the name for the .pfx file that will be created. During the process, you'll be prompted to input the password you created in step 1.

3. Get the base 64 string representation of the .pfx file

This is pretty straightforward, and can be done with the Get-Content Powershell cmdlet and the System.Convert.ToBase64String method. For this, open a Powershell window and run the following:

$content = Get-Content TestCert.pfx -Encoding Byte
[System.Convert]::ToBase64String($content) | Out-File "TestCert.txt"

Now we have the base 64 string for our .pfx file in TestCert.txt.

4. Create the certificate programmatically

Now we can create the certificate in the code like so:

namespace MyTests
{
    using System;
    using System.Security.Cryptography.X509Certificates;

    public class MyTests
    {
        // Copy and paste the string from TestCert.txt here.
        private const string CertText = "<text>";

        // Use the password you created in steps 1 and 2 here.
        private const string Password = "p4ssw0rd";

        // Create the certificate object.
        private readonly X509Certificate2 TestCert = new X509Certificate2(
            Convert.FromBase64String(MyTests.CertText),
            MyTests.Password);
    }
}
Timothy G.
  • 6,335
  • 7
  • 30
  • 46
wlyles
  • 2,236
  • 1
  • 20
  • 38