Как определить имя сервера для аутентификации сервера клиентом в c#

недавно я пытался сделать SSL-зашифрованный сервер / клиент на C#.

я следовал этой учебник по MSDN, однако, он требовал, чтобы сертификат был создан для использования сервера и клиента с помощью программы makecert.exe поэтому я нашел пример, и он создал сертификат fine:

makecert-sr LocalMachine-ss My-n "CN=тест" - sky exchange-sk 123456 c:/Test - ... cer

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

127.0.0.1

, а затем требуется имя сервера который должен соответствовать имя сервера на сертификат (

6 ответов


ответ можно найти в SslStream.AuthenticateAsClient Метод Примечания:

значение, указанное для targetHost, должно совпадать с именем сертификата сервера.

Если для сервера используется сертификат, субъектом которого является "CN=localhost", необходимо вызвать AuthenticateAsClient с параметром "localhost" в качестве параметра targetHost для успешной аутентификации на стороне клиента. Если вы используете "CN=David-PC" в качестве сертификата тема, которую вы должны назвать AuthenticateAsClient с "David-PC" в качестве targetHost. SslStream проверяет идентификатор сервера, сопоставляя имя сервера, который вы собираетесь подключить (и который вы передаете AuthenticateAsClient) с субъектом в сертификате, полученном от сервера. Практика заключается в том, что имя машины, которая запускает сервер, совпадает с именем субъекта сертификата, и в клиенте вы передаете то же имя хоста AuthenticateAsClient, которое вы использовали для открытия соединение (с TcpClient в этом случае).

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

теперь, связанный с вашим образцом кода, ваша проблема связана с генерацией и использованием сертификата.

  • вы не предоставляете эмитента для своего сертификата, и таким образом ему нельзя доверять - это является причиной исключения RemoteCertificateChainErrors. Я предлагаю создать самозаверяющий сертификат для целей разработки, указав опцию-r makecert.

  • для доверия сертификат должен быть либо самозаверяющим и помещен в надежное место в хранилище сертификатов Windows, либо должен быть связан цепочкой подписи уже доверенного центра сертификации. Поэтому вместо опции-ss My, которая разместит сертификат в личном хранилище, используйте-SS root, который разместит его в доверенных корневых центрах сертификации, и он будет доверен на вашей машине (из кода я предполагаю, что ваш клиент работает на той же машине с сервером, а также сертификат генерируется на нем).

  • Если вы укажете выходной файл для makecert, он экспортирует сертификат .cer но этот формат содержит только открытый ключ, а не закрытый ключ, который необходим серверу для установления SSL-соединения. Самый простой способ-прочитать сертификат из хранилища сертификатов Windows в коде сервера. (Вы также можете экспортировать его из магазина в другом формате, который позволяет хранить закрытый ключ, как описано здесь экспорт сертификата с закрытым ключом и прочитайте этот файл в коде сервера).

вы можете найти подробную информацию о параметрах makecert, используемых здесь Инструмент Создания Сертификата (Makecert.exe)

В заключение ваш код нуждается в следующих изменениях для запуска (протестирован с последними обновлениями кода):

  • используйте следующую команду для создания сертификата:

makecert-sr LocalMachine-SS root-r-n "CN=localhost" - sky exchange - sk 123456

  • читать сертификат Хранилище сертификатов Windows вместо файла (для простоты этого примера), поэтому замените

serverCertificate = X509Certificate.CreateFromCertFile(сертификат);

в коде сервера с:

X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, "CN=localhost", false);
store.Close();

if (certificates.Count == 0)
{
    Console.WriteLine("Server certificate not found...");
    return;
}
else
{
    serverCertificate = certificates[0];
}

Не забудьте заменить "CN=localhost" на тему сертификата, который вы собираетесь использовать, если вы измените код позже (в этом случае должно быть то же значение, что и параметр-n, переданный makecert). Также использовать имя компьютера, на котором запускается сервер вместо localhost в теме сертификата сервера.


CN сертификата сервера должен быть точно таким же, как доменное имя сервера. Я полагаю, в вашем случае, общее имя должно быть имя "localhost" (без кавычек).

важно: точно, как вы могли прочитать в других ответах, никогда не используйте CN="localhost" в производстве.


во-первых, не создавайте сертификат с субъектом "CN=localhost" или эквивалентом. Он никогда не будет использоваться в производстве, поэтому не делайте этого. Всегда выдавайте его на имя хоста вашего компьютера, например CN= "mycomputer", и используйте имя хоста при подключении к нему, а не localhost. Вы можете указать несколько имен, используя расширение "альтернативное имя субъекта", но makecert не поддерживает его.

во-вторых, при выдаче SSL-сертификата сервера необходимо добавить "сервер аутентификация " OID для расширения расширенного использования ключей (EKU) сертификата. Добавьте до makecert в вашем примере. Если вы хотите сделать аутентификацию сертификата клиента, используйте OID "аутентификация клиента" 1.3.6.1.5.5.7.3.2.

наконец, сертификат по умолчанию, созданный makecert, использует MD5 в качестве алгоритма хэширования. MD5 считается небезопасным и, хотя это не повлияет на ваше тестирование, привыкайте использовать SHA1. Добавить -a sha1 до makecert параметры выше, чтобы заставить SHA1. Размер ключа по умолчанию также должен быть увеличен с 1024 бит до 2048 бит, но вы получаете идею.


чтобы заставить это работать с WCF, необходимо сначала создать самозаверяющий сертификат корневого Центра управления, а затем использовать его для создания сертификата для localhost.

Я думаю, что то же самое может относиться и к вашему проекту, пожалуйста, посмотрите на эту статью как: создание временных сертификатов для использования во время разработки для сведения.


вы пробовали:?

создайте сертификат для полного доменного имени, например example.net (хорошо использовать example.net, example.com или example.org для всего, что намеренно не настоящее имя) или имя, которое будет использоваться в живом использовании, если это один сайт, и вы знаете, что это будет.

обновите файл hosts, чтобы он использовал 127.0.0.1 для этого имени.


в отношении вашего обновления:

один из конструкторов SslStream позволяет обеспечить делегат RemoteCertificateValidationCallback. Вы должны иметь возможность поставить точку останова в методе, который вы предоставляете, чтобы увидеть, какая фактическая ошибка вы получаете. Проверьте SslPolicyErrors значение отправил.