Force HttpClient доверять одному сертификату

Можете ли вы заставить HttpClient доверять только одному сертификату?

Я знаю, что вы можете сделать:

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

Но это заставит его доверять только одному сертификату или доверять этому сертификату И всем сертификатам, которые fx. GlobalSign может проверить?

В основном я хочу, чтобы он мог ТОЛЬКО быть моим сервером/сертификатом, с которым разговаривает мой клиент.

Ответ 1

Можете ли вы заставить HttpClient доверять только одному сертификату?    ... В основном я хочу, чтобы он мог ТОЛЬКО быть моим сервером/сертификатом, с которым разговаривает мой клиент.

Да. Но какой тип сертификата? Сервер или ЦС? Примеры для обоих.

Кроме того, может быть лучше связать открытый ключ, а не сертификат в случае сервера. Это потому, что некоторые организации, такие как Google, поворачивают свои серверные сертификаты каждые 30 дней или около того, пытаясь ограничить CRL небольшими для мобильных клиентов. Тем не менее, организации будут повторно сертифицировать один и тот же открытый ключ.


Вот пример привязки CA от Использовать определенный CA для SSL-соединения. Это не требует размещения сертификата в магазине сертификатов. Вы можете переносить 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;
}

Ответ 2

Для тех, кто сталкивается с этим в будущем, следует помнить, что некоторые сертификационные органы больше не будут переиздавать сертификаты с тем же открытым ключом, когда сертификат будет обновлен. У нас была эта проблема специально с Globalsign, которая оставила нам очень сложную логистическую проблему обновления клиентского программного обеспечения с новыми подробными сведениями о публичных ключах для всех наших клиентов за очень короткий промежуток времени, несмотря на опубликованные политические документы, в которых говорилось, что они предоставили вариант для повторного использования открытого ключа. Если это может быть проблемой, если вы заранее подтвердите политику поставщика сертификатов и не используете Globalsign!

Ответ 3

Клиент может использовать 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;
        };