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.
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.
C# HttpListener total failure on SSL
2 Solutions for this problem:
If you need a quick fix for localhost (Just for localhost)
Thanks to SushiHangovers comment, setting the missing environment variable 'MONO_TLS_PROVIDER' to 'btls' (BoringSSL) fixed the problem.
$ MONO_TLS_PROVIDER=btls mono servertest.exe
If you need it to work on a server
Thanks to Gusman here is a short and simple to follow guide on how to use Nginx for managing SSL and sending Http requests to the Mono server.
1. First install/setup Nginx
www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-16-04
2. Then install Certbot
certbot.eff.org
3. And secure Nginx with Certbot
www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-16-04
4. Reverse proxy to your HttpListener port (taken from here)
4.1 Open the Nginx configfile
$ sudo nano /etc/nginx/sites-available/default
4.2 Comment out the default try_files line and specify the Reverse proxy settings
. . .
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
# try_files $uri $uri/ =404;
# Reverse proxy settings
include proxy_params;
proxy_pass http://localhost:8010;
}
. . .
And change
server_name _;
to
server_name example.com www.example.com;
Now you should be good to go.
How to use SSL with HttpListener with an mkbundle'd Mono app
Yes the path that HttpListener
expects to find certificates at is predefined, and cannot be specified by the user, programatically or through a config file. The Mono EndPointListener
class will look for the path:
~/.config/.mono/httplistener/
HttpListener code:
string dirname = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
string path = Path.Combine (dirname, ".mono");
path = Path.Combine (path, "httplistener");
As you have noted this is the same path the httpcfg
copies certificates to.
Even though you are using mkbundle
, this is still where HttpListener
will expect to read the certificate from, regardless of the fact that the Mono runtime is installed.
In your application startup, you should:
- Check for the existence of the directories, and create as required
- Write your certificate and key to that path from an embedded resource in your application. PouPou's answer here shows the method used by HttpCfg.exe.
Therefore eliminating the requirement to run httpcfg
, you will effectively be building that functionality straight into your application.
Does Mono perform any validation of the certificates it loads from there for HttpListener? i.e., will it expect to find the issuer's certificate in the certificate store?
I don't know for sure if Mono checks for a valid corresponding issuers certificate in the certificate store at the point of creating the listener, or upon each connection request. However you can add a CA cert to the certificate store yourself, or import all the standard Mozroot
certificates.
The full source code for Mozroots
is here. This shows how to import the CA certs.
Is the path to the certificate store also hard-coded?
The certificate store should be managed through the X509StoreManager
provider.
C# HTTPS Server on Ubuntu
This is dependent on the backend web server being used. On Ubuntu, you'd probably be using Kestrel. For Kestrel, an SSL certificate can be defined using:
"Kestrel": {
"Certificates": {
"Default": {
"Path": "<path>/fullchain.pem",
"KeyPath": "<path>/privkey.pem"
}
},
"Endpoints": {
"Http": {
"Url": "http://*:80"
},
"Https": {
"Url": "https://*:443"
}
}
in appsettings.json
For more information about how to set up SSL for Kestrel refer to: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/endpoints?view=aspnetcore-6.0
HttpListener (.NET Core) doesn't work with https on windows dockers
According to the "official" answer in the .NET Core forum:
"In general the HttpListener class is a legacy component we ported from .NET Framework (which is Windows only) to .NET Core. But HTTPS functionality is not supported on Linux. See: #19752
If you need a cross-platform HTTP/HTTPS server, we recommend you use ASP.NET Core. The 'Kestrel' component supports HTTPS fully and is available cross platform (Windows/Linux/Mac).
See: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-3.1"
Related Topics
How to Get My C# Program to Sleep for 50 Msec
Is There a Difference Between I==0 and 0==I
Load CSS Dynamically in ASP.NET
Operators as Method Parameters in C#
Why C# Implements Methods as Non-Virtual by Default
Webexception How to Get Whole Response with a Body
Ef Code-First One-To-One Relationship: Multiplicity Is Not Valid in Role * in Relationship
Loading Dlls into a Separate Appdomain
Use the Long Reserved Word as a Variable Name in C#
Establish a Link Between Two Lists in Linq to Entities Where Clause
ASP.NET Adding Class to Current Menuitem
Does Java Have Something Like C#'s Ref and Out Keywords
What's the Difference Between System.Type and System.Runtimetype in C#