Сбой рукопожатия Quic TLS (ENCRYPTION_HANDSHAKE) 46: сертификат неизвестен

Вопрос или проблема

в качестве прототипа был создан quic сервер с использованием .net и .net клиента, чтобы протестировать, работает ли двунаправленное соединение и можно ли передавать данные с положительным результатом. Сервер – это компьютер с Windows 11 Pro.

Следующим шагом было протестировать, может ли клиент на JavaScript, работающий в веб-браузере, подключиться к серверу и обмениваться данными (такими же, как и .net клиент). С помощью .net клиента я мог игнорировать проверку сертификатов, однако веб-браузеры не позволяют это. Я выяснил, что нужно сгенерировать собственный CA, промежуточный и серверный сертификаты. Я сделал это с помощью командной строки openssl. Сертификат был сгенерирован для IP-адреса, который используется сервером quic. Я импортировал все сертификаты в систему с помощью mmc. Затем я также установил сертификат, используя IIS Manager на IP-адресе и порту, используемом сервером quic, и увидел, что соединение зашифровано и сертификаты приняты.

Для js-клиента я узнал, что WebTransport должен выполнять соединение с сервером. Он инициализируется с помощью отпечатка открытого сертификата сервера, сгенерированного этой командой:
openssl x509 -in server.pem -noout -fingerprint -sha256 sha256 Fingerprint=D8:31:A7:0D:DC:6D:30:58:1D:41:19:03:77:12:AC:6E:87:00:73:7B:98:A7:60:93:25:55:E4:DC:83:DC:F5:F4

Однако при подключении js-клиента в Chrome возникают следующие ошибки/исключения.

Информация об исключении сервера:
исключение сервера

   at System.Net.Quic.ValueTaskSource.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
   at System.Net.Quic.QuicListener.<StartConnectionHandshake>d__16.MoveNext()
   at System.Net.Quic.QuicListener.<AcceptConnectionAsync>d__15.MoveNext()
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at QuicDemoLib.QuicServer.<ListenNewConnections>d__16.MoveNext() in D:\Gitlab\PreviewingStreamer\src\Previewing Streamer\QuicDemoLib\QuicServer.cs:line 57

Исключение клиента:

исключение клиента

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

public static QuicServerConnectionOptions GetQuicServerConnectionOptions()
{

    return new QuicServerConnectionOptions
    {
        // Используется для прерывания потока, если он не закрыт должным образом пользователем.
        // См. https://www.rfc-editor.org/rfc/rfc9000#section-20.2
        DefaultStreamErrorCode = 0x0A, // Код ошибки, зависящий от протокола.

        // Используется для закрытия соединения, если это не сделано пользователем.
        // См. https://www.rfc-editor.org/rfc/rfc9000#section-20.2
        DefaultCloseErrorCode = 0x0B, // Код ошибки, зависящий от протокола.
        MaxInboundBidirectionalStreams = QuicConfig.MaxInboundBidirectionalStreams,
        MaxInboundUnidirectionalStreams = QuicConfig.MaxInboundUnidirectionalStreams,
        IdleTimeout = TimeSpan.FromSeconds(QuicConfig.IdleTimeout),

        // Те же параметры, что и для стороны сервера SslStream.
        ServerAuthenticationOptions = new SslServerAuthenticationOptions
        {
            ApplicationProtocols = new List<SslApplicationProtocol> { new SslApplicationProtocol(QuicConfig.SslApplicationProtocolName) },
            ServerCertificate = SelfSignedCertificate.LoadCertificateFromPfx(CER_COMBINED, ""),
            CertificateRevocationCheckMode = X509RevocationMode.NoCheck,
            RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
            {
                if (sslPolicyErrors == SslPolicyErrors.None)
                {
                    return true; // Все в порядке.
                }

                if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors && chain != null)
                {
                    foreach (var chainStatus in chain.ChainStatus)
                    {
                        if (chainStatus.Status != X509ChainStatusFlags.UntrustedRoot)
                        {
                            return false; // Другие ошибки, не связанные с самоподписанными сертификатами.
                        }
                    }
                    return true; // Разрешить самоподписанные.
                }

                return false; // По умолчанию: не разрешать соединение.
            },
            
        }
    };
}

public static QuicListenerOptions GetQuicListenerConnectionOptions()
{
    var _serverConnectionOptions = GetQuicServerConnectionOptions();
    var cert = SelfSignedCertificate.LoadCertificateFromPfx(CER_COMBINED, "");

    var quicListenerOptions = new QuicListenerOptions
    {
        ApplicationProtocols = new List<SslApplicationProtocol> { new SslApplicationProtocol(QuicConfig.SslApplicationProtocolName) },

        ListenEndPoint = new IPEndPoint(IPAddress.Parse(QuicConfig.IpAdressServer), QuicConfig.PortNumberServer),
        ConnectionOptionsCallback = (connection, clientHelloInfo, cancellationToken) =>
        {
            var options = new QuicServerConnectionOptions
            {
                ServerAuthenticationOptions = new SslServerAuthenticationOptions
                {
                    ServerCertificate = cert,
                    ApplicationProtocols = new List<SslApplicationProtocol> { new SslApplicationProtocol(QuicConfig.SslApplicationProtocolName) }

                }
                ,
                DefaultCloseErrorCode = 11,
                DefaultStreamErrorCode = 10,
            };
            return ValueTask.FromResult(options);
        }
    };

    return quicListenerOptions;
}

