Подключиться к серверу openssl с помощью .Net

Мне дали серверное приложение, которое прослушивает клиентское соединение. Следующая команда openssl подключается ...

openssl s_client -key provided.key -cert provided.crt -CAfile provided.pem -connect 127.0.0.1:59123

У меня есть приложение C #, которое создает TcpClient и пытается аутентифицировать SslStream. BeginAuthenticateAsClient требует сертификата. Очевидно, что предоставленный файл .crt недостаточно хорош, поскольку должен быть задействован предоставленный .key. Я попытался использовать openssl для создания файла .pfx из файлов .crt и .key, но это не сработало (файл создан, но сервер закрыл соединение). Это не может быть слишком сложно имитировать команду open ssl в .net, но я не нашел в сети ничего, что работало бы или хотя бы приближалось к тому, что я пытаюсь сделать. Кто-нибудь знает, как это сделать, или я должен использовать другой подход? P.S. Стороннее программное обеспечение на самом деле не вариант.


person Hendrik Vis    schedule 17.08.2017    source источник
comment
Вы когда-нибудь находили решение этого @Hendrik? У меня сейчас аналогичная проблема.   -  person M0rty    schedule 07.09.2017
comment
К сожалению нет. Я все еще борюсь с этим. Я импортировал оболочку openssl C # и не смог найти никакой информации о том, как ее использовать для реального подключения к серверу. Можно найти тонны информации о создании ключей, но ничего об ее использовании для реального подключения.   -  person Hendrik Vis    schedule 11.09.2017
comment
Я нашел способ обойти свою проблему. Я просто установил RemoteCertificateValidationCallback, чтобы он всегда возвращал истину. Очевидно, это не правильный способ для производства, но поможет в целях тестирования. Я опубликую свое решение ниже, возможно, это не решение вашей проблемы, но, надеюсь, это поможет.   -  person M0rty    schedule 12.09.2017


Ответы (2)


openssl pkcs12 -export -out provided.pfx -inkey provided.key -in provided.crt

...

var coll = new X509Certificate2Collection();
var cert = new X509Certificate2("provided.pfx", "WhateverPasswordYouGaveIt");
coll.Add(cert);

// do TcpClient stuff
var sslStream = new SslStream(tcpClient.GetStream());
const SslProtocols allowedProtocols = SslProtocols.Tls12 | SslProtocols.Tls11;
sslStream.AuthenticateAsClient("127.0.0.1", coll, allowedProtocols, checkCertificateRevocation: true);

Для -CAFile эквивалента вам нужно будет выполнить собственный обратный вызов и сравнить сертификат в chain.ChainElements[chain.ChainElements.Length - 1].Certificate с тем, который вы ожидаете.

