Просить SslStream принять ТОЛЬКО сертификат, подписанный определенным открытым ключом

2

У меня есть рабочая реализация этого, но я хочу убедиться, что он безопасен. Целью является использование SSLStream и только прием сертификатов SSL с сервера, которые подписаны определенным ключом RSA.

Вот мой код подключения:

        var client = new TcpClient("server_address", port_number);
        var sslStream = new SslStream(client.GetStream(), false,
            new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
        sslStream.AuthenticateAsClient("SpeechGrid");

И вот моя реализация ValidateServerCertificate:

    private static bool ValidateServerCertificate(object sender, X509Certificate certificate,
            X509Chain chain, SslPolicyErrors sslPolicyErrors) {

        // Only accept our specific key pair
        foreach (var cert in chain.ChainElements) {
            if (cert.Certificate.GetPublicKeyString() == k_prodPublicKey) {
                return true;
            }
        }

        return false;
    }

Из-за богатства объекта X509Chain я хочу убедиться, что мне не нужно проверять такие вещи, как X509ChainStatusFlags.NotSignatureValid и т.д.

Например, может ли злоумышленник "требовать" быть подписанным моим открытым ключом, отправить недопустимую подпись, и эта атака будет работать, потому что .NET предполагает, что я проверяю все эти флаги?

Спасибо!!

ОБНОВЛЕНИЕ: Хорошо, до сих пор я решил поставить следующие проверки выше оригинального foreach. Обратите внимание, что это несколько специфическое приложение; например, если мне нужны сертификаты для истечения срока действия, я бы проверял NotTimeValid и т.д.

        foreach (var status in chain.ChainStatus) {
            switch (status.Status) {
                case X509ChainStatusFlags.Cyclic:
                case X509ChainStatusFlags.NotSignatureValid:
                case X509ChainStatusFlags.PartialChain:
                    return false;
            }
        }
  • 0
    Открытый ключ используется для общего доступа ... Однако только вы (тот, кто имеет закрытый ключ) могут расшифровать зашифрованное сообщение. Если вы хотите убедиться, что данные действительно принадлежат третьей стороне, получите от них открытый ключ (они будут шифровать данные своим ключом).
Теги:
security
ssl
sslstream

2 ответа

1

Я бы отменил логику проверки, добавленной вами в обновлении, на ваш вопрос. Вместо того, чтобы искать то, что может быть неправильно, и принимать все остальное:

foreach (thing that I can think of that might be wrong)
 return false;

if (public key matches regardless of other policy errors)
 return true;

... Вместо этого я искал бы то, что может быть неправильным, но приемлемым, и отклонить любые другие ошибки политики:

if (policy errors)
{
 foreach (error that is acceptable: remote name mismatch, untrusted root, etc.)
   policy errors -= that particular error
}

if (any policy errors left)
 return false;
else if (public key matches)
 return true;
else
 return false;

Что-то вроде этого для первой части (я не тестировал и не компилировал это):

if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) == SslPolicyErrors.RemoteCertificateNameMismatch)
{
    sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNameMismatch;
}

if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) == SslPolicyErrors.RemoteCertificateChainErrors)
{
    var otherFlagsFound =
        from i in chain.ChainStatus
        where (i.Status & ~X509ChainStatusFlags.UntrustedRoot) != X509ChainStatusFlags.NoError
        select i;

    if (otherFlagsFound.Count() == 0)
    {
        sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors;
    }
}
0

Вы можете проверить параметр sslPolicyErrors для дополнительных ошибок, таких как истек, или если сертификатам не доверяют. Если все в порядке, оно должно вернуть SslPolicyErrors.None. Нецелесообразно выводить закрытый ключ из открытого ключа, поэтому вам не нужно беспокоиться о том, чтобы кто-то другой создал одну и ту же пару ключей и подписал его.

  • 0
    Хорошо в моем случае это показывает SslPolicyErrors как "System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch | System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors". Под статусом цепочки написано UntrustedRoot. Обратите внимание, что мой сертификат не подписан центром сертификации. Так я должен просто проверить, чтобы убедиться, что UntrustedRoot является единственным статусом цепочки?
  • 2
    RemoteCertificateNameMismatch означает, что ваш клиент подключается к имени хоста, которое не совпадает с тем, что указано в сертификате. Если вы подключаетесь к «www.myapp.com», то имя в сертификате также должно соответствовать этому. Ошибка untrustedRoot возникает из-за того, что сертификат не установлен в качестве доверенного корневого центра сертификации в хранилище сертификатов клиентского компьютера. Вы можете либо установить этот ЦС в хранилище, либо проигнорировать эту ошибку, поскольку у вас есть еще одна явная проверка открытого ключа.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню