How to I Catch Ssl Exceptions in a Mono Httplistener Server

HttpListener with HTTPS on MonoTouch

This is a very good question. In some cases, like for HttpListener, .NET requires tools or .config files (using System.Configuration) to tweak the configuration of an application. In many cases there are API do achieve the same purpose, but not always (and not in this case).

The solution is to look at Mono's source code to see what it expects the HttpCfg.exe tool to setup for the application. From github:

string dirname = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
string path = Path.Combine (dirname, ".mono");
path = Path.Combine (path, "httplistener");
string cert_file = Path.Combine (path, String.Format ("{0}.cer", port));
if (!File.Exists (cert_file))
return;
string pvk_file = Path.Combine (path, String.Format ("{0}.pvk", port));
if (!File.Exists (pvk_file))
return;
cert = new X509Certificate2 (cert_file);
key = PrivateKey.CreateFromFile (pvk_file).RSA;

So the solution is to create the same directory structure (it's possible since it will point under the Documents directory) and copy the .cer file (binary DER-encoded certificate) and the .pvk file (which is the private key in the format that makecert creates) with the port number as the file name.

With those files in place you should be able to start the HttpListener and have it load the required certificate and private key required to handle SSL requests.

SSL on Service Stack

I ended up writing some code in my app host to enable SSL support for Service Stack, which uses HttpListener under the covers. Here is some code that will enable SSL for Service Stack for a console application:

public class AppHost : AppHostHttpListenerBase
{
public AppHost() : base("Service", typeof(AppHost).Assembly)
{
}

public override void Configure(Funq.Container container)
{
Plugins.Add(new RazorFormat());
}

static void AddP12 (string filename, string password, ushort port)
{
string dirname = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string path = Path.Combine(dirname, ".mono");
path = Path.Combine(path, "httplistener");
if (!Directory.Exists(path))
{
Console.WriteLine("Creating directory: " + path);
Directory.CreateDirectory(path);
}
X509Certificate2 x509 = null;
try {
x509 = new X509Certificate2 (filename, password);
} catch (Exception e) {
Console.Error.WriteLine ("error loading certificate [{0}]", e.Message);
return;
}

string target_cert = Path.Combine (path, String.Format ("{0}.cer", port));
if (File.Exists(target_cert))
{
Console.Error.WriteLine ("error: there is already a certificate for that port.");
return;
}
string target_pvk = Path.Combine (path, String.Format ("{0}.pvk", port));
if (File.Exists(target_pvk)) {
Console.Error.WriteLine ("error: there is already a certificate for that port.");
return;
}

using (Stream cer = File.OpenWrite (target_cert))
{
byte[] raw = x509.RawData;
cer.Write (raw, 0, raw.Length);
}

PrivateKey pvk = new PrivateKey();
pvk.RSA = x509.PrivateKey as RSA;
pvk.Save(target_pvk);
}

public static void Main(string[] args)
{
string listeningOn = string.Empty;
if (args.Length == 1)
listeningOn = "http://*:" + args[0] + "/";
else if (args.Length == 3)
{
listeningOn = "https://*:" + args[0] + "/";
AddP12(args[1], args[2], Convert.ToUInt16(args[0]));
}
else
{
Console.WriteLine("Usage: [port] [p12 certificate] [p12 password]");
return;
}
AppHost appHost = new AppHost();
appHost.Init();
appHost.Start(listeningOn);
Console.WriteLine("Service Stack Server started at {0}, listening on {1}", DateTime.Now, listeningOn);
while (true) System.Threading.Thread.Sleep(100);
}
}

HTTPS Client Certificates with Monotouch

Use HttpWebRequest. In the question notes point 2 I said the HttpWebRequest.ClientCertificates property throws a not implemented exception, and I therefore ruled out this option. It does if you try to set it the property with a new collection, but if you just use the Get accessor, you can add a client certificate to the collection.

As a side note, using HttpWebRequest makes the app more transportable to other devices, part of the reason we are using MonoDevelop so win-win.

HttpListener Access Denied

Yes you can run HttpListener in non-admin mode. All you need to do is grant permissions to the particular URL. e.g.

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

Documentation is here.



Related Topics



Leave a reply



Submit