문제

자체 서명된 SSL 인증서를 사용하는 API에 연결하려고 합니다.저는 .NET의 HttpWebRequest 및 HttpWebResponse 개체를 사용하여 이를 수행하고 있습니다.그리고 다음과 같은 예외가 발생합니다.

기본 연결이 닫혔습니다.SSL/TLS 보안 채널에 대한 신뢰 관계를 설정할 수 없습니다.

나는 이것이 무엇을 의미하는지 이해합니다.그리고 나는 이해한다 .NET에서는 나에게 경고하고 연결을 닫아야 한다고 생각합니다.하지만 이 경우에는 어쨌든 API에 연결하고 싶습니다. 중간자 공격은 망할 것입니다.

그렇다면 이 자체 서명된 인증서에 대한 예외를 추가하려면 어떻게 해야 합니까?아니면 HttpWebRequest/Response에 인증서의 유효성을 전혀 검사하지 않도록 지시하는 접근 방식입니까?어떻게 해야 합니까?

도움이 되었습니까?

해결책

@domster : 작동하지만 인증서 해시가 귀하가 기대하는 것에 일치하는지 확인하여 약간의 보안을 시행 할 수 있습니다. 따라서 확장 된 버전은 다음과 비슷해 보입니다 (우리가 사용하는 일부 라이브 코드를 기반으로).

static readonly byte[] apiCertHash = { 0xZZ, 0xYY, ....};

/// <summary>
/// Somewhere in your application's startup/init sequence...
/// </summary>
void InitPhase()
{
    // Override automatic validation of SSL server certificates.
    ServicePointManager.ServerCertificateValidationCallback =
           ValidateServerCertficate;
}

/// <summary>
/// Validates the SSL server certificate.
/// </summary>
/// <param name="sender">An object that contains state information for this
/// validation.</param>
/// <param name="cert">The certificate used to authenticate the remote party.</param>
/// <param name="chain">The chain of certificate authorities associated with the
/// remote certificate.</param>
/// <param name="sslPolicyErrors">One or more errors associated with the remote
/// certificate.</param>
/// <returns>Returns a boolean value that determines whether the specified
/// certificate is accepted for authentication; true to accept or false to
/// reject.</returns>
private static bool ValidateServerCertficate(
        object sender,
        X509Certificate cert,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
        // Good certificate.
        return true;
    }

    log.DebugFormat("SSL certificate error: {0}", sslPolicyErrors);

    bool certMatch = false; // Assume failure
    byte[] certHash = cert.GetCertHash();
    if (certHash.Length == apiCertHash.Length)
    {
        certMatch = true; // Now assume success.
        for (int idx = 0; idx < certHash.Length; idx++)
        {
            if (certHash[idx] != apiCertHash[idx])
            {
                certMatch = false; // No match
                break;
            }
        }
    }

    // Return true => allow unauthenticated server,
    //        false => disallow unauthenticated server.
    return certMatch;
}

다른 팁

인증서 유효성 검사를 완전히 비활성화하려면 ServicePointManager에서 ServerCertificateValidationCallback을 변경할 수 있습니다.

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

이것은 모든 인증서 (유효하지 않거나 만료되거나 자체 서명 된 인증서 포함)를 유효성으로 검증합니다.

.NET 4.5에서는 HttpWebRequest 자체별로 SSL 유효성 검사를 재정의할 수 있습니다(모든 요청에 ​​영향을 미치는 전역 대리자를 통하지 않음).

http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.servercertificatevalidationcallback.aspx

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.ServerCertificateValidationCallback = delegate { return true; };

로컬 컴퓨터 신뢰할 수있는 루트 인증 당국에 자체 서명 인증서 추가

MMC를 관리자로 실행하여 인증서를 가져올 수 있습니다.

방법 : MMC 스냅인으로 인증서를 봅니다

사용 된 유효성 검사 콜백의 범위 Domster의 대답 발신자 매개 변수를 사용하여 특정 요청으로 제한 될 수 있습니다. ServerCertificateValidationCallback 대리자. 다음 간단한 스코프 클래스는이 기술을 사용하여 주어진 요청 객체에 대해서만 실행되는 유효성 검사 콜백을 일시적으로 연결합니다.

public class ServerCertificateValidationScope : IDisposable
{
    private readonly RemoteCertificateValidationCallback _callback;

    public ServerCertificateValidationScope(object request,
        RemoteCertificateValidationCallback callback)
    {
        var previous = ServicePointManager.ServerCertificateValidationCallback;
        _callback = (sender, certificate, chain, errors) =>
            {
                if (sender == request)
                {
                    return callback(sender, certificate, chain, errors);
                }
                if (previous != null)
                {
                    return previous(sender, certificate, chain, errors);
                }
                return errors == SslPolicyErrors.None;
            };
        ServicePointManager.ServerCertificateValidationCallback += _callback;
    }

    public void Dispose()
    {
        ServicePointManager.ServerCertificateValidationCallback -= _callback;
    }
}

위의 클래스는 다음과 같이 특정 요청에 대한 모든 인증서 오류를 무시하는 데 사용될 수 있습니다.

var request = WebRequest.Create(uri);
using (new ServerCertificateValidationScope(request, delegate { return true; }))
{
    request.GetResponse();
}

다른 사람에게 가능한 도움으로 추가하려면 ... 사용자가 자체 서명 된 인증서를 설치하도록 프롬프트하려면이 코드를 사용할 수 있습니다 (위에서 수정).

