views:

3147

answers:

3

I'm trying to connect to an API that uses a self-signed SSL certificate. I'm doing so using .NET's HttpWebRequest and HttpWebResponse objects. And I'm getting an exception that:

The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.

I understand what this means. And I understand why .NET feels it should warn me and close the connection. But in this case, I'd like to just connect to the API anyway, man-in-the-middle attacks be damned.

So, how do I go about adding an exception for this self-signed certificate? Or is the approach to tell HttpWebRequest/Response not to validate the certificate at all? How would I do that?

+11  A: 

Turns out, if you just want to disable certificate validation altogether, you can change the ServerCertificateValidationCallback on the ServicePointManager, like so:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

This will validate all certificates (including invalid, expired or self-signed ones).

Domster
This is clever and made me giggle. +1
Joshua Hudson
+12  A: 

@Domster: that works, but you might want to enforce a bit of security by checking if the certificate hash matches what you expect. So an expanded version looks a bit like this (based on some live code we're using):

const 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;
}
devstuff
Any comment by the down-voter?
devstuff
+1  A: 

Add the self signed cert to the Local Computer Trusted Root Certification Authorities

You can import the cert by running the MMC as Administrator.

How to: View Certificates with the MMC Snap-in http://msdn.microsoft.com/en-us/library/ms788967.aspx

wgthom