强制HttpClient信任单个证书

13

您能强制 HttpClient 仅信任单个证书吗?

我知道您可以执行以下操作:

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

那么这是否会强制它只信任单个证书,还是它会信任该证书以及fx. GlobalSign可以验证的所有证书?

基本上,我希望确保我的客户端仅与我的服务器/证书进行通信。


好问题。很高兴看到您采取积极措施来加强通道的安全性。 - jww
3个回答

19

你能强制HttpClient只信任单一证书吗? ... 基本上我想确保客户端只与我的服务器/证书通信。

可以。但是是什么类型的证书?服务器还是CA?下面分别给出了两个示例。

此外,在服务器的情况下,固定公钥可能比固定证书更好。这是因为一些组织(如Google)每30天左右会轮换其服务器证书,以使移动客户端的CRL保持较小。然而,这些组织将重新认证相同的公钥。


这是一个关于从使用特定CA建立SSL连接中固定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的证书和公钥固定的服务器证书固定示例。它不需要将证书放置在证书存储中。您可以在应用程序中携带证书或公钥。
// 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;
}

我曾经以我的回答所述的方式做过。我在这里学到了一些好东西。谢谢! - Typist
@André Snede Hansen,感谢您的提问。 - Typist
2
如果 (chain2.ChainStatus.Length == 0),那么测试应该返回 false,对吗? - Vince
使用HttpClient时回调未被触发。是否有特定的服务器回调?我正在查看文档,但没有找到相关内容。 - Ali123

2

如果您在未来遇到这种情况,应该知道有些证书颁发机构不再重新发放具有相同公钥的证书。我们在与Globalsign合作时就遇到了这个问题,尽管他们的政策文件明确表示提供重复使用公钥的选项,但他们留下了一个非常棘手的后果:在短时间内更新所有客户端软件中的新公钥固定详细信息。如果您也可能遇到此类问题,请事先确认您的证书提供商的政策,并不要使用Globalsign!


-12

客户端可以像下面这样使用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;
        };

3
这里完全没有检查。OP 要求信任一个单一的证书,而所提供的答案信任所有东西(即使是错误的证书)。建议与《世界上最危险的代码:非浏览器软件中验证 SSL 证书》中讨论的问题别无二致。 - jww
太棒了!@jww,我明白你的意思。 - Typist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接