Hacer una llamada Https usando HttpClient

157

He estado usando HttpClientpara hacer llamadas WebApi usando C #. Parece ordenado y rápido en comparación con WebClient. Sin embargo, estoy atrapado mientras hago Httpsllamadas.

¿Cómo puedo hacer el siguiente código para hacer Httpsllamadas?

HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://foobar.com/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/xml"));

var task = httpClient.PostAsXmlAsync<DeviceRequest>(
                "api/SaveData", request);

EDITAR 1: El código anterior funciona bien para hacer llamadas http. Pero cuando cambio el esquema a https, no funciona. Aquí está el error obtenido:

Se cerró la conexión subyacente: no se pudo establecer una relación de confianza para el canal seguro SSL / TLS.

EDITAR 2: Cambiar el esquema a https es: paso uno.

¿Cómo proporciono el certificado y la clave pública / privada junto con la solicitud de C #?

Abhijeet
fuente
2
estás haciendo llamadas https simplemente especificandonew Uri("https://foobar.com/");
felickz
2
Estoy confundido. ¿Eso ya no funciona? ¿Estás obteniendo un error? (Editar: publicado antes de que el OP cambiara el URI de https a http)
Tim

Respuestas:

215

Si el servidor solo admite una versión TLS más alta como TLS 1.2 solamente, seguirá fallando a menos que su PC cliente esté configurada para usar una versión TLS más alta de manera predeterminada. Para superar este problema, agregue lo siguiente en su código.

System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

Modificando su código de ejemplo, sería

HttpClient httpClient = new HttpClient();   

//specify to use TLS 1.2 as default connection
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

httpClient.BaseAddress = new Uri("https://foobar.com/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));

var task = httpClient.PostAsXmlAsync<DeviceRequest>("api/SaveData", request);
Ronald Ramos
fuente
@DerekS Me alegro y de nada. Si por alguna razón no puede modificar el código en la configuración de producción pero puede hacer algo de administrador en el servidor, utilizo esta utilidad para configurar TLS 1.2 como predeterminado. nartac.com/Products/IISCrypto
Ronald Ramos
SecurityProtocolType.Tls12No podía encontrar esos valores de enumeración que usted ha mencionado
JobaDiniz
1
@JobaDiniz utiliza .NET 4.5 o superior e incluye el espacio de nombres System.Net.
Ronald Ramos
¿Es necesario configurar SecurityProtocol solo una vez? ¿Como en el inicio de la aplicación?
CamHart
Esto funciona muy bien. Gracias. También descubrí que no necesitaba borrar ningún encabezado que usaba este cliente HttpClient = new HttpClient (); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; Respuesta HttpResponseMessage = aguardar cliente. GetAsync ("https: // ...");
AtLeastTheresToast
127

Simplemente especifique HTTPS en el URI.

new Uri("https://foobar.com/");

Foobar.com necesitará tener un certificado SSL de confianza o sus llamadas fallarán con un error no confiable.

EDITAR respuesta: certificados de cliente con HttpClient

WebRequestHandler handler = new WebRequestHandler();
X509Certificate2 certificate = GetMyX509Certificate();
handler.ClientCertificates.Add(certificate);
HttpClient client = new HttpClient(handler);

EDITAR Respuesta2: Si el servidor al que se está conectando ha deshabilitado SSL, TLS 1.0 y 1.1 y todavía está ejecutando .NET Framework 4.5 (o inferior), debe elegir

  1. Actualice a .Net 4.6+ ( Admite TLS 1.2 de forma predeterminada )
  2. Agregue cambios en el registro para indicar a 4.5 que se conecte a través de TLS1.2 (consulte: redacción de salesforce para compatibilidad y claves para cambiar O pagar IISCrypto ver Ronald Ramos responder comentarios )
  3. Agregue el código de la aplicación para configurar manualmente .NET para conectarse a través de TLS1.2 (consulte la respuesta de Ronald Ramos )
felickz
fuente
@felickz gran respuesta. ¿Hay algún equivalente a la biblioteca WebRequestHandler para Windows Phone 8?
Billatron
@Billatron diferentes bibliotecas para WP / Win8 .. ver
felickz
66
Al desarrollar o tratar con certificados autofirmados, puede ignorar los errores de certificados no confiables con lo siguiente: ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
desarrollador
66
¿Qué es GetMyX509Certificate?
Fandi Susanto
66
@developer esperemos que el código no se deslice en su versión de producción :)
felickz
15

Su código debe modificarse de esta manera:

httpClient.BaseAddress = new Uri("https://foobar.com/");

Solo tiene que usar el https:esquema URI. Hay una página útil aquí en MSDN sobre las conexiones HTTP seguras. En efecto:

Use el esquema https: URI

El protocolo HTTP define dos esquemas de URI:

http: utilizado para conexiones sin cifrar.

https: se utiliza para conexiones seguras que deben cifrarse. Esta opción también utiliza certificados digitales y autoridades de certificación para verificar que el servidor es quien dice ser.

