SSLStream example - how do I get certificates that work? - c#

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.

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
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
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:
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:
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

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;

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.
Double click on ssl-certificate.pfx file.
Select "Local Machine" option and Next.
Type the password and select the checkbox "Mark this key as exportable."
Select the radio button "Place all certificates in the following store".
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.

Related

recreate a certificate with the same credentials with makecert

I have a question, I have created my own certificate with this command :
1) makecert -r -pe -n "CN=MyTest" -sky exchange -sv MyTest.pvk MyTest.cer
2) pvk2pfx -pvk MyTest.pvk -spc MyTest.cer -pfx MyTest.pfx
So I have created a simple console application for crypt an decrypt text with this certificate, all work fine... example:
1) Crypt from text "1" I get "RLSym/wwReReo3GMM27ueIcMFRWHAB1AELnFVERnYuMbjBJi0QrW+oV2ADdJQ8VoZlShun0="
2) Decrypt from "RLSym/wwReReo3GMM27ueIcMFRWHAB1AELnFVERnYuMbjBJi0QrW+oV2ADdJQ8VoZlShun0="
I get "1".
Now I ricreate the certificate with the same command and password and use new one, when I try to Decrypt text create with The first certificate I receive an error, why? It is not possible to ricreate the certificate with the same credential and option if I lose the first one certificate? all my data saved on database is lost?
Thx.

Validate certificate TLS/SSL Server

