omitir el certificado SSL no válido en .net core

104

Estoy trabajando en un proyecto que necesita conectarse a un sitio https. Cada vez que me conecto, mi código genera una excepción porque el certificado de ese sitio proviene de un sitio que no es de confianza. ¿Hay alguna forma de omitir la verificación de certificados en .net core http?

Vi este código de la versión anterior de .NET. Supongo que solo necesito algo como esto.

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

Respuestas:

28

ServicePointManager.ServerCertificateValidationCallback no es compatible con .Net Core.

La situación actual es que será un nuevo método ServerCertificateCustomValidationCallback para el próximo contrato 4.1. * System.Net.Http (HttpClient). El equipo de .NET Core está finalizando el contrato 4.1 ahora. Puedes leer sobre esto aquí en github

Puede probar la versión preliminar de System.Net.Http 4.1 utilizando las fuentes directamente aquí en CoreFx o en el feed de MYGET: https://dotnet.myget.org/gallery/dotnet-core

Definición actual de WinHttpHandler.ServerCertificateCustomValidationCallback en Github

Conjunto
fuente
8
Esto solo funciona en Windows. ¿Tiene una solución para Linux? Gracias.
Vladimir
146

Actualizar:

Como se menciona a continuación, no todas las implementaciones admiten esta devolución de llamada (es decir, plataformas como iOS). En este caso, como dicen los documentos , puede configurar el validador explícitamente:

handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

Esto también funciona para .NET Core 2.2, 3.0 y 3.1

Respuesta anterior , con más control pero puede arrojar PlatformNotSupportedException:

Puede anular la verificación del certificado SSL en una llamada HTTP con una función de devolución de llamada anónima como esta

using (var httpClientHandler = new HttpClientHandler())
{
   httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
   using (var client = new HttpClient(httpClientHandler))
   {
       // Make your request...
   }
}

Además, sugiero usar un patrón de fábrica HttpClientporque es un objeto compartido que podría no eliminarse de inmediato y, por lo tanto, las conexiones permanecerán abiertas .

kdaveid
fuente
3
Estoy usando .Net Core 1.0 y esto funcionó para mí. Como aviso, parece que .Net Core 2.0 ha agregado una HttpClientpropiedad llamada DangerousAcceptAnyServerCertificateValidatorque proporciona una manera de hacer que esto funcione en MacOSX. Más información aquí - github.com/dotnet/corefx/pull/19908
Troy Witthoeft
1
Al usar esto con AWS Lambda, .NET Core 1.0 corrigió lo que me impedía conectarme a un HTTPS interno con un certificado de CA raíz personalizado.
QuickNull
¿Alguno factory patternpara HttpClient?
Kiquenet
@Kiquenet Simplemente crea una fábrica, donde el GetHttpClientMétodo devuelve lo configurado HttpClienty lo usa dentro de un using-bloque.
LuckyLikey
Esta debería ser la respuesta aceptada, especialmente porque se puede aplicar al uso de un solo cliente.
BinaryPatrick
37

Resuelvo con esto:

Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpClient("HttpClientWithSSLUntrusted").ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
        {
            ClientCertificateOptions = ClientCertificateOption.Manual,
            ServerCertificateCustomValidationCallback =
            (httpRequestMessage, cert, cetChain, policyErrors) =>
            {
                return true;
            }
        });

YourService.cs

public UserService(IHttpClientFactory clientFactory, IOptions<AppSettings> appSettings)
    {
        _appSettings = appSettings.Value;
        _clientFactory = clientFactory;
    }

