Monday, November 12, 2012

HTTPS Communication – HttpListener based Hosting and Client Certification

KEYWORDS: HTTPS, SSL, HttpListener, X509Chain, X509Certificate2, makecert, OpenSSL, client certificate

(Just found, a wonderful tool set that could help you to host easily, The site referred my post, and I didn't realize till now  :-) thanks for refering.  [2016-2-6])

HttpListener is the easiest way for you to host an HTTP/HTTPS server. This article provides you step-by-step instructions to create your own server and authenticate clients based on client certificate from ground up in C#.

Download the sample code


Firstly, you should create your .net application and add these four lines.

var server = new HttpListener();
HttpListenerContext context = server.GetContext();

These fourlines will make your server started and listening on the port. Be aware of the exceptions (HttpListenerException) thrown from the invocation server.Start(), and see step 2 to solve it.


Step 1 shows you it’s so easy to start a server. But wait, Start() throws an exception (HttpListenerException: Access Denied, native error code 5, HRESULT 80004005), if you run your app under non-privilege account. If you want a non-privilege account to run the server, you have to add ACL (Access Control Lists) to the system. In command line:

netsh http add urlacl url=https://+:80/MyUri user=DOMAIN\user

Pay attention to the parameter ‘user’. Put whatever user you want to assign the start server right to here. If set the parameter user=users, it will grant all user account (non-privileged) to start the app and listen on the specific ip and port. The ip part ‘+’ stands for all IPs of your machine. For the server you want to handle urls from root (e.g. http://localhost/), you don’t need ‘MyUri’ part, and your command is like this:

netsh http add urlacl url=https://+:80/ user=DOMAIN\user


And then your app won’t throw any exception. Your app would be blocked at server.GetContext() and waiting for incoming connections. Try the url https://localhost:90/ in your browser, there is still an error page with HTTP 101 ERR_CONNECTION_RESET. This because you haven’t assign a certificate to the server and the browser can’t verify the validity of the server. Remember we are visiting an HTTPS site. The server certificate is a must.

So, let’s create the certificates. You can either create your certificates by makecert or by OpenSSL. And this How to Setup a CA gives you an easy tutorial of creating certificates hierachy by OpenSSL. First is the root CA certificate. For experimental cases, makecert is enough. But for product, you may want to use OpenSSL or apply a certificate from CA like VeriSign.

makecert -n "CN=TestCA" -r -sv TestCA.pvk TestCA.cer

And import the root certificate to the system certificate storage of Rusted Root Certification Authority. See this article.

Then create the certificate for your HTTPS web site.

makecert -iv TestCA.pvk -n "CN=TestSite" -sv TestSite.pvk -ic TestCA.cer TestSite.cer -sr LocalMachine -ss My -sky exchange -pe

If you will test your client app on a machine other than the server machine, you have to import the TestCA.cer to the client machine as well. So that the client machine trust TestCA (the root cert), it will also trust the server certificate (TestSite).

Hosting an HTTPS site, you must have a certificate with private key. But the last makecert command creates the private key in TestCA.pvk which can’t be imported to the system storage directly. We have to convert it to .pfx format:

pvk2pfx -pvk "TestSite.pvk" -spc "TestSite.cer" -pfx "TestSite.pfx"

Then you will see the certificate for your site:


How to use the server certificate? At this point, the when client connect to the server, the client will throw an exception (WebException The underlying connection was closed: An unexpected error occurred on a send), simply because the server doesn’t use the certificate yet. To resolve the exception,just binding the certifiate to the server’s ip and port by netsh.

netsh http add sslcert ipport= appid={61047666-992C-4137-9303-7C01781B054E} certhash=75d0fed71881f2141b5b6cb24801dfa554439b1c clientcertnegotiation=enable

‘’ in the ipport parameter means every ip of this machine would be assigned with the certificate. The parameter appid is your application id. You can see it in the project property, the ‘Application’ page, and the dialog poped up by clicking ‘Assembly Information’ button. The parameter ‘clientcertnegotiation=enable’ will allows C/S mutually authentication based on certificates, i.e. server side could verfiy the certificate validation of the client side as well as the client side verifying the server side. If you don’t want verification for client side, just omit the parameter.


Visit https://localhost:90/ again, your browser will warning you that the site is not the owner of the certificate. It’s because we don’t have a domain for our experimental site and no domain name was set into the certificate. So just click continue to view the page and the browser will show you a blank page.

Let’s add responding code to the server side, so that we can see something on the page.

string message = "Hello World!";
var buffer = System.Text.Encoding.UTF8.GetBytes(message);
context.Response.OutputStream.Write(buffer, 0, buffer.Length);

Now the page displays “Hello World!”.


We have done the work of constructing server side. The server can show its identity by providing its certificate and client can verify it. Client still shows no certificate to the server. In some cases, the server need to verify the client’s identity, and only when the client is valid (e.g. a valid member of some organization) the server would start data communication. In this case, a client app (other than web browser) is a must. So let’s create a client app.

Here is the basic client code without client certificate.

ServicePointManager.ServerCertificateValidationCallback =
       new RemoteCertificateValidationCallback(CheckValidationResult);

string url = "https://localhost:90/";
Console.WriteLine("Visiting " + url);
HttpWebRequest objRequest = System.Net.HttpWebRequest.Create(url) as HttpWebRequest;
objRequest.ProtocolVersion = HttpVersion.Version10;

var response = objRequest.GetResponse();
var responseReader = new StreamReader(response.GetResponseStream());
var responseContent = responseReader.ReadToEnd();
Console.WriteLine("Server replied: " + responseContent);

CheckValidationResult is a callback function which allows you to perform customized validation against server certificate, returns true to accept the certificate. As expected, the client gets the server reply: “Hello World!”.


Here we add client certification code. Basically you have two ways of creating a X509Certificate2 which could contain public/private key pair. Other ways like manipulating public/private key pair raw data directly, may be tricky and complex.
  1. Load .pfx from file;
  2. Load certificate with private key from the system’s certificate store.
Here is the first one, load from file:

HttpWebRequest objRequest = System.Net.HttpWebRequest.Create(url) as HttpWebRequest;
X509Certificate2 clientCertificate = new X509Certificate2("TestClient.pfx", "the key password");

You have to add certificate to the https request right after you created the request, because GetResponse() will use the certificate immediately. Here is the second way of creating X509Certificate2 - loading the certificate from the system store:

static X509Certificate2 LoadClientCertificate()
    // Note: Change "My" and StoreLocation.CurrentUser to where your certificate stored.
    var store = new X509Store("My", StoreLocation.CurrentUser);
    var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, "TestClient", true);
    if (certificates.Count != 0)
       return null;

    return certificates[0];

Before running it, you have to import the certificate (with private key) to the store just like you did with the server certificate. Loading a certificate (without private key) can be done by a non-privileged account, while accessing private key of a certificate from the system store requires administrator privilege. So when you run above code by a non-privileged account, you will get the certificate although, but only public key is in it. While the server side needs the client to sign something to verify the client’s identity, so the client must have the private key. So when carrying out further steps of HTTPS communication
  1. When the client certificate loaded from system store, the client code will get an WebException;
  2. When the client cerfiticate loaded from file,  the server will get no client cert (GetClientCertificate() returns null).

Loading from store and loading from file both has pros and cons.


Server side still doesn’t verify the client certificate. So let’s add the code logic.

HttpListenerContext context = server.GetContext();

var clientCertificate = context.Request.GetClientCertificate();
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
if (chain.ChainStatus.Length != 0)
    // Invalid certificate

X509Chain is a tool which builds the chain of trust of the certificate. If the certificate is invalid, then you can find error information in chain.ChainStatus. You can implement detailed logic upon X509Chain rather than only checking chain.ChainStatus.Length. Set RevocationMode to NoCheck because we don’t have a certificate server to tell you whether a certificate is revoked.


Anonymous said...

Curгently іt loοkѕ lіke Wοгdρгess iѕ the best
blоgging рlаtform аvailablе rіght noω.
(from whаt I've read) Is that what you're using on your blоg?

my web page > TENS units

Anonymous said...

Ноla! Ι waѕ inteгested
to know if setting up a website such your own: httρ:
// is chаllеnging tο do
for unskilled people? I've been hoping to set up my own website for a while now but have been turned off mainly because I've alωays assumed it demanԁed tons of work.
What do yоu think? Thanks

Have a look at my web page ;

Anonymous said...

The year progresses along actually, I'm sure! Would this specific turn into probable to acquire your web blog converted straight to German? The english language is actually our 2nd language.
my website: Genital Warts Home Treatment

Anonymous said...

Ηoωdy! This is kind of off topic but I neeԁ sοmе help from an eѕtablished blog.

Is it very hard to ѕet up your own blog? I'm not very techincal but I can figure things out pretty quick. I'm thinking
about making my oωn but I'm not sure where to start. Do you have any ideas or suggestions? Thanks
Feel free to visit my weblog ... stock broker license

Anonymous said...

You're able to do both equally Keith even if you should really focus more about the actual assessment webpage on its own.
Review my web blog - Online Vehicle Insurance

Anonymous said...

Gdаy. Sοrry to trouble you but I happened to run across your blogging ѕite anԁ noticed
you're using the exact same template as me. The only problem is on my website, I'm struggling to get the theme lоoking likе yours.

Woulԁ you mind emаilіng me at: stormyanԁeгs@yаhoо.
de so Ι can get thiѕ figured out. By the waу I've bookmarked your internet site: and will certainly be visiting frequently. Many thanks!
My webpage: used boom trucks

Anonymous said...

My spousе and I ѕtumbled oѵer hеre coming from a dіfferent pagе
аnd thought I might аs ωell check things out.
І likе what I seе so now i аm fοllowing yοu.
Look forωarԁ to going over your ωeb раge repeateԁly.

Visit my web site ... Croadria-agency.Com
Also visit my blog post - bbq guru review

Anonymous said...

Hello practical write-up. I needed just a little difficulity looking at this post
onSafari however, not sure the key reason why?
Here is my web site ; How To Treat Genital Warts

Anonymous said...

My pal recommended I would this way website. He had
been right. This genuinely manufactured this evening.
You can't imagine the time I had spent interested in this specific.
Also visit my webpage :

Anonymous said...

Νiсe post. I used tο be checking continuously this weblog and I
аm imprеssеd! Extгеmely helpful info
speсially the last sectіon :) I handle such info a lot.
I used to be ѕeeking thiѕ certаin info for
a vеry long tіme. Thanks аnd
best of luck.

Stop by my webpage :: dallas auto insurance
Here is my weblog :: dallas car insurance

Anonymous said...

Hello there! I recently desired to uncover you ever get difficulties with cyber criminals?
This last blog site (wordpress platforms) appeared to be hacked i appeared getting rid of several months involving hard work as
a result of no copy. Are there just about any solutions to avoid online hackers?
Visit my weblog : Online Vehicle Insurance

Anonymous said...

Amаzіng blog! Do yοu havе аnу rеcommendatiоns fοr
asρiring wгiterѕ? I'm hoping to start my own site soon but I'm a lіttle lost on eνerything.
Wοulԁ you гecоmmenԁ
ѕtartіng with a frеe platform like Wοrdpreѕs or go fοr а paіd oρtion?

Τheге arе so many chоicеs out there that
I'm completely overwhelmed .. Any suggestions? Thanks!

Also visit my site
My web page ; cheap auto insurance dallas

Anonymous said...

Woah! I'm really loving the template/theme of this website. It's
ѕimplе, уet effеctіvе.

A lot of times it's tough to get that "perfect balance" between usability and visual appeal. I must say you have done a superb job with this. In addition, the blog loads extremely quick for me on Internet explorer. Excellent Blog!

My website :: Tens units

Anonymous said...

Just desire to sаy your artіclе iѕ аs astonishіng.
Thе clearness in yоur post is just cool anԁ i
coulԁ assume уou're an expert on this subject. Well with your permission allow me to grab your RSS feed to keep updated with forthcoming post. Thanks a million and please continue the rewarding work.

My web site: irving taxi service

leena pearl said...

If your web site goes down in the middle of the night when they're not available, this means lost revenue for a business. You should make sure the web host you select is always available for support. ssd web hosting in Bangladesh

Cafeine said...

Thank you so much for writing this guide.
I have spent so much time trying to figure all this out and your guide was the only that was simple and worked!