Además, considere que las conexiones HTTPS usan un certificado SSL. Asegúrese de que su conexión segura tenga este certificado; de lo contrario, las solicitudes fallarán.

EDITAR:

El código anterior funciona bien para hacer llamadas http. Pero cuando cambio el esquema a https no funciona, permíteme publicar el error.

¿Qué significa que no funciona? Las solicitudes fallan? ¿Se lanza una excepción? Aclara tu pregunta.

Si las solicitudes fallan, entonces el problema debería ser el certificado SSL.

Para solucionar el problema, puede usar la clase HttpWebRequesty luego su propiedad ClientCertificate. Además, puede encontrar aquí una muestra útil sobre cómo realizar una solicitud HTTPS utilizando el certificado.

Un ejemplo es el siguiente (como se muestra en la página de MSDN vinculada anteriormente):

//You must change the path to point to your .cer file location. 
X509Certificate Cert = X509Certificate.CreateFromCertFile("C:\\mycert.cer");
// Handle any certificate errors on the certificate from the server.
ServicePointManager.CertificatePolicy = new CertPolicy();
// You must change the URL to point to your Web server.
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://YourServer/sample.asp");
Request.ClientCertificates.Add(Cert);
Request.UserAgent = "Client Cert Sample";
Request.Method = "GET";
HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
Alberto Solano
fuente
Precisamente, el truco radica en enviar certificados junto con la solicitud. ¿como hacer eso?
Abhijeet
¿Dónde obtener "C: \\ mycert.cer"? ¿Cómo generar el archivo mycert.cer?
masiboo
@masiboo Lo que escribí responde cómo hacer llamadas HTTPS. Pregúntele a Google sobre cómo generar un archivo * .cer y encontrará la respuesta usted mismo.
Alberto Solano
9

Cuando me conecto https, también recibí este error, agrego esta línea antes HttpClient httpClient = new HttpClient();y me conecto con éxito:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

Lo sé por esta respuesta y otra respuesta similar y el comentario menciona:

Este es un truco útil en el desarrollo, por lo que poner una declaración #if DEBUG #endif a su alrededor es lo mínimo que debe hacer para hacer esto más seguro y evitar que esto termine en producción

Además, no probé el método en Otra respuesta que usa new X509Certificate()o new X509Certificate2()para hacer un Certificado, no estoy seguro de que simplemente crear por new()funcione o no.

EDITAR: Algunas referencias:

Crear un certificado de servidor autofirmado en IIS 7

Importar y exportar certificados SSL en IIS 7

Convertir .pfx a .cer

Mejores prácticas para usar ServerCertificateValidationCallback

Creo que el valor de Thumbprint es igual a x509certificate.GetCertHashString():

Recuperar la huella digital de un certificado

Yu Jang Jian
fuente
6

Tuve el mismo problema al conectarme a GitHub, que requiere un agente de usuario. Por lo tanto, es suficiente proporcionar esto en lugar de generar un certificado

var client = new HttpClient();

client.BaseAddress = new Uri("https://api.github.com");
client.DefaultRequestHeaders.Add(
    "Authorization",
    "token 123456789307d8c1d138ddb0848ede028ed30567");