var request = new HttpRequestMessage(...

var client = _clientFactory.CreateClient("HttpClientWithSSLUntrusted");

HttpResponseMessage response = await client.SendAsync(request);
O. Machado
fuente
32

Vine aquí buscando una respuesta al mismo problema, pero estoy usando WCF para NET Core. Si estás en el mismo barco, usa:

client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = 
    new X509ServiceCertificateAuthentication()
    {
        CertificateValidationMode = X509CertificateValidationMode.None,
        RevocationMode = X509RevocationMode.NoCheck
    };
Troels Larsen
fuente
¿Global para todos los certificados y AppDomain?
Kiquenet
@Kiquenet: Eso creo, sí. Busque una respuesta actualizada en otro lugar, podría haber una solución mejor ahora. A sido un año. Supongo que podría subclasificar el autenticador al menos. Y no, no hay una fábrica nativa para HttpClient que yo sepa. Si necesita más funcionalidad, consulte RestClient.
Troels Larsen
No hay ninguna propiedad ClientCredentials en HttpClient (.NET Core 3.1).
Павле
@ Павле: No he actualizado este proyecto a 3.1 todavía, pero debería haber una propiedad de este tipo: docs.microsoft.com/en-us/dotnet/api/… .
Troels Larsen
@ Павле: Esta respuesta no se trata de HttpClient sino de un cliente generado por el servicio WCF. También funcionó para mi ASMX SoapClient, ¡muchas gracias!
Jan Zahradník
14

En .NetCore, puede agregar el siguiente fragmento de código en el método de configuración de servicios, agregué una verificación para asegurarme solo de que pasamos el certificado SSL solo en el entorno de desarrollo

services.AddHttpClient("HttpClientName", client => {
// code to configure headers etc..
}).ConfigurePrimaryHttpMessageHandler(() => {
                  var handler = new HttpClientHandler();
                  if (hostingEnvironment.IsDevelopment())
                  {
                      handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
                  }
                  return handler;
              });
Sameh
fuente
1
Por qué el -ve, esto está implementando exactamente lo que otros sugirieron en el código mvc.net y obtuvieron puntos, solo estoy ilustrando la misma implementación en el código .netCore
Sameh
probablemente. porque carece de explicación alguna. por qué este enfoque debería reemplazar a cualquier otro, qué código debería escribirse en la sección de llamada (por ejemplo, mycontroller.cs), que podría ser parte de la explicación. cualquier documentación / citación oficial.
Bhanu Chhabra
Como dije, si revisó otros comentarios en la parte superior del hilo, no hay mucha diferencia y, sin embargo, obtuvieron 18 y 81 puntos,
Sameh
1
Debido a que han agregado texto que respalda sus respuestas, lea las pautas una vez más. Podría ayudarlo, los @moderadores pueden señalar los problemas exactos en mi humilde opinión.
Bhanu Chhabra
8

Me enfrenté al mismo problema cuando trabajaba con certificados autofirmados y autenticación de certificados de cliente en contenedores .NET Core 2.2 y Docker Linux. Todo funcionó bien en mi máquina de Windows de desarrollo, pero en Docker obtuve tal error:

System.Security.Authentication.AuthenticationException: el certificado remoto no es válido según el procedimiento de validación

Afortunadamente, el certificado se generó mediante una cadena. Por supuesto, siempre puede ignorar esta solución y utilizar las soluciones anteriores.

Entonces aquí está mi solución:

  1. Guardé el certificado usando Chrome en mi computadora en formato P7B .

  2. Convierta el certificado a formato PEM usando este comando:
    openssl pkcs7 -inform DER -outform PEM -in <cert>.p7b -print_certs > ca_bundle.crt

  3. Abra el archivo ca_bundle.crt y elimine todas las grabaciones del sujeto, dejando un archivo limpio. Ejemplo a continuación:

    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
  1. Coloque estas líneas en el Dockerfile (en los pasos finales):
    # Update system and install curl and ca-certificates
    RUN apt-get update && apt-get install -y curl && apt-get install -y ca-certificates
    # Copy your bundle file to the system trusted storage
    COPY ./ca_bundle.crt /usr/local/share/ca-certificates/ca_bundle.crt
    # During docker build, after this line you will get such output: 1 added, 0 removed; done.
    RUN update-ca-certificates
  1. En la aplicación:
    var address = new EndpointAddress("https://serviceUrl");                
    var binding = new BasicHttpsBinding
    {
        CloseTimeout = new TimeSpan(0, 1, 0),
        OpenTimeout = new TimeSpan(0, 1, 0),
        ReceiveTimeout = new TimeSpan(0, 1, 0),
        SendTimeout = new TimeSpan(0, 1, 0),
        MaxBufferPoolSize = 524288,
        MaxBufferSize = 65536,
        MaxReceivedMessageSize = 65536,
        TextEncoding = Encoding.UTF8,
        TransferMode = TransferMode.Buffered,
        UseDefaultWebProxy = true,
        AllowCookies = false,
        BypassProxyOnLocal = false,
        ReaderQuotas = XmlDictionaryReaderQuotas.Max,
        Security =
        {
            Mode = BasicHttpsSecurityMode.Transport,
            Transport = new HttpTransportSecurity
            {
                ClientCredentialType = HttpClientCredentialType.Certificate,
                ProxyCredentialType = HttpProxyCredentialType.None
            }
        }
    };
    var client = new MyWSClient(binding, address);
    client.ClientCredentials.ClientCertificate.Certificate = GetClientCertificate("clientCert.pfx", "passwordForClientCert");
    // Client certs must be installed
    client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication
    {
        CertificateValidationMode = X509CertificateValidationMode.ChainTrust,
        TrustedStoreLocation = StoreLocation.LocalMachine,
        RevocationMode = X509RevocationMode.NoCheck
    };

Método GetClientCertificate:

private static X509Certificate2 GetClientCertificate(string clientCertName, string password)
{
    //Create X509Certificate2 object from .pfx file
    byte[] rawData = null;
    using (var f = new FileStream(Path.Combine(AppContext.BaseDirectory, clientCertName), FileMode.Open, FileAccess.Read))
    {
        var size = (int)f.Length;
        var rawData = new byte[size];
        f.Read(rawData, 0, size);
        f.Close();
    }
    return new X509Certificate2(rawData, password);
}
DerSkythe
fuente
4

En primer lugar, NO LO USE EN PRODUCCIÓN

Si está utilizando el middleware AddHttpClient, esto será útil. Creo que es necesario para el desarrollo, no para la producción. Hasta que cree un certificado válido, puede utilizar esta función.

Func<HttpMessageHandler> configureHandler = () =>
        {
            var bypassCertValidation = Configuration.GetValue<bool>("BypassRemoteCertificateValidation");
            var handler = new HttpClientHandler();
            //!DO NOT DO IT IN PRODUCTION!! GO AND CREATE VALID CERTIFICATE!
            if (bypassCertValidation)
            {
                handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, x509Certificate2, x509Chain, sslPolicyErrors) =>
                {
                    return true;
                };
            }
            return handler;
        };

y aplicarlo como

services.AddHttpClient<IMyClient, MyClient>(x => { x.BaseAddress = new Uri("https://localhost:5005"); })
        .ConfigurePrimaryHttpMessageHandler(configureHandler);
Ozan ERTÜRK
fuente
3

Permitir todos los certificados es muy poderoso, pero también puede ser peligroso. Si solo desea permitir certificados válidos más algunos certificados determinados, puede hacerlo así.

using (var httpClientHandler = new HttpClientHandler())
{
    httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => {
        if (sslPolicyErrors == SslPolicyErrors.None)
        {
            return true;   //Is valid
        }

        if (cert.GetCertHashString() == "99E92D8447AEF30483B1D7527812C9B7B3A915A7")
        {
            return true;
        }
        return false;
    };
    using (var httpClient = new HttpClient(httpClientHandler))
    {
        var httpResponse = httpClient.GetAsync("https://example.com").Result;
    }
}

Fuente original:

https://stackoverflow.com/a/44140506/3850405

Ogglas
fuente