servicepointmanager - C#y dotnet 4.7.1 no agregan certificado personalizado para llamadas TLS 1.2
tls 1.2 c# (3)
Tengo el siguiente código C #, construyendo una llamada https con un certificado personalizado. Cuando se usa Tls 1.1, la llamada funciona bien. Cuando se usa Tls 1.2 se interrumpe la llamada. Yo usando curl, usando tls 1.2 funciona bien también.
Código C #:
X509Certificate2Collection collection = new X509Certificate2Collection();
collection.Import("C://SomePath//MyCertificate.pfx", "MyPassword", X509KeyStorageFlags.PersistKeySet);
var cert = collection[0];
ServicePointManager.SecurityProtocol = ...;
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) => true;
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true;
handler.ClientCertificates.Add(cert);
var content = new ByteArrayContent(Encoding.GetEncoding("latin1").GetBytes("Hello world"));
HttpClient client = new HttpClient(handler);
var resp = client.PostAsync(requestUri: url, content: content).Result.Content.ReadAsStringAsync().Result;
Funciona con:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11;
Error con:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Mensaje de error .Net: SocketException: una conexión existente fue cerrada por la fuerza por el host remoto
Versión .Net: 4.7.1
SO: Windows 10 versión 1703 (lista de cifrado compatible: https://msdn.microsoft.com/en-us/library/windows/desktop/mt808163(v=vs.85).aspx ) - y el servidor especifica que TLS_RSA_WITH_AES_256_GCM_SHA384 será utilizado, que se encuentra entre los cifrados soportados.
En wireshark puedo ver que con las llamadas de trabajo (C # / Tls 1.1 y Curl Tls 1.2) el certificado se envía al servidor. Aquí está el volcado de wirehark para la llamada de C # tls 1.1:
Sin embargo, también en wireshark, puedo ver que con C # / Tls 1.2 no se envía ningún certificado del cliente al servidor. Aquí está el volcado de wirehark para la llamada de C # tls 1.2:
¿Alguien puede ver lo que me estoy perdiendo aquí?
ACTUALIZAR
Parece que el certificado tiene una firma md5 que no es compatible con Schannel en Windows en combinación con tls 1.2. Nuestro proveedor nos ha creado otro certificado como solución.
Me encontré con este hilo aleatorio que trata el problema: https://community.qualys.com/thread/15498
¿No deberías haber especificado la propiedad SslProtocols
del controlador?
Intente agregar esta línea después de la definición del hander
:
handler.SslProtocols = SslProtocols.Tls12;
Creo que este código está enmascarando algún tipo de error de certificado devolviendo siempre verdadero a ciegas:
handler.ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true;
Recomiendo que tenga una función para analizar verdaderamente los resultados de arg4. Esos son los errores de su política SSL. Regístralos y obtendrás tu respuesta. En mi ejemplo, escribo en la consola, pero puede escribir en la traza, o en un archivo. Obtendrá un número al que se le asociará un valor para la enumeración SslPolicyErrors. Según los resultados, es posible que necesites revisar tu arg3, que es tu cadena.
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => {
SslPolicyErrors errs = sslPolicyErrors;
Console.WriteLine("Policy Errors " + sslPolicyErrors.ToString());
return true;};
Tiene razón en la causa raíz de este problema: De forma predeterminada, los clientes basados en schannel ofrecen SHA1, SHA256, SHA384 y SHA512 (en Win10 / Server 2016). Por lo tanto, los servidores TLS 1.2 no deben enviar sus certificados MD5 a estos clientes.
El cliente (HttpClient) no muestra MD5 en la extensión signature_algorithms, por lo que el protocolo de enlace TLS 1.2 falla. La solución es utilizar un certificado de servidor seguro.