.net 如何将“ServerCertificateValidationCallback”属性设置回其默认行为?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4156365/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 14:55:40  来源:igfitidea点击:

How do I set the 'ServerCertificateValidationCallback' property back to its default behavior?

.netvb.netsslssl-certificate

提问by atconway

I use the following line of code within a single method to explicitly check and trust an SSL cert from the following host: MyTrustedCompany.com:

我在单个方法中使用以下代码行来明确检查和信任来自以下主机的 SSL 证书:MyTrustedCompany.com:

ServicePointManager.ServerCertificateValidationCallback = Function(obj As [Object], certificate As X509Certificate, chain As X509Chain, errors As SslPolicyErrors) (certificate.Subject.Contains("CN=MyTrustedCompany.com"))

No problem with the code -> works perfectly 100%.

代码没问题 -> 100% 完美运行。

The problem is, it is too far reaching. I thought its scope would only be within the method I decalred it, but apparently it is a Shared property on the 'ServicePointManager' object, and must then persist for the entire application, which I do not want.

问题是,它太远了。我认为它的范围只会在我对其进行标记的方法内,但显然它是“ServicePointManager”对象上的共享属性,并且必须在整个应用程序中持续存在,这是我不想要的。

The problem is later I am calling web services of mine, etc and getting the "Could not establish a trust relationship..." exception. This is because in the line of code above I check for the host name of an SSL cert specefic to that method. I quickly tested Returning 'True' from the callback so all certs would be trusted instead of checking for a specefic name (i.e. MyTrustedCompany) and subsiquent requests worked. This is how I know this callback assignment reaches father than that single method. Sure I could extend the callback to include all other certitificate names, but what I would ratherdo is set the 'ServerCertificateValidationCallback' back to its default behavior. Like the pseudo code below:

问题是后来我正在调用我的 Web 服务等,并收到“无法建立信任关系...”异常。这是因为在上面的代码行中,我检查了该方法特定的 SSL 证书的主机名。我快速测试了从回调中返回“真”,因此所有证书都将被信任,而不是检查特定名称(即 MyTrustedCompany)并且后续请求有效。这就是我知道这个回调分配比那个单一方法到达父亲的方式。当然我可以扩展回调以包含所有其他证书名称,但我更愿意做的是将“ServerCertificateValidationCallback”设置回其默认行为。就像下面的伪代码:

ServicePointManager.ServerCertificateValidationCallback = Nothing  'Default checking behavior

How do I remove the custom validation and set it back to its default behavior? Thanks!

如何删除自定义验证并将其设置回其默认行为?谢谢!

采纳答案by atconway

This actually appears to work (as simple as it is) and makes the object behave in its default manner.

这实际上似乎有效(就这么简单)并使对象以其默认方式运行。

ServicePointManager.ServerCertificateValidationCallback = Nothing

回答by Tom

You'll want to only return true for certain URLs, but otherwise, this does what you want, leaving the possibility of using multiple delegates. The logic for each callback could include a check via reflection so that the callback only returns true when called from certain components, thus creating a sort of tunnel between certain URLs and certain applications.

您只想为某些 URL 返回 true,否则,这会满足您的需求,从而留下使用多个委托的可能性。每个回调的逻辑可以包括通过反射进行的检查,以便回调仅在从某些组件调用时返回 true,从而在某些 URL 和某些应用程序之间创建一种隧道。

One way to use this code: 1. Define the mIgnoreBadCertificates delegate early in your object's life 2. Set a property containing the 'beSecure' code true 3. Send the Http Request. 4. Set the property false. This is very important, and should be implemented in a way that guarantees it gets called. The IDisposable pattern is one option.

使用此代码的一种方法: 1. 在对象生命周期的早期定义 mIgnoreBadCertificates 委托 2. 将包含“beSecure”代码的属性设置为 true 3. 发送 Http 请求。4. 将属性设置为 false。这非常重要,并且应该以保证它被调用的方式实现。IDisposable 模式是一种选择。

 private System.Net.Security.RemoteCertificateValidationCallback mIgnoreBadCertificates = new
    System.Net.Security.RemoteCertificateValidationCallback(
      delegate { return true; });

if (beSecure)
    {   //require secure communications
        System.Net.ServicePointManager.ServerCertificateValidationCallback -= mIgnoreBadCertificates;
        Iwds.EventLogger.LogVeryFrequentEvent("Requiring Good Certificates from Remote Sites");
    }
    else
    {   /// Allow connections to SSL sites that have unsafe certificates.
        System.Net.ServicePointManager.ServerCertificateValidationCallback += mIgnoreBadCertificates;
        Iwds.EventLogger.LogVeryFrequentEvent("Ignoring Bad Certificates from Remote Sites");
    }

回答by Tom Winter

A key point to solving your problem is the fact that the senderparameter to the RemoteCertificateValidationCallbackis the WebRequest. You can check the sender against your webrequest so you only do checking for your webrequest. Here is my (relatively untested) solution:

解决您的问题的一个关键点是 的sender参数RemoteCertificateValidationCallbackWebRequest. 您可以根据您的网络请求检查发件人,因此您只需检查您的网络请求。这是我的(相对未经测试的)解决方案:

// Main Code

request = (FtpWebRequest)FtpWebRequest.Create("ftp://example.com");

using(var validator = new WebRequestCertificateValidator(request))
{
    // etc...
}

// WebRequestCertificateValidator Class

public sealed class WebRequestCertificateValidator : IDisposable
{
    private bool disposed;

    private WebRequest request;

    private RemoteCertificateValidationCallback callback;

    /// <summary>
    /// Creates a certificate validator that allows all certificates for the supplied web request.
    /// </summary>
    /// <param name="request">The WebRequest to validate for.</param>
    public WebRequestCertificateValidator(WebRequest request) : this(request, null)
    {
        //
    }

    /// <summary>
    /// Creates a certificate validator that only allows certificates for the supplied web request of the callback returns true.
    /// </summary>
    /// <param name="request">The WebRequest to validate for.</param>
    /// <param name="callback">The delegate that will be called to validate certificates for the WebRequest.</param>
    public WebRequestCertificateValidator(WebRequest request, RemoteCertificateValidationCallback callback)
    {
        this.disposed = false;

        this.request = request;

        this.callback = callback;

        ServicePointManager.ServerCertificateValidationCallback += this.InternalCallback;
    }

    private bool InternalCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        WebRequest request = sender as WebRequest;

        if(request != null)
        {
            if(request == this.request)
            {
                if(this.callback != null)
                {
                    return this.callback(sender, certificate, chain, sslPolicyErrors);
                }
            }
        }

        return true;
    }

    public void Dispose()
    {
        if(!this.disposed)
        {
            ServicePointManager.ServerCertificateValidationCallback -= this.InternalCallback;

            this.callback = null;

            this.request = null;

            this.disposed = true;
        }
    }
}

回答by OzBob

public class OAuthRequestHandler : WebRequestHandler
{
    public OAuthRequestHandler() : base()
    {
        base.ServerCertificateValidationCallback  += this.InternalCallback;
    }

    private bool InternalCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        return certificate.Subject.Contains("CN=MyTrustedCompany.com");
    }
}

and in my program.cs commandline test:

在我的 program.cs 命令行测试中:

HttpClient client = new HttpClient(new OAuthRequestHandler());
responseString = await client.GetStringAsync("https://localhost:2345");

It is possible to do more with the certificate and sender parameters but the OP asked for the above.

可以使用证书和发件人参数做更多事情,但 OP 要求上述内容。