client.DefaultRequestHeaders.Accept.Add(
    new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add(
    "User-Agent",
    "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
Carlo V. Dango
fuente
2
deberías revocar ese token que has estado compartiendo con Internet, creo que GitHub puede haberlo hecho automáticamente.
Amias
21
¿De verdad crees que mi token comienza con 123456789?
Carlo V. Dango
5

Hay una configuración no global a nivel de HttpClientHandler:

var handler = new HttpClientHandler()
{
    SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls
};

var client = new HttpClient(handler);

Por lo tanto, uno habilita las últimas versiones de TLS.

Tenga en cuenta que el valor predeterminado SslProtocols.Defaultes en realidad SslProtocols.Ssl3 | SslProtocols.Tls(verificado para .Net Core 2.1 y .Net Framework 4.7.1).

stop-cran
fuente
Gracias por la respuesta. Una pequeña nota: según esta documentación: docs.microsoft.com/en-us/dotnet/api/… Requiere al menos el marco 4.7.1.
GELR
@GELR sí, tienes razón. El código anterior produce un error de tiempo de ejecución en .Net 4.6.1. Se arregló la respuesta.
Stop-Cran
En versiones anteriores de .NET Framework, esta propiedad es privada
JotaBe
En Net Core 3.1 esto es todo lo que funcionó para mí. La configuración de System.Net.ServicePointManager.SecurityProtocol = xxx global tuvo un efecto absolutamente nulo en un rastreo de paquetes.
Rowan Smith
3

Solo especificar HTTPS en el URI debería ser suficiente.

httpClient.BaseAddress = new Uri("https://foobar.com/");

Si la solicitud funciona con HTTP pero falla con HTTPS, este es sin duda un problema de certificado . Asegúrese de que la persona que llama confíe en el emisor del certificado y que el certificado no haya caducado. Una forma rápida y fácil de verificar eso es intentar realizar la consulta en un navegador.

También es posible que desee verificar en el servidor (si es suyo y / o si puede) que está configurado para atender las solicitudes HTTPS correctamente.

Crono
fuente
: Tengo un problema similar: realicé la consulta en el navegador y el certificado es válido (la llamada pasa, está autorizada como nivel de servicio y todo funciona de maravilla) pero programáticamente no funciona. ¿Qué más puede ser? si uso exactamente el mismo certificado del navegador "llamando manualmente al servicio" que de la aplicación web que llama al servicio mediante programación?
Carlos
2

También recibí el error:

Se cerró la conexión subyacente: no se pudo establecer una relación de confianza para el canal seguro SSL / TLS.

... con una aplicación de orientación Xamarin Forms para Android que intenta solicitar recursos de un proveedor de API que requirió TLS 1.3.

La solución fue actualizar la configuración del proyecto para intercambiar el cliente http "administrado" (.NET) de Xamarin (que no es compatible con TLS 1.3 a partir de Xamarin Forms v2.5), y en su lugar usar el cliente nativo de Android.

Es un simple cambio de proyecto en Visual Studio. Ver captura de pantalla a continuación.

  • Propiedades del proyecto
  • Opciones de Android
  • Avanzado
  • Elemento de la lista
  • Cambie "Implementación de HttpClient" a "Android"
  • Cambie la implementación de SSL / TLS a "Native TLS 1.2+"

ingrese la descripción de la imagen aquí

MSC
fuente
1

Agregue las siguientes declaraciones a su clase:

public const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
public const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;

Después:

var client = new HttpClient();

Y:

ServicePointManager.SecurityProtocol = Tls12;
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 /*| SecurityProtocolType.Tls */| Tls12;

¿Contento? :)

Greg Stawski
fuente
1

Tuve este problema y en mi caso la solución fue estúpidamente simple: abrir Visual Studio con derechos de administrador. Probé todas las soluciones anteriores y no funcionó hasta que hice esto. Espero que le ahorre a alguien un tiempo precioso.

Lucian Satmarean
fuente
Como es nuevo en el sitio, es posible que no sepa que si una respuesta tiene la marca verde al lado, eso significa que fue aceptada por OP como la solución correcta para su problema. A menos que pueda dar una respuesta mejor y más completa, probablemente sea mejor no enviar otra respuesta, especialmente en una pregunta tan antigua.
Sam W
44
Bueno, también tuve el problema, y ​​la respuesta marcada en verde no ayudó en absoluto. Pensé en dar una solución alternativa que me ayudó, pero bueno, no lo haré la próxima vez. Gracias por los puntos negativos;) tenga un gran día.
Lucian Satmarean
0

Puede intentar usar el paquete ModernHttpClient Nuget: después de descargar el paquete, puede implementarlo de esta manera:

 var handler = new ModernHttpClient.NativeMessageHandler()
 {
     UseProxy = true,
 };


 handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
 handler.PreAuthenticate = true;
 HttpClient client = new HttpClient(handler);
Uchenna Nnodim
fuente
Desafortunadamente, esto no funcionó de mí. La solución fue habilitar TLS 1.3 cambiando del cliente http administrado xamarin al cliente http nativo de Android. Vea mi respuesta a continuación.
MSC
0

Estoy de acuerdo con felickz pero también quiero agregar un ejemplo para aclarar el uso en c #. Yo uso SSL en el servicio de Windows de la siguiente manera.

    var certificatePath = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "bin");
    gateway = new GatewayService();
    gateway.PreAuthenticate = true;


    X509Certificate2 cert = new X509Certificate2(certificatePath + @"\Attached\my_certificate.pfx","certificate_password");
    gateway.ClientCertificates.Add(cert);

    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    gateway.UserAgent = Guid.NewGuid().ToString();
    gateway.Timeout = int.MaxValue;

Si voy a usarlo en una aplicación web, solo estoy cambiando la implementación en el lado del proxy de esta manera:

public partial class GatewayService : System.Web.Services.Protocols.SoapHttpClientProtocol // to => Microsoft.Web.Services2.WebServicesClientProtocol
Hamit YILDIRIM
fuente
-4

Por error:

Se cerró la conexión subyacente: no se pudo establecer una relación de confianza para el canal seguro SSL / TLS.

Creo que debe aceptar el certificado incondicionalmente con el siguiente código

ServicePointManager.ServerCertificateValidationCallback += 
    (sender, cert, chain, sslPolicyErrors) => true;

como escribió Oppositional en su respuesta a la pregunta .NET client que se conecta a la API web SSL .

Nemanja Simović
fuente
44
Esto está pasando por alto la validación del certificado. Nunca hagas esto en producción.
John Korsnes
Bueno, @JohnKorsnes, si Abhijeet está accediendo a algún servidor público de WebAPI, no creo que pueda hacer mucho al respecto. Este fue mi caso.
Nemanja Simović