program tip

.NET의 HttpWebRequest / Response와 함께 자체 서명 된 인증서 사용

radiobox 2020. 10. 13. 07:37
반응형

.NET의 HttpWebRequest / Response와 함께 자체 서명 된 인증서 사용


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

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

이것이 의미하는 바를 이해합니다. 그리고 .NET이 경고하고 연결을 닫아야한다고 느끼는 이유를 이해 합니다. 그러나이 경우에는 어쨌든 API에 연결하고 싶습니다. man-in-the-middle 공격은 저주받을 수 있습니다.

그렇다면이 자체 서명 된 인증서에 대한 예외를 추가하려면 어떻게해야합니까? 아니면 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대리자 의 sender 매개 변수를 사용하여 특정 요청으로 제한 될 수 있습니다 . 다음 간단한 범위 클래스는이 기술을 사용하여 주어진 요청 객체에 대해서만 실행되는 유효성 검사 콜백을 일시적으로 연결합니다.

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

이것은 우리 응용 프로그램에서 잘 작동하는 것으로 보이며 사용자가 아니오를 누르면 통신이 작동하지 않습니다.

Update: 2015-12-11 - Changed StoreName.Root to StoreName.My - My will install into the local users store, instead of Root. Root on some systems will not work, even if you "run as administrator"


Just building on answer from devstuff to include subject and issuer...comments welcome...

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

One thing to keep in mind is that having the ServicePointManager.ServerCertificateValidationCallback does not seem to mean that the CRL check and servername validation are not done, it only provides a means to override their result. So your service might still take a while to get a CRL, you'll only know afterwards that it failed some checks.


I was running into the same problem as the OP where the web request would throw that exact exception. I had everything setup correctly I thought, the certificate was installed, I could locate it in the machine store just fine and attach it to the web request, and I had disabled the verification of certificates on the request context.

It turned out that I was running under my user account, and that the certificate was installed to the machine store. This caused the web request to throw this exception. To solve the problem I had to either be running as administrator or install the certificate to the user store and read it from there.

It would seem that C# is able to find the certificate in the machine store even though it can't be used with a web request, and that this results in the OP's exception being thrown once the web request is issued.

참고URL : https://stackoverflow.com/questions/526711/using-a-self-signed-certificate-with-nets-httpwebrequest-response

반응형