Jump to navigation

Poupou's Corner of the Web

Looking for perfect security? Try a wireless brick.
Otherwise you may find some unperfect stuff here...

Weblog

An SSL server in 3 simple steps (and without boiling water)

Step 1 - Creating a SSL server certificate

A SSL certificate is used to bind a public key with a host name. Without this protection any valid certificate could be used on any host (as long as you have the private key). So you must know your host name before creating your certificate. In doubt use the hostname command. As an alternative it is also possible to use the IP address of your computer.

> hostname pollux

Building a SSL test (i.e. non trusted) certificate is easy once your know your host's name. Mono ships with a utility, makecert, for doing so. The following command will create a test certificate for an SSL server.

> makecert -r -eku 1.3.6.1.5.5.7.3.1 -n "CN=pollux" -sv pollux.pvk pollux.cer Success

The parameters used to build this test certificate are:

-r
Create a self-signed certificate (we don't need/want to build a hierarchy here). Maybe I should blog about being your own CA one of these days...
-eku 1.3.6.1.5.5.7.3.1
Optional (as sadly most client don't require it). This indicates that your certificate is intended for server-side authentication.
-n "CN=pollux"
Common Name (CN) = Host name. This is verified the SSL client and must match the connected host (or else you'll get a warning or error or *gasp* nothing).
-sv private.key
The private key file. The key (1024 bits RSA key pair) will be automatically generated if the specified file isn't present.
pollux.cer
The created SSL certificate for your host.

Use man makecert to see all the options provided by makecert.

Step 2 - The SSL server

using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using Mono.Security.Authenticode; using Mono.Security.Protocol.Tls; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; namespace SslHttpServer { class SslHttpServer { private static X509Certificate _certificate; private static string certfile; private static string keyfile; static void Main (string [] args) { certfile = (args.Length > 1) ? args [0] : "ssl.cer"; keyfile = (args.Length > 1) ? args [1] : "ssl.pvk"; Socket listenSocket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint localEndPoint = new IPEndPoint (IPAddress.Any, 1888); Socket requestSocket; listenSocket.Bind (localEndPoint); listenSocket.Listen (10); while (true) { try { requestSocket = listenSocket.Accept (); using (NetworkStream ns = new NetworkStream (requestSocket, FileAccess.ReadWrite, true)) { using (SslServerStream s = new SslServerStream (ns, Certificate, false, false)) { s.PrivateKeyCertSelectionDelegate += new PrivateKeySelectionCallback (GetPrivateKey); StreamReader reader = new StreamReader (s); StreamWriter writer = new StreamWriter (s, Encoding.ASCII); string line; string answer = "HTTP/1.0 200\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "Content-Encoding: " + Encoding.ASCII.WebName + "\r\n" + "\r\n" + "<html><body><h1>Hello World!</h1></body></html>\r\n"; // Read request header do { line = reader.ReadLine (); if (line != null) Console.WriteLine (line); } while (line != null && line.Length > 0); // Send response writer.Write (answer); writer.Flush (); s.Flush (); ns.Flush (); } } } catch (Exception ex) { Console.WriteLine (ex.ToString ()); } } } private static X509Certificate Certificate { get { if (_certificate == null) _certificate = X509Certificate.CreateFromCertFile (certfile); return _certificate; } } // note: makecert creates the private key in the PVK format private static AsymmetricAlgorithm GetPrivateKey (X509Certificate certificate, string targetHost) { PrivateKey key = PrivateKey.CreateFromFile (keyfile); return key.RSA; } } }

Note that the sample code use the port 1888 for SSL communication while the default SSL port is 443. This allows me (and others ;-) to let Apache runs with the default SSL port.

Now compile the SSL server source code by including a reference to the Mono.Security.dll assembly (the whole point of this blog entry).

> mcs sslserver.cs /r:Mono.Security.dll Compilation succeeded

Step 3 - Executing the SSL server

Now execute the server using Mono and specify your certificate and private key (created in the first step).

> mono sslserver.exe pollux.cer pollux.pvk

Then from another terminal window, type: (don't forget to adjust host name and port number)

> wget https://pollux:1888/

then ENTER. A similar output should then be printed:

--18:10:02-- https://pollux:1888/ => `index.html' Resolving pollux... 192.168.0.666 Connecting to pollux[192.168.0.666]:1888... connected. HTTP request sent, awaiting response... 200 Length: unspecified [text/html] [ <=> ] 49 --.--K/s 18:10:02 (478.52 KB/s) - `index.html' saved [49]

during this time the server terminal should have printed something like:

GET / HTTP/1.0 User-Agent: Wget/1.9.1 Host: pollux:1888 Accept: */* Connection: Keep-Alive

Notes

  • FireFox, IE and even wget, have the "bad habit" of starting a SSL connection using SSL v2 (i.e. with a version 2 HelloClient structure). This is a bad habit because TLS, RFC2246, specifies in Appendix E:

    Warning: The ability to send Version 2.0 client hello messages will be phased out with all due haste. Implementors should make every effort to move forward as quickly as possible. Version 3.0 provides better mechanisms for moving to newer versions.

    and was published in January 1999 (also note that TLS 1.0 is seen as SSL 3.1)! Another funny incident with appendix E is that both FireFox and IE (but not wget) have their own interpretation of "This value must be 32." where 32 == 16. I admit that (a) the must isn't capitalized (probably because the RFC doesn't reference RFC 2119 (Key words for use in RFCs to Indicate Requirement Levels) and that no capitalized MUST are present in the document - and (b) that both implementations predate the TLS specification - but still they both proclaims to support TLS ;-)
  • On a Mono related note, support for version 2.0 client hello is very recent in SVN. Actually it was planned to *not* ever exists!. So this will only be available in the next stable/unstable releases (i.e. FireFox and IE won't work with this sample if you use Mono 1.0.4 or 1.1.2).
  • Acute readers have noticed that I didn't share the true address of pollux on my home network ;-).
  • Very acute readers have noticed that they haven't been warned that you were using a test certificate by wget! - but Firefox and IE would have - no one is perfect it seems ;-)

Thanks

Many thanks to Carlos Guzman Alvarez for his excellent client/server SSL/TLS implementation and support, and to Joerg Rosenkranz for providing me his server sample code (which was my last reason not to blog about this earlier ;-). I'm glad we have both of you in the Mono community :-)


11/29/2004 18:07:16 | Comments

The views expressed on this website/weblog are mine alone and do not necessarily reflect the views of my employer.