Search code examples
c#.netsslx509certificate

Associate a private key with the X509Certificate2 class in .net


I'm working on some code that creates a X509certificate and a public/private key pair. The public key is added to the certificate and it is sent to an CA which signs it.

The returned certificate is then accessed through the System.Security.Cryptography.X509Certificates.X509Certificate2 class. Now I want to use this certificate to initiate a secure connection with other clients. Therefore I use the SslStream class. To start the SSL Handshake I use this method:

server.AssociatedSslStream.AuthenticateAsServer(
                        MyCertificate,                      // Client Certificate
                        true,                               // Require Certificate from connecting Peer
                        SslProtocols.Tls,                   // Use TLS 1.0
                        false                               // check Certificate revocation
                    );

This method requires that the private key is associated with the certificate. Of course the certificate returned by the CA does not contain a private key. But it is stored as .key file on the harddrive. The X509Certificate2 class has a property called PrivateKey which I guess will associate a private key with the certificate, but I can't find a way to set this property.

Is there any way I can associate the private key with the .net X509 class?


Solution

  • For everyone else with the same problem, I found a neat little piece of code that let's you do exactly that:

    http://www.codeproject.com/Articles/162194/Certificates-to-DB-and-Back

    byte[] certBuffer = Helpers.GetBytesFromPEM(publicCert, PemStringType.Certificate);
    byte[] keyBuffer  = Helpers.GetBytesFromPEM(privateKey, PemStringType.RsaPrivateKey);
    
    X509Certificate2 certificate = new X509Certificate2(certBuffer, password);
    
    RSACryptoServiceProvider prov = Crypto.DecodeRsaPrivateKey(keyBuffer);
    certificate.PrivateKey = prov;
    

    EDIT: The code for the Helper method (which otherwise requires a codeproject login) is as follows:

    public static byte[] GetBytesFromPEM(string pemString, PemStringType type)
    {
        string header; string footer;
        switch (type)
        {
            case PemStringType.Certificate:
                header = "-----BEGIN CERTIFICATE-----";
                footer = "-----END CERTIFICATE-----";
                break;
            case PemStringType.RsaPrivateKey:
                header = "-----BEGIN RSA PRIVATE KEY-----";
                footer = "-----END RSA PRIVATE KEY-----";
                break;
            default:
                return null;
        }
    
        int start = pemString.IndexOf(header) + header.Length;
        int end = pemString.IndexOf(footer, start) - start;
        return Convert.FromBase64String(pemString.Substring(start, end));
    }
    

    Update

    As of .NET 5 you can simply use CreateFromPem(ReadOnlySpan, ReadOnlySpan):

    Creates a new X509 certificate from the contents of an RFC 7468 PEM-encoded certificate and private key.

    example:

    X509Certificate2 cert = X509Certificate2.CreateFromPem(
                    certPem, // The text of the PEM-encoded X509 certificate.
                    keyPem // The text of the PEM-encoded private key.
                );
    
    

    Or if you have a string with both the cert and its private key, you can pass it in for both the cert arg and the key arg:

    X509Certificate2 cert = X509Certificate2.CreateFromPem(
                    certPem, // The text of the PEM-encoded X509 certificate.
                    keyPem // The text of the PEM-encoded private key.
                );