ข้ามใบรับรอง SSL ที่ไม่ถูกต้องใน. net core


110

ฉันกำลังทำโปรเจ็กต์ที่ต้องเชื่อมต่อกับไซต์ https ทุกครั้งที่เชื่อมต่อรหัสของฉันจะแสดงข้อยกเว้นเนื่องจากใบรับรองของไซต์นั้นมาจากไซต์ที่ไม่น่าเชื่อถือ มีวิธีเลี่ยงการตรวจสอบใบรับรองใน. net core http หรือไม่?

ฉันเห็นรหัสนี้จาก. NET เวอร์ชันก่อนหน้า ฉันเดาว่าฉันต้องการอะไรแบบนี้

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

คำตอบ:


29

ServicePointManager.ServerCertificateValidationCallback ไม่รองรับใน. Net Core

สถานการณ์ปัจจุบันคือมันจะเป็น วิธีการใหม่ของServerCertificateCustomValidationCallbackสำหรับสัญญา 4.1. * System.Net.Http (HttpClient) ที่กำลังจะมาถึง ทีมงาน. NET Core กำลังสรุปสัญญา 4.1 ในขณะนี้ คุณสามารถอ่านเกี่ยวกับเรื่องนี้ได้ที่นี่บน github

คุณสามารถทดลองใช้ System.Net.Http 4.1 เวอร์ชันก่อนวางจำหน่ายโดยใช้แหล่งข้อมูลโดยตรงที่นี่ใน CoreFx หรือบนฟีด MYGET: https://dotnet.myget.org/gallery/dotnet-core

WinHttpHandler.ServerCertificateCustomValidationCallbackคำจำกัดความปัจจุบันบน Github


8
สิ่งนี้ใช้ได้กับ Windows เท่านั้น คุณมีวิธีแก้ปัญหาสำหรับ Linux หรือไม่? ขอบคุณ.
Vladimir

154

อัปเดต:

ดังที่กล่าวไว้ด้านล่างการใช้งานบางอย่างไม่รองรับการเรียกกลับนี้ (เช่นแพลตฟอร์มเช่น iOS) ในกรณีนี้ตามที่เอกสารกล่าวคุณสามารถตั้งค่าเครื่องมือตรวจสอบได้อย่างชัดเจน:

handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

สิ่งนี้ใช้ได้กับ. NET Core 2.2, 3.0 และ 3.1 เช่นกัน

คำตอบเก่ามีการควบคุมมากขึ้น แต่อาจส่งผลPlatformNotSupportedException:

คุณสามารถแทนที่การตรวจสอบใบรับรอง SSL ในการโทร HTTP ด้วยฟังก์ชันการโทรกลับแบบไม่ระบุตัวตนเช่นนี้

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

นอกจากนี้ฉันขอแนะนำให้ใช้รูปแบบโรงงานHttpClientเนื่องจากเป็นวัตถุที่ใช้ร่วมกันซึ่งอาจไม่ได้รับการกำจัดทันทีดังนั้นการเชื่อมต่อจะยังคงเปิดอยู่


3
ฉันใช้. Net Core 1.0 และใช้ได้ผลสำหรับฉัน ในแง่หนึ่งดูเหมือนว่า. Net Core 2.0 ได้เพิ่มHttpClientคุณสมบัติที่เรียกว่าDangerousAcceptAnyServerCertificateValidatorซึ่งเป็นวิธีที่จะทำให้สิ่งนี้ทำงานบน MacOSX ได้ ข้อมูลเพิ่มเติมที่นี่ - github.com/dotnet/corefx/pull/19908
Troy Witthoeft

1
การใช้สิ่งนี้กับ AWS Lambda ทำให้. NET Core 1.0 แก้ไขสิ่งที่ขัดขวางไม่ให้ฉันเชื่อมต่อกับ HTTPS ภายในด้วยใบรับรอง CA หลักที่กำหนดเอง
QuickNull

ใด ๆ factory patternสำหรับHttpClient?
Kiquenet

@Kiquenet เพียงแค่สร้างโรงงานโดยที่GetHttpClientMethod จะส่งคืนค่าที่กำหนดค่า HttpClientและใช้ภายใน a using-block
LuckyLikey

นี่ควรเป็นคำตอบที่ได้รับการยอมรับโดยเฉพาะอย่างยิ่งเนื่องจากสามารถกำหนดขอบเขตการใช้งานไคลเอ็นต์เดียวได้
BinaryPatrick

40

ฉันแก้ด้วยสิ่งนี้:

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

35

มาที่นี่เพื่อหาคำตอบสำหรับปัญหาเดียวกัน แต่ฉันใช้ WCF สำหรับ NET Core หากคุณอยู่ในเรือลำเดียวกันให้ใช้:

client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = 
    new X509ServiceCertificateAuthentication()
    {
        CertificateValidationMode = X509CertificateValidationMode.None,
        RevocationMode = X509RevocationMode.NoCheck
    };

Global สำหรับใบรับรองและ AppDomain ทั้งหมด?
Kiquenet