I have been attempting to create an SSL server that loads a certificate from a .crt. I have tried both X509Certificate.CreateFromCertFile(#".\Secure\Certificate\" + CertName + ".crt"); and the cert.import, and neither works. On both, I get an issue saying "The server mode SSL must use a certificate with the associated private key". And the key is there! My directory:
Secure/
Certificate/
ZeusHTTP.crt
ZeusHTTP.csr
ZeusHTTP.key
Plugins/
...
The certs are created with OpenSSL.
A simple read of the docs tells us that you should be using a pkcs7 file that usually has file suffix p7b. You'll need to either convert your OpenSSL cert to this format, or find a utility that can generate one from scratch.
The server mode SSL must use a certificate with the associated private key". And the key is there...
As other have stated, they must be in the same file. Here are the steps to do it.
First
Copy ZeusHTTP.crt to ZeusHTTP-chain.crt:
cp ZeusHTTP.crt ZeusHTTP-chain.crt
Second
Open ZeusHTTP-chain.crt and ensure it has all the intermediates certificates required to validate the server certificate. So you will have 2 or more certificates:
-----BEGIN CERTIFICATE-----
<server certificate>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<intermediate certificate>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<intermediate certificate>
-----END CERTIFICATE-----
Add certificates as required. For example, if you got a free Startcom certificate, then you need to add the sub.class1.server.ca.pem intermediate from StartSSL's Index of Certs.
Sending all certificates is required to solve the "which directory" problem in PKI. Its a well known problem in PKI, and essentially it means a client does not know where to go to fetch missing intermediate certificates.
Third
Perform the following to generate a PKCS 12 file:
openssl pkcs12 -export -in ZeusHTTP-chain.crt -inkey ZeusHTTP.key -out ZeusHTTP.p12
Fourth
Finally, install the certificate on IIS as a test.
For your code, I believe you need to load it into a Certificate2 and not a Certificate.
Also see How to read a .p12 file in my web service on Stack Overflow and how to create x509 certificate and use it in sslstream on MSDN.

PushSharp APNS Service Stop working after windows shutdown

I can send notifications to my iPhone device succeffully using Push Sharp via sandbox APNS server but I am having a problem.
I have generated .cer and .p12 files and then installed them on my windows 8 development machine successfully.
I used this tutorial to install the certificates on my windows 8 machine.
Yesterday things were working fine and I was sending the notification successfully. I shutdown my system and then next day when I try to run the code I was getting following exception:
the message was unexpected or badly formatted pushsharp
I tried different solution available on Google but nothing helped. Then I delete the certificates from my machine and then re-install them and things started to work again.
In order to make the service fool proof I shutdown the system to check if notification sending fails or not, and yes it fail again with the same exception.
I again deleted the certificates and re-install them to correct the issue. I do not know whats the actuall problem? what makes PUSHSharp to stop sending notification after shutdown.
Note: Windows firwall is disabled.
Any idea?
I have been working with PushSharp for the past few weeks and have not had this problem. My environment is Windows 7 however. After you've created the appropriate Push Notification Certificate in iPhone Developer Program Portal you should have downloaded a file named something like apn_developer_identity.cer. If you have not done so already, you should open/import this file into Keychain, into your login section.
Finally, if you filter Keychain to show your login container's Certificates, you should see your Certificate listed. Expand the certificate, and there should be a Key underneath/attached to it.
Right Click or Ctrl+Click on the appropriate Certificate and choose Export. Keychain will ask you to choose a password to export to. Pick one and remember it. You should end up with a .p12 file. You will need this file and the password you picked to use the Notification and Feedback Libraries here.
OpenSSL
Here is how to create a PKCS12 format file using open ssl, you will need your developer private key (which can be exported from the keychain) and the CertificateSigningRequest??.certSigningRequest
Convert apn_developer_identity.cer (der format) to pem:
openssl x509 -in apn_developer_identity.cer -inform DER -out apn_developer_identity.pem -outform PEM}
Next, Convert p12 private key to pem (requires the input of a minimum 4 char password):
openssl pkcs12 -nocerts -out private_dev_key.pem -in private_dev_key.p12
(Optional): If you want to remove password from the private key:
openssl rsa -out private_key_noenc.pem -in private_key.pem
Take the certificate and the key (with or without password) and create a PKCS#12 format file:
openssl pkcs12 -export -in apn_developer_identity.pem -inkey private_key_noenc.pem -certfile CertificateSigningRequest??.certSigningRequest -name "apn_developer_identity" -out apn_developer_identity.p12
Once you generate the p12 file using these steps you will not really need to snap it to your console. You will just need to make changes in your code as follows:
var appleCert = File.ReadAllBytes("C:/Certificate/aps_dev_identity.p12");
Hope this helps.
I have been working on MOON APNS since 2012 and it's working fine but from last few days i am getting below error message
Error Message : System.Security.Authentication.AuthenticationException: A Call to SSPI failed, see inner exception.
System.ComponentModel.Win32Exception: The message received was
unexpected or badly formatted
Solution : in PushNotification.cs file replace
_apnsStream.AuthenticateAsClient(host, certificates, System.Security.Authentication.SslProtocols.Ssl3, false);
with
_apnsStream.AuthenticateAsClient(host, certificates, System.Security.Authentication.SslProtocols.Tls, false);
As, I didn't find any confirmation from apple side but from github.com I found solution for this and it's work for us.
It's seems that apple depricate "unsafer" protocol SSL.
Check first if you can do the notifications with C# code like this one first, then worry about the installation. I had the same message when I was trying to execute the code, I didn't care about installation, and solved it by making sure that I use a certificate of type .p12 and not .pem and to make sure that the .p12 had a password. I can now send notifications to my iPhone from a console C# app in my pc.

How to get Thumbprint or Public Key of Issuer Certificate?

