【问题标题】:Force HttpClient to trust single Certificate强制 HttpClient 信任单个证书
【发布时间】:2014-08-04 23:54:35
【问题描述】:

你能强制 HttpClient 只信任一个证书吗?

我知道你可以做到:

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

但这会迫使它只信任那个单一的证书,还是会信任那个证书和所有能生效的证书。 GlobalSign 可以验证吗?

基本上我想确保它只能是我的客户端正在与之交谈的服务器/证书。

【问题讨论】:

  • 好问题。很高兴看到您采取积极措施强化渠道。

标签: c# asp.net ssl ssl-certificate


【解决方案1】:

客户端可以使用ServerCertificateValidationCallback 如下 -

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

【讨论】:

【解决方案2】:

你能强制 HttpClient 只信任一个证书吗? ... 基本上我想确保它只能是我的客户正在与之交谈的服务器/证书。

是的。但是什么类型的证书?服务器还是 CA?以下是两者的示例。

此外,在服务器的情况下,固定公钥而不是证书可能会更好。这是因为一些组织,比如谷歌,每 30 天左右轮换一次他们的服务器证书,以努力保持 CRL 对移动客户端来说很小。但是,组织将重新认证相同的公钥。


下面是从Use a particular CA for a SSL connection 固定 CA 的示例。它需要将证书放置在证书存储中。您可以在应用中随身携带 CA。

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

没有知道如何默认使用这个链(上面的chain2),这样就不需要回调了。也就是说,将它安装在 ssl 套接字上,连接将“正常工作”。

而且我没有想出了如何安装它以便将其传递到回调中。也就是说,我必须为回调的每次调用构建链,因为我的chain2 没有作为chain 传递到函数中。


这是一个从 OWASP 的Certificate and Public Key Pinning 固定服务器证书的示例。它需要将证书放置在证书存储中。您可以在应用中随身携带证书或公钥。

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

【讨论】:

  • 我曾经按照我的回答所述的方式进行操作。我在这里学到了一些好东西。谢谢!
  • @André Snede Hansen,感谢您的提问。
  • if (chain2.ChainStatus.Length == 0) 测试应该返回 false,不是吗?
【解决方案3】:

对于将来遇到此问题的任何人,都应该知道,当证书更新时,某些证书颁发机构将不再使用相同的公钥重新颁发证书。我们特别遇到了 Globalsign 的这个问题,尽管他们发布的政策文件说他们提供了选项重用公钥。如果这对您来说可能是个问题,请提前确认您的证书提供商的政策,并且不要使用 Globalsign!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 2011-02-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多