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
Differencebetween an Int and an Integer in Java and C#
Compiling/Executing a C# Source File in Command Prompt
Web App Blocked While Processing Another Web App on Sharing Same Session
Creating a Copy of an Object in C#
How to Deserialize Xml into List<T>
How to Check If an Object Is Nullable
System.Unauthorizedaccessexception While Running .Exe Under Program Files
How to Read an Attribute on a Class at Runtime
Difference Between Equals/Equals and == Operator
How to Deserialize Xml to Object
Calling Async Method Synchronously
Free File Locked by New Bitmap(Filepath)
Send a File via Http Post with C#