Mengizinkan Sertifikat SSL Tidak Tepercaya dengan HttpClient

114

Saya kesulitan untuk membuat aplikasi Windows 8 saya berkomunikasi dengan API web pengujian saya melalui SSL.

Tampaknya HttpClient / HttpClientHandler tidak menyediakan dan opsi untuk mengabaikan sertifikat tidak tepercaya seperti WebRequest memungkinkan Anda untuk (meskipun dengan cara "hacky" ServerCertificateValidationCallback).

Bantuan apa pun akan sangat dihargai!

Jamie
sumber
5
Jika Anda menggunakan .NET Core, Anda mungkin tertarik dengan jawaban ini .
kdaveid

Jawaban:

13

Dengan Windows 8.1, Anda sekarang dapat mempercayai sertifikat SSL yang tidak valid. Anda harus menggunakan Windows.Web.HttpClient atau jika Anda ingin menggunakan System.Net.Http.HttpClient, Anda dapat menggunakan adaptor penangan pesan yang saya tulis: http://www.nuget.org/packages/WinRtHttpClientHandler

Docs ada di GitHub: https://github.com/onovotny/WinRtHttpClientHandler

Claire Novotny
sumber
12
Adakah cara untuk melakukan ini tanpa menggunakan semua kode Anda? Dengan kata lain, apa inti dari solusi Anda?
wensveen
Inti dari solusi ini adalah membungkus penangan klien http windows dan menggunakannya sebagai implementasi HttpClient. Semuanya ada di file ini: github.com/onovotny/WinRtHttpClientHandler/blob/master/… silakan menyalinnya tetapi tidak yakin mengapa tidak menggunakan paket saja.
Claire Novotny
@OrenNovotny jadi solusi Anda tidak terikat pada versi Windows?
chester89
Saya menerapkan ini di aplikasi UWP saya, dan saya menggunakan contoh filter di bawah. Saya tetap mengalami masalah yang sama.
Christian Findlay
158

Solusi cepat dan kotor adalah menggunakan ServicePointManager.ServerCertificateValidationCallbackdelegasi. Ini memungkinkan Anda untuk memberikan validasi sertifikat Anda sendiri. Validasi diterapkan secara global di seluruh Domain Aplikasi.

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

Saya menggunakan ini terutama untuk pengujian unit dalam situasi di mana saya ingin menjalankan titik akhir yang saya hosting dalam proses dan saya mencoba untuk memukulnya dengan klien WCF atau HttpClient.

Untuk kode produksi Anda mungkin menginginkan kontrol yang lebih halus dan akan lebih baik menggunakan properti WebRequestHandlerdan ServerCertificateValidationCallbackdelegasinya (Lihat jawaban dtb di bawah ). Atau jawaban ctacke menggunakan HttpClientHandler. Saya lebih memilih salah satu dari keduanya sekarang bahkan dengan tes integrasi saya daripada bagaimana saya dulu melakukannya kecuali saya tidak dapat menemukan kait lain.

Bronumski
sumber
32
Bukan downvoter, tapi salah satu masalah terbesar dengan ServerCertificateValidationCallback adalah pada dasarnya global untuk AppDomain Anda. Jadi jika Anda menulis pustaka yang perlu melakukan panggilan ke situs dengan sertifikat yang tidak tepercaya dan menggunakan solusi ini, Anda mengubah perilaku seluruh aplikasi, bukan hanya pustaka Anda. Selain itu, orang harus selalu berhati-hati dengan pendekatan 'membabi buta kembali benar'. Ini memiliki implikasi keamanan yang serius. Itu harus membaca / * memeriksa parameter yang disediakan dan kemudian dengan hati-hati memutuskan apakah * / mengembalikan true;
scottt732
@ scottt732 Tentu saja hal yang valid dan layak untuk disebutkan, tetapi ini masih merupakan solusi yang valid. Mungkin setelah membaca ulang pertanyaan asli oleh OP, tampaknya dia sudah mengetahui handler
ServerCertificateValidationCallback
2
Saya akan merekomendasikan untuk setidaknya memfilter beberapa kriteria seperti pengirim
Boas Enkler
2
Saya benar-benar harus merekomendasikan opsi WebRequestHandler vs. ServicePointManager global.
Thomas S. Trias
3
Anda tidak harus menggunakan ServicePointManager secara global, Anda dapat menggunakan ServicePointManager.GetServicePoint(Uri) (lihat dokumen ) untuk mendapatkan titik layanan yang hanya berlaku untuk panggilan ke URI tersebut. Anda kemudian dapat menyetel properti dan menangani acara berdasarkan subset tersebut.
oatsoda
87

Lihat Kelas WebRequestHandler dan Properti ServerCertificateValidationCallback -nya :

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

    using (var client = new HttpClient(handler))
    {
        ...
    }
}
dtb
sumber
5
Terima kasih atas tanggapannya namun saya sudah memeriksanya - itu tidak ada di .NET untuk aplikasi Windows 8 Store.
Jamie
Namun, membuat kelas turunan HttpClientHandler atau HttpMessageHandler sendiri cukup mudah, terutama mengingat fakta bahwa sumber WebRequestHandler sudah tersedia.
Thomas S. Trias
@ ThomasS.Trias ada contoh tentang itu? derived class?
Kiquenet
2
Menggunakan HttpClientHandler?
Kiquenet
1
@Kiquenet balasan ini berasal dari tahun 2012, sudah berusia 6 tahun, dan ya - banyak orang saat ini yakin bahwa ini adalah cara yang tepat untuk menggunakan httpclient :)
justmara
63

