web-dev-qa-db-fra.com

Autoriser des certificats SSL non approuvés avec HttpClient

Je ne parviens pas à faire communiquer mon application Windows 8 avec mon API Web de test sur SSL.

Il semble que HttpClient/HttpClientHandler ne fournit pas d'option permettant d'ignorer les certificats non fiables tels que WebRequest vous permet de le faire (bien que de manière "simpliste" avec ServerCertificateValidationCallback).

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

79
Jamie

Avec Windows 8.1, vous pouvez maintenant faire confiance aux certificats SSL non valides. Vous devez 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

9
Oren Novotny

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

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

J'utilise ceci principalement pour les tests unitaires dans les situations où je souhaite exécuter un processus sur un point de terminaison que j'héberge et que j'essaie de le toucher avec un client WCF ou le HttpClient .

Pour le code de production, vous souhaiterez peut-être un contrôle plus fin et utiliseriez mieux la propriété WebRequestHandler et sa ServerCertificateValidationCallback delegate (Voir la réponse de dtb ci-dessous ). Ou ctacke answer en utilisant le HttpClientHandler . Je préfère l’un ou l’autre de ces deux programmes, même avec mes tests d’intégration, sur la façon dont je le faisais, à moins que je ne trouve aucun autre crochet.

116
Bronumski

Consultez la WebRequestHandler Class et sa Propriété ServerCertificateValidationCallback :

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

    using (var client = new HttpClient(handler))
    {
        ...
    }
}
69
dtb

Si vous essayez de le faire dans une bibliothèque .NET Standard, voici une solution simple, avec tous les risques de simplement renvoyer true dans 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);
21
ctacke

Ou vous pouvez utiliser pour le HttpClient dans l'espace de noms Windows.Web.Http:

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)) {
    ...
}
21
dschüsä

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 document appxmanifest.

Les documents sont ici: http://msdn.Microsoft.com/en-us/library/windows/apps/hh465031.aspx

Même chose si cela provient d'une autorité de certification qui n'est pas fiable (comme une autorité de certification privée à laquelle la machine elle-même n'a 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 cert correctement signé.

6
Oren Novotny

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

Si vous utilisez Fiddler2 pour surveiller le trafic ET activez le décryptage HTTPS, votre environnement de développement ne se plaindra pas. Cela ne fonctionnera pas sur les périphériques 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, accédez à Outils> Options Fiddler> HTTPS (Onglet)> Cochez "Déchiffrer le trafic HTTPS".

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

2
Laith

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

Vous créez d’abord un nouveau 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-le 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/

1
TombMedia

J'ai trouvé un exemple dans ce client Kubernetes où ils utilisaient X509VerificationFlags.AllowUnknownCertificateAuthority pour faire confiance à des certificats racine auto-signés et autosignés. J'ai légèrement retravaillé leur exemple pour qu'il fonctionne avec nos propres certificats racine codé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>();
  }
}
0
PaulB

La plupart des réponses suggèrent d'utiliser le schéma typique:

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

en raison de l'interface IDisposable. S'il vous plaît ne pas!

Microsoft vous explique pourquoi:

Et vous trouverez ici 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;
  }
 }
}
0
Bernhard