Search code examples
asp.netvb.netdotnet-httpclientclient-certificates.net-4.8

The request was aborted: Could not create SSL/TLS secure channel. .NET Framework 4.8


I need to implement an API integration with Itaú bank (in Brazil).

There is a first call to authenticate and then I would call the action I need. But it won't go passed the authentication point.

I get the exception "The request was aborted: Could not create SSL/TLS secure channel." when using .NET Framework 4.8 on the published application.

I have the following code that returns the exception "The request was aborted: Could not create SSL/TLS secure channel.":

    '.NET Framework 2.0
    Public Sub New()
        ServicePointManager.ServerCertificateValidationCallback = New RemoteCertificateValidationCallback(AddressOf ValidateServerCertificate)
        ServicePointManager.SecurityProtocol = CType(&HC00, Net.SecurityProtocolType)
    End Sub
    
    Public Function ValidateServerCertificate(ByVal sender As Object, ByVal certificate As X509Certificate, ByVal chain As X509Chain, ByVal sslPolicyErrors As SslPolicyErrors) As Boolean
        Return True
    End Function

'.NET Framework 4.8
Private ReadOnly Property Certificado As X509Certificate2
    Get
        If m_Certificado Is Nothing Then
            Dim assembly As Assembly = Assembly.GetExecutingAssembly()
            Using stream As Stream = assembly.GetManifestResourceStream(GetType(AppModule), "certificado.pfx")
                If stream Is Nothing Then
                    Throw New ApplicationException("Certificado não encontrado.")
                End If

                Dim rawData(CInt(stream.Length)) As Byte
                stream.Read(rawData, 0, rawData.Length)

                m_Certificado = New X509Certificate2(rawData, "PASSWORD")
            End Using
        End If
        Return m_Certificado
    End Get
End Property

Public Function EnviarAutenticacao() As RetornoRequisicao
    Dim handler As New WebRequestHandler()
    Dim httpContent As StringContent = Nothing
    Dim responseMessage As HttpResponseMessage = Nothing
    Dim resposta As New RetornoRequisicao
    Dim sClient As String = ""

    Dim contentData As String = "grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET"

    Try
        handler.ClientCertificates.Add(Certificado)
        'With the 2 lines below, removing them from the constructor or leaving them in the constructor and removing from here
        handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12
        handler.ServerCertificateValidationCallback = New RemoteCertificateValidationCallback(AddressOf ValidateServerCertificate)

        Using client As New HttpClient(handler)
            Try
                httpContent = New StringContent(contentData, TextEncoding, "application/x-www-form-urlencoded")

                responseMessage = client.PostAsync(URLAutenticacao, httpContent).GetAwaiter().GetResult()

                If responseMessage.IsSuccessStatusCode Then
                    resposta.Status = CInt(HttpStatusCode.OK)
                Else
                    resposta.Status = CInt(responseMessage.StatusCode)
                End If
                resposta.Conteudo = responseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult()

            Finally
                Try
                    sClient = JsonConvert.SerializeObject(client, ErrorSerializerSettings)
                Catch
                End Try
            End Try
        End Using

    Catch ex As Exception
        Dim sbMensagem As New StringBuilder

        sbMensagem.AppendLine("Erro ao Autenticar")

        Try
            sbMensagem.AppendLine($"Handler:{vbCrLf}{JsonConvert.SerializeObject(handler, ErrorSerializerSettings)}")
        Catch
        End Try
        If Not String.IsNullOrEmpty(sClient) Then
            sbMensagem.AppendLine($"Client:{vbCrLf}{sClient}")
        End If
        Try
            sbMensagem.AppendLine($"Certificate:{vbCrLf}{JsonConvert.SerializeObject(Certificado, ErrorSerializerSettings)}")
        Catch
        End Try
        If Not String.IsNullOrEmpty(contentData) Then
            sbMensagem.AppendLine($"Content Data:{vbCrLf}{contentData}")
        End If
        If httpContent IsNot Nothing Then
            Try
                sbMensagem.AppendLine($"Http Content:{vbCrLf}{JsonConvert.SerializeObject(httpContent, ErrorSerializerSettings)}")
            Catch
            End Try
        End If
        If responseMessage IsNot Nothing Then
            Try
                sbMensagem.AppendLine($"Response Message:{vbCrLf}{JsonConvert.SerializeObject(responseMessage, ErrorSerializerSettings)}")
            Catch
            End Try
        End If
        Try
            sbMensagem.AppendLine($"Resposta:{vbCrLf}{JsonConvert.SerializeObject(resposta, ErrorSerializerSettings)}")
        Catch
        End Try

        Throw New ApplicationException(sbMensagem.ToString, ex)
    End Try

    Return resposta
End Function

I've tried different approaches to sending the request, such as the following, receiving the same error:

