melewati sertifikat SSL yang tidak valid di inti .net

110

Saya sedang mengerjakan proyek yang perlu terhubung ke situs https. Setiap kali saya terhubung, kode saya memunculkan pengecualian karena sertifikat situs tersebut berasal dari situs tidak tepercaya. Apakah ada cara untuk melewati pemeriksaan sertifikat di .net core http?

Saya melihat kode ini dari versi .NET sebelumnya. Saya rasa saya hanya butuh sesuatu seperti ini.

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

Jawaban:

29

ServicePointManager.ServerCertificateValidationCallback tidak didukung di .Net Core.

Situasi saat ini adalah bahwa itu akan menjadi metode ServerCertificateCustomValidationCallback baru untuk 4.1. * Kontrak System.Net.Http mendatang (HttpClient). Tim .NET Core sedang menyelesaikan kontrak 4.1 sekarang. Anda dapat membaca tentang ini di sini di github

Anda dapat mencoba versi pra-rilis System.Net.Http 4.1 dengan menggunakan sumber langsung di sini di CoreFx atau di umpan MYGET: https://dotnet.myget.org/gallery/dotnet-core

Definisi WinHttpHandler.ServerCertificateCustomValidationCallback saat ini di Github

Set
sumber
8
Ini hanya berfungsi di Windows. Apakah Anda punya solusi untuk Linux? Terima kasih.
Vladimir
154

Memperbarui:

Seperti disebutkan di bawah, tidak semua implementasi mendukung callback ini (yaitu, platform seperti iOS). Dalam kasus ini, seperti yang dikatakan dokumen , Anda dapat menyetel validator secara eksplisit:

handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

Ini juga berfungsi untuk .NET Core 2.2, 3.0 dan 3.1

Jawaban lama , dengan lebih banyak kendali tetapi mungkin melempar PlatformNotSupportedException:

Anda dapat mengganti pemeriksaan sertifikat SSL pada panggilan HTTP dengan fungsi panggilan balik anonim seperti ini

using (var httpClientHandler = new HttpClientHandler())
{
   httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
   using (var client = new HttpClient(httpClientHandler))
   {
       // Make your request...
   }
}

Selain itu, saya menyarankan untuk menggunakan pola pabrik HttpClientkarena ini adalah objek bersama yang mungkin tidak segera dibuang dan karena itu koneksi akan tetap terbuka .

kdaveid
sumber
3
Saya menggunakan .Net Core 1.0 dan ini berhasil untuk saya. Sebagai permulaan, sepertinya .Net Core 2.0 telah menambahkan HttpClientproperti bernama DangerousAcceptAnyServerCertificateValidatoryang menyediakan cara untuk membuat ini berfungsi di MacOSX. Info lebih lanjut di sini - github.com/dotnet/corefx/pull/19908
Troy Witthoeft
1
Menggunakan ini dengan AWS Lambda, .NET Core 1.0 memperbaiki apa yang mencegah saya menyambungkan ke HTTPS internal dengan sertifikat CA root kustom.
QuickNull
Ada factory patternuntuk HttpClient?
Kiquenet
@Kiquenet Buatlah pabrik, di mana GetHttpClientMetode mengembalikan konfigurasi HttpClientdan menggunakannya dalam using-block.
LuckyLikey
Ini harus menjadi jawaban yang diterima, terutama karena dapat digunakan untuk satu klien.
BinaryPatrick
40

Saya menyelesaikannya dengan ini:

Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpClient("HttpClientWithSSLUntrusted").ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
        {
            ClientCertificateOptions = ClientCertificateOption.Manual,
            ServerCertificateCustomValidationCallback =
            (httpRequestMessage, cert, cetChain, policyErrors) =>
            {
                return true;
            }
        });

YourService.cs

public UserService(IHttpClientFactory clientFactory, IOptions<AppSettings> appSettings)
    {
        _appSettings = appSettings.Value;
        _clientFactory = clientFactory;
    }

