Consentire certificati SSL non attendibili con HttpClient


114

Faccio fatica a far comunicare la mia applicazione Windows 8 con la mia API Web di prova su SSL.

Sembra che HttpClient / HttpClientHandler non fornisca e l'opzione per ignorare certificati non attendibili come WebRequest ti consente di (anche se in modo "hacky" con ServerCertificateValidationCallback).

Qualsiasi aiuto sarebbe molto apprezzato!


5
Nel caso in cui utilizzi .NET Core potresti essere interessato a questa risposta.
kdaveid

Risposte:


13

Con Windows 8.1, ora puoi fidarti dei certificati SSL non validi. Devi usare Windows.Web.HttpClient o se vuoi usare System.Net.Http.HttpClient, puoi usare l'adattatore del gestore di messaggi che ho scritto: http://www.nuget.org/packages/WinRtHttpClientHandler

I documenti sono su GitHub: https://github.com/onovotny/WinRtHttpClientHandler


12
C'è un modo per farlo senza utilizzare tutto il codice? In altre parole, qual è l'essenza della tua soluzione?
wensveen

L'essenza della soluzione è racchiudere il gestore del client http di Windows e usarlo come implementazione di HttpClient. È tutto in questo file: github.com/onovotny/WinRtHttpClientHandler/blob/master/… sentiti libero di copiarlo ma non sei sicuro del perché non usare semplicemente il pacchetto.
Claire Novotny

@OrenNovotny quindi la tua soluzione non è vincolata alla versione Windows?
chester89

L'ho implementato nella mia app UWP e ho utilizzato l'esempio di filtri di seguito. Ottengo ancora lo stesso errore.
Christian Findlay

158

Una soluzione rapida e sporca consiste nell'usare il ServicePointManager.ServerCertificateValidationCallbackdelegato. Ciò consente di fornire la propria convalida del certificato. La convalida viene applicata globalmente all'intero dominio dell'app.

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

Lo uso principalmente per i test di unità in situazioni in cui voglio eseguire su un endpoint che sto ospitando in corso e sto cercando di colpirlo con un client WCF o con HttpClient.

Per il codice di produzione potresti volere un controllo più dettagliato e sarebbe meglio usare la proprietà WebRequestHandlere la sua ServerCertificateValidationCallbackproprietà delegate (vedi la risposta di dtb di seguito ). O ctacke risposta usando il HttpClientHandler. Sto preferendo uno di questi due ora anche con i miei test di integrazione rispetto a come lo facevo prima, a meno che non riesco a trovare nessun altro gancio.


32
Non il downvoter, ma uno dei maggiori problemi con ServerCertificateValidationCallback è che è fondamentalmente globale per il tuo AppDomain. Quindi, se stai scrivendo una libreria che deve effettuare chiamate a un sito con un certificato non attendibile e utilizzare questa soluzione alternativa, stai modificando il comportamento dell'intera applicazione, non solo della tua libreria. Inoltre, le persone dovrebbero sempre fare attenzione con l'approccio "restituisci ciecamente vero". Ha gravi implicazioni per la sicurezza. Dovrebbe leggere / * ispezionare i parametri forniti e quindi decidere attentamente se * / restituire true;
scottt732

@ scottt732 Certamente un punto valido e degno di nota ma è comunque una soluzione valida. Forse dopo aver riletto la domanda originale dell'OP sembra che fosse già a conoscenza del gestore ServerCertificateValidationCallback
Bronumski

2
Consiglierei di filtrare almeno su alcuni criteri come mittente
Boas Enkler

2
Devo davvero consigliare l'opzione WebRequestHandler rispetto al ServicePointManager globale.
Thomas S. Trias

3
Non è necessario utilizzare ServicePointManager a livello globale, è possibile utilizzare ServicePointManager.GetServicePoint(Uri) (vedere la documentazione ) per ottenere il punto di servizio che si applica solo alle chiamate a quell'URI. È quindi possibile impostare le proprietà e gestire gli eventi in base a quel sottoinsieme.
oatsoda

87

Dai un'occhiata alla classe WebRequestHandler e alla sua proprietà ServerCertificateValidationCallback :

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

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

5
Grazie per la risposta, tuttavia l'ho già esaminato: non è presente in .NET per le app di Windows 8 Store.
Jamie

