Использование SSL и SslStream для одноранговой аутентификации?

StackOverflow https://stackoverflow.com/questions/695802

Вопрос

Мне нужно обеспечить безопасную связь между различными процессами, использующими для связи сокеты TCP/IP.Мне нужна и аутентификация, и шифрование.Вместо того, чтобы изобретать велосипед, мне бы очень хотелось использовать SSL, класс SslStream и самозаверяющие сертификаты.Я хочу проверить сертификат удаленного процесса на соответствие известной копии в моем локальном приложении.(Центр сертификации не обязателен, поскольку я намерен копировать сертификаты вручную).

Для этого я хочу, чтобы приложение могло автоматически генерировать новый сертификат при первом запуске.Помимо makecert.exe это выглядит так эта ссылка показывает способ автоматического создания самозаверяющих сертификатов, и это только начало.

Я рассмотрел методы AuthenticateAsServer и AuthenticateAsClient SslStream.Вы можете предоставить обратные звонки для проверки, так что похоже, что это возможно.Но теперь, когда я углубился в детали, я действительно не думаю, что это возможно.

Я иду в правильном направлении?Есть ли лучшая альтернатива?Кто-нибудь делал что-нибудь подобное раньше (в основном одноранговый SSL, а не клиент-сервер)?

Это было полезно?

Решение

Шаг 1: Генерация самозаверяющего сертификата:

  • я скачал Класс сертификата.cs опубликовал Дуг Кук
  • Я использовал этот код для создания файла сертификата .pfx:

    byte[] c = Certificate.CreateSelfSignCertificatePfx(
            "CN=yourhostname.com", //host name
            DateTime.Parse("2000-01-01"), //not valid before
            DateTime.Parse("2010-01-01"), //not valid after
            "mypassword"); //password to encrypt key file
    
        using (BinaryWriter binWriter = new BinaryWriter(
            File.Open(@"testcert.pfx", FileMode.Create)))
        {
            binWriter.Write(c);
        }
    

Шаг 2: Загрузка сертификата

    X509Certificate cert = new X509Certificate2(
                            @"testcert.pfx", 
                            "mypassword");

Шаг 3: Собираем это вместе

  • Я основывал это на этот очень простой пример SslStream
  • Вы получите ошибку времени компиляции перечисления SslProtocolType.Просто измените это с SslProtocolType.Default на SslProtocols.Default.
  • Было 3 предупреждения об устаревших функциях.Я заменил их все предложенными заменами.
  • Я заменил эту строку в файле Server Program.cs строкой из шага 2:

    Сертификат X509Certificate = getServerCert();

  • В файле Client Program.cs убедитесь, что вы установили имя_сервера = имя_вашего_хоста.com (и что оно соответствует имени в сертификате).

  • В файле Client Program.cs функция CertificateValidationCallback завершается с ошибкой, поскольку sslPolicyErrors содержит RemoteCertificateChainErrors.Если копнуть немного глубже, это связано с тем, что орган, выдавший сертификат, не является доверенным корнем.
  • Я не хочу, чтобы пользователь импортировал сертификаты в корневое хранилище и т. д., поэтому я сделал для этого особый случай и проверяю, что certificate.GetPublicKeyString() равен открытому ключу, который у меня есть в файле. для этого сервера.Если оно совпадает, я возвращаю True из этой функции.Кажется, это работает.

Шаг 4: Аутентификация клиента

Вот как аутентифицируется мой клиент (он немного отличается от сервера):

TcpClient client = new TcpClient();
client.Connect(hostName, port);

SslStream sslStream = new SslStream(client.GetStream(), false,
    new RemoteCertificateValidationCallback(CertificateValidationCallback),
    new LocalCertificateSelectionCallback(CertificateSelectionCallback));

bool authenticationPassed = true;
try
{
    string serverName = System.Environment.MachineName;

    X509Certificate cert = GetServerCert(SERVER_CERT_FILENAME, SERVER_CERT_PASSWORD);
    X509CertificateCollection certs = new X509CertificateCollection();
    certs.Add(cert);

    sslStream.AuthenticateAsClient(
        serverName,
        certs,
        SslProtocols.Default,
        false); // check cert revokation
}
catch (AuthenticationException)
{
    authenticationPassed = false;
}
if (authenticationPassed)
{
    //do stuff
}

CertificateValidationCallback такой же, как и в случае с сервером, но обратите внимание, что AuthenticateAsClient принимает коллекцию сертификатов, а не только один сертификат.Итак, вам нужно добавить LocalCertificateSelectionCallback, вот так (в данном случае у меня есть только один клиентский сертификат, поэтому я просто возвращаю первый из коллекции):

static X509Certificate CertificateSelectionCallback(object sender,
    string targetHost,
    X509CertificateCollection localCertificates,
    X509Certificate remoteCertificate,
    string[] acceptableIssuers)
{
    return localCertificates[0];
}

Другие советы

Вы также можете посмотреть этот пример пример асинхронного реализации клиента/сервера SSLStreamhttp://blogs.msdn.com/joncole/archive/2007/06/13/sample-asynchronous-sslstream-client-server-implementation.aspx

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

базовый пример сертификата

makecert -sr LocalMachine -ss My -n CN=Test -sky Exchange -sk 123456

или

как внешний файл

makecert -sr LocalMachine -ss My -n CN=Test -sky Exchange -sk 123456 c: est.cer

Инструмент создания сертификатов (Makecert.exe)
http://msdn.microsoft.com/en-us/library/bfsktky3%28VS.80%29.aspx

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

Однако эти классы переопределяемы;поэтому теоретически вы можете создать производный класс, который сначала проверяет, нужно ли создавать сертификат, генерирует его, если необходимо, а затем вызывает родительский AuthenticateAsX метод.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top