person bartonjs    schedule 17.08.2017
comment
Спасибо, bartonjs. Я в значительной степени так и делаю, и сервер закрывает мое соединение. Было бы неплохо, если бы отказ был указан относительно того, что его вызвало, но я просто получаю, что старая удаленная сторона закрыла транспортный поток, когда я вызываю EndAuthenticateAsClient. - person Hendrik Vis; 17.08.2017
comment
Команда openssl работает и подключается. Я пробую программно на C #, но не подключается. Единственная разница между вашим предложением и тем, что я делаю, - это то, что я асинхронен. - person Hendrik Vis; 17.08.2017
comment
Хорошо, вот где я запутался. Серверу нужен закрытый ключ от клиента, верно? Сервер настроен так, чтобы не требовать от клиента наличия сертификата. Как же тогда на стороне клиента передать ключ без сертификата? - person Hendrik Vis; 17.08.2017
comment
Серверу не нужен закрытый ключ. Ему нужен сертификат (общедоступные данные) и клиент для работы, которая доказывает, что у него есть закрытый ключ. Я бы рекомендовал провести сеанс с openssl s_server и попытаться подключиться, OpenSSL должен сообщить, где что-то идет не так. - person bartonjs; 18.08.2017
comment
Именно здесь в игру вступает мое невежество в отношении SSL. Как вы предложили, я использовал openssl s_server с предоставленным серверным ключом и файлами сертификатов и попытался установить соединение с помощью своего программного обеспечения. Я получаю следующую ошибку на стороне сервера: 3524: ошибка: 1408A0C1: подпрограммы SSL: ssl3_get_client_hello: нет общего шифра:. \ Ssl \ s3 _srvr.c: 1411: НО ... Если я подключаюсь с помощью openssl с предоставленным мной клиентским ключом и crt, подключаю нормально. В C # есть что-то, чего мне не хватает. - person Hendrik Vis; 18.08.2017
comment
Ах, вы, вероятно, работаете над более старой структурой, которая по умолчанию использует TLS 1.0 или SSL3 в качестве единственных протоколов, о которых она хочет говорить. Я обновил свой ответ, чтобы показать использование перегрузки, которая принимает протоколы. Если вам удастся уйти с Tls12, это лучше, но вам также может понадобиться Tls11, поэтому я бросил его туда. - person bartonjs; 18.08.2017
comment
К сожалению, я получаю ту же ошибку. На стороне клиента обратный вызов аутентификации (как клиент) выдает исключение, в котором говорится, что вызов SSPI завершился неудачно, см. Внутреннее исключение. Полученное сообщение было неожиданным или неправильно отформатировано. Я думаю, что мне приходится сталкиваться со слишком большим количеством переменных, и мне нужно начинать с нуля. У меня есть файл ключа и файл сертификата как для сервера, так и для клиента. Когда я использую openssl s_server и s_client, обе стороны могут разговаривать друг с другом (после ввода пароля). Цель состоит в том, чтобы приложение C # могло взаимодействовать с сервером независимо от того, как это происходит. Какие-либо предложения? - person Hendrik Vis; 18.08.2017

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

Тестовый класс SslStream:

    public string TestingSSL(string sessionId)
    {
        TcpClient client = new TcpClient("name of the server", port);

        //Create certificate
        var collection = new X509Certificate2Collection();

        var certificate = new X509Certificate2("provided.pfx", "Passwordyouchoosewhencreatingthe.pfx", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
        collection.Add(certificate);

        const SslProtocols allowedProtocols = SslProtocols.Tls12 | SslProtocols.Tls11;
        // Create an SSL stream that will close the client's stream.
        SslStream sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificateTrue), null);

        // The server name must match the name on the server certificate.
        try
        {
            sslStream.AuthenticateAsClient("name of the server", collection, allowedProtocols, checkCertificateRevocation: true);
        }
        catch (AuthenticationException e)
        {
            if (e.InnerException != null)
            {
                return e.InnerException.ToString();
            }
        }

        // Read message from the server.
        string serverMessage = ServerResponseMessage(sslStream);

        byte[] sessionBill = Encoding.UTF8.GetBytes("sessions.bill\r\n");
        byte[] sessionBillId = Encoding.UTF8.GetBytes($",session_uuid,{sessionId}\r\n");

        // Send command to the server. 
        sslStream.Write(sessionBill, 0, sessionBill.Length);
        sslStream.Flush();
        sslStream.Write(sessionBillId, 0, sessionBillId.Length);
        sslStream.Flush();

        string sessionBillResponseMessage = ServerResponseMessage(sslStream);

        // Close the client connection.
        client.Close();

        return sessionBillResponseMessage;
    }


    public static bool ValidateServerCertificateTrue(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        return true;
    }


    static string ServerResponseMessage(SslStream sslStream)
    {     
        byte[] buffer = new byte[2048];
        StringBuilder messageData = new StringBuilder();

        int bytes = -1;

        bytes = sslStream.Read(buffer, 0, buffer.Length);
        // Use Decoder class to convert from bytes to UTF8
        // in case a character spans two buffers.
        Decoder decoder = Encoding.UTF8.GetDecoder();
        char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];

        decoder.GetChars(buffer, 0, bytes, chars, 0);
        messageData.Append(chars);     

        return messageData.ToString();
    }
person M0rty    schedule 12.09.2017
comment
Спасибо, M0rty, это работает, пока сервер использует .NET, однако мой сервер использует openssl, и, похоже, ничего не работает. - person Hendrik Vis; 20.09.2017