Проверьте сертификат удаленного сервера X509 с помощью файла сертификата CA - PullRequest
7 голосов
/ 08 октября 2011

Я сгенерировал CA и несколько сертификатов (подписанных CA), используя OpenSSL, и у меня есть клиент и сервер .NET / C #, оба из которых используют SslStream, каждый из которых имеет свои собственные сертификаты / ключи, включена взаимная аутентификация и отзыв отключен.

Я использую RemoteCertificateValidationCallback для SslStream для проверки сертификата удаленного сервера, и я надеялся, что смогу просто загрузить открытый сертификат CA (в виде файла) в программу и использовать его для проверки удаленного сертификата, а не фактически установка CA в хранилище сертификатов Windows. Проблема в том, что X509Chain больше ничего не покажет, если я не установлю ЦС в хранилище, равно как и оболочка Windows CryptoAPI при открытии версии PEM одного из сертификатов.

Мой вопрос: как я могу проверить, что сертификат был подписан моим конкретным ЦС, просто используя файл открытого сертификата ЦС без использования хранилища сертификатов Windows или WCF при RemoteCertificateValidationCallback, X509Certificate и X509Chain Кажется, мне не с чем работать?

Ответы [ 2 ]

5 голосов
/ 16 марта 2012

Поскольку сертификат CA НЕ находится в хранилище корневых сертификатов, в RemoteCertificateValidationCallback () будет установлен флаг ошибки SslPolicyErrors.RemoteCertificateChainErrors ;существует возможность явной проверки цепочки сертификатов по вашей собственной X509Certificate2Collection , поскольку вы не используете локальное хранилище.

if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors)
{
    X509Chain chain0 = new X509Chain();
    chain0.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
    // add all your extra certificate chain
    chain0.ChainPolicy.ExtraStore.Add(new X509Certificate2(PublicResource.my_ca));
    chain0.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
    isValid = chain0.Build((X509Certificate2)certificate);
}

Вы также можете повторно использовать цепочку, переданную в обратном вызове, добавить дополнительные сертификаты в коллекцию ExtraStore и выполнить проверку с помощью AllowUnknownCertificateAuthority флаг, который необходим, так как вы добавляете ненадежные сертификаты в цепочку.

Вы также можете предотвратить исходную ошибку, программно добавив сертификат CA в доверенное корневое хранилище (конечно, оно открывает всплывающее окно, дляэто серьезная проблема безопасности для глобального добавления нового доверенного корня ЦС):

var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
X509Certificate2 ca_cert = new X509Certificate2(PublicResource.my_ca);
store.Add(ca_cert);
store.Close();

РЕДАКТИРОВАТЬ: Для тех, кто хочет четко проверить цепочку с вашим ЦС:

Другая возможность - использовать библиотеку BouncyCastle для построения цепочки сертификатов и проверки доверия.Варианты понятны, а ошибки легко понять.В случае успеха он построит цепочку, в противном случае возвращается исключение.Пример ниже:

        // rootCerts : collection of CA
        // currentCertificate : the one you want to test
        var builderParams = new PkixBuilderParameters(rootCerts, 
                                new X509CertStoreSelector { Certificate = currentCertificate });
        // crls : The certificate revocation list
        builderParams.IsRevocationEnabled = crls.Count != 0;
        // validationDate : probably "now"
        builderParams.Date = new DateTimeObject(validationDate);

        // The indermediate certs are items necessary to create the certificate chain
        builderParams.AddStore(X509StoreFactory.Create("Certificate/Collection", new X509CollectionStoreParameters(intermediateCerts)));
        builderParams.AddStore(X509StoreFactory.Create("CRL/Collection", new X509CollectionStoreParameters(crls)));

        try
        {
            PkixCertPathBuilderResult result = builder.Build(builderParams);
            return result.CertPath.Certificates.Cast<X509Certificate>();
            ...
1 голос
/ 28 марта 2014

Как проверить, что сертификат был подписан моим конкретным центром сертификации, просто используя файл открытого сертификата центра сертификации, не используя хранилище сертификатов Windows или WCF, когда кажется, что RemoteCertificateValidationCallback, X509Certificate и X509Chain не дают мне ничего для работы?

Следующий код позволит избежать хранения сертификатов Windows и проверки цепочки. Это немного отличается от кода JB, особенно в использовании флагов. Приведенный ниже код не требует AllowUnknownCertificateAuthority (но он использует X509RevocationMode.NoCheck, поскольку у меня нет CRL).

Название функции не имеет значения. Ниже VerifyServerCertificate - это тот же обратный вызов, что и RemoteCertificateValidationCallback в SslStream классе. Вы также можете использовать его для ServerCertificateValidationCallback в ServicePointManager.

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

        // 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, и соединение будет «просто работать». И я не разобрался, как его установить так, чтобы он передавался в обратный вызов. То есть я должен построить цепочку для каждого вызова обратного вызова. Я думаю, что это архитектурные дефекты в .Net, но я могу упустить что-то очевидное.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...