Tuttavia, la creazione di una propria classe derivata HttpClientHandler o HttpMessageHandler è abbastanza semplice, soprattutto considerando il fatto che l'origine WebRequestHandler è prontamente disponibile.
Thomas S. Trias

@ ThomasS.Trias qualche esempio a riguardo? derived class?
Kiquenet

2
Usando HttpClientHandler?
Kiquenet

1
@Kiquenet questa risposta è del 2012, ha già 6 anni, e sì - molte persone queste volte erano sicure che questo fosse il modo giusto di usare httpclient :)
justmara

63

Se stai tentando di farlo in una libreria .NET Standard, ecco una soluzione semplice, con tutti i rischi di tornare truenel tuo gestore. Lascio a te la sicurezza.

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

var client = new HttpClient(handler);

1
questo si traduce in una piattaforma non supportata se citata da uwp
Ivan

ha lavorato per me in UWP. forse la copertura del supporto è maturata in 14 mesi. nota: questo hack dovrebbe essere necessario solo in test / dev env (dove vengono utilizzati certificati autofirmati)
Ben McIntyre

Ho eseguito questo codice e ho sempre incontrato System.NotImplementedException. Non so perché
Liu Feng

25

Oppure puoi usare per HttpClient nello Windows.Web.Httpspazio dei nomi:

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)) {
    ...
}

Posso usare System.Net.Http, System.Webe Windows.Web.Httpinsieme?
Kiquenet

2
HttpClient non sembra avere questa sostituzione in .NET Standard o UWP.
Christian Findlay


15

Se stai usando, System.Net.Http.HttpClientcredo che il modello corretto sia

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

var http = new HttpClient(handler);
var res = http.GetAsync(url);

8

La maggior parte delle risposte qui suggeriscono di utilizzare il modello tipico:

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

a causa dell'interfaccia IDisposable. Per favore non farlo!

Microsoft ti spiega perché:

E qui puoi trovare un'analisi dettagliata di cosa sta succedendo dietro le quinte: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

Per quanto riguarda la tua domanda SSL e basata su https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem

Ecco il tuo modello:

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;
  }
 }
}

La domanda è per il rapporto di fiducia del certificato autofirmato. La tua intera risposta è (anche se è valida) sul rendere sicuro il thread HttpClient.
Adarsha

1
Non si tratta solo di "thread safety". Volevo mostrare il modo "giusto" di utilizzare il client http con la verifica SSL "disabilitata". Altre risposte modificano "globalmente" il gestore dei certificati.
Bernhard

7

Se si tratta di un'applicazione Windows Runtime, è necessario aggiungere il certificato autofirmato al progetto e fare riferimento ad esso nell'appxmanifest.

I documenti sono qui: http://msdn.microsoft.com/en-us/library/windows/apps/hh465031.aspx

Stessa cosa se proviene da una CA non attendibile (come una CA privata di cui la macchina stessa non si fida): è necessario ottenere il certificato pubblico della CA, aggiungerlo come contenuto all'app, quindi aggiungerlo al manifest.

Una volta fatto, l'app lo vedrà come un certificato firmato correttamente.


2

Non ho una risposta, ma ho un'alternativa.

Se utilizzi Fiddler2 per monitorare il traffico E abilitare la decrittografia HTTPS, il tuo ambiente di sviluppo non si lamenterà. Questo non funzionerà sui dispositivi WinRT, come Microsoft Surface, perché non è possibile installare app standard su di essi. Ma il tuo computer di sviluppo Win8 andrà bene.

Per abilitare la crittografia HTTPS in Fiddler2, vai su Strumenti> Opzioni di Fiddler> HTTPS (scheda)> seleziona "Decrittografa traffico HTTPS" .

Terrò d'occhio questo thread sperando che qualcuno abbia una soluzione elegante.


2

Ho trovato un esempio in questo client Kubernetes in cui utilizzavano X509VerificationFlags.AllowUnknownCertificateAuthority per fidarsi dei certificati radice autofirmati e autofirmati. Ho leggermente rielaborato il loro esempio per lavorare con i nostri certificati radice codificati PEM. Si spera che questo aiuti qualcuno.

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>();
  }
}

1

Ho trovato un esempio online che sembra funzionare bene:

Per prima cosa crei un nuovo 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;
  }
}

Quindi usalo prima di inviare la tua richiesta http in questo modo:

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

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


1
ServicePointManager.CertificatePolicyè deprecato: docs.microsoft.com/en-us/dotnet/framework/whats-new/…
schmidlop
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.