'.NET Framework 4.8
Protected Overrides Function EnviarAutenticacao() As RetornoRequisicao
    Dim request As HttpWebRequest = Nothing
    Dim requestResponse As HttpWebResponse = Nothing
    Dim resposta As New RetornoRequisicao

    Try
        request = CType(WebRequest.Create(URLAutenticacao), HttpWebRequest)
        request.ContentType = "application/x-www-form-urlencoded"
        request.Method = TipoEnvioAutenticacao
        request.ServerCertificateValidationCallback = New RemoteCertificateValidationCallback(AddressOf ValidateServerCertificate)

        Dim dataBody As String = "grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET"
        Dim body As Byte() = Encoding.ASCII.GetBytes(dataBody)
        request.ContentLength = body.Length

        Dim myWriter As Stream = Nothing
        request.ClientCertificates.Add(Certificado)

        myWriter = request.GetRequestStream()
        myWriter.Write(body, 0, body.Length)

        requestResponse = CType(request.GetResponse(), HttpWebResponse)
        resposta.Conteudo = New StreamReader(requestResponse.GetResponseStream()).ReadToEnd()

        If requestResponse.StatusCode >= 200 AndAlso requestResponse.StatusCode <= 299 Then
            resposta.Status = CInt(HttpStatusCode.OK)
        Else
            resposta.Status = CInt(requestResponse.StatusCode)
        End If

    Catch ex As Exception
        Dim sbMensagem As New StringBuilder

        sbMensagem.AppendLine("Erro ao Autenticar")

        Try
            sbMensagem.AppendLine($"Request:{vbCrLf}{JsonConvert.SerializeObject(request, ErrorSerializerSettings)}")
        Catch
        End Try
        If requestResponse IsNot Nothing Then
            Try
                sbMensagem.AppendLine($"Request Response:{vbCrLf}{JsonConvert.SerializeObject(requestResponse, ErrorSerializerSettings)}")
            Catch
            End Try
        End If
        Try
            sbMensagem.AppendLine($"Resposta:{vbCrLf}{JsonConvert.SerializeObject(resposta, ErrorSerializerSettings)}")
        Catch
        End Try

        Throw New ApplicationException(sbMensagem.ToString, ex)
    End Try

    Return resposta
End Function

This is a VB.NET web forms application with the web application and a project running on .NET Framework 4.8 and another project running on .NET Framework 2.0. The .NET Framework 4.8 project has a reference to the .NET Framework 2.0 project. The code above shows where each method is. The server is a Windows Server 2019 Standard.

Everything works fine while debuging on Visual Studio 2022 but doesn't when published on the server. On IIS the application pool is set to use .NET CLR version 4.0.30319. I tried installing the .NET Framework 4.8 Runtime on the server and it says it already has that or later version installed.

Below is the sbMensagem from the Catch with the Stack Trace:

Error Message:
Erro ao Autenticar
Handler:
{
  "AuthenticationLevel": 1,
  "ImpersonationLevel": 4,
  "AllowPipelining": true,
  "UnsafeAuthenticatedConnectionSharing": false,
  "MaxResponseHeadersLength": 64,
  "ReadWriteTimeout": 300000,
  "CachePolicy": {
    "Level": 1
  },
  "ContinueTimeout": "00:00:00.3500000",
  "ClientCertificates": [
    {
      "RawData": "RAW_DATA"
    }
  ],
  "ServerCertificateValidationCallback": null,
  "CheckCertificateRevocationList": false,
  "DefaultProxyCredentials": null,
  "MaxConnectionsPerServer": 2147483647,
  "Properties": {},
  "ServerCertificateCustomValidationCallback": null,
  "SslProtocols": 0,
  "SupportsAutomaticDecompression": true,
  "SupportsProxy": true,
  "SupportsRedirectConfiguration": true,
  "UseCookies": true,
  "CookieContainer": {
    "Capacity": 300,
    "Count": 0,
    "MaxCookieSize": 4096,
    "PerDomainCapacity": 20
  },
  "ClientCertificateOptions": 0,
  "AutomaticDecompression": 0,
  "UseProxy": true,
  "Proxy": null,
  "PreAuthenticate": false,
  "UseDefaultCredentials": false,
  "Credentials": null,
  "AllowAutoRedirect": true,
  "MaxAutomaticRedirections": 50,
  "MaxRequestContentBufferSize": 2147483647
}
Client:
{
  "DefaultRequestHeaders": [],
  "BaseAddress": null,
  "Timeout": "00:01:40",
  "MaxResponseContentBufferSize": 2147483647
}
Certificate:
{
  "RawData": "RAW_DATA"
}
Content Data:
grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET
Http Content:
{
  "Headers": [
    {
      "Key": "Content-Type",
      "Value": [
        "application/x-www-form-urlencoded; charset=utf-8"
      ]
    },
    {
      "Key": "Content-Length",
      "Value": [
        "127"
      ]
    }
  ]
}
Resposta:
{
  "Status": 0,
  "Conteudo": null
}
Exception ToString: System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel.
   at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context)
   at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
   --- End of inner exception stack trace ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at SIARM.NFSE.Data.ArrecadacaoPIXAPIItau.EnviarAutenticacao()
 (System.ApplicationException)
   at SIARM.NFSE.Data.ArrecadacaoPIXAPIItau.EnviarAutenticacao()
   at SIARM.NFSE.Data.ArrecadacaoPIXAPI.Autenticar()

Error Message:
An error occurred while sending the request. (System.Net.Http.HttpRequestException)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at ArrecadacaoPIXAPIItau.EnviarAutenticacao()

Error Message:
The request was aborted: Could not create SSL/TLS secure channel. (System.Net.WebException)
   at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context)
   at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)

Solution

  • It turns out we had to change a configuration on the IIS Application pool (got the answer from https://stackoverflow.com/a/27242467/8508096)

    1. Go to IIS Manager
    2. Go to the application pool instance
    3. Click advanced settings
    4. Under Process model, set Load User Profile to true