Hosting SignalR under SSL/https
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 useappid={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
- Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
- Use IIS Application Initialization for keeping ASP.NET Apps alive
- Smoothing out <div> scrolling in Mobile WebKit Browsers
- Re-examining ASP.NET and Helios Performance Tests
- Self-Hosting SignalR in a Windows Service
The Voices of Reason
# re: Hosting SignalR under SSL/https
Here is a great tool that I stumbled upon by Rafaele (http://www.iamraf.net/) for handeling certificates and getting the thumbprint without clutter.
Check it out http://www.iamraf.net/Downloads/DeployManager.exe
# re: Hosting SignalR under SSL/https
# re: Hosting SignalR under SSL/https
# re: Hosting SignalR under SSL/https
Clients.client(connectionID).publishMessage()
However, when sending a message back to a client caller it works fine using this method:
Clients.Caller.publishMessage()
The only thing that changed recently was the connection string to the database server.
Have you run into this problem with HTTPS ? Thanks.
# re: Hosting SignalR under SSL/https
"The remote certificate is invalid according to the validation procedure."
Is there a way to tell SignalR to not worry about self signed certificates?
# re: Hosting SignalR under SSL/https
# re: Hosting SignalR under SSL/https
# re: Hosting SignalR under SSL/https
# re: Hosting SignalR under SSL/https
What kind of SSL certificate do i have to create to distribute to multiple clients to communicate. How do i register the SSL certificate in local system and communicate over it. Any pointers over this will be of great help.
Thanks in anticipation.
# re: Hosting SignalR under SSL/https
If you want a certificate that 'just works' on machines then you have to use a certificate authority signed certificate from a full CA. These days you can use Lets Encrypt to create these certificates for IIS and then use them in your SignalR appplications. For more info see:
* https://weblog.west-wind.com/posts/2016/Feb/22/Using-Lets-Encrypt-with-IIS-on-Windows
and
* https://weblog.west-wind.com/posts/2016/Jul/09/Moving-to-Lets-Encrypt-SSL-Certificates
# re: Hosting SignalR under SSL/https
Had same issue as Richard L and used their solution. Excellent comment Richard.
# re: Hosting SignalR under SSL/https
Hi thanks for a great article..
I wonder if anyone has an input on doing this from app.config only. I use "Lets encrypt" certificates (valid 3 month and i autorenew) I dont want to "netsh" with new thumbprint, every 3 month. I want to find the cert in store by name and use it in my windows service.
Any comments?
# re: Hosting SignalR under SSL/https
Hi Rick, Thanks for great post. I have developed SignalR HttpListener based host. My web application communicates with this SignalR host installed on local machine. It works fine with HTTP.
I have tried to apply SSL certificate (HTTPS) using SelfSSL on SignalR based host (certificate issued to HOSTNAME), but when my web page(HTTPS) access signalR host (HTTPS) it does not connect with SignalR host and proxy is coming undefined.(CONNECTION_REFUSED error)
when i open SignalR proxy on browser tab and add it in Exception List(Security Exception) of browser, it connect with signalR host, but browser turns from secure site to Not Secure site. How can i resolve this Not Secure error comes in browser address bar? How can i make it working without adding in security exception list of browser?
If i want to use SignalR host on 100 computers, Is there any easy way i can install certificate on hundred of computers? Do i have to buy SSL certificate issued to local HostName from Certificate autority? If yes, single wildcard certificate can work for all 100 computers? OR i will have to buy for each computers seperately? Please guide.
# re: Hosting SignalR under SSL/https
If my IIS is using the valid certificate and I want to apply the same certificate to a self-hosted SignalR I have no problem?
Ex: https://www.contoso.com.br = My site running with SSL Ex: https://www.contoso.com.br:9090 = my self-hosted SignalR running with the same certificate
I get this scenario?
# re: Hosting SignalR under SSL/https
4 years on and this article still seems relevant today. We're struggling with an HTTPS setup for SignalR, but not for the obvious reasons. We seem to have an issue that only occurs when there's an HTTPS binding and reliably disappears when we remove the HTTPS binding and use HTTP only. However, we have had the same code working fine on HTTPS and sometimes it seems to be okay... Due to a recent bunch of tests (swapping in/out various bindings), we are convinced it's something to do with our code on an HTTPS setup, but the fact it works sometimes on HTTPS is really frustrating!
We're getting the following error chain: System.Net.Http.HttpRequestException: An error occurred while sending the request. --→ System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. --→ System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. --→ System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
I'm currently thinking it has to be that we're not making enough attempts to restart the connection on disconnect, but any advice would be welcome. Many thanks!
# re: Hosting SignalR under SSL/https
Hi Rick, thanks for the solution. When I started with SignalR you blog helped a lot and it still does.
I have a question...do you perhaps know why when I run netsh http show servicestate the registered urls is different to the certificate I loaded. I bound to https://abc.domnain.xx.xx:8088/MyUri but in servicestate it show HTTPS://ABC.DOMAIN.xx.xx:443/SINGALR/:8088
When I browse to my url to see if the Hub is started I get 'resource (url) is online but are not responding to conncetion attempts'
Could you please help and guide.
Thanks
# re: Hosting SignalR under SSL/https
Thanks...got it sorted. It was the MyUri portion I used at the end of my netsh command. Once I removed that it all worked fine.
Thanks again for your blog it is a real reference when getting stuck.
# re: Hosting SignalR under SSL/https
Thanks Rick for the great article.
Could you please give inputs on implementing browser client side certificate verification. I mean, two way certificate verification.
# re: Hosting SignalR under SSL/https
I get the same error as Oliver Clare:
System.Net.Http.HttpRequestException: An error occurred while sending the request. --->
System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. --->
System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. --->
System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
The strange thing is, I get the exception only in my .net client using SSL. The same implementation is working like a charm in .net without SSL or in a web client with SSL.
At the moment, I am running out of ideas what could be wrong...
# re: Hosting SignalR under SSL/https
I meant to revisit this a while ago. In response to my TLS question: when Big Brother pushed the SChannel regedits to our machines, they didn't set the "DisabledByDefault" key to "0" for TLS 1.2. For machines running Windows 7, TLS 1.2 is installed, but disabled, so it is necessary to set this key value as per Microsoft's instruction. As soon as I added it manually and restarted, everything worked no problem.
# re: Hosting SignalR under SSL/https
I have same problem with Junaid where my webapp is hosted on one server, but my signalR service is hosted on a different server. They are both using selfsigned certs and I've set things up properly on the server side. The problem is I get this error on the browser client side: Error in connection establishment: net::ERR_CERT_AUTHORITY_INVALID
Now if I open another tab in same browser to the server hosting the signalR service and click advanced and proceed to unsafe site, then everything works. Is it possible on the javascript client side to ignore cert warnings? I know we can do this in chrome by changing the chrome setting but this would be much easier if I could do this in javascript code. I have a feeling it's not possible due to security reasons but can someone confirm? I know only other options are to get a paid cert, add cert to client's trusted root store, or have it added on the domain controllers so all computers on domain have it trusted.
# re: Hosting SignalR under SSL/https
hi,where is the “App ID”? I can‘t find it!
netsh http add sslcert ipport=0.0.0.0:8082
**appid={12345678-db90-4b66-8b01-88f7af2e36bf} **
certhash=d37b844594e5c23702ef4e6bd17719a079b9bdf
# re: Hosting SignalR under SSL/https
As the post says: "The AppId is a hardcoded value that never changes". I presume it's specific to HttpHandler.
# re: Hosting SignalR under SSL/https
For those with the same issue as Olivier Clare and Markus Wagner, it's probably because of TLS version not enabled for the version of net framework you use. I had the same issue and also took me a while to find the root cause. Following this StackOverflow link for details: https://stackoverflow.com/a/46223433
# re: Hosting SignalR under SSL/https
Hi i read through your post i am also facing a similar issue not sure what is the correct way to resolve, it works in http://localhost:port/signalr but when i host it in IIS with https://domain.com/api/signalr it says and http error 404 with the following error message No HTTP resource was found that matches the request URI http://domain.com/api/signalr/negotiate?clientProtocol=2.0&connectionData=[{"name":"progress"}]&_=1551264914856' SignalR error: Error: Error during negotiation request. This is my client js call $(function () {
//$.support.cors = true;
$.connection.hub.logging = true;
$.connection.hub.url = Config.APIHost + "api/signalr";
var chat = $.connection.progress;
chat.client.addProgress = function (message, percentage) {
ProgressBarModal("show", message + " " + percentage);
$('#ProgressMessage').width(percentage);
if (percentage == "100%") {
ProgressBarModal();
}
};
$.connection.hub.start().done(function () {
//debugger;
var connectionId = $.connection.hub.id;
console.log(connectionId);
});
$.connection.hub.error(function (error) {
//debugger;
console.log('SignalR error: ' + error)
});
});
The request header although has the https://domain.com/api/signalr/negotiate?clientProtocol=2.0&connectionData=[{"name":"progress"}]&_=1551264914856'
I am really not clear on what is wrong here.
# re: Hosting SignalR under SSL/https
This is one of those things that I don't need very often but when I do I've forgotten everything and need to look it all up again 😃
Just a note that netsh has been "superseded" by powershell these days so the above commands can actually be done using the following:
netsh http add sslcert ipport=0.0.0.0:8082
appid={12345678-db90-4b66-8b01-88f7af2e36bf}
certhash=d37b844594e5c23702ef4e6bd17719a079b9bdf
translates to
Add-NetIPHttpsCertBinding -IpPort "0.0.0.0:8282" -ApplicationId "{12345678-db90-4b66-8b01-88f7af2e36bf}" -CertificateHash "d37b844594e5c23702ef4e6bd17719a079b9bdf" -CertificateStoreName "My" -NullEncryption $false
you might also need
New-SelfSignedCertificate -Subject "CertSubject"
# re: Hosting SignalR under SSL/https
Hi Rick
Myself Aravind from India. I have created certificate using Opc.Ua.Generator and did port binding for my application certificate. Initially I was getting certificate issues on the client side hub saying "The underline connection was closed.Could not establish true relationship for SSL/TLS secure channel." I got this issue fixed by changing the Certificate name to Host name of the system which is running SignalR service. After this i was able to setup chat and communicate successfully with 3 systems in the same domain but this didn't work out when I took the setup to a different domain. I ended up in the former error and applying of the Host name didn't work out for the other network. Is there any problem in the fix I have applied or any alternate solution is there, Please let me know.
# re: Hosting SignalR under SSL/https
One thing to beware of though - when copying the thumbprint from Certificate Manager in the details window, it appears that a hidden character gets copied at the start. You can check this by using the cursor to move through it - delete it if it's there! Leaving it in caused the "The Parameter is Incorrect" message for me, when the command on the command prompt *looked* correct. This was at least the case for me on Windows 8 and Server 2012.