28

I'm using the SSLStream example from msdn here. The client code "seems" to work fine, as I can connect to google and it at least gets past authentication, but the server doesn't.

From the comments from the msdn page, I used the procedure on this page to generate my own private key, but it just doesn't work. I get an exception of System.NotSupportedException: The server mode SSL must use a certificate with the associated private key. So I'm pretty sure whatever I'm doing is wrong.

So my question is simple: how do I get/generate keys that will work for my own little example program from msdn? It can be self-signed, whatever, but I'm too new to SSL to even know what exactly I need. All I want to do is to run the example as-given, except for specifying my own certificates for my local server. And it'd be great to know what I'd have to install on my 2nd machine if I just want to communicate between the two of them too (so it's not a 100% localhost example).

Personally I see this as a flaw in the example document. It should say "to run this, you need to do A, B, C, etc," but it doesn't.

Kevin Anderson
  • 6,850
  • 4
  • 32
  • 54
  • Do you able to read client certificate??? I also use this msdn tutorial. I want to read client certificate? How is it possible? – Jaimesh May 26 '16 at 09:55

3 Answers3

54

You can get the example to work even with self-signed certificates. I've extracted the commands from the makecert tutorial that you're using with minor modifications:

makecert -sv RootCATest.pvk -r -n "CN=FakeServerName" RootCATest.cer
makecert -ic RootCATest.cer -iv RootCATest.pvk -n "CN=FakeServerName" -sv  TempCert.pvk -pe -sky exchange TempCert.cer
cert2spc TempCert.cer TempCert.spc
pvkimprt -pfx TempCert.spc TempCert.pvk

makecert and cert2psc can be found in your Microsoft SDKs\Window\v7.0A\Bin folder. The pvkImport.exe installer can be downloaded here (Provided by @Jospeph and VirusTotal verified). This used to be downloadable from the Microsoft Site, but they have since taken it down. Alternatively, @Dweeberly pointed us to a new Microsoft-provided replacement, pvk2pfx.

For this next step make sure that you select to EXPORT the private key when the dialog from pvkimprt comes up:

pvkimprt -pfx TempCert.spc TempCert.pvk

enter image description here

pvkimprt will prompt you for a password when you elect to include the private key. You will need to provide this password later when you import the generated .pfx file into the personal store of your server machine

enter image description here

Next, import RootCATest.cer into your Computer store's Trusted Root Certification Authorities (on both the server and client). Notice that the certificate is issued to FakeServerName. This must match the server name that the SslTcpClient expects: sslStream.AuthenticateAsClient(serverName), where serverName is the value of the second argument passed to SslTcpClient.exe.

When your client connects, the server presents a certificate that tells the client "I'm FakeServerName". The client will accept this claim if the client machine trusts the CA that issued the certificate, which is achieved by importing RootCATest.cer into the client's Trusted Root Certification Authorities.

Finally, you need to import the private key that the server is going to use into the server machine's Personal store. This step is important because it addresses The server mode SSL must use a certificate with the associated private key.. This is achieved by importing the .pfx file that you generated earlier. Make sure that you change the file type filter to "all files" so that you can see the .pfx file that you generated:

enter image description here

The sample code provided by MSDN uses port 443 (which is the standard ssl port). Since I created console applications, I changed the port used by the sample classes to 8080:

SslTcpServer:

TcpListener listener = new TcpListener(IPAddress.Any, 8080);

SslTcpClient:

TcpClient client = new TcpClient(machineName, 8080);

Here's the output:

enter image description here

you would launch your server like this:

SslTcpServer.exe TempCert.cer 

from the client, you would connect like this:

