0

I am building a Web Api (using ASP.NET Web API), that connects via Secure WebSockets to an endpoint that our client exposed (wss://client-domain:4747/app/engineData). They gave me their certificates all in .pem format (root.pem and client.pem), and a private key (client_key.pem).

In order to get this done I did the following:

1) Converted client.pem and client_key.pem to a single .pfx file (used this here: Convert a CERT/PEM certificate to a PFX certificate)

2) I used the library System.Net.WebSockets, and wrote the following code:

private void InitWebSockesClient()
{
    client = new ClientWebSocket();
    client.Options.SetRequestHeader(HEADER_KEY, HEADER_VALUE); //Some headers I need
    AddCertificatesSecurity();
}

private void AddCertificatesSecurity()
{
     ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
         | SecurityProtocolType.Tls11
         | SecurityProtocolType.Tls12;

     // I KNOW THIS SHOULDNT BE USED ON PROD, had to use it to make it 
     // work locally.
     ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; 

      X509Certificate2 x509 = new X509Certificate2();
      // this is the pfx I converted from client.pem and client_key
      byte[] rawData = ReadFile(certificatesPath + @"\cert.pfx");
      x509.Import(rawData, "123456", X509KeyStorageFlags.UserKeySet);

      X509Certificate2Collection certificateCollection = new X509Certificate2Collection(x509);

      client.Options.ClientCertificates = certificateCollection;
 }

And when I want to connect I call:

 public async Task<bool> Connect()
 {
    Uri uriToConnect = new Uri(URL);
    await client.ConnectAsync(uriToConnect, CancellationToken.None);
    return client.State == WebSocketState.Open;
 }

This works fine locally. But whenever I deploy my Web Api on Azure (App Service) and make an HTTP request to it, it throws:

System.Net.WebSockets.WebSocketException - Unable to connect to the remote server.

And the inner exception:

System.Net.WebException - The request was aborted: Could not create SSL/TLS secure channel.

I enabled WebSockets on the AppService instance.

If I delete the line that always return true for the certificate validation, it doesn't work even locally, and the message says something like:

The remote certificate is invalid according to the validation procedure.

So definitely I got something wrong with the certificates, those three .pem files are being used right now in a similar [![enter image description here][1]][1]app in a node.js and work fine, the WSS connection is established properly. I don't really know what usage give to each one, so I am kind of lost here.

These are the cipher suites of the domain I want to connect: https://i.stack.imgur.com/ZFbo3.png

  • 1
    Have you referenced [Use an SSL certificate in your application code in Azure App Service](https://learn.microsoft.com/en-us/azure/app-service/app-service-web-ssl-cert-load)? If not, please have a try to upload the pfx to azure WebApp and add an app setting called WEBSITE_LOAD_CERTIFICATES and set its value to the thumbprint of the certificate. You also could get the demo code how to use the cert from the document. – Tom Sun - MSFT Jan 24 '18 at 02:17
  • Thank you Tom! It was that :) – Gabriel Piffaretti Jan 25 '18 at 03:43

1 Answers1

1

Inspired by Tom's comment, I finally made it work by just adding the certificate to the Web App in Azure App Service, instead of trying to use it from the filesystem. First I uploaded the .pfx file in the SSL Certificates section in Azure. Then, in the App settings, I added a setting called WEBSITE_LOAD_CERTIFICATES, with the thumbprint of the certificate I wanted (the .pfx).

After that, I modified my code to do work like this:

private void InitWebSockesClient()
{
     client = new ClientWebSocket();
     client.Options.SetRequestHeader(HEADER_KEY, HEADER_VALUE); //Some headers I need
     AddCertificateToWebSocketsClient();
}

private void AddCertificateToWebSocketsClient()
{
     ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11
                        | SecurityProtocolType.Tls12;

     // this should really validate the cert
     ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; 

    // reading cert from store
    X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
    certStore.Open(OpenFlags.ReadOnly);
    X509Certificate2Collection certCollection = 
           certStore.Certificates.Find(X509FindType.FindByThumbprint,
                            CERTIFICATES_THUMBPRINT,
                            false);

     if (certCollection.Count > 0)
     {
         client.Options.ClientCertificates = certCollection;
     }
     else
     {
         // handle error
     }
     certStore.Close();
}

Where CERTIFICATES_THUMBPRINT is a string (thumbsprint of your certificate, the one you saw on Azure).

In case you want to make it work locally, you just need to install the certificate on your computer, as otherwise it won't obviously find it on the store.

Reference for all this in Azure docs: https://learn.microsoft.com/en-us/azure/app-service/app-service-web-ssl-cert-load.