Permitir certificados SSL que no son de confianza con HttpClient

114

Estoy luchando para que mi aplicación de Windows 8 se comunique con mi API web de prueba a través de SSL.

Parece que HttpClient / HttpClientHandler no proporciona una opción para ignorar los certificados que no son de confianza, como te permite WebRequest (aunque de una manera "pirata" ServerCertificateValidationCallback).

¡Cualquier ayuda será muy apreciada!

Jamie
fuente
5
En caso de que esté utilizando .NET Core, es posible que le interese esta respuesta.
kdaveid

Respuestas:

13

Con Windows 8.1, ahora puede confiar en certificados SSL no válidos. Tiene que usar Windows.Web.HttpClient o si desea usar System.Net.Http.HttpClient, puede usar el adaptador de controlador de mensajes que escribí: http://www.nuget.org/packages/WinRtHttpClientHandler

Los documentos están en GitHub: https://github.com/onovotny/WinRtHttpClientHandler

Claire Novotny
fuente
12
¿Hay alguna forma de hacer esto sin usar todo su código? En otras palabras, ¿cuál es la esencia de su solución?
wensveen
La esencia de la solución es envolver el controlador de cliente http de Windows y usarlo como implementación del HttpClient. Todo está en este archivo: github.com/onovotny/WinRtHttpClientHandler/blob/master/… siéntase libre de copiarlo, pero no estoy seguro de por qué no usar el paquete.
Claire Novotny
@OrenNovotny, ¿su solución no está vinculada a la versión de Windows?
chester89
Implementé esto en mi aplicación para UWP y usé el ejemplo de filtros a continuación. Sigo recibiendo el mismo error.
Christian Findlay
158

Una solución rápida y sucia es utilizar al ServicePointManager.ServerCertificateValidationCallbackdelegado. Esto le permite proporcionar su propia validación de certificado. La validación se aplica globalmente en todo el dominio de la aplicación.

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

Utilizo esto principalmente para pruebas unitarias en situaciones en las que quiero ejecutar un punto final que estoy alojando en proceso y estoy tratando de conectarlo con un cliente WCF o el HttpClient.

Para el código de producción, es posible que desee un control más detallado y sería mejor usar la propiedad WebRequestHandlery su ServerCertificateValidationCallbackdelegado (consulte la respuesta de dtb a continuación ). O ctacke responde usando el HttpClientHandler. Prefiero cualquiera de estos dos ahora, incluso con mis pruebas de integración, sobre cómo solía hacerlo, a menos que no pueda encontrar ningún otro gancho.

Bronumski
fuente
32
No es el votante negativo, pero uno de los mayores problemas con ServerCertificateValidationCallback es que es básicamente global para su AppDomain. Entonces, si está escribiendo una biblioteca que necesita realizar llamadas a un sitio con un certificado que no es de confianza y emplea esta solución, está cambiando el comportamiento de toda la aplicación, no solo de su biblioteca. Además, las personas siempre deben tener cuidado con el enfoque de "devolver ciegamente la verdad". Tiene serias implicaciones de seguridad. Debe leer / * inspeccionar los parámetros suministrados y luego decidir cuidadosamente si * / devolver verdadero;
scottt732
@ scottt732 Ciertamente es un punto válido y vale la pena mencionarlo, pero sigue siendo una solución válida. Quizás después de leer la pregunta original del OP, parece que ya estaba al tanto del controlador
ServerCertificateValidationCallback
2
Recomendaría al menos filtrar por algunos criterios como remitente
Boas Enkler
2
Realmente tengo que recomendar la opción WebRequestHandler frente al ServicePointManager global.
Thomas S. Trias
3
No tiene que usar ServicePointManager globalmente, puede usar ServicePointManager.GetServicePoint(Uri) (ver documentos ) para obtener el punto de servicio que se aplica solo a las llamadas a ese URI. Luego puede establecer propiedades y manejar eventos basados ​​en ese subconjunto.
oatsoda
87

Eche un vistazo a la clase WebRequestHandler y su propiedad ServerCertificateValidationCallback :

