Force HttpClient to trust single Certificate

2019-03-10 14:43发布

Can you force HttpClient to only trust a single certificate?

I know you can do:

WebRequestHandler handler = new WebRequestHandler();
X509Certificate2 certificate = GetMyX509Certificate();
handler.ClientCertificates.Add(certificate);
HttpClient client = new HttpClient(handler);

But will this force it to only trust that single certificate, or will it trust that certifate AND all certificates that fx. GlobalSign can verify?

Basicly I want to ensure that it can ONLY be my server/certificate that my client is talking to.

3条回答
Bombasti
2楼-- · 2019-03-10 14:46

Client can use ServerCertificateValidationCallback like below -

 System.Net.ServicePointManager.ServerCertificateValidationCallback +=
    delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
                            System.Security.Cryptography.X509Certificates.X509Chain chain,
                            System.Net.Security.SslPolicyErrors sslPolicyErrors)
        {
            return true;
        };
查看更多
姐就是有狂的资本
3楼-- · 2019-03-10 14:59

For anyone who comes across this in the future tou should be aware that some certificate authorities will no longer reissue certificates with the same public key when the certificate is renewed. We had this problem specifically with Globalsign who left us with the very difficult logistical problem of updating the client software with new public key pinning details for all our customers in a very short space of time, despite their published policy documents saying that they provided the option to reuse the public key. If this may be an issue for you confirm your certificate provider's policy in advance, and don't use Globalsign!

查看更多
The star\"
4楼-- · 2019-03-10 15:01

Can you force HttpClient to only trust a single certificate? ... Basically I want to ensure that it can ONLY be my server/certificate that my client is talking to.

Yes. But what type of certificate? Server or CA? Examples for both follow.

Also, it might be better to pin the public key rather than the certificate in the case of a server. That's because some organizations, like Google, rotate their server certificates every 30 days or so in an effort to keep the CRLs small for mobile clients. However, the organizations will re-certify the same public key.


Here's an example of pinning the CA from Use a particular CA for a SSL connection. It does not require placing the certificate in a Certificate Store. You can carry the CA around in your app.

static bool VerifyServerCertificate(object sender, X509Certificate certificate,
    X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    try
    {
        String CA_FILE = "ca-cert.der";
        X509Certificate2 ca = new X509Certificate2(CA_FILE);

        X509Chain chain2 = new X509Chain();
        chain2.ChainPolicy.ExtraStore.Add(ca);

        // Check all properties (NoFlag is correct)
        chain2.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;

        // This setup does not have revocation information
        chain2.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;

        // Build the chain
        chain2.Build(new X509Certificate2(certificate));

        // Are there any failures from building the chain?
        if (chain2.ChainStatus.Length == 0)
            return false;

        // If there is a status, verify the status is NoError
        bool result = chain2.ChainStatus[0].Status == X509ChainStatusFlags.NoError;
        Debug.Assert(result == true);

        return result;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }

    return false;
}

I have not figured out how to use this chain (chain2 above) by default such that there's no need for the callback. That is, install it on the ssl socket and the connection will "just work".

And I have not figured out how install it such that its passed into the callback. That is, I have to build the chain for each invocation of the callback because my chain2 is not passed into the functions as chain.


Here's an example of pinning the server certificate from OWASP's Certificate and Public Key Pinning. It does not require placing the certificate in a Certificate Store. You can carry the certificate or public key around in your app.

// Encoded RSAPublicKey
private static String PUB_KEY = "30818902818100C4A06B7B52F8D17DC1CCB47362" +
    "C64AB799AAE19E245A7559E9CEEC7D8AA4DF07CB0B21FDFD763C63A313A668FE9D764E" +
    "D913C51A676788DB62AF624F422C2F112C1316922AA5D37823CD9F43D1FC54513D14B2" +
    "9E36991F08A042C42EAAEEE5FE8E2CB10167174A359CEBF6FACC2C9CA933AD403137EE" +
    "2C3F4CBED9460129C72B0203010001";

public static void Main(string[] args)
{
  ServicePointManager.ServerCertificateValidationCallback = PinPublicKey;
  WebRequest wr = WebRequest.Create("https://encrypted.google.com/");
  wr.GetResponse();
}

public static bool PinPublicKey(object sender, X509Certificate certificate, X509Chain chain,
                                SslPolicyErrors sslPolicyErrors)
{
  if (null == certificate)
    return false;

  String pk = certificate.GetPublicKeyString();
  if (pk.Equals(PUB_KEY))
    return true;

  // Bad dog
  return false;
}
查看更多
登录 后发表回答