.net 코어에서 유효하지 않은 SSL 인증서 우회


106

https 사이트에 연결해야하는 프로젝트를 진행 중입니다. 연결할 때마다 해당 사이트의 인증서가 신뢰할 수없는 사이트에서 온 것이므로 코드에서 예외가 발생합니다. .net core http에서 인증서 확인을 우회하는 방법이 있습니까?

이전 버전의 .NET에서이 코드를 보았습니다. 이런 게 필요한 것 같아요.

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

답변:


29

ServicePointManager.ServerCertificateValidationCallback은 .Net Core에서 지원되지 않습니다.

현재 상황은 곧 출시 될 4.1. * System.Net.Http 계약 (HttpClient)에 대한 새로운 ServerCertificateCustomValidationCallback 메서드 가 될 것입니다 . .NET Core 팀은 현재 4.1 계약을 마무리하고 있습니다. 여기 github 에서 이에 대해 읽을 수 있습니다.

CoreFx 또는 MYGET 피드 ( https://dotnet.myget.org/gallery/dotnet-core) 에서 직접 소스를 사용하여 System.Net.Http 4.1의 시험판 버전을 사용해 볼 수 있습니다.

Github의 현재 WinHttpHandler.ServerCertificateCustomValidationCallback 정의


8
이것은 Windows에서만 작동합니다. Linux 용 솔루션이 있습니까? 감사.
Vladimir

149

최신 정보:

아래에서 언급했듯이 모든 구현이이 콜백을 지원하는 것은 아닙니다 (예 : iOS와 같은 플랫폼). 이 경우 문서에서 말했듯이 유효성 검사기를 명시 적으로 설정할 수 있습니다.

handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

.NET Core 2.2, 3.0 및 3.1에서도 작동합니다.

더 많은 제어권을 가진 오래된 대답 이지만 던질 수 있습니다 PlatformNotSupportedException.

다음과 같은 익명 콜백 함수를 사용하여 HTTP 호출에서 SSL 인증서 검사를 재정의 할 수 있습니다.

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은 MacOSX에서이 작업을 수행하는 방법을 제공 하는 HttpClient속성 을 추가 한 DangerousAcceptAnyServerCertificateValidator것 같습니다. 여기에 더 많은 정보 - github.com/dotnet/corefx/pull/19908
트로이 Witthoeft

1
이를 AWS Lambda와 함께 사용하여 .NET Core 1.0은 사용자 지정 루트 CA 인증서로 내부 HTTPS에 연결할 수없는 문제를 수정했습니다.
QuickNull

factory pattern대한 HttpClient무엇입니까?
Kiquenet

@Kiquenet 그냥 팩토리를 생성하면 GetHttpClient메소드가 구성된 것을 반환하고 블록 HttpClient내에서 사용합니다 using.
LuckyLikey

특히 단일 클라이언트 사용으로 범위를 지정할 수 있기 때문에 이것은 허용되는 대답이어야합니다.
BinaryPatrick

38

나는 이것으로 해결한다.

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

동일한 문제에 대한 답을 찾기 위해 여기에 왔지만 NET Core 용 WCF를 사용하고 있습니다. 같은 보트에있는 경우 다음을 사용하십시오.

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

모든 인증서 및 AppDomain에 대해 전역?
Kiquenet

@Kiquenet : 그렇습니다. 다른 곳에서 업데이트 된 답변을 확인하십시오. 지금 더 나은 솔루션이있을 수 있습니다. 1 년이 지났습니다. 다른 것이 없다면 인증자를 하위 클래스로 만들 수 있다고 생각합니다. 그리고 내가 아는 HttpClient에 대한 기본 팩토리가 없습니다. 더 많은 기능이 필요하면 RestClient를 살펴보십시오.
Troels Larsen

HttpClient (.NET Core 3.1)에는 ClientCredentials 속성이 없습니다.
Павле

@ Павле : 아직이 프로젝트를 3.1로 업데이트하지 않았지만 docs.microsoft.com/en-us/dotnet/api/… 속성이 있어야합니다 .
Troels Larsen

@ Павле :이 답변은 HttpClient에 관한 것이 아니라 WCF 서비스 생성 클라이언트에 관한 것입니다. 내 ASMX SoapClient에서도 일했습니다. 감사합니다!
Jan Zahradník

14

.NetCore에서 services configure method에서 다음 코드 조각을 추가 할 수 있습니다. 개발 환경에서만 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

.NET Core 2.2 및 Docker Linux 컨테이너에서 자체 서명 된 인증서 및 클라이언트 인증서 인증으로 작업 할 때 동일한 문제에 직면했습니다. 내 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.