관리 권한이 필요하지 않으며 로컬 사용자에게 신뢰할 수있는 프로필에 설치됩니다.

    private static bool ValidateServerCertficate(
        object sender,
        X509Certificate cert,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
        {
            // Good certificate.
            return true;
        }

        Common.Helpers.Logger.Log.Error(string.Format("SSL certificate error: {0}", sslPolicyErrors));
        try
        {
            using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
            {
                store.Open(OpenFlags.ReadWrite);
                store.Add(new X509Certificate2(cert));
                store.Close();
            }
            return true;
        }
        catch (Exception ex)
        {
            Common.Helpers.Logger.Log.Error(string.Format("SSL certificate add Error: {0}", ex.Message));
        }

        return false;
    }

이것은 응용 프로그램에 잘 작동하는 것으로 보이며 사용자가 NO를 누르면 커뮤니케이션이 작동하지 않습니다.

업데이트 : 2015-12-11 -Storename.root를 Storename.my로 변경했습니다. 루트 대신 로컬 사용자 저장소에 설치됩니다. "관리자로 실행"하더라도 일부 시스템에 루트가 작동하지 않습니다.

답을 구축하십시오 Devstuff 주제와 발행인을 포함시키려면 ... 의견을 환영합니다 ...

public class SelfSignedCertificateValidator
{
    private class CertificateAttributes
    {
        public string Subject { get; private set; }
        public string Issuer { get; private set; }
        public string Thumbprint { get; private set; }

        public CertificateAttributes(string subject, string issuer, string thumbprint)
        {
            Subject = subject;
            Issuer = issuer;                
            Thumbprint = thumbprint.Trim(
                new char[] { '\u200e', '\u200f' } // strip any lrt and rlt markers from copy/paste
                ); 
        }

        public bool IsMatch(X509Certificate cert)
        {
            bool subjectMatches = Subject.Replace(" ", "").Equals(cert.Subject.Replace(" ", ""), StringComparison.InvariantCulture);
            bool issuerMatches = Issuer.Replace(" ", "").Equals(cert.Issuer.Replace(" ", ""), StringComparison.InvariantCulture);
            bool thumbprintMatches = Thumbprint == String.Join(" ", cert.GetCertHash().Select(h => h.ToString("x2")));
            return subjectMatches && issuerMatches && thumbprintMatches; 
        }
    }

    private readonly List<CertificateAttributes> __knownSelfSignedCertificates = new List<CertificateAttributes> {
        new CertificateAttributes(  // can paste values from "view cert" dialog
            "CN = subject.company.int", 
            "CN = issuer.company.int", 
            "f6 23 16 3d 5a d8 e5 1e 13 58 85 0a 34 9f d6 d3 c8 23 a8 f4") 
    };       

    private static bool __createdSingleton = false;

    public SelfSignedCertificateValidator()
    {
        lock (this)
        {
            if (__createdSingleton)
                throw new Exception("Only a single instance can be instanciated.");

            // Hook in validation of SSL server certificates.  
            ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertficate;

            __createdSingleton = true;
        }
    }

    /// <summary>
    /// Validates the SSL server certificate.
    /// </summary>
    /// <param name="sender">An object that contains state information for this
    /// validation.</param>
    /// <param name="cert">The certificate used to authenticate the remote party.</param>
    /// <param name="chain">The chain of certificate authorities associated with the
    /// remote certificate.</param>
    /// <param name="sslPolicyErrors">One or more errors associated with the remote
    /// certificate.</param>
    /// <returns>Returns a boolean value that determines whether the specified
    /// certificate is accepted for authentication; true to accept or false to
    /// reject.</returns>
    private bool ValidateServerCertficate(
        object sender,
        X509Certificate cert,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
            return true;   // Good certificate.

        Dbg.WriteLine("SSL certificate error: {0}", sslPolicyErrors);
        return __knownSelfSignedCertificates.Any(c => c.IsMatch(cert));            
    }
}

명심해야 할 한 가지는 ServicePointManager.ServerCercerTificateValidationCallback을 사용하는 것이 CRL Check 및 ServerName 유효성 검사가 수행되지 않았다는 것을 의미하지는 않지만 결과를 무시할 수있는 수단 만 제공한다는 것입니다. 따라서 귀하의 서비스는 여전히 CRL을 얻는 데 시간이 걸릴 수 있습니다. 나중에 수표에 실패했다는 것을 알게 될 것입니다.

웹 요청이 정확한 예외를 던질 OP와 같은 문제가 발생했습니다. 모든 것을 올바르게 설정했으며 인증서가 설치되었으며 기계 저장소에서 잘 찾아서 웹 요청에 첨부 할 수 있으며 요청 컨텍스트에서 인증서 확인을 비활성화했습니다.

내 사용자 계정으로 실행 중이며 인증서가 머신 스토어에 설치되었습니다. 이로 인해 웹 요청 이이 예외를 던졌습니다. 문제를 해결하기 위해 관리자로 실행 중이거나 인증서를 사용자 저장소에 설치하고 거기에서 읽어야했습니다.

C#은 웹 요청과 함께 사용할 수 없더라도 기계 스토어에서 인증서를 찾을 수 있으며 웹 요청이 발행되면 OP의 예외가 발생하는 것으로 보입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top