Jika Anda mencoba melakukan ini di pustaka .NET Standard, berikut adalah solusi sederhana, dengan semua risiko hanya kembali trueke penangan Anda. Saya serahkan keamanan kepada Anda.

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

var client = new HttpClient(handler);
ctacke
sumber
1
ini menghasilkan platform yang tidak didukung ketika direferensikan dari uwp
Ivan
bekerja untuk saya di UWP. mungkin cakupan dukungan telah jatuh tempo dalam 14 bulan. catatan: peretasan ini harus diperlukan hanya di test / dev env (di mana sertifikat yang ditandatangani sendiri digunakan)
Ben McIntyre
Saya menjalankan kode ini dan selalu memenuhi System.NotImplementedException. Saya tidak tahu kenapa.
Liu Feng
25

Atau Anda bisa menggunakan untuk HttpClient di Windows.Web.Httpnamespace:

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ä
sumber
Bisakah saya menggunakan System.Net.Http, System.Webdan Windows.Web.Httpbersama - sama?
Kiquenet
2
HttpClient tampaknya tidak memiliki timpaan ini di .NET Standard atau UWP.
Christian Findlay
jangan gunakan pola "menggunakan": docs.microsoft.com/en-us/azure/architecture/antipatterns/…
Bernhard
15

Jika Anda menggunakan System.Net.Http.HttpClientSaya yakin pola yang benar adalah

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

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

Sebagian besar jawaban di sini menyarankan untuk menggunakan pola tipikal:

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

karena antarmuka IDisposable. Tolong jangan!

Microsoft memberi tahu Anda alasannya:

Dan di sini Anda dapat menemukan analisis terperinci tentang apa yang terjadi di balik layar: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

Mengenai pertanyaan SSL Anda dan berdasarkan https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem

Inilah pola Anda:

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
sumber
Pertanyaannya adalah untuk hubungan kepercayaan sertifikat yang ditandatangani sendiri. Seluruh jawaban Anda adalah (meskipun dianggap valid) tentang membuat utas HttpClient aman.
Adarsha
1
Ini bukan hanya tentang "keamanan benang". Saya ingin menunjukkan cara yang "benar" dalam menggunakan httpclient dengan verifikasi SSL yang "dinonaktifkan". Jawaban lain "secara global" mengubah pengelola sertifikat.
Bernhard
7

Jika ini untuk aplikasi Windows Runtime, maka Anda harus menambahkan sertifikat yang ditandatangani sendiri ke proyek dan mereferensikannya di appxmanifest.

Dokumennya ada di sini: http://msdn.microsoft.com/en-us/library/windows/apps/hh465031.aspx

Hal yang sama jika berasal dari CA yang tidak tepercaya (seperti CA pribadi yang tidak dipercaya oleh mesin itu sendiri) - Anda perlu mendapatkan sertifikat publik CA, tambahkan sebagai konten ke aplikasi lalu tambahkan ke manifes.

Setelah selesai, aplikasi akan melihatnya sebagai sertifikat yang ditandatangani dengan benar.

Claire Novotny
sumber
2

Saya tidak punya jawaban, tapi saya punya alternatif.

Jika Anda menggunakan Fiddler2 untuk memantau lalu lintas DAN mengaktifkan Dekripsi HTTPS, lingkungan pengembangan Anda tidak akan mengeluh. Ini tidak akan berfungsi pada perangkat WinRT, seperti Microsoft Surface, karena Anda tidak dapat menginstal aplikasi standar pada perangkat tersebut. Tapi komputer Win8 pengembangan Anda akan baik-baik saja.

Untuk mengaktifkan enkripsi HTTPS di Fiddler2, buka Alat> Opsi Fiddler> HTTPS (Tab)> Centang "Dekripsi Lalu Lintas HTTPS" .

Saya akan terus mengawasi utas ini dengan harapan ada seseorang yang memiliki solusi yang elegan.

Laith
sumber
2

Saya menemukan contoh di klien Kubernetes ini di mana mereka menggunakan X509VerificationFlags.AllowUnknownCertificateAuthority untuk memercayai sertifikat akar yang ditandatangani sendiri yang ditandatangani sendiri. Saya sedikit mengerjakan ulang contoh mereka untuk bekerja dengan sertifikat dasar yang dikodekan PEM kami sendiri. Semoga ini membantu seseorang.

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
sumber
1

Saya menemukan contoh online yang tampaknya berfungsi dengan baik:

Pertama, Anda membuat ICertificatePolicy baru

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

Kemudian gunakan saja ini sebelum mengirim permintaan http Anda seperti:

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

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

TombMedia
sumber
1
ServicePointManager.CertificatePolicytidak digunakan lagi: docs.microsoft.com/en-us/dotnet/framework/whats-new/…
schmidlop