0

I'm trying to create an X.509 certificate file that will work with the following code as found in the smtp4dev project:

var certificate = new X509Certificate(Settings.Default.SSLCertificatePath);
var clientCertificateRequired = false;
var protocols = SslProtocols.Ssl2 | SslProtocols.Ssl3 | SslProtocols.Tls;
var checkRevocation = false;

var stream = new SslStream(stream);
stream.AuthenticateAsServer(certificate, clientCertificateRequired, protocols, checkRevocation);

Settings.Default.SSLCertificatePath points to a .cer file.

Whatever I try, this fails with a System.NotSupportedException with the message:

The server mode SSL must use a certificate with the associated private key.

I have created an X509 certificate using the New-SelfSignedCertificate PowerShell CommandLet in an elevated PowerShell session:

New-SelfSignedCertificate `
    -DnsName "localhost" `
    -CertStoreLocation "cert:\CurrentUser\My" `
    -FriendlyName "smtp4dev" `
    -TextExtension "2.5.29.37={text}1.3.6.1.5.5.7.3.1" `
    -KeyUsage DigitalSignature,KeyEncipherment,DataEncipherment `
    -Provider "Microsoft RSA SChannel Cryptographic Provider"

I added these options to mirror as closely as possible a certificate that is already stored on my machine, and that was generated by the IIS Express installer. Because that particular certificate does work, although I don't know why.

Both certificates are located in the Local Computer > Personal certificate store. For both certificates the Certificate Manager mentions that "You have a private key that corresponds to this certificate".

When I compare the properties of these 2 certificates, the smtp4dev certificate differs from the IIS Express Development Certificate in the following 3 ways (besides the Thumbprint and Serial number):

  • Key Usage is marked as a critical extension
  • Subject Alternative Name extension with value DNS Name=localhost
  • Subject Key Identifier extension with value e7 12 f5 ...

I exported both certificates to my desktop without the private key as "Base-64 encoded x.509 (.CER)" files using the Certificate Manager. (Including the private key would limit the export format to PKCS #12 (.PFX) which is not supported by smtp4dev.)

After exporting the certificates this way, when I double-click them, neither shows the message about having the private key.

Only the IIS Express Development Certificate works in the code shown at the beginning of this question. The smtp4dev one does not.

Other things I tried:

  1. I often come across tutorials that use makecert, but I don't have that program.

  2. I have tried using a self-signed certificate generated by openssl and only setting the Common Name to localhost but I get the same exception ("The server mode SSL must use a certificate with the associated private key").

  3. Changing the code to use X509Certificate2 is not an option, as it would be time-consuming to get another release of smtp4dev with the patch applied, since the project seems not to be actively developed.

  4. Trying a different certificate file format (such as PFX) is actually possible, as long as I rename the file to .cer I can trick smtp4dev into using the certificate. This allows me to include the private key in the certificate. It works with an export of the IIS Express Development Certificate, but it doesn't work with an export of the smtp4dev certificate.

Michiel van Oosterhout
  • 22,839
  • 15
  • 90
  • 132
  • can you add the certificate details using certutil -v -store my ? Also, **.pfx/.p12** are the extensions that support storage of Private key. I expect the above exception to be thrown if I am using a .cer certificate as it does not have a private key. – Kaushal Kumar Panday Jul 24 '17 at 13:39
  • @KaushalKumarPanday It works with the "IIS Express Development Certificate" which I also export as a .cer file. So that can't be the problem. – Michiel van Oosterhout Jul 25 '17 at 07:23
  • 1
    Thats interesting. So if the private key is not coming into play, then you need to look at the crypto providers. – Kaushal Kumar Panday Jul 25 '17 at 11:26

1 Answers1

2

Only one thing comes to mind: key provider compatibility. It appears that SslStream do not support CNG KSP (Key Storage Provider) which is default key provider type for New-SelfSignedCertificate` cmdlet.

I would suggest to explicitly specify any of legacy providers in the PowerShell call:

New-SelfSignedCertificate `
    -DnsName "localhost" `
    -CertStoreLocation "cert:\LocalMachine\My" `
    -FriendlyName "smtp4dev" `
    -TextExtension "2.5.29.37={text}1.3.6.1.5.5.7.3.1" `
    -KeyUsage DigitalSignature,KeyEncipherment,DataEncipherment `
    -Provider "Microsoft RSA SChannel Cryptographic Provider"
Crypt32
  • 12,850
  • 2
  • 41
  • 70
  • Thanks for the suggestion, I had not considered that at all! I created a new certificate using the provider option you specified, and exported it to a .cer file. But unfortunately I get the same exception. I'll sty a few other providers. Is there any way to find out the provider used to generate an existing certificate in the certificate store? – Michiel van Oosterhout Jul 25 '17 at 07:14
  • I tried all providers listed [here](https://technet.microsoft.com/en-us/itpro/powershell/windows/pkiclient/new-selfsignedcertificate) but none of them create a certificate that works after exporting it to a .cer file. – Michiel van Oosterhout Jul 25 '17 at 07:30
  • .cer? .cer does not include private key, hence the error. Instead, you should open the certificate from certificate store (via X509Store class). – Crypt32 Jul 25 '17 at 07:38
  • .cer works just fine when I use the "IIS Express Development Certificate". I edited my question to add what I tried in terms of .cer vs .pfx. It all comes down to one certificate works, the other does not. I'd like to understand the difference between these certificates that makes one of them work and the other not. – Michiel van Oosterhout Jul 25 '17 at 09:13
  • This actually does work. The reason it did not work the first time I tried, is because private keys for certificates stored in the LocalMachine store need their permissions changed so that programs ran by regular (non-Administrator) users can access them. I switched the command to generate the certificate in my question to use the CurrentUser store, so that permissions are no longer at play. In that context, your answer is absolutely the correct one. Thanks! – Michiel van Oosterhout Jul 25 '17 at 09:41