web-dev-qa-db-fra.com

Ignorer le mauvais certificat - .NET CORE

J'écris une application .NET Core pour interroger un serveur distant et transférer des données telles qu'elles apparaissent. Cela fonctionne parfaitement dans PHP parce que le PHP ignore le certificat (qui est également un problème dans les navigateurs) mais nous voulons le déplacer vers C # .NET CORE car c'est le seul PHP PHP restant dans le système).

Nous savons que le serveur est bon, mais pour diverses raisons, le certificat ne peut/ne sera pas mis à jour de sitôt.

La demande utilise HttpClient:

        HttpClient httpClient = new HttpClient();
        try
        {
            string url = "https://URLGoesHere.php";
            MyData md = new MyData();  // this is some data we need to pass as a json
            string postBody = JsonConvert.SerializeObject(md);
            httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));                
            HttpResponseMessage wcfResponse = await httpClient.PostAsync(url, new StringContent(postBody, Encoding.UTF8, "application/json"));
            Console.WriteLine(wcfResponse.Content);
        }
        catch (HttpRequestException hre)
        {
        // This exception is being triggered
        }

Après avoir étudié cela, il semble que la recommandation universelle soit d'utiliser ServicePointManager, mais ce n'est pas disponible dans .NET Core et j'ai du mal à trouver le remplacement recommandé.

Existe-t-il un moyen simple ou meilleur de le faire dans .NET Core?

15
DaveEP

Au lieu de new HttpClient() vous voulez quelque chose de semblable à

var handler = new System.Net.Http.HttpClientHandler();
using (var httpClient = new System.Net.Http.HttpClient(handler))
{
    handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) =>
    {
        // Log it, then use the same answer it would have had if we didn't make a callback.
        Console.WriteLine(cert);
        return errors == SslPolicyErrors.None;
    };

    ...
}

Cela devrait fonctionner sous Windows et sous Linux où libcurl est compilé pour utiliser openssl. Avec d'autres backends curl, Linux lèvera une exception.

11
bartonjs

Faire fonctionner Linux et macOS

Si vous travaillez sous Linux ou macOS, vous pouvez rencontrer un scénario dans lequel HttpClient ne vous permettra pas d'accéder à un certificat auto-signé même s'il se trouve dans votre magasin de confiance. Vous obtiendrez probablement les éléments suivants:

System.Net.Http.CurlException: Peer certificate cannot be authenticated with given CA certificates environment variable

de si vous implémentez (comme indiqué dans l'autre réponse)

handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) =>
{
    // Log it, then use the same answer it would have had if we didn't make a callback.
    Console.WriteLine(cert);
    return errors == SslPolicyErrors.None;
};

Cela est dû à la version de libcurl sur la machine qui ne prend pas en charge les rappels appropriés dont .Net Core a besoin pour rassembler les données appropriées pour appeler le ServerCertificateCustomValidationCallback. Par exemple, le framework ne peut pas créer l'objet cert ou un autre des paramètres. Plus d'informations peuvent être trouvées dans la discussion sur la solution de contournement fournie dans .NET Core en cause dans le dépôt github de dotnet core:

https://github.com/dotnet/corefx/issues/19709

La solution de contournement (qui ne doit être utilisée que pour les tests ou des applications internes spécifiques) est la suivante:

using System;
using System.Net.Http;
using System.Runtime.InteropServices;

namespace netcurl
{
    class Program
    {
        static void Main(string[] args)
        {
            var url = "https://localhost:5001/.well-known/openid-configuration";
            var handler = new HttpClientHandler();
            using (var httpClient = new HttpClient(handler))
            {
                // Only do this for testing and potentially on linux/mac machines
                if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && IsTestUrl(url))
                {
                    handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
                }

                var output = httpClient.GetStringAsync(url).Result;

                Console.WriteLine(output);
            }
        }

        static  bool IsTestUrl(string url) => url.Contains("localhost");
    }
}

Il existe un autre moyen de résoudre ce problème, et c'est d'utiliser une version de libcurl qui est compilée avec le support openssl. Pour macOS, voici un bon tutoriel sur la façon de procéder:

https://spin.atomicobject.com/2017/09/28/net-core-osx-libcurl-openssl/

Pour la version courte, récupérez une copie de la dernière libcurl compilée avec le support openssl:

brew install curl --with-openssl

Vous ne voudrez probablement pas forcer l'intégralité du système d'exploitation à utiliser la version non-Apple de libcurl, vous voudrez donc probablement utiliser la variable d'environnement DYLD_LIBRARY_PATH Au lieu d'utiliser brew pour forcer le lien entre les binaires et les fichiers réguliers. chemin de l'OS.

export DYLD_LIBRARY_PATH=/usr/local/opt/curl/lib${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}

La commande ci-dessus peut être utilisée pour définir la variable d'environnement appropriée lors de l'exécution de dotnet dans un terminal. Cependant, cela ne s'applique pas vraiment aux applications GUI. Si vous utilisez Visual Studio pour Mac, vous pouvez définir la variable d'environnement dans les paramètres d'exécution du projet:

Visual Studio for Mac project run settings

La deuxième approche était nécessaire pour moi lors de l'utilisation d'IdentityServer4 et de l'autorisation de jeton. Le pipeline d'autorisation .NET Core 2.0 effectuait un appel à l'autorité de jeton à l'aide d'une instance HttpClient. Comme je n'avais pas accès à HttpClient ou à son objet HttpClientHandler, je devais forcer l'instance HttpClient à utiliser la version appropriée de libcurl qui examinerait mon KeyChain racines système pour mon certificat de confiance. Sinon, j'obtiendrais le System.Net.Http.CurlException: Peer certificate cannot be authenticated with given CA certificates environment variable En essayant de sécuriser un point de terminaison webapi en utilisant l'attribut Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme)].

J'ai passé des heures à faire des recherches avant de trouver des solutions. Mon objectif était d'utiliser un certificat auto-signé lors du développement de macOs utilisant IdentityServer4 pour sécuriser ma webapi. J'espère que cela t'aides.

5
Dave Ferguson

// au démarrage, configurez les services ajoutez le code suivant

services.AddHttpClient(settings.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;
              });

vous pouvez maintenant utiliser la méthode IHttpClientFactory CreateClient au sein de votre service

1
Sameh