Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SignalR with Self-Signed SSL and Self-Host

Tags:

ssl

signalr

Tried my luck at research, but so far no joy.

I would like to connect a SignalR javascript client to a self-hosted SignalR Windows Service binding to a self-signed SSL certificate.

My application works quite well over http, but the client repetitively disconnects when the Owin WebApplication starts using https.

Here is what I've done to configure SignalR with SSL.

  1. Created a Self-Signed certificate using IIS
  2. Imported the certificate into the Trusted Root Certification Authorities in the mmc (not sure if that helped)
  3. Ran NETSH command to bind SSL to port 8080

    netsh http add sslcert ipport=0.0.0.0:8080 certhash=123456f6790a35f4b017b55d09e28f7ebe001bd appid={12345678-db90-4b66-8b01-88f7af2e36bf}
    
  4. Added code in self-hosted HubConnection instances to add exported SSL like this (though this shouldn't matter because it's the client that cannot connect):

    if (File.Exists("MyCert.cer") 
        && Settings.GetSetting(Settings.Setting.SrProtocol).Equals("https", StringComparison.InvariantCultureIgnoreCase))
            connection.AddClientCertificate(X509Certificate.CreateFromCertFile("MyCert.cer"));
    
  5. Starting Owin WebApplication using https (this should create the binding in http.sys)

    string registerUrl = string.Format("{0}://SOME.WHERE.COM:{1}", Service.Server.SrProtocol, Service.Server.SrPort);
    WebApp.Start<StartUp>(registerUrl);
    

In the SignalR 2.0 documentation, it says:

To start the web server, call WebApplication.Start(endpoint). You should now be able to navigate to endpoint/signalr/hubs in your browser.

When I browse to the URL http://SOME.WHERE.COM:8080/signalr/hubs I am successful receiving the javascript that drives SignalR.

When I browse to the URL https://SOME.WHERE.COM:8080/signalr/hubs I am unsuccessful and I receive "The connection to the server was reset" using FF.

Some additional points I've considered:

  • NETSH SHOW indicates the url is registered

    URL group ID: E300000240000022
    State: Active
    Request queue name: Request queue is unnamed.
    Properties:
        Max bandwidth: inherited
        Max connections: inherited
        Timeouts:
            Timeout values inherited
        Number of registered URLs: 1
        Registered URLs: HTTPS://SOME.WHERE.COM:8080/
    
  • NETSH SHOW indicates the SSL certificate is bound to 8080:

    IP:port                 : 0.0.0.0:8080 
    Certificate Hash        : 123456f6790a35f4b017b55d09e28f7ebe001bd
    Application ID          : {12345678-db90-4b66-8b01-88f7af2e36bf} 
    Certificate Store Name  : (null) 
    Verify Client Certificate Revocation    : Enabled
    Verify Revocation Using Cached Client Certificate Only    : Disabled
    Usage Check    : Enabled
    Revocation Freshness Time : 0 
    URL Retrieval Timeout   : 0 
    Ctl Identifier          : (null) 
    Ctl Store Name          : (null) 
    DS Mapper Usage    : Disabled
    Negotiate Client Certificate    : Disabled
    

Any help is greatly appreciated!

like image 518
ISZ Avatar asked Aug 09 '13 15:08

ISZ


1 Answers

I believe its all working for me now. Here is a run down of the steps I took to get things flowing:

SSL NOTES