using (var handler = new WebRequestHandler())
{
    handler.ServerCertificateValidationCallback = ...

    using (var client = new HttpClient(handler))
    {
        ...
    }
}
dtb
fuente
5
Gracias por la respuesta, sin embargo, ya lo he investigado: no está presente en las aplicaciones de .NET para Windows 8 Store.
Jamie
Sin embargo, crear la propia clase derivada HttpClientHandler o HttpMessageHandler es bastante fácil, especialmente dado que la fuente WebRequestHandler está fácilmente disponible.
Thomas S. Trias
@ ThomasS.Trias alguna muestra al respecto? derived class?
Kiquenet
2
Usando HttpClientHandler?
Kiquenet
1
@Kiquenet esta respuesta es de 2012, ya tiene 6 años, y sí, muchas personas en estos tiempos estaban seguras de que esta es la forma correcta de usar httpclient :)
justmara
63

Si está intentando hacer esto en una biblioteca .NET Standard, aquí tiene una solución simple, con todos los riesgos de regresar trueen su controlador. Dejo la seguridad en tus manos.

var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback = 
    (httpRequestMessage, cert, cetChain, policyErrors) =>
{
    return true;
};

var client = new HttpClient(handler);
ctacke
fuente
1
esto da como resultado una plataforma no soportada cuando se hace referencia a uwp
Ivan
trabajó para mí en UWP. quizás la cobertura de soporte haya madurado en 14 meses. nota: este truco debería ser necesario solo en test / dev env (donde se utilizan certificados autofirmados)
Ben McIntyre
Ejecuté este código y siempre cumplo con System.NotImplementedException. No sé por qué.
Liu Feng
25

O puede usar para HttpClient en el Windows.Web.Httpespacio de nombres:

var filter = new HttpBaseProtocolFilter();
#if DEBUG
    filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Expired);
    filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted);
    filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName);
#endif
using (var httpClient = new HttpClient(filter)) {
    ...
}
dschüsä
fuente
¿Puedo utilizar System.Net.Http, System.Weby Windows.Web.Httpjuntos?
Kiquenet
2
HttpClient no parece tener esta anulación en .NET Standard o UWP.
Christian Findlay
15

Si está utilizando System.Net.Http.HttpClient, creo que el patrón correcto es

var handler = new HttpClientHandler() 
{ 
    ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};

var http = new HttpClient(handler);
var res = http.GetAsync(url);
Jakub Šturc
fuente
8

La mayoría de las respuestas aquí sugieren usar el patrón típico:

using (var httpClient = new HttpClient())
{
 // do something
}

debido a la interfaz IDisposable. ¡Por favor no lo hagas!

Microsoft te dice por qué:

Y aquí puede encontrar un análisis detallado de lo que sucede detrás de escena: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

Con respecto a su pregunta sobre SSL y basado en https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem

Aquí está tu patrón:

class HttpInterface
{
 // https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem
 // https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient#remarks
 private static readonly HttpClient client;

 // static initialize
 static HttpInterface()
 {
  // choose one of these depending on your framework

  // HttpClientHandler is an HttpMessageHandler with a common set of properties
  var handler = new HttpClientHandler();
  {
      ServerCertificateCustomValidationCallback = delegate { return true; },
  };
  // derives from HttpClientHandler but adds properties that generally only are available on full .NET
  var handler = new WebRequestHandler()
  {
      ServerCertificateValidationCallback = delegate { return true; },
      ServerCertificateCustomValidationCallback = delegate { return true; },
  };

  client = new HttpClient(handler);
 }

 .....

 // in your code use the static client to do your stuff
 var jsonEncoded = new StringContent(someJsonString, Encoding.UTF8, "application/json");

 // here in sync
 using (HttpResponseMessage resultMsg = client.PostAsync(someRequestUrl, jsonEncoded).Result)
 {
  using (HttpContent respContent = resultMsg.Content)
  {
   return respContent.ReadAsStringAsync().Result;
  }
 }
}
Bernhard
fuente
La pregunta es para la relación de confianza del certificado autofirmado. Toda su respuesta es (incluso aunque sea válida) sobre hacer que el hilo HttpClient sea seguro.
Adarsha
1
No se trata solo de "seguridad de subprocesos". Quería mostrar la forma "correcta" de usar el httpclient con la verificación SSL "deshabilitada". Otras respuestas modifican "globalmente" el administrador de certificados.
Bernhard
7

Si se trata de una aplicación de Windows Runtime, debe agregar el certificado autofirmado al proyecto y hacer referencia a él en el archivo appxmanifest.