We have created a self signed CA certificate which we use to sign other certificates for SSL purposes. These certificates will be installed in other servers which we do not have access to and will be strictly to communicate with other clients like mobile applications.
When these clients (written in .NET) make a request to the servers using HTTPS we get the "Invalid certificate received from server" error because the CA cert is not a trusted CA on that client.
We want to bypass this security using the ServicePointManager.ServerCertificateValidationCallback, but only if the certificate being used was signed by our CA certificate.
I can check the certificate.Issuer, but that can easily be spoofed by anyone. How can I get the Thumbprint or Public Key of the Issuer certificate of the invalid certificate? If I can get access to that I can easily compare it to the one I know is valid and ignore the certificate error and continue on with the request.
UPDATE
I think I am getting closer. It looks like what we're looking to do is not doable so went a slightly different direction.
Using the X509Chain we can verify whether the certificate is a child of the CA using the code below:
var caCert = new X509Certificate2(#"[path]\MyCA.cer");
var newChain = new X509Chain();
newChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
newChain.ChainPolicy.ExtraStore.Add(caCert);
var res = newChain.Build(certInQuestion);
Build() still returns false (as expected because the CA is not trusted on the client), but now newChain.ChainStatus[0].Status is returning UntrustedRoot. Based on my testing this means the chain validated because if I supply a different CA Certificate it fails with InvalidChain.
In conclusion, that tells me that if the Status is UntrustedRoot, the certificate was created with our CA certificate and thus it's valid, anything else it's a fake one!
Are my assumptions correct?
That's the wrong solution. You should install your self-signed certificate as a trusted CA certificate in all clients, or better still just get it signed by a CA that is already trusted. Don't write code for this.
Here are some links with info and some tools to generate free public and private keys:
https://www.igolder.com/pgp/
https://www.igolder.com/pgp/generate-key/
I think EJP's solution is the most acceptable. #EJP, can you give us some examples of applications that can help us become a "tiny CA".
I'm not entirely sure this this is what you're looking for but it might push you in the right direction. Here's a PowerShell script I use to find a cert that I just created and extracted using MAKECERT.EXE & CERTMGR.EXE:
# get certificate thumbprint
$appCertificate = Get-PfxCertificate -FilePath $certificateFullPath
Write-Host " .. adding certificate to local machine root" -ForegroundColor Gray
& $ExeCertManager /add $certificateFullPath /s /r localMachine root
Write-Host " Certificate installed on local machine" -ForegroundColor Gray
Write-Host " .. exporting private key for certificate" -ForegroundColor Gray
Get-ChildItem cert:\\localmachine\my | Where-Object {$_.Thumbprint -eq $appCertificate.Thumbprint} | ForEach-Object {
$CertPfxName = (Get-Item -Path $certificateFullPath).BaseName
}
It seems that a possible solution would be that you could email the certificate, according to this answer: https://stackoverflow.com/a/4473799/674700.

Why does makecert not make a *valid* certificate?

I want to create an X509 certificate for testing purposes. This certificate has to be shared by 3 developers on their local machines (i.e. all share the same certificate so we can use the same thumbprint).
So the private key in this certificate has to be exportable.
I create a certificate with the following command:
makecert -r -pe -n "CN=mytestsite.local" -b 01/01/2000 -e 01/01/2036 -ss my -sr localMachine -sky exchange localhost.cer
This certificate works fine, but the trouble is that the isValid argument has to be false when calling Certificates.Find...
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.Find(
X509FindType.FindByThumbprint,
Config.PdfCertificateThumbprint,
false //********************* This has to be false.
).OfType<X509Certificate>().FirstOrDefault();
As soon as I set that IsValid property to True, my certificate is no longer returned by the Find method. Why would makecert generate an "invalid" certificate? Or how do I figure out why the certificate is deemed invalid?
Well, it's because it's not issued by a "Trusted Certificate Authority" like the "real" ssl certificates used on the internet. (for example issued by VeriSign)
What you can do locally to work is to add the certificate manually in the Trusted Certificates for your user and/or local machine. But this procedure must be done for everyone using it until you will obtain a valid SSL certificate issued by a CA (certificate authority).
But your question points to the scenario where it's for dev purposes only so what you can do is either manually add the certificate to Trusted or you can override the certificate validation mechanism in .Net and write code that will consider your certificate valid.
You might want to experiment with the following setting that can be used in client config to bypass the certificate validation process:
<serviceCertificate>
<authentication certificateValidationMode="None"
revocationMode="NoCheck" />
</serviceCertificate>

Categories

Resources