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

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

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

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

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

3 ответов


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

  • Я скачал сертификаты.класс cs опубликовано Doug Cook
  • я использовал этот код для создания .файл 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.По умолчанию используется SslProtocols.По умолчанию
  • было 3 предупреждения об устаревших функциях. Я заменил их предлагаемыми заменами.
  • Я заменил эту строку в программе сервера.cs файл со строкой из шага 2:

    X509Certificate cert = getServerCert ();

  • In клиентская программа.cs-файл, убедитесь, что вы установили имя_сервера = yourhostname.com (и что он соответствует имени в сертификате)

  • в клиентской программе.cs, функция CertificateValidationCallback завершается ошибкой, поскольку sslPolicyErrors содержит RemoteCertificateChainErrors. Если вы копнете немного глубже, это связано с тем, что центр выдачи, подписавший сертификат, не является доверенным корнем.
  • Я не хочу, чтобы пользователь импортировал сертификаты в магазин корня, etc., поэтому я сделал особый случай для этого, и я проверяю этот сертификат.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];
}

вы можете посмотреть тоже этот пример Пример Асинхронной Реализации SslStream Клиент / Сервер http://blogs.msdn.com/joncole/archive/2007/06/13/sample-asynchronous-sslstream-client-server-implementation.aspx

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

простой пример сертификата

makecert-sr LocalMachine-ss My-N CN=тест-небо exchange-sk 123456

или

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

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

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


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

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