@Kiquenet: ฉันเชื่ออย่างนั้นใช่ ตรวจสอบคำตอบที่อัปเดตจากที่อื่นอาจมีทางออกที่ดีกว่าในขณะนี้ ผ่านมาเป็นปีแล้ว ฉันเดาว่าคุณสามารถ subclass the authenticator ได้ถ้าไม่มีอะไรอื่น และไม่ไม่มีโรงงานดั้งเดิมสำหรับ HttpClient ที่ฉันรู้จัก หากคุณต้องการฟังก์ชันเพิ่มเติมโปรดดูที่ RestClient
Troels Larsen

ไม่มีคุณสมบัติ ClientCredentials ใน HttpClient (.NET Core 3.1)
Павле

@ Павле: ผมยังไม่ได้ปรับปรุงโครงการนี้เป็น 3.1 แต่ควรมีคุณสมบัติดังกล่าวdocs.microsoft.com/en-us/dotnet/api/...
Troels Larsen

@ Павле: คำตอบนี้ไม่เกี่ยวกับ HttpClient แต่เป็นไคลเอนต์ที่สร้างบริการ WCF ทำงานให้กับ ASMX SoapClient ของฉันด้วยขอบคุณมาก!
Jan Zahradník

15

ใน. NetCore คุณสามารถเพิ่มข้อมูลโค้ดต่อไปนี้ได้ที่วิธีการกำหนดค่าบริการฉันเพิ่มการตรวจสอบเพื่อให้แน่ใจว่าเราผ่านใบรับรอง SSL ในสภาพแวดล้อมการพัฒนาเท่านั้น

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

1
ทำไม -ve ถึงใช้สิ่งที่คนอื่นแนะนำในโค้ด mvc.net และพวกเขาทำคะแนนได้ฉันแค่แสดงให้เห็นถึงการใช้งานเดียวกันในรหัส. netCore
Sameh

อาจ. เพราะไม่มีคำอธิบายใด ๆ เหตุใดจึงควรใช้แนวทางนี้แทนรหัสใดควรเขียนในส่วนการโทร (พูด mycontroller.cs) ซึ่งอาจเป็นส่วนหนึ่งของคำอธิบาย เอกสารทางการ / การอ้างอิงใด ๆ
Bhanu Chhabra

อย่างที่บอกถ้าคุณตรวจสอบความคิดเห็นอื่น ๆ ที่ด้านบนสุดของเธรดก็ไม่มีความแตกต่างกันมากนัก แต่พวกเขาได้คะแนน 18 และ 81 คะแนน
Sameh

1
เนื่องจากพวกเขาได้เพิ่มข้อความที่สนับสนุนคำตอบของพวกเขาโปรดอ่านหลักเกณฑ์อีกครั้ง อาจช่วยคุณได้ @moderators สามารถชี้ให้เห็นปัญหาที่แท้จริงของ IMHO
Bhanu Chhabra

9

ฉันประสบปัญหาเดียวกันเมื่อทำงานกับใบรับรองที่ลงนามด้วยตนเองและการรับรองความถูกต้องของไคลเอนต์บนคอนเทนเนอร์. ทุกอย่างทำงานได้ดีบนเครื่อง dev Windows ของฉัน แต่ใน Docker ฉันพบข้อผิดพลาดดังกล่าว:

System.Security.Authentication.AuthenticationException: ใบรับรองระยะไกลไม่ถูกต้องตามขั้นตอนการตรวจสอบความถูกต้อง

โชคดีที่ใบรับรองถูกสร้างขึ้นโดยใช้โซ่ แน่นอนคุณสามารถเพิกเฉยต่อโซลูชันนี้และใช้วิธีแก้ไขปัญหาข้างต้นได้ตลอดเวลา

นี่คือทางออกของฉัน:

  1. ฉันบันทึกใบรับรองโดยใช้ Chrome บนคอมพิวเตอร์ในรูปแบบP7B

  2. แปลงใบรับรองเป็นรูปแบบ PEM โดยใช้คำสั่งนี้:
    openssl pkcs7 -inform DER -outform PEM -in <cert>.p7b -print_certs > ca_bundle.crt

  3. เปิดไฟล์ ca_bundle.crt และลบการบันทึกเรื่องทั้งหมดออกจากไฟล์ที่สะอาด ตัวอย่างด้านล่าง:

    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
  1. ใส่บรรทัดเหล่านี้ใน Dockerfile (ในขั้นตอนสุดท้าย):
    # 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. ในแอป:
    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
    };

เมธอด 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);
}

5

ประการแรกอย่าใช้ในการผลิต

หากคุณใช้มิดเดิลแวร์ AddHttpClient สิ่งนี้จะเป็นประโยชน์ ฉันคิดว่ามันเป็นสิ่งจำเป็นสำหรับวัตถุประสงค์ในการพัฒนาไม่ใช่การผลิต จนกว่าคุณจะสร้างใบรับรองที่ถูกต้องคุณสามารถใช้ Func นี้ได้

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

และใช้มันเช่น

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

3

การอนุญาตให้ใบรับรองทั้งหมดมีประสิทธิภาพมาก แต่ก็อาจเป็นอันตรายได้เช่นกัน หากคุณต้องการอนุญาตเฉพาะใบรับรองที่ถูกต้องและใบรับรองบางอย่างก็สามารถทำได้เช่นนี้

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

แหล่งที่มาดั้งเดิม:

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.