Код js-клиента:

async function connectToHttp3Server() {
    try {
        const url="https://192.168.1.119:60830";
        const options = {
            serverCertificateHashes: [
                {
                    algorithm: 'sha-256',
                    value: new Uint8Array([
                        0x3D, 0xFD, 0xF5, 0xA8, 0x80, 0x17, 0xDC, 0xFD,
                        0x7E, 0x98, 0xBC, 0x06, 0x18, 0xDA, 0x81, 0x4F,
                        0x21, 0x37, 0x03, 0xD5, 0xCD, 0xEB, 0x27, 0x95,
                        0x3C, 0x09, 0xAE, 0x57, 0xBD, 0xFA, 0x2A, 0xC7
                    ])

                }],
            protocol: 'h3'

        };
        const transport = new WebTransport(url, options);

        await transport.ready;
        console.log('Подключено к HTTP/3 серверу с использованием QUIC.');

        const reader = transport.incomingUnidirectionalStreams.getReader();
        while (true) {
            const { value, done } = await reader.read();
            if (done) {
                console.log('Нет больше входящих потоков от сервера.');
                break;
            }

            handleIncomingStream(value);
        }
    } catch (error) {
        console.error('Не удалось подключиться к HTTP/3 серверу:', error);
    }
}

async function handleIncomingStream(stream) {
    const streamReader = stream.getReader();
    while (true) {
        const { value, done } = await streamReader.read();
        if (done) {
            console.log('Входящий поток закрыт.');
            break;
        }
        const text = new TextDecoder().decode(value);
        console.log('Данные входящего потока:', text);
    }
}

connectToHttp3Server();

Я ожидаю, что в этом случае, пока сервер работает, клиент сможет инициализировать соединение и чтение/запись данных (эта часть js-клиента должна быть разработана дальше) будет работать без значительных отличий от .net клиента.

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

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

Ответ или решение

Ошибка TLS в QUIC: «certificate unknown» и её решение

При работе с QUIC и TLS важно учитывать множество нюансов, связанных с сертификатами и защищёнными соединениями. В данной статье мы подробно рассмотрим проблему «TLS handshake failure (ENCRYPTION_HANDSHAKE) 46: certificate unknown» в контексте вашего проекта, где вы пытаетесь установить соединение между сервером на .NET и клиентом на JavaScript.

Основные причины проблемы

  1. Неверные сертификаты: Ошибка может возникать, если сертификаты (CA, промежуточные и серверные) не были корректно созданы или установлены. Для успешного TLS-соединения все сертификаты должны быть корректно подписаны и доверены клиентом.

  2. Цепочка сертификатов: Убедитесь, что цепочка доверия к вашему серверному сертификату корректно сформирована. Сертификаты должны быть установлены на сервере в правильной последовательности (серверный сертификат, промежуточные сертификаты и корневой сертификат).

  3. Шифрование и протоколы: Проверьте, поддерживают ли клиент и сервер одинаковые протоколы TLS. Например, убедитесь, что вы используете h3, как указано в вашем клиенте, и что сервер настроен на его поддержку.

Проверка и исправление ошибки

  1. Проверка сертификатов:

    • Убедитесь, что ваш серверный сертификат действительно подписан върожденным корневым сертификатом и что этот корневой сертификат установлен в доверенных корнях на клиенте.
    • Проверьте интеграцию с IIS для корректного отображения сертификатов и их статуса.
  2. Изменение настроек клиента:

    • Убедитесь, что вы правильно указываете отпечаток сертификата serverCertificateHashes. Обратите внимание, что для протокола h3 должен быть использован правильный формат соответствующего отпечатка. Убедитесь, что указанное значение совпадает с отпечатком вашего действительного сертификата.
  3. Логирование и трактовка ошибок:

    • Включите детальное логирование на сервере и клиенте для выявления проблем на этапе рукопожатия TLS. Возможно, вам понадобится информация о конкретной ошибке или статусе проверки сертификата.
    • Используйте инструменты типа Fiddler или Wireshark для захвата TLS-сессий и анализа процесса рукопожатия, чтобы понять, где именно возникает ошибка.
  4. Параметры конфигурации:

    • Проверьте настройки SslServerAuthenticationOptions на сервере. Возможно, стоит использовать более строгие политики проверки для получения ясной информации об ошибках.
  5. Тестирование на локальном окружении:

    • Попробуйте протестировать серверный сертификат, используя различные браузеры и клиента на .NET, чтобы исключить зависимость от конкретной реализации или конфигурации.

Заключение

Ошибки при установлении TLS-соединений в рамках QUIC могут быть вызваны множеством факторов, включая неверные или неподписанные сертификаты, неправильную конфигурацию серверного окружения или же неверные параметры на клиенте. Выполнение вышеуказанных шагов поможет вам устранить ошибку «certificate unknown» и установить стабильное и безопасное соединение между сервером и клиентом.

Если после выполнения этих шагов проблема продолжает возникать, рекомендуется обратиться к специализированной документации или профессиональным форумам, посвящённым QUIC и TLS, для более целенаправленного анализа вашей ситуации.

Оцените материал
Добавить комментарий

Капча загружается...