C# 强制HttpClient信任单个证书

C# 强制HttpClient信任单个证书,c#,asp.net,ssl,ssl-certificate,C#,Asp.net,Ssl,Ssl Certificate,您可以强制HttpClient只信任一个证书吗 我知道你可以做到: WebRequestHandler handler = new WebRequestHandler(); X509Certificate2 certificate = GetMyX509Certificate(); handler.ClientCertificates.Add(certificate); HttpClient client = new HttpClient(handler); 但这会迫使它只信任那个单一的证书,

您可以强制HttpClient只信任一个证书吗

我知道你可以做到:

WebRequestHandler handler = new WebRequestHandler();
X509Certificate2 certificate = GetMyX509Certificate();
handler.ClientCertificates.Add(certificate);
HttpClient client = new HttpClient(handler);
但这会迫使它只信任那个单一的证书,还是会信任那个certifate和fx提供的所有证书。GlobalSign可以验证吗


基本上,我希望确保我的客户端只能与我的服务器/证书通话。

客户端可以使用
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;
        };
您可以强制HttpClient只信任一个证书吗? ... 基本上,我希望确保它只能是我的客户机正在与之交谈的服务器/证书

对。但是什么类型的证书呢?服务器还是CA?下面是两个例子

此外,对于服务器来说,最好是锁定公钥而不是证书。这是因为一些组织,如谷歌,每隔30天左右轮换一次服务器证书,以使移动客户端的CRL保持较小。但是,各组织将重新认证相同的公钥


下面是一个将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;
}
// 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;
}
我还没有弄清楚默认情况下如何使用此链(
chain2
),因此不需要回调。也就是说,将其安装在ssl套接字上,连接将“正常工作”

我还没有弄清楚如何安装它,从而将其传递到回调中。也就是说,我必须为每次回调调用构建链,因为我的
chain2
没有作为
chain
传递到函数中


下面是一个将服务器证书与OWASP绑定的示例。它不需要将证书放在证书存储中。您可以在应用程序中随身携带证书或公钥

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

对于将来遇到此问题的任何人,tou都应该知道,某些证书颁发机构在证书续订时将不再使用相同的公钥重新颁发证书。我们特别是Globalsign遇到了这个问题,他给我们留下了一个非常困难的后勤问题,即在很短的时间内为我们的所有客户使用新的公钥锁定详细信息更新客户端软件,尽管他们发布的政策文件说他们提供了重用公钥的选项。如果这可能是您的问题,请提前确认您的证书提供商的策略,并且不要使用Globalsign

好问题。很高兴看到你采取积极措施来强化渠道。根本就没有检查。OP要求信任一个证书,而提供的答案信任所有证书(甚至是错误的证书)。这个建议与中讨论的问题没有什么不同。太棒了@jww,我明白了。我以前是按照我回答的方式做的。我在这里学到了一些好东西。谢谢@AndréSnede Hansen,谢谢你的提问。如果(chain2.ChainStatus.Length==0)测试应该返回false,否?