Autoriser les certificats SSL non approuvés avec HttpClient

114

J'ai du mal à faire communiquer mon application Windows 8 avec mon API Web de test via SSL.

Il semble que HttpClient / HttpClientHandler ne fournit pas et l'option d'ignorer les certificats non approuvés comme WebRequest vous permet de (bien que d'une manière "hacky" avec ServerCertificateValidationCallback).

Toute aide serait très appréciée!

Jamie
la source
5
Si vous utilisez .NET Core, cette réponse pourrait vous intéresser .
kdaveid le

Réponses:

13

Avec Windows 8.1, vous pouvez désormais faire confiance aux certificats SSL non valides. Vous devez soit utiliser Windows.Web.HttpClient ou si vous souhaitez utiliser System.Net.Http.HttpClient, vous pouvez utiliser l'adaptateur de gestionnaire de messages que j'ai écrit: http://www.nuget.org/packages/WinRtHttpClientHandler

Les documents sont sur le GitHub: https://github.com/onovotny/WinRtHttpClientHandler

Claire Novotny
la source
12
Existe-t-il un moyen de faire cela sans utiliser tout votre code? En d'autres termes, quel est l'essentiel de votre solution?
wensveen
L'essentiel de la solution est d'encapsuler le gestionnaire de client Windows http et de l'utiliser comme implémentation de HttpClient. Tout est dans ce fichier: github.com/onovotny/WinRtHttpClientHandler/blob/master/… n'hésitez pas à le copier mais ne savez pas pourquoi ne pas simplement utiliser le package.
Claire Novotny
@OrenNovotny pour que votre solution ne soit pas liée à la version Windows?
chester89
J'ai implémenté cela dans mon application UWP et j'ai utilisé l'exemple de filtres ci-dessous. Je reçois toujours la même erreur.
Christian Findlay
158

Une solution rapide et sale consiste à utiliser le ServicePointManager.ServerCertificateValidationCallbackdélégué. Cela vous permet de fournir votre propre validation de certificat. La validation est appliquée globalement sur l'ensemble du domaine d'application.

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

Je l'utilise principalement pour les tests unitaires dans les situations où je veux exécuter sur un point de terminaison que j'héberge en cours et que j'essaie de le frapper avec un client WCF ou le HttpClient.

Pour le code de production, vous voudrez peut-être un contrôle plus fin et il serait préférable d'utiliser la propriété WebRequestHandleret sa ServerCertificateValidationCallbackpropriété déléguée (voir la réponse de dtb ci-dessous ). Ou ctacke la réponse en utilisant le HttpClientHandler. Je préfère l'un de ces deux maintenant, même avec mes tests d'intégration, à la façon dont je le faisais à moins que je ne trouve aucun autre crochet.

Bronumski
la source
32
Pas le downvoter, mais l'un des plus gros problèmes avec le ServerCertificateValidationCallback est qu'il est fondamentalement global à votre AppDomain. Donc, si vous écrivez une bibliothèque qui a besoin d'appeler un site avec un certificat non approuvé et que vous utilisez cette solution de contournement, vous modifiez le comportement de l'ensemble de l'application, pas seulement de votre bibliothèque. En outre, les gens doivent toujours faire attention à l'approche du «retour aveugle du vrai». Cela a de graves implications pour la sécurité. Il doit lire / * inspecter les paramètres fournis, puis décider soigneusement s'il faut * / retourner vrai;
scottt732
@ scottt732 Certainement un point valable et qui mérite d'être mentionné mais c'est toujours une solution valable. Peut-être qu'après avoir relu la question originale du PO, il semble qu'il était déjà au courant du gestionnaire
ServerCertificateValidationCallback
2
Je recommanderais au moins de filtrer sur certains critères comme l'expéditeur
Boas Enkler
2
Je dois vraiment recommander l'option WebRequestHandler par rapport au ServicePointManager global.
Thomas S.Trias
3
Vous n'avez pas besoin d'utiliser ServicePointManager globalement, vous pouvez utiliser ServicePointManager.GetServicePoint(Uri) (voir la documentation ) pour obtenir le point de service qui s'applique uniquement aux appels à cet URI. Vous pouvez ensuite définir les propriétés et gérer les événements en fonction de ce sous-ensemble.
oatsoda
87

Jetez un œil à la classe WebRequestHandler et à sa propriété ServerCertificateValidationCallback :

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

    using (var client = new HttpClient(handler))
    {
        ...
    }
}
dtb
la source
5
Merci pour la réponse, mais j'ai déjà examiné cela - il n'est pas présent dans les applications .NET pour Windows 8 Store.
Jamie
Cependant, créer sa propre classe dérivée HttpClientHandler ou HttpMessageHandler est assez simple, d'autant plus que la source WebRequestHandler est facilement disponible.
Thomas S.Trias
@ ThomasS.Trias un échantillon à ce sujet? derived class?
Kiquenet
2
Utilisation HttpClientHandler?
Kiquenet
1
@Kiquenet cette réponse date de 2012, elle a déjà 6 ans, et oui - beaucoup de gens ces temps-ci étaient sûrs que c'était la bonne façon d'utiliser httpclient :)
justmara
63