var request = new HttpRequestMessage(...

var client = _clientFactory.CreateClient("HttpClientWithSSLUntrusted");

HttpResponseMessage response = await client.SendAsync(request);
O.Machado
sumber
35

Datang ke sini mencari jawaban untuk masalah yang sama, tetapi saya menggunakan WCF untuk NET Core. Jika Anda berada di perahu yang sama, gunakan:

client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = 
    new X509ServiceCertificateAuthentication()
    {
        CertificateValidationMode = X509CertificateValidationMode.None,
        RevocationMode = X509RevocationMode.NoCheck
    };
Troels Larsen
sumber
Global untuk semua sertifikat dan AppDomain?
Kiquenet
@Kiquenet: Saya yakin begitu, ya. Periksa jawaban yang diperbarui di tempat lain, mungkin ada solusi yang lebih baik sekarang. Sudah setahun. Saya kira Anda dapat membuat subkelas pengautentikasi jika tidak ada yang lain. Dan tidak, tidak ada pabrik asli untuk HttpClient yang saya ketahui. Jika Anda membutuhkan lebih banyak fungsionalitas, lihat RestClient.
Troels Larsen
Tidak ada properti ClientCredentials di HttpClient (.NET Core 3.1).
Павле
@ Павле: Saya belum memperbarui proyek ini ke 3.1, tetapi seharusnya ada properti seperti itu: docs.microsoft.com/en-us/dotnet/api/… .
Troels Larsen
@ Павле: Jawaban ini bukan tentang HttpClient tetapi klien yang dihasilkan Layanan WCF. Bekerja untuk ASMX SoapClient saya juga, terima kasih banyak!
Jan Zahradník
14

Di .NetCore, Anda dapat menambahkan potongan kode berikut pada metode konfigurasi layanan, saya menambahkan tanda centang untuk memastikan hanya bahwa kami melewatkan sertifikat SSL di lingkungan pengembangan saja

services.AddHttpClient("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;
              });
Sameh
sumber
1
Mengapa -ve, ini persis menerapkan apa yang disarankan orang lain dalam kode mvc.net dan mereka mencetak poin di atasnya, saya hanya menggambarkan implementasi yang sama dalam kode .netCore
Sameh
mungkin. karena tidak ada penjelasan apapun. mengapa pendekatan ini harus mengambil alih yang lain, Kode apa yang harus ditulis di bagian panggilan (katakanlah mycontroller.cs), yang bisa menjadi bagian dari penjelasan. dokumentasi / kutipan resmi apa pun.
Bhanu Chhabra
Seperti yang saya katakan jika Anda meninjau komentar lain di bagian atas utas, tidak ada banyak perbedaan namun mereka mencetak 18 dan 81 poin,
Sameh
1
karena mereka menambahkan teks yang mendukung jawaban mereka, harap baca pedoman sekali lagi. Semoga membantu Anda, @moderators dapat menunjukkan masalah yang sebenarnya IMHO.
Bhanu Chhabra
9

Saya menghadapi masalah yang sama ketika bekerja dengan sertifikat yang ditandatangani sendiri dan autentikasi sertifikat klien pada wadah .NET Core 2.2 dan Docker Linux. Semuanya bekerja dengan baik di mesin Windows dev saya, tetapi di Docker saya mendapat kesalahan seperti itu:

System.Security.Authentication.AuthenticationException: Sertifikat jarak jauh tidak valid sesuai dengan prosedur validasi

Untungnya, sertifikat dibuat menggunakan rantai. Tentu saja, Anda selalu dapat mengabaikan solusi ini dan menggunakan solusi di atas.