SSL & SignalR (Owin WebApplication) requires binding a certificate to a port.

  1. Use IIS to generate an self-signed cert, this should place the certificate into the LOCAL COMPUTER > Personal > Certificates folder in CERTMGR
  2. In CERTMGR shift+drag certificate to LOCAL COMPUTER > Trusted Root Certification Authorities > Certificates folder, which should make a copy of it there
  3. Run the following command to bind the SSL certificate to 0.0.0.0:8080

    netsh http add sslcert ipport=0.0.0.0:8080 certhash=123456f6790a35f4b017b55d09e28f7ebe001bd appid={12345678-db90-4b66-8b01-88f7af2e36bf}  netsh http show urlacl > D:\urlacl.txt 

    Output:

    Reserved URL            : https://*:8080/  User: SOMEWHERE\Administrator Listen: Yes Delegate: No SDDL: D:(A;;GX;;;S-1-5-21-138209071-46972887-2260295844-1106)  
  4. Run the following NETSH command to reserve all IP addresses for port 8080 to the My Service application ID and service account

    netsh http add urlacl url=https://*:8080/ user=SOMEWHERE\Administrator listen=yes netsh http show sslcert > D:\sslcert.txt 

    Output:

    IP:port                 : 0.0.0.0:8080  Certificate Hash        : 123456f6790a35f4b017b55d09e28f7ebe001bd Application ID          : {12345678-db90-4b66-8b01-88f7af2e36bf}  Certificate Store Name  : (null)  Verify Client Certificate Revocation    : Enabled Verify Revocation Using Cached Client Certificate Only    : Disabled Usage Check    : Enabled Revocation Freshness Time : 0  URL Retrieval Timeout   : 0  Ctl Identifier          : (null)  Ctl Store Name          : (null)  DS Mapper Usage    : Disabled Negotiate Client Certificate    : Disabled 
  5. Update the MyServices.exe.config file to use https protocol (These are appSetting keys used to dynamically set the protocol and port of SignalR when My Service starts)

    <add key="SrProtocol" value="https" /> <add key="SrPort" value="8080" /> 
  6. Start the My Service using the NETSTAT START command

  7. Run the following NETSH command to show the service state is occupying the registered url

    netsh http show servicestate > D:\servicestate.txt 

    Output:

    Server session ID: C300000320000039 Version: 2.0 State: Active Properties:     Max bandwidth: 4294967295     Timeouts:         Entity body timeout (secs): 120         Drain entity body timeout (secs): 120         Request queue timeout (secs): 120         Idle connection timeout (secs): 120         Header wait timeout (secs): 120         Minimum send rate (bytes/sec): 150 URL groups: URL group ID: C600000340000138     State: Active     Request queue name: Request queue is unnamed.     Properties:         Max bandwidth: inherited         Max connections: inherited         Timeouts:             Timeout values inherited         Number of registered URLs: 1         Registered URLs:             HTTPS://*:8080/ 

My application does NOT depend on IIS, but once I used IIS to temporarily create a port binding to my SSL certificate, my application started to work, and I was able to inspect the NETSH servicestate to see how IIS does it. I have since dropped the IIS binding and ran through the setup notes, and still have success.

My Owing startup looks somethign like this:

private void configureMessaging() {     string registerUrl = string.Format("{0}://*:{1}", Service.Server.SrProtocol, Service.Server.SrPort);      try     { #if DEBUG         //System.Diagnostics.Debugger.Launch(); #endif         //  Starts an owin web application to host SignalR, using the protocol and port defined.         WebApp.Start<StartUp>(registerUrl);     }     catch (Exception ex)     {         Logger.Logs.Log(string.Format("Failed to configure messaging.  Exception: {0}", ex.RecurseInnerException()), LogType.Error);                      if (ex is HttpListenerException || ex.InnerException is HttpListenerException)         {             try             {                 Process p = new Process();                 p.StartInfo.UseShellExecute = false;                 p.StartInfo.RedirectStandardOutput = true;                 p.StartInfo.FileName = "netsh.exe";                 p.StartInfo.Arguments = string.Format("netsh http delete urlacl url={0}"                     , registerUrl                     );                 p.Start();                 p.StandardOutput.ReadToEnd();                 p.WaitForExit();             }             catch (Exception exP)             {                 Logger.Logs.Log(string.Format("Failed to delete urlacl {0}.  Exception: {1}"                     , registerUrl                     , exP.RecurseInnerException()                     )                     , LogType.Error                     )                     ;                  retries = 5;             }         }      if (retries < 5)     {         retries++;          Logger.Logs.Log(string.Format("Attempting to configure messaging again.  Attempt No. {0}", retries), LogType.Warn);          Thread.Sleep(1000);          configureMessaging();     }     else         Logger.Logs.Log(string.Format("Exceeded total number of retries to configure messaging.", retries), LogType.Error);      } 

}

And self-hosted HubConnetion instances look like this:

    public IHubProxy MyHubProxy     {         get         {             if (this._MyHubProxy == null)             {                 var connection = new HubConnection(string.Format("{0}://{1}:{2}/"                     , Settings.GetSetting(Settings.Setting.SrProtocol)                     , MyHub.GetLocalhostFqdn(null)                     , Settings.GetSetting(Settings.Setting.SrPort)                     )                     )                     ;                 this._MyHubProxy = connection.CreateHubProxy("MyHub");                  if (File.Exists("My.cer")                     && Settings.GetSetting(Settings.Setting.SrProtocol).Equals("https", StringComparison.InvariantCultureIgnoreCase))                     connection.AddClientCertificate(X509Certificate.CreateFromCertFile("My.cer"));                  connection.Start().Wait();             }              return this._MyHubProxy;         }     } 

There is a little more code here than relevant, but hopefully it may be of help!

like image 78
ISZ Avatar answered Sep 17 '22 14:09

ISZ