Si vous essayez de le faire dans une bibliothèque .NET Standard, voici une solution simple, avec tous les risques de simplement retourner truedans votre gestionnaire. Je vous laisse la sécurité.

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

var client = new HttpClient(handler);
ctacke
la source
1
cela se traduit par une plate-forme non prise en charge lorsqu'elle est référencée à partir d'uwp
Ivan
a travaillé pour moi dans UWP. peut-être que la couverture de soutien a mûri en 14 mois. Remarque: ce hack devrait être nécessaire uniquement dans l'environnement test / dev (où des certificats auto-signés sont utilisés)
Ben McIntyre
J'ai exécuté ce code et je rencontre toujours l'exception System.NotImplementedException. Je ne sais pas pourquoi.
Liu Feng
25

Ou vous pouvez utiliser pour HttpClient dans l' Windows.Web.Httpespace de noms:

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ä
la source
Puis-je utiliser System.Net.Http, System.Webet Windows.Web.Httpensemble?
Kiquenet
2
HttpClient ne semble pas avoir cette substitution dans .NET Standard ou UWP.
Christian Findlay
n'utilisez pas le modèle "using": docs.microsoft.com/en-us/azure/architecture/antipatterns
Bernhard
15

Si vous utilisez, System.Net.Http.HttpClientje pense que le modèle correct est

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

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

La plupart des réponses ici suggèrent d'utiliser le modèle typique:

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

en raison de l'interface IDisposable. Veuillez ne pas!

Microsoft vous explique pourquoi:

Et ici vous pouvez trouver une analyse détaillée de ce qui se passe dans les coulisses: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

Concernant votre question SSL et basée sur https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem

Voici votre modèle:

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
la source
La question est pour la relation de confiance du cert auto-signé. Toute votre réponse est (même si elle est valide) de rendre le thread HttpClient sûr.
Adarsha
1
Il ne s'agit pas seulement de "sécurité des threads". Je voulais montrer la "bonne" façon d'utiliser le client http avec la vérification SSL "désactivée". D'autres réponses «globalement» modifient le gestionnaire de certificats.
Bernhard le
7

S'il s'agit d'une application Windows Runtime, vous devez ajouter le certificat auto-signé au projet et le référencer dans le fichier appxmanifest.

La documentation est ici: http://msdn.microsoft.com/en-us/library/windows/apps/hh465031.aspx

Même chose si elle provient d'une autorité de certification non approuvée (comme une autorité de certification privée à laquelle la machine elle-même ne fait pas confiance) - vous devez obtenir le certificat public de l'autorité de certification, l'ajouter en tant que contenu à l'application, puis l'ajouter au manifeste.

Une fois que cela est fait, l'application le verra comme un certificat correctement signé.

Claire Novotny
la source
2

Je n'ai pas de réponse, mais j'ai une alternative.

Si vous utilisez Fiddler2 pour surveiller le trafic ET activer le décryptage HTTPS, votre environnement de développement ne se plaindra pas. Cela ne fonctionnera pas sur les appareils WinRT, tels que Microsoft Surface, car vous ne pouvez pas y installer d'applications standard. Mais votre ordinateur de développement Win8 ira bien.

Pour activer le cryptage HTTPS dans Fiddler2, allez dans Outils> Options Fiddler> HTTPS (onglet)> Cochez "Décrypter le trafic HTTPS" .

Je vais garder un œil sur ce fil en espérant que quelqu'un ait une solution élégante.

Laith
la source
2

J'ai trouvé un exemple dans ce client Kubernetes où ils utilisaient X509VerificationFlags.AllowUnknownCertificateAuthority pour faire confiance aux certificats racine auto-signés auto-signés. J'ai légèrement retravaillé leur exemple pour travailler avec nos propres certificats racine encodés PEM. Espérons que cela aide quelqu'un.

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
la source
1

J'ai trouvé un exemple en ligne qui semble bien fonctionner:

Commencez par créer une nouvelle 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;
  }
}

Ensuite, utilisez simplement ceci avant d'envoyer votre requête http comme ceci:

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

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

TombMedia
la source
1
ServicePointManager.CertificatePolicyest obsolète: docs.microsoft.com/en-us/dotnet/framework/whats-new/…
schmidlop