Jadi inilah solusi saya:

  1. Saya menyimpan sertifikat menggunakan Chrome di komputer saya dalam format P7B .

  2. Ubah sertifikat ke format PEM menggunakan perintah ini:
    openssl pkcs7 -inform DER -outform PEM -in <cert>.p7b -print_certs > ca_bundle.crt

  3. Buka file ca_bundle.crt dan hapus semua rekaman Subjek, biarkan file bersih. Contoh di bawah ini:

    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
  1. Letakkan baris ini ke Dockerfile (di langkah terakhir):
    # Update system and install curl and ca-certificates
    RUN apt-get update && apt-get install -y curl && apt-get install -y ca-certificates
    # Copy your bundle file to the system trusted storage
    COPY ./ca_bundle.crt /usr/local/share/ca-certificates/ca_bundle.crt
    # During docker build, after this line you will get such output: 1 added, 0 removed; done.
    RUN update-ca-certificates
  1. Di aplikasi:
    var address = new EndpointAddress("https://serviceUrl");                
    var binding = new BasicHttpsBinding
    {
        CloseTimeout = new TimeSpan(0, 1, 0),
        OpenTimeout = new TimeSpan(0, 1, 0),
        ReceiveTimeout = new TimeSpan(0, 1, 0),
        SendTimeout = new TimeSpan(0, 1, 0),
        MaxBufferPoolSize = 524288,
        MaxBufferSize = 65536,
        MaxReceivedMessageSize = 65536,
        TextEncoding = Encoding.UTF8,
        TransferMode = TransferMode.Buffered,
        UseDefaultWebProxy = true,
        AllowCookies = false,
        BypassProxyOnLocal = false,
        ReaderQuotas = XmlDictionaryReaderQuotas.Max,
        Security =
        {
            Mode = BasicHttpsSecurityMode.Transport,
            Transport = new HttpTransportSecurity
            {
                ClientCredentialType = HttpClientCredentialType.Certificate,
                ProxyCredentialType = HttpProxyCredentialType.None
            }
        }
    };
    var client = new MyWSClient(binding, address);
    client.ClientCredentials.ClientCertificate.Certificate = GetClientCertificate("clientCert.pfx", "passwordForClientCert");
    // Client certs must be installed
    client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication
    {
        CertificateValidationMode = X509CertificateValidationMode.ChainTrust,
        TrustedStoreLocation = StoreLocation.LocalMachine,
        RevocationMode = X509RevocationMode.NoCheck
    };

Metode GetClientCertificate:

private static X509Certificate2 GetClientCertificate(string clientCertName, string password)
{
    //Create X509Certificate2 object from .pfx file
    byte[] rawData = null;
    using (var f = new FileStream(Path.Combine(AppContext.BaseDirectory, clientCertName), FileMode.Open, FileAccess.Read))
    {
        var size = (int)f.Length;
        var rawData = new byte[size];
        f.Read(rawData, 0, size);
        f.Close();
    }
    return new X509Certificate2(rawData, password);
}
DerSkythe
sumber
5

Pertama, JANGAN GUNAKAN DALAM PRODUKSI

Jika Anda menggunakan middleware AddHttpClient, ini akan berguna. Saya pikir itu dibutuhkan untuk tujuan pengembangan, bukan produksi. Sampai Anda membuat sertifikat yang valid, Anda dapat menggunakan Func ini.

Func<HttpMessageHandler> configureHandler = () =>
        {
            var bypassCertValidation = Configuration.GetValue<bool>("BypassRemoteCertificateValidation");
            var handler = new HttpClientHandler();
            //!DO NOT DO IT IN PRODUCTION!! GO AND CREATE VALID CERTIFICATE!
            if (bypassCertValidation)
            {
                handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, x509Certificate2, x509Chain, sslPolicyErrors) =>
                {
                    return true;
                };
            }
            return handler;
        };

dan menerapkannya seperti

services.AddHttpClient<IMyClient, MyClient>(x => { x.BaseAddress = new Uri("https://localhost:5005"); })
        .ConfigurePrimaryHttpMessageHandler(configureHandler);
Ozan ERTÜRK
sumber
3

Mengizinkan semua sertifikat sangat efektif, tetapi juga bisa berbahaya. Jika Anda hanya ingin mengizinkan sertifikat yang valid ditambah beberapa sertifikat tertentu, hal itu dapat dilakukan seperti ini.

using (var httpClientHandler = new HttpClientHandler())
{
    httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => {
        if (sslPolicyErrors == SslPolicyErrors.None)
        {
            return true;   //Is valid
        }

        if (cert.GetCertHashString() == "99E92D8447AEF30483B1D7527812C9B7B3A915A7")
        {
            return true;
        }
        return false;
    };
    using (var httpClient = new HttpClient(httpClientHandler))
    {
        var httpResponse = httpClient.GetAsync("https://example.com").Result;
    }
}

Sumber asli:

https://stackoverflow.com/a/44140506/3850405

Ogglas
sumber