Unity TlsException: рукопожатие не удалось UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED - PullRequest
1 голос
/ 28 января 2020

Я пытаюсь обновить TcpClient моего приложения, чтобы использовать TLS с SslStream вместо обычного Stream, код, который я использую для этого, кажется, работает за пределами Unity, но не работает, когда интегрируется в мой Проект Unity 2019.1.8 (протестирован также на 2018 и 2017).

Чтобы установить sh соединение и открыть новый SslStream Я использую следующий код:

public static void InitClient(string hostName, int port, string certificateName)
{
    client = new TcpClient(hostName, port);

    if (client.Client.Connected)
    {
        Debug.LogFormat("Client connected succesfully");
    }
    else
    {
        Debug.LogErrorFormat("Client couldn't connect");
        return;
    }

    stream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);

    try
    {
        stream.AuthenticateAsClient(certificateName);
    }
    catch (AuthenticationException e)
    {
        Debug.LogErrorFormat("Error authenticating: {0}", e);
        if (e.InnerException != null)
        {
            Debug.LogErrorFormat("Inner exception: {0}", e);
        }
        Debug.LogErrorFormat("Authentication failed - closing connection");
        stream.Close();
        client.Close();
    }
}

И для проверки сертификата

public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None)
        return true;

    Debug.LogErrorFormat("Certificate error: {0}", sslPolicyErrors);
    return false;
}

В Unity 2019.1.8 клиент подключается и попытается проверить удаленный сертификат, который завершается неудачно с ошибкой TlsException: Handshake failed - error code: UNITYTLS_INTERNAL_ERROR, verify result: UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED.

Создание ValidateServerCertificate всегда return true позволяет моему клиенту подключаться без проблем.

Я попытался воспроизвести проблему в автономном C# нацеливании на консольное приложение. net framework 4.7.1, используя точно такой же код. Запуск клиента в этом приложении вернет true из ValidateServerCertificate из проверки sslPolicyErrors == SslPolicyErrors.None.

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

Почему проверка не проходит в Unity, но нигде больше?

1 Ответ

2 голосов
/ 28 января 2020

Почему Unity не может проверить:
Хотя сертификат действителен и корректен, он не включил цепочку промежуточных сертификатов в root CA. Это приводит к тому, что цепочка доверия не может быть сформирована (Unity не кэширует / не получает промежуточные данные), в результате чего устанавливается флаг UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED.

Чтобы это исправить, мне нужно было добавить цепочку сертификатов на мой листовой сертификат, чтобы Unity могла проверить всю цепочку вплоть до root CA. Чтобы найти цепочку сертификатов для вашего сертификата, вы можете использовать «цепочку сертификатов TLS composer».

В зависимости от того, какое программное обеспечение вы используете, вам может понадобиться включить цепочку в ваш сертификат или сохранить ее в отдельном файле. С whatsmychaincert (я никоим образом не связан с этим сайтом, просто использовал его):

Примечание: для некоторых программ требуется поставить сертификат вашего сайта (например, example.com). .crt) и ваши цепные сертификаты (например, example.com.chain.crt) в отдельных файлах, в то время как другое программное обеспечение требует, чтобы вы поместили свои цепные сертификаты после сертификата вашего сайта в тот же файл.


Почему браузер / консольное приложение может проверять:
Программное обеспечение может иметь различную реализацию того, как оно работает с неполными цепями. Он может либо выдать ошибку, указав, что цепочка разорвана и поэтому не может быть доверенной (как в случае с Unity), либо кэшировать и сохранить промежуточные продукты для последующего использования / извлечь их из предыдущей sessiosn (как браузеры и Microsoft. net (ядро) делать).
Как объяснено в этот ответ (выделено мое)

В общем, клиенты SSL / TLS будут пытаться проверить цепочку сертификатов сервера как получено с сервера. Если эта цепочка не радует клиента, то поведение клиента зависит от реализации: некоторые клиенты просто сдаются; другие (особенно Windows / Inte rnet Explorer) попытаются создать другую цепочку, используя локально известный промежуточный ЦС, а также загружать сертификаты с URL-адреса, найденного в других сертификатах (расширение «доступ к информации о полномочиях»).


Не доверять сертификату, если цепочка не предоставлена, является преднамеренным решением, принятым Unity из-за кросс-платформенной совместимости, как указано в ответе на вопрос 1115214 :

Возможно, мы сможем решить эту проблему, выполнив проверку через системные параметры c TLS api вместо использования OpenSSL / MbedTLS для проверки сертификатов root, как мы делаем сегодня, однако это решение будет тогда не работает кроссплатформенность. Поэтому мы не хотим внедрять его сегодня, поскольку он будет скрывать неправильно настроенный сервер от пользователя на некоторых, но не на всех платформах.


Я понял, что это решение Моя конкретная ситуация при задании этого вопроса, поэтому решил самостоятельно ответить на него для будущих ссылок. Однако UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED может иметь всевозможные причины, и это только одна из них.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...