As I've described in several previous posts, self hosting SignalR is very straight forward to set up. It's easy enough to do, which is great if you need to hook SignalR as an event source to standard Windows based application such as a Service or even a WPF or Winforms desktop application that needs to send push notifications to many users.
One aspect of self-hosting that's not quite so transparent or documented though, is running a self hosted SignalR service under SSL. The Windows certificate store and creation, configuration and installation of certificates is still a pain as there's no UI in Windows that provides linking endpoints to certificates and the process is not very well documented end to end. It's easy enough once you know what command line tool you need to call, but this process certainly could be a little smoother and better documented. Hence I'm rehashing this topic here to provide a little more detail and hopefully a more coherent description of setting up a certificate for self-hosting an OWIN service in general and specifically for SignalR.
Self-Hosting and OWIN
When you're self hosting SignalR you're essentially using the hosting services provided by OWIN/Katana. OWIN is a low level spec that for implementing custom hosting providers that can be used interchangeably. The idea is to decouple the hosting process from a specific implementation and make it pluggable, so you can choose your hosting implementations.
Katana is Microsoft's implementation of OWIN, that provides a couple of specific implementations. For self-hosting there's the HttpListener based host which is completely decoupled from IIS and its infrastructure. For hosting inside of ASP.NET there also is an ASP.NET based implementation that is used for SignalR apps running inside of ASP.NET. Both implementations provide the base hosting support for SignalR, so that for the most part the same code base can be used for running SignalR under ASP.NET or under your own self-hosted EXEs like services, console or desktop apps.
Binding certificates to SSL Ports for Self-Hosting
Self hosting under HttpListener is wonderful and completely self-contained, but one of the downsides of not being part of IIS is that it also doesn't know about certificates that are installed for IIS, which means that certificates you want to use have to be explicitly bound to a port. Note that you can use IIS certificates and if you need to acquire a full certificate for use with a self-hosted application, going through the IIS certificate process is the easiest way to get the certificate loaded. If you need a certificate for local testing too IIS's self-signed certificate creation tool makes that very easy as well (I'll describe that below).
For now let's assume you already have a certificate installed in the Windows certificate store. In order to bind the certificate to a self-hosted endpoint, you have to use the netsh
command line utility to register it on the machine (all on one line):
netsh http add sslcert ipport=0.0.0.0:8082
appid={12345678-db90-4b66-8b01-88f7af2e36bf}
certhash=d37b844594e5c23702ef4e6bd17719a079b9bdf
For every endpoint mapping you need to supply 3 values:
The ipport which identifies the ip and port
Specified as ipport=0.0.0.0:8082 where the zeros mean all ip addresses on port 8082. Otherwise you can also specify a specific Ip Address.
The certhash which is the Certificate's Thumbprint
The certhash is the id that maps the certificate to the IP endpoint above. You can find this hash by looking at the certificate in the Windows Certificate store. More on this in a minute.
An AppID which is fixed for HttpListener Hosting
This value is static magic value so always use appid={12345678-db90-4b66-8b01-88f7af2e36bf}
.
Once the above command has been run you should check if it worked by looking at the binding. Use this:
netsh http show sslcert ipport=0.0.0.0:8082
which gives you a display like this:
Finding the CertHash
I mentioned the certhash above: To find the certhash, you need to find the certificate's ThumbPrint which can be found in a couple of ways using:
- The IIS Certificate Manager
- The Windows Certificate Storage Manager
Using IIS to get Certificate Info
If IIS is installed the former is the easiest. Here you can easily see all installed certificates and this UI is also the easiest way to create local self-signed certificates.
To look up an existing certificate, simply bring up the IIS Management Console, go to the Machine node, then Server Certificates:
You can see the certificate hash in the rightmost column. You can also double click and open the certificate and go in the Details of the certificate. Look for the thumbprint which contains the hash.
Unfortunately neither of these places makes it easy to copy the hash, so you either have to copy it manually or remove the spaces from the thumbprint data in the dialog.
Using IIS to create a self-signed Certificate
If you don't have a full server certificate yet, but you'd like to test with SSL operations locally you can also use the IIS Admin interface to very easily create a self-signed certificate. The IIS Management console provides one of the easiest ways to create a local self-signed certificate.
Here's how to do it:
- Go to the machine root of the IIS Service Manager
- Go to the Server Certificates Item in the IIS section
- On the left click Create Self-Signed Certificate
- Give it a name, and select the Personal store
- Click OK
That's all there is to create the self-signed local certificate.
Copy the self-signed Certificate to the Trusted Root Certification Store
Once you have a self-signed certificate, you need one more step to make the certificate trusted, so Http clients will accept it on your machine without certificate errors. The process involves copying the certificate from the personal store to the trusted machine store.
To do this:
- From the StartMenu use Manage Computer Certificates
- Go into Personal | Certificates and find your certificate
- Drag and Copy (Ctrl-Drag) the certificate to Trusted Root Certificates | Certificates
You should now have a certificate that browsers will trust. This works fine for IE, Chrome and Safari, but FireFox will need some special steps (thanks to Eric Lawrence) and Opera also requires specific registration of certificates.
Using a full IIS Certificate
Self signed certificates are great for testing under SSL to make sure your application works, but it's not so nice for production apps as the certificate would have to be installed on any machine you'd expect to trust this certificate which is a hassle.
Once you go to production, especially public production you'll need an 'official' certificate signed by a one of the global certificate authorities for $$$ (or these days LetsEncrypt).
The easiest way to do this is to purchase or generate a full IIS certificate and install it in IIS. The IIS certificate can also be used for self-hosted applications using the HttpListener so it will work just fine with a self-hosted SignalR or any HttpListener application.
So once the time comes to go live, register a new certificate through IIS, then use netsh http add sslcert
to register that certificate as shown above. A public SSL certificate in most cases is already recognized so no further certificate store moving is required - all you need is the netsh registration to tie it to a particular port and app Id.
Running SignalR with SSL
With the certificate installed, switching SignalR to start with SSL is as easy as changing the startup URL.
Self Hosted Server Configuration
In the self hosted server, you now specify the new SSL URL in your startup factory invocation:
var signalR = WebApp.Start<SignalRStartup>([https://*:8082/](https://*:8082/));
This binds SignalR to the all ip addresses on port 8082. You can also specify a specific IP address, but using * is more portable especially if you set the value as part of a shared configuration file.
If you recall from my last self-hosting post, OWIN uses a startup class (SignalRStartup in this case) to handle OWIN and SignalR HubConfiguration, but the only thing that needs to change is the startup URL and your self-hosted server is ready to go.
SignalR Web App Page Url Configuration
On the Web Page consume the SignalR service to hubs or connections change the script URL that loads up the SignalR client library for your hubs or connections like this:
<script src="[https://RasXps:8082/signalr/hubs">script>
where RasXps here is my exact local machine name that has the certificate registered to it. As with all certificates make sure that the domain name matches the certificate's name exactly. For local machines that means don't use localhost if the certificate is assigned to your local machines NetBios name as it is by default. Don't use your IP address either - use whatever the certificate is assigned to.
You'll also need to assign the hub Url to your SSL url as part of the SignalR startup routine that calls $connection.hub.start:
$.connection.hub.url = self.hubUrl; // ie. "[https://rasxps:8082/signalR](https://rasxps:8082/signalR);"
For more context here's a typical hub startup/error handler setup routine that I use to get the hub going:
startHub: function () {
$.connection.hub.url = self.hubUrl; // ie. "https://rasxps:8082/signalR";
// capture the hub for easier access
var hub = $.connection.queueMonitorServiceHub;
// This means the <script> proxy failed - have to reload
if (hub == null) {
self.viewModel.connectionStatus("Offline");
toastr.error("Couldn't connect to server. Please refresh the page.");
return;
}
// Connection Events
hub.connection.error(function (error) {
if (error)
toastr.error("An error occurred: " + error.message);
self.hub = null;
});
hub.connection.disconnected(function (error) {
self.viewModel.connectionStatus("Connection lost");
toastr.error("Connection lost. " + error);
// IMPORTANT: continuously try re-starting connection
setTimeout(function () {
$.connection.hub.start();
}, 2000);
});
// map client callbacks
hub.client.writeMessage = self.writeMessage;
hub.client.writeQueueMessage = self.writeQueueMessage;
hub.client.statusMessage = self.statusMessage;
…
// start the hub and handle after start actions
$.connection.hub
.start()
.done(function () {
hub.connection.stateChanged(function (change) {
if (change.newState === $.signalR.connectionState.reconnecting)
self.viewModel.connectionStatus("Connection lost");
else if (change.newState === $.signalR.connectionState.connected) {
self.viewModel.connectionStatus("Online");
// IMPORTANT: On reconnection you have to reset the hub
self.hub = $.connection.queueMonitorServiceHub;
}
else if (change.newState === $.signalR.connectionState.disconnected)
self.viewModel.connectionStatus("Disconnected");
})
.error(function (error) {
if (!error)
error = "Disconnected";
toastr.error(error.message);
})
.disconnected(function (msg) {
toastr.warning("Disconnected: " + msg);
});
self.viewModel.connectionStatus("Online");
// get initial status from the server (RPC style method)
self.getServiceStatus();
self.getInitialMessages();
});
},
From a code perspective other than the two small URL code changes there isn't anything that changes for SSL operation, which is nice.
And… you're done!
SSL Configuration
SSL usage is becoming ever more important as more and more application require transport security. Even if your self-hosted SignalR application doesn't explicitly require SSL, if the SignalR client is hosted inside of a Web page that's running SSL you have to run SignalR under SSL, if you want it to work without browser error messages or failures under some browsers that will reject mixed content on SSL pages.
SSL configuration is always a drag, as it's not intuitive and requires a bit of research. It'd be nice if the HttpListener certificate configuration would be as easy as IIS configuration is today or better yet, if self-hosted apps could just use already installed IIS certificates. Unfortunately it's not quite that easy and you do need to run a command line utility with some magic ID associated with it.
Installing a certificate isn't rocket science, but it's not exactly well documented. While looking for information I found a few unrelated articles that discuss the process but a few were dated and others didn't specifically cover SignalR or even self-hosting Web sites. So I hope this post makes it a little easier to find this information in the proper context.
This article focuses on SignalR self-hosting with SSL, but the same concepts can be applied to any self-hosted application using HttpListener.
Resources
Other Posts you might also like