Los documentos están aquí: http://msdn.microsoft.com/en-us/library/windows/apps/hh465031.aspx

Lo mismo ocurre si es de una CA que no es de confianza (como una CA privada en la que la máquina no confía): debe obtener el certificado público de la CA, agregarlo como contenido a la aplicación y luego agregarlo al manifiesto.

Una vez hecho esto, la aplicación lo verá como un certificado firmado correctamente.

Claire Novotny
fuente
2

No tengo una respuesta, pero tengo una alternativa.

Si usa Fiddler2 para monitorear el tráfico Y habilita el descifrado HTTPS, su entorno de desarrollo no se quejará. Esto no funcionará en dispositivos WinRT, como Microsoft Surface, porque no puede instalar aplicaciones estándar en ellos. Pero su computadora Win8 de desarrollo estará bien.

Para habilitar el cifrado HTTPS en Fiddler2, vaya a Herramientas> Opciones de Fiddler> HTTPS (pestaña)> Marque "Descifrar tráfico HTTPS" .

Voy a estar atento a este hilo esperando que alguien tenga una solución elegante.

Laith
fuente
2

Encontré un ejemplo en este cliente de Kubernetes en el que estaban usando X509VerificationFlags.AllowUnknownCertificateAuthority para confiar en certificados raíz autofirmados y autofirmados. Modifiqué ligeramente su ejemplo para trabajar con nuestros propios certificados raíz codificados en PEM. Ojalá esto ayude a alguien.

namespace Utils
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Net.Security;
  using System.Security.Cryptography.X509Certificates;

  /// <summary>
  /// Verifies that specific self signed root certificates are trusted.
  /// </summary>
  public class HttpClientHandler : System.Net.Http.HttpClientHandler
  {
    /// <summary>
    /// Initializes a new instance of the <see cref="HttpClientHandler"/> class.
    /// </summary>
    /// <param name="pemRootCerts">The PEM encoded root certificates to trust.</param>
    public HttpClientHandler(IEnumerable<string> pemRootCerts)
    {
      foreach (var pemRootCert in pemRootCerts)
      {
        var text = pemRootCert.Trim();
        text = text.Replace("-----BEGIN CERTIFICATE-----", string.Empty);
        text = text.Replace("-----END CERTIFICATE-----", string.Empty);
        this.rootCerts.Add(new X509Certificate2(Convert.FromBase64String(text)));
      }

      this.ServerCertificateCustomValidationCallback = this.VerifyServerCertificate;
    }

    private bool VerifyServerCertificate(
      object sender,
      X509Certificate certificate,
      X509Chain chain,
      SslPolicyErrors sslPolicyErrors)
    {
      // If the certificate is a valid, signed certificate, return true.
      if (sslPolicyErrors == SslPolicyErrors.None)
      {
        return true;
      }

      // If there are errors in the certificate chain, look at each error to determine the cause.
      if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0)
      {
        chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;

        // add all your extra certificate chain
        foreach (var rootCert in this.rootCerts)
        {
          chain.ChainPolicy.ExtraStore.Add(rootCert);
        }

        chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
        var isValid = chain.Build((X509Certificate2)certificate);

        var rootCertActual = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
        var rootCertExpected = this.rootCerts[this.rootCerts.Count - 1];
        isValid = isValid && rootCertActual.RawData.SequenceEqual(rootCertExpected.RawData);

        return isValid;
      }

      // In all other cases, return false.
      return false;
    }

    private readonly IList<X509Certificate2> rootCerts = new List<X509Certificate2>();
  }
}
PaulB
fuente
1

Encontré un ejemplo en línea que parece funcionar bien:

Primero crea una nueva ICertificatePolicy

using System.Security.Cryptography.X509Certificates;
using System.Net;

public class MyPolicy : ICertificatePolicy
{
  public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, 
int certificateProblem)
  {
    //Return True to force the certificate to be accepted.
    return true;
  }
}

Luego, use esto antes de enviar su solicitud http así:

System.Net.ServicePointManager.CertificatePolicy = new MyPolicy();

http://www.terminally-incoherent.com/blog/2008/05/05/send-a-https-post-request-with-c/

TombMedia
fuente
1
ServicePointManager.CertificatePolicyestá en desuso: docs.microsoft.com/en-us/dotnet/framework/whats-new/…
schmidlop