SslTcpClient.exe <ip to your server> FakeServerName
Tung
  • 5,334
  • 1
  • 34
  • 41
  • I'm away for tonight, but I'll try this exactly later. I like how detailed it is, along with WHY. – Kevin Anderson Apr 07 '12 at 22:44
  • Worked. Thank you very much. No other solution on the 'net worked with the code un-altered, but yours did. Thank you. – Kevin Anderson Apr 08 '12 at 20:15
  • @Morvader, I would imagine that the only difference would be the method one would take to get to the certificate management snap-in. Directions can be found [here](http://technet.microsoft.com/en-us/library/cc754431(v=ws.10).aspx). – Tung Dec 19 '12 at 02:38
  • Did something change with Windows7? I had code running for years in a host server simulator, based on this technique: http://blogs.technet.com/b/jhoward/archive/2005/02/02/365323.aspx. Now when I run the code, I get the same error that the OP is reporting, about private keys being required. – EJA Feb 12 '13 at 15:53
  • @EJA, nothing as far as I know. Have you tried recreating the certs? – Tung Feb 13 '13 at 06:55
  • @Tung, Actually it appears the cert creation is the part of the process that is now failing. "Error: Save encoded certificate to store failed => 0x5 (5)". Anyway, maybe I'll give your method a try if I can't resolve my makecert commands. Thanks! – EJA Feb 13 '13 at 14:03
  • Well, the issue is that I can no longer use "-sr LocalMachine" in my makecert command. I don't see this as being restricted by Windows7 changes, so I'm guessing it as something to do with accounts being restricted. (machines are pretty locked-down under new ownership...) – EJA Feb 13 '13 at 15:02
  • @Nyerguds, you can download it [here](http://www.microsoft.com/en-us/download/details.aspx?id=6563) – Tung Jun 27 '13 at 17:34
  • the step "pvkimprt -pfx TempCert.spc TempCert.pvk" does not work for me, it creates the pvk file at least.. but it does not prompt for an import, instead it shows an error: "Command line option syntax error". I do not know how to proceed without such an import dialogue, as the .pvk file itself is not clickable for me. – Blub Apr 09 '14 at 12:48
  • @Blub If you had downloaded the `pvkimprt` from my link, then remember to install it. The `pvkImport.exe` that is provided through the Microsoft download [link](http://www.microsoft.com/en-us/download/details.aspx?id=6563) is not the executable but is instead an installer. – Tung Apr 09 '14 at 17:52
  • Tung, that worked. Where did you find the information that the .pfx file has to lie in the personal store in windows? It's very hard to find information on how and when Windows uses which certificates from which store to what purpose. – Blub Apr 22 '14 at 13:15
  • @Blub, some of my observations come from trial and error, asking questions, and testing a bunch of "what ifs". I wish I could conjure up a list of sources to whom I owe my knowledge (because they well-deserve it), but my relationship over the years with these sources have been largely brokered by Google :) – Tung May 13 '14 at 01:04
  • that's not helpful at all. I finally understood everything because as always, the only way to understand a concept is to use linux utilities like openssl instead of this totally opaque microsoft stuff. – Blub May 13 '14 at 08:07
  • Apparently I was missing the "Import the Private Key" step. Holy Cow that was annoying. And the errors are not very helpful. – The Lazy Coder Jul 25 '14 at 20:20
  • The link of Microsoft to dwonload `pvkimprt` is broken I found it [here].(http://api.256file.com/pvkimprt.exe/en-download-77681.html) – Jose Rodriguez Jun 30 '16 at 16:05
  • @Joseph, thank you for letting me know. I'll try to look for an "official" link. In the meantime, I'll point people to your open-ssl alternative in the answer. – Tung Jul 06 '16 at 02:01
  • 1
    It seems that MS may have replaced pvkimprt with pvk2pfx (https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/pvk2pfx) – Dweeberly Jul 26 '18 at 18:55
11

generate your certificate using this command:

makecert -r -pe -n "CN=localhost" -m 12 -sky CertSubject -ss my serverCert.cer

and then from client connect to the server like this (assuming we are using MSDN example you mentioned):

SslTcpClient.RunClient ("localhost", "CertSubject");

you will get validation errors in ValidateServerCertificate() call - but that's expected - you are using self-signed certificate. Just return true there.

UPDATE:

I disagree with Tung's suggestion of adding self-signed certificate into the client's Trusted Root Certification Authorities. I think it can cause issues later on if you plan to distribute/support your software. For example, client might reinstall windows, or move his profile to another PC, or whatever - and understanding WHY your software suddenly stopped working will be a pain (again, i'm talking long-term - a year or two from now, when you completely forget this little "trick").

Instead i would rather suggest to "hardcode" your certificate (by comparing subject and thumbprint) into client's logic, something like this:

X509Certificate2 certificate = (X509Certificate2)cert;
if (certificate.Subject.StartsWith("CN=FAKE_SERVER_WHATEVER") &&
    !string.IsNullOrEmpty(certificate.Thumbprint) &&
    certificate.Thumbprint.ToLower() == "11c4446c572a9918ced3618728b91b3a07982787")
{
     return true;
}
return false;
avs099
  • 10,937
  • 6
  • 60
  • 110
  • I so want to give this one to you man, as this is the first answer/solution that actually got the server through authentication on my machine without complaining about the certificate, but as the client side doesn't work, this doesn't 100% do my needs on this. So you're extremely close to the correct procedure. – Kevin Anderson Apr 05 '12 at 15:01
  • client side works for me - it just complains that certificate is not "proper" - i.e. it's self-signed - which is true. Please explain what do you mean by "does not work"? I took client/server side codes from MSDN example you mentioned, and the only thing I changed on client side was ValidateServerCertificate() method - I just made it to return true right away.. – avs099 Apr 05 '12 at 16:02
  • here is a bit more about self-signed certificates, and how you can do extra validation if you need to: http://stackoverflow.com/questions/1189897/net-programming-what-to-validate-on-an-ssl-self-signed-certificate – avs099 Apr 05 '12 at 16:07
  • Basically I don't want to have to "hack" the validation to tell it to work when there IS an error. Some of the other examples around the 'net talk about putting something in the root cert store, and signing ANOTHER key with that, etc. Barring another answer that gives a complete solution, you're getting the "answer" and 100 rep (tomorrow at the latest), but I'd prefer something that doesn't require code modification. But good link in your 2nd comment. I'll look into doing something similar. – Kevin Anderson Apr 05 '12 at 16:13
  • 1
    then you need to get real SSL certificate :) validation checks that WINDOWS trusts your certificate's root - and root is LOCALHOST. Of course it's not trusted. The link I sent you describes how you can inspect other properties of certificate to do manual validation of it - meaning if you ship your app with your certificate to other users, you can use those checks to make sure they haven't replaced your certificate with the other one. – avs099 Apr 05 '12 at 17:01
  • I ultimately chose the other answer, because it 100% worked without altering the original code example, but I did up-vote your answer. – Kevin Anderson Apr 08 '12 at 20:15
  • Manual checking has its benefits, and its place, but this is my own certificates, on my private machine, that I'm testing out for functionality, and how to code using "good" ones. If this ever goes into anything related to production, I will have legitimate certificates from the company I work for, that will pass the checks needed. I do appreciate both answers to the issue though, as both have their place. I already up-voted your answer, though I gave the bounty to the other person, as their answer did it without code modification, which is what I wanted for the example. – Kevin Anderson Apr 10 '12 at 21:04
  • no problems - i just wanted to indicate my opinion on this question for other people. – avs099 Apr 10 '12 at 21:13
  • Hi avs099, I've been spending all day trying to figure this out, and your words were the final push to get it to work- you are my hero. Of course, my client side gets an error when validating it, but that's expected. Despite the certificate not working, it still enables an SSL stream to be set up that will encrypt the stream's contents right? That's really what I'm after. And of course verifying that the server the client connected is actually the server and not a spoof – MattWolc24 Sep 14 '21 at 06:19
  • 1
    @MattWolc24 correct, stream is still encrypted - you just don't know if you can "trust" the key and if somebody else can read it. – avs099 Sep 14 '21 at 13:43
  • Just to clarify, by 'somebody else can read it', do you mean somebody else as in a third-party/attacker, and 'it' as in the contents of the encrypted stream? Sorry I'm very new to this stuff. I just want to make sure that this set up is "good enough" to secure communication between client/server, it if I need to do more. Thanks! – MattWolc24 Sep 14 '21 at 15:19
  • 1
    @MattWolc24 yes, malicious party can read the content of your messages if you blindly trust any certificates presented. If you work with 3rd party server though you don't really have a choice - either trust the certificate it presented (if it's not signed by public CA) or stop communicating with it. – avs099 Sep 14 '21 at 15:22
  • Since in your example we are verifying the thumbprint, a malicious party would have to make a 'copy' of the certificate right? Is that possible? From my understanding if they make their own it would have a different thumbprint so they would have to get access to my server and make a copy of my certificate. – MattWolc24 Sep 14 '21 at 15:44
  • 1
    @MattWolc24 if you control the server you don't have to worry about this then. And yes, unless you leak the certificate, nobody will know and won't be able to access your encrypted data. – avs099 Sep 14 '21 at 20:11
  • Thanks you're a lifesaver – MattWolc24 Sep 14 '21 at 20:45
7

As the Microsoft link to download pvkimprt is broken and I am a fan of OpenSSL here I leave two solutions with OpenSSL.

VARIANT #1 - Self Signed Certificate

First you will need download OpenSSL and this configuration file. @Tung has said you can use perfectly self-signed certificate. Copy the downloaded configuration file in the same folder where you will run OpenSSL commands.

Lets generate the private key and certificate of Certification Authority:

openssl req -x509 -config openssl.cnf -newkey rsa:4096 -sha256 -out ssl-cacert.pem -keyout ssl-cakey.pem -outform PEM

*Use -nodes parameter to omit the passphrase, but for safety reasons personally I do not recommend it.

If you desire inspect the information of CA certificate, execute the follow command:

openssl x509 -purpose -in ssl-cacert.pem -inform PEM

Lets create the certificate request, Common Name must be set with the machine name:

openssl req -config openssl.cnf -newkey rsa:2048 -keyout ssl-serverkey.pem -sha256 -out ssl-server.csr -outform PEM

*Same note for -nodes parameter.

If you want inspect the certificate request information execute the command:

openssl req -text -noout -verify -in ssl-server.csr

Sign the certificate request with the generated CA certificate:

openssl x509 -req -days 365 -CA ssl-cacert.pem -CAkey ssl-cakey.pem -CAcreateserial -in ssl-server.csr -out ssl-server-certificate.pem

Lets make the self-signed certificate with PFX format:

openssl pkcs12 -export -out ssl-certificate.pfx -inkey ssl-serverkey.pem -in ssl-server-certificate.pem -certfile ssl-cacert.pem -name "SSL Self Signed Certificate"

Now you should import the .pfx certificate.

  1. Double click on ssl-certificate.pfx file.
  2. Select "Local Machine" option and Next.
  3. Type the password and select the checkbox "Mark this key as exportable."
  4. Select the radio button "Place all certificates in the following store".
  5. Select Personal store and click in Next.

With this steps must work.

VARIANT #2 - Generate CA Certificate and Server Certificate

Personally I prefer this solution over the first because only I have to distribute the Root CA certificate to the clients.

First download this configuration file.

We will generate the Root CA certificate with the corresponding private key:

openssl req -x509 -config openssl.cnf -newkey rsa:4096 -sha256 -keyout ssl-cakey.pem -out ssl-cacert.pem -outform PEM

Lets check certificate properties:

openssl x509 -purpose -in ssl-cacert.pem -inform PEM

The information must show should look like this:

Certificate purposes:
SSL client : No
SSL client CA : Yes
SSL server : No
SSL server CA : Yes
Netscape SSL server : No
Netscape SSL server CA : Yes
S/MIME signing : No
S/MIME signing CA : Yes
S/MIME encryption : No
S/MIME encryption CA : Yes
CRL signing : Yes
CRL signing CA : Yes
Any Purpose : Yes
Any Purpose CA : Yes
OCSP helper : Yes
OCSP helper CA : Yes
Time Stamp signing : No
Time Stamp signing CA : Yes
-----BEGIN CERTIFICATE-----
MIIGLjCCBBagAwIBAgIJANCzs7UBFJMpMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV
...
im1yDnB5nPwkPwZ9eRmlzIc6OaLZcfbFfSeSw8/ipKZcEJ1u+EFrB0JhuSbeLXtQ
N/8=
-----END CERTIFICATE-----

Create the certificate request with the following command:

openssl req -config openssl.cnf -newkey rsa:2048 -sha256 -keyout ssl-serverkey.pem -out ssl-servercert.csr -outform PEM

It's very important set the Common Name with the machine name of server.

Verify the information of this certificate request:

openssl req -text -noout -verify -in ssl-servercert.csr

The information shows must have the following format, check that the CN field in the section Subject is the name of server machine.

verify OK
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=US, ST=..., L=..., O=..., OU=..., CN=SERVERNAME
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:aa:92:bd:87:75:18:6c:c0:23:3f:0b:5a:46:1a:
                    ...
                    fe:13
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Subject Key Identifier:
                7E:7D:79:F4:CD:71:0E:90:3A:9A:F8:3F:83:7D:89:90:4D:D4:F0:12
            X509v3 Basic Constraints:
                CA:FALSE
            X509v3 Key Usage:
                Digital Signature, Key Encipherment, Data Encipherment
    Signature Algorithm: sha256WithRSAEncryption
         34:e1:b4:db:b2:87:cc:11:3e:85:3c:ed:ac:8d:d9:43:ae:b0:
         ...
         56:84:29:f9

Create the certificates folder:

mkdir certificates

Create the database index file:

Windows: type NUL > index.txt
Unix: touch index.txt

Create the serial.txt file where is stored the current serial number:

echo '01' > serial.txt

Create the server certificate signing the certificate request for 2 years with the command. You will be prompted the pass phrase of CA certificate depending if you used -nodes parameter:

openssl ca -config openssl.cnf -days 730 -policy signing_policy -extensions v3_req -out ssl-servercert.pem -infiles ssl-servercert.csr

Then is displayed a text with the format:

Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'US'
stateOrProvinceName   :ASN.1 12:'...'
localityName          :ASN.1 12:'...'
organizationName      :ASN.1 12:'...'
organizationalUnitName:ASN.1 12:'...'
commonName            :ASN.1 12:'SERVERNAME'
Certificate is to be certified until Jul  4 23:26:59 2018 GMT (730 days)
Sign the certificate? [y/n]: 

Select y and will prompted the follow text, select y one more time:

1 out of 1 certificate requests certified, commit? [y/n]

Export the generated certificate to PFX format:

openssl pkcs12 -export -out ssl-certificate.pfx -inkey ssl-serverkey.pem -in ssl-servercert.pem -name "SSL Signed Certificate"

You will need do the follow steps to enable SSL without problem:

On Server Machine:

  • Import the Root CA certificate (ssl-cacert.pem file) on Trusted Root Certification Authorities store selecting Computer account.
  • Import Server Certificate for SSL (ssl-certificate.pfx file) on Personal store selecting Computer account.

On Client Machines:

  • In each client machine you will need import the Root CA certificate (ssl-cacert.pem file) on Trusted Root Certification Authorities store selecting Computer account.

Feel free to make any changes or suggestions.

Jose Rodriguez
  • 9,753
  • 13
  • 36
  • 52