c# - TLS 1.2 no se negocia en.NET 4.7 sin una llamada explícita a ServicePointManager.SecurityProtocol
winforms httpwebrequest (4)
Necesito actualizar una aplicación .NET para admitir una llamada a una API en un sitio web que solo admita TLS 1.2. Por lo que leí, si la aplicación está orientada a 4.6 o más, entonces usará TLS 1.2 por defecto.
Para probar, creé una aplicación de Windows Forms que tiene como objetivo 4.7. Desafortunadamente, se produce un error cuando no configuro explícitamente ServicePointManager.SecurityProtocol. Aquí está el código:
HttpClient _client = new HttpClient();
var msg = new StringBuilder();
// If I uncomment the next line it works, but fails even with 4.7
// ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://sandbox.authorize.net");
httpWebRequest.KeepAlive = false;
try
{
var httpWebResponse = (HttpWebResponse) httpWebRequest.GetResponse();
msg.AppendLine("The HTTP request Headers for the first request are: ");
foreach (var header in httpWebRequest.Headers)
{
msg.AppendLine(header.ToString());
}
ResponseTextBox.Text = msg.ToString();
}
catch (Exception exception)
{
ResponseTextBox.Text = exception.Message;
if (exception.InnerException != null)
{
ResponseTextBox.Text += Environment.NewLine + @" ->" + exception.InnerException.Message;
if (exception.InnerException.InnerException != null)
{
ResponseTextBox.Text += Environment.NewLine + @" ->" + exception.InnerException.InnerException.Message;
}
}
}
Si no comenta la siguiente línea:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
funciona. Esta no es una buena solución ya que codifica la versión de TLS que se usará, por lo que no usará TLS 1.3 en el futuro.
¿Qué más debo hacer para que funcione sin tener esta línea? Estoy probando desde una máquina Windows 10 con 4.7 instalado.
Actualizar
Probé una prueba con HttpClient y obtuve los mismos resultados, tuve que configurar de forma explícita SecurityProtocol.
Código:
var msg = new StringBuilder();
// Need to uncomment code below for TLS 1.2 to be used
// ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
try
{
var response = await _client.GetAsync(@"https://sandbox.authorize.net");
msg.AppendLine("response.IsSuccessStatusCode : " + response.IsSuccessStatusCode);
msg.AppendLine(await response.Content.ReadAsStringAsync());
textBox.Text = msg.ToString();
}
catch (Exception exception)
{
textBox.Text = exception.Message;
if (exception.InnerException != null)
{
textBox.Text += Environment.NewLine + @" ->" + exception.InnerException.Message;
}
}
A partir de las aplicaciones que apuntan a .NET Framework 4.7, el valor predeterminado de la propiedad ServicePointManager.SecurityProtocol es SecurityProtocolType.SystemDefault .
Este cambio permite que las API de redes de .NET Framework basadas en SslStream (como FTP, HTTPS y SMTP) hereden los protocolos de seguridad predeterminados del sistema operativo en lugar de utilizar valores codificados definidos por .NET Framework.
Esa es la razón del nuevo comportamiento que experimentó y la necesidad de la nueva configuración:
<runtime>
<AppContextSwitchOverrides value="Switch.System.ServiceModel.DisableUsingServicePointManagerSecurityProtocols=false;Switch.System.Net.DontEnableSchUseStrongCrypto=false" />
</runtime>
Como alternativa a la respuesta de Nick Y, descubrí que en Windows 7 usando .NET 4.7+, necesitaba habilitar estas configuraciones de registro para que el paquete Microsoft Secure Channel (Schannel) envíe correctamente TLS1.1 y TLS1.2.
Esto permite que el cliente .NET continúe teniendo System.Net.ServicePointManager.SecurityProtocol
configurado en SystemDefault
y obtenga TLS 1.1 y 1.2 en una computadora con Windows 7.
El uso de la opción SystemDefault
permite a .NET diferir la selección de protocolos al sistema operativo. Esto significa que cuando Microsoft lanza revisiones al sistema operativo para deshabilitar protocolos inseguros o habilita la compatibilidad con otros nuevos en su biblioteca SCHANNEL nativa, las aplicaciones de .NET framework que se ejecutan automáticamente obtendrán este nuevo comportamiento.
Aquí están las entradas de registro:
[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/SecurityProviders/SCHANNEL/Protocols/TLS 1.1/Client]
"DisabledByDefault"=dword:00000000
[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/SecurityProviders/SCHANNEL/Protocols/TLS 1.2/Client]
"DisabledByDefault"=dword:00000000
Estoy en Windows 7 y .NET 4.7.1
La recomendación de usar Switch.System.ServiceModel.DisableUsingServicePointManagerSecurityProtocols y Switch.System.Net.DontEnableSchUseStrongCrypto mencionado en otras dos respuestas no funcionó en mi proyecto y el código de OP también estaba fallando.
Al revisar el código fuente de ServicePointManager y LocalAppContextSwitches , encontré otra configuración que funcionó.
<runtime>
<AppContextSwitchOverrides value="Switch.System.Net.DontEnableSystemDefaultTlsVersions=true" />
</runtime>
He encontrado una solución. No responde la pregunta sobre por qué TLS 1.2 no se usa de forma predeterminada en Win10 con .NET 4.7, pero me permite no tener que configurar ServicePointManager.SecurityProtocol.
La solución que funcionó desde mis aplicaciones de prueba 4.5.2 y 4.7 es agregar lo siguiente a app.config:
<AppContextSwitchOverrides value="Switch.System.Net.DontEnableSchUseStrongCrypto=false"/>
Aquí toda la app.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7"/>
</startup>
<runtime>
<AppContextSwitchOverrides value="Switch.System.Net.DontEnableSchUseStrongCrypto=false"/>
</runtime>
</configuration>