"Received an unexpected EOF or 0 bytes from the transport stream" Azure WebService
Asked Answered
M

0

9

I have the following issue: I have built a webservice that is supposed to run as an Azure Webservice, which calls an external API retrieve some data. For the connection, the external company has provided me with a certificate which I succesfully stored in the Azure Keyvault. After some trial and error I managed to verify that the certificate is succesfully being retrieved from the vault as I locally tested the code block below. This resulted in a success response with the data I wanted to retrieve. So far so good - until I publish this to Azure. Everything seems to work fine, however I get the following error message:

Received an unexpected EOF or 0 bytes from the transport stream

I have exhausted the first 5 pages of google. I keep finding answers regarding TLS, but I explicitly set it to the correct version as I tested the options (even in this code snippet, i am actually good only using TLS 1.2).

What causes this? Is it some mysterious Azure config setting that I am missing? As I said, the code works 100% on my computer. The issue seems not to be the key or the vault. The biggest difference is that locally I run the code as the tenant admin, however I provided the app service with a managed identity with the correct permissions to the keyvault.

I will also provide the stacktrace. var result = await client.GetAsync(url);

The error is thrown at

var result = await client.GetAsync(url);

I hope I have provided enough information. Thank you in advance

Code snippet:

 public async Task<string>GetData(string url) {
  try {
    var client = await GetHttpClient();
    var result = await client.GetAsync(url);
    if (result.IsSuccessStatusCode) {
      var content = await result.Content.ReadAsStringAsync();
      return content;
    } else {
      Exception ex = new Exception("Failed getting data: " + result.Content.ReadAsStringAsync().Result);
      throw ex;
    }
  } catch (Exception ex) {
    _telemetry.TrackException(ex);
    throw;
  }
}
public async Task<HttpClient> GetHttpClient() {
  var cert = await GetCertFromVault();
  var handler = new HttpClientHandler() {
    ClientCertificateOptions = ClientCertificateOption.Manual,
    SslProtocols = SslProtocols.Tls 
    | SslProtocols.Tls11 
    | SslProtocols.Tls12,
  };
  handler.ClientCertificates.Add(cert);
  handler.ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true;
  var client = new HttpClient(handler);
  return client;
}

async Task<X509Certificate2> GetCertFromVault() {
  try {
    string keyVaultUrl = _configuration["KeyVault"];
    string certKeyName = _configuration["CertKeyName"];
    
    var cred = new DefaultAzureCredential();

    //Get certificate as secret as to include the key:
    var keyVaultClient = new SecretClient(new Uri(keyVaultUrl), cred);
    var certResponse = await keyVaultClient.GetSecretAsync(certKeyName);
    byte[] cert = System.Convert.FromBase64String(certResponse.Value.Value);

    //Convert retrieved values to X509Certificate2
    var resultKey = new X509Certificate2(cert, "", X509KeyStorageFlags.MachineKeySet
                         | X509KeyStorageFlags.PersistKeySet
                         | X509KeyStorageFlags.Exportable);
    return resultKey;

  } catch (Exception ex) {
    _telemetry.TrackException(ex);
    throw;
  }
}

Stacktrace:

System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.IO.IOException:  Received an unexpected EOF or 0 bytes from the transport stream.
   at System.Net.Security.SslStream.<FillHandshakeBufferAsync>g__InternalFillHandshakeBufferAsync|182_0[TIOAdapter](TIOAdapter adap, ValueTask`1 task, Int32 minSize)
   at System.Net.Security.SslStream.ReceiveBlobAsync[TIOAdapter](TIOAdapter adapter)
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Boolean async, Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Boolean async, Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.SendAsyncCore(HttpRequestMessage request, HttpCompletionOption completionOption, Boolean async, Boolean emitTelemetryStartStop, CancellationToken cancellationToken)
   at MyService.MyClient.GetData(String url) in [path]\MyClient.cs:line 40

Line 40 corresponds with

var result = await client.GetAsync(url);
Mays answered 26/7, 2022 at 14:45 Comment(7)
Use a sniffer like wireshark and look at the TLS packets. The version of TLS will be seen in the sniffer. The server sends a TLS certificate block with names of certificate. Make sure your certificate name is in the block. Also open the certifcate and check encryption mode and expiration date. Valid encryption modes for TLS 1.2 is shown here : en.wikipedia.org/wiki/…. If sniffer shows the HTTP Request being sent than TLS is passing. TLS is performed before the HTTP request is sent.Courtland
How can I use such software (I use fiddler) in combination with Azure then? Because locally everything works - I use the same certificate from the same keyvault. Azure AppInsights gives me some info, but not TLS info. The only thing I can see is 'faulted' - which I guess is not a response code from the server but indicating that the connection failed before the packet is sent.Mays
I also found this resource: learn.microsoft.com/en-us/azure/app-service/… It describes a different way of configuring a TLS certificate in Azure which I dont use because I use our dev environment / free tier, in which it is unavailable. I currentlyl get my certificate from the keyvault, but i could be that I need to configure it here. Is there anyone with more Azure knowledge who can confirm this?Mays
Fiddle can be run on any machine in the Ethernet subnet that can see the messages. The certificate has to be loaded on the client user stores and the machine stores. Ethernet packets are broadcast to all machine in the subnet. Some protocols like TCP are a connection between only two machines, but the other machines in the subnet still get the packets. Azure is a virtual operating system (Linux) on the machine. Since TLS is performed by the operating system I think you have to do the Azure configuration.Courtland
I'm encountering this exact same problem. The problem only happens when running in Azure but not locally. Have you tried running a curl command? I did and got a similar error about receiving an unexpected EOF, which points to either a problem in Azure or with the API.Pastorale
Hi, same problem here. Did anyone manage to find out what the problem was?Mender
I never found out why.. I switched companies a while back and this never got solved. From the top of my head it might be that the cert was created/encoded on my machine, and that it won't work on an Azure machine since it has a different machine key. Unfortunately, I don't know all the details anymore.Mays

© 2022 - 2024 — McMap. All rights reserved.