WCF per connection server certificate validation

2019-04-06 11:03发布

问题:

I'm trying to bypass https certificate validation only to our own testing environment (multiple machines), while trying to keep certificate validation for all the other connection.

From reading online, most (if not all) WCF related suggestion seems to point to the something similar of following

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

However, this is a global setting and I would like to apply this for only a specific connection. Is this even possible/supported usage scenario?

回答1:

Something like this:

ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(ValidateCert);

public static bool ValidateCert(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    string requestHost;

    if(sender is string)
    {
        requestHost = sender.ToString();
    }
    else
    {
        HttpWebRequest request = sender as HttpWebRequest;

        if(request != null)
        {
            requestHost = request.Host;
        }
    }

    if(!string.IsNullOrEmpty(requestHost) && requestHost == "my_test_machine")
        return true;

    return sslPolicyErrors == SslPolicyErrors.None;
}

Note the documentation on the sender parameter:

sender parameter passed to the RemoteCertificateValidationCallback can be a host string name or an object derived from WebRequest (HttpWebRequest, for example) depending on the CertificatePolicy property

Disclaimer - I didn't test this, I wrote it based on the documentation. YMMV.



回答2:

I was finally able to found a real solution when using .net 4.5.

This code allows you to use a custom validator only for a specific WCF client.

It has been tested against BasicHttpBinding with BasicHttpSecurityMode.Transport.

There is a new property named SslCertificateAuthentication in ClientBase.ClientCredentials.ServiceCertificate.

You can initialize this property with a X509ServiceCertificateAuthentication where you can provide a custom X509CertificateValidator.

For example:

// initialize the ssl certificate authentication
client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication()
{
   CertificateValidationMode = X509CertificateValidationMode.Custom,
   CustomCertificateValidator = new CustomValidator(serverCert)
};

// simple custom validator, only valid against a specific thumbprint
class CustomValidator : X509CertificateValidator
{
    private readonly X509Certificate2 knownCertificate;

    public CustomValidator(X509Certificate2 knownCertificate)
    {
        this.knownCertificate = knownCertificate;
    }

    public override void Validate(X509Certificate2 certificate)
    {
        if (this.knownCertificate.Thumbprint != certificate.Thumbprint)
        {
            throw new SecurityTokenValidationException("Unknown certificate");
        }
    }
}


回答3:

Seems like in .NET 4.5 you can do the following:

var request = (HttpWebRequest)WebRequest.Create(url);
request.ServerCertificateValidationCallback += 
    (sender, certificate, chain, sslPolicyErrors) => true

I did not realize it initially, as you actually have to cast result of Create method to HttpWebRequest, as abstract WebRequest does not contain this delegate.



回答4:

I'd like to propose you a more "kosher" way to do it (from the point of information security):

  1. Create a self-signed unique certificate for your developer machine
  2. Add this certificate to "Trusted root" certificate store on your test service instance
  3. Add the certificate to application's allowed list (if it exists)

This will allow you to work with your service without creating "backdoors" in code. This also will allow you to limit test access to test environment only while having same code both on Test and Prod instances.



回答5:

You actually can disable SSL cert validation per client/channel with this code:

var noCertValidationAuth = new X509ServiceCertificateAuthentication() 
{ 
    CertificateValidationMode = X509CertificateValidationMode.None 
};
client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = noCertValidationAuth;

It is actually a variation of answer in this thread, so kudos there.



回答6:

What you could do is place a setting in Your web.config file.

Then in Your code check the value in the web.config file. Then set the certificate validation depending on that value.

Edit

One option is:

String url = "https://www.stackoverflow.com";
HttpWebRequest request = HttpWebRequest.CreateHttp(url);
request.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => { return true; };

Another is: http://weblog.west-wind.com/posts/2011/Feb/11/HttpWebRequest-and-Ignoring-SSL-Certificate-Errors

Edit 2

Try something like this:

ServicePointManager.ServerCertificateValidationCallback += customXertificateValidation;


private static bool customXertificateValidation(object sender, X509Certificate cert, 
X509Chain chain, SslPolicyErrors error)
{
    if (check something here)
   {
    return true;
   }
   return false;
}


回答7:

I used a flag in the Web.config file to indicate when a certificate is self-signed and we also know that we are in a development environment:

<add key="isSelfSignedCertificate" value="true"/>

In the code we must check this flag to apply the certificate validation exception:

using System.Net;

bool isSelfSignedCertificate = Convert.ToBoolean(ConfigurationManager.AppSettings["isSelfSignedCertificate"]);

if (isSelfSignedCertificate)
{
    ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
}


标签: c# wcf ssl