it-roy-ru.com

Как читать веб-сокеты сертификатов TLS с помощью PHP?

Я пытаюсь подключиться к защищенной веб-розетке, созданной PHP, но по какой-то причине это не работает. Файлы сертификатов доступны для чтения на PHP.

Это мой код до сих пор (сторона PHP; для простоты выделен код):

$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'allow_self_signed', false);
stream_context_set_option($context, 'ssl', 'verify_peer', true);
stream_context_set_option($context,  'ssl', 'peer_name', 'example.com');
stream_context_set_option($context,  'ssl', 'CN_match', 'example.com');
stream_context_set_option($context,  'ssl', 'SNI_enabled', true);
stream_context_set_option($context, 'ssl', 'local_cert',  '/path/to/ssl/cert/example.com');
stream_context_set_option($context, 'ssl', 'local_pk', '/path/to/ssl/private/example.com');

$serverSocket = stream_socket_server(
        'tls://example.com:8090',
        $errNo,
        $errStr,
        \STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN,
        $context
);
$client = stream_socket_accept($serverSocket);

// send initial websocket connection stuff
$request = socket_read($client, 5000);
preg_match('#Sec-WebSocket-Key: (.*)\r\n#', $request, $matches);
$key = base64_encode(pack(
    'H*',
    sha1($matches[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
));
$headers = "HTTP/1.1 101 Switching Protocols\r\n";
$headers .= "Upgrade: websocket\r\n";
$headers .= "Connection: Upgrade\r\n";
$headers .= 'Sec-WebSocket-Version: 13' . "\r\n";
$headers .= 'Sec-WebSocket-Accept: ' . $key . "\r\n\r\n";
socket_write($client, $headers, \mb_strlen($headers));

// do something here...

socket_close($client);
socket_close($serverSocket);

Клиентская сторона:

var con = new WebSocket('wss://' + Host + ':' + port);
var $chat = $('#chat');

con.onmessage = function(e) {
    $chat.append('<p>' + e.data + '</p>');
};

con.onopen = function(e) {
    con.send('Hello Me!');
};

con.onclose = function (e) {
    console.log('connection closed.', arguments);
}

У меня нет * .pem файла. Только два файла, которые используются на веб-сервере Apache. Было бы возможно преобразовать эти файлы в файл pem, если это необходимо. Но я думаю, что это должно работать в php с этими обоими файлами, не так ли?

Для лучшего тестирования мы используем изолированный поддомен с сертификатом Let's Encrypt. Потому что мы получили полный доступ к этому серверу. Однако из генератора сертификатов я получил только те два упомянутых файла. Для веб-сервера это работает отлично. Но как сделать то же самое для веб-сокета в php?

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

Редактировать: После некоторого исследования выясняется, что php не работает с каждой комбинацией с другой ошибкой.

Мои сгенерированные файлы сертификатов выглядят так:

Файл/opt/psa/var/сертификаты/cert-0x8zHR:

-----BEGIN CERTIFICATE REQUEST-----
some letters
-----END CERTIFICATE REQUEST-----

-----BEGIN PRIVATE KEY-----
some letters
-----END PRIVATE KEY-----

-----BEGIN CERTIFICATE-----
some letters
-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----
some letters
-----END CERTIFICATE-----

Файл/opt/psa/var/сертификаты/cert-Du9H8N:

-----BEGIN CERTIFICATE-----
some letters
-----END CERTIFICATE-----

Все строки из строк сертификата от обеих из них заканчиваются в позиции символа 65.

Как вы можете прочитать в документации php, php ожидает получить файл в формате PEM: http://php.net/manual/en/context.ssl.php#context.ssl.local-cert

Я нашел этот учебник, чтобы преобразовать мои два файла в один файл PEM, но мои сертификаты выглядят иначе, чем упомянуто на сайте, а также я не знаю, что именно включить в файл PEM, что нужно php: https: //www.digicert.com/ssl-support/pem-ssl-creation.htm

Редактировать 2: Как упоминалось ниже, вот точные ошибки, которые я получаю:

с

stream_context_set_option($cont, 'ssl',  'local_pk', '/opt/psa/var/certificates/cert-0x8zHR');
stream_context_set_option($cont, 'ssl', 'local_cert', '/opt/psa/var/certificates/cert-Du9H8N');

Предупреждение: stream_socket_accept (): невозможно установить файл закрытого ключа `/ opt/psa/var/сертификаты/cert-0x8zHR 'в ... on line ...

Предупреждение: stream_socket_accept (): не удалось включить шифрование в ... on line ...

Предупреждение: stream_socket_accept (): принять не удалось: Успешно ... в линии ...

Предупреждение: socket_read () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Примечание: неопределенное смещение: 1 in ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Предупреждение: socket_close () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

И с

stream_context_set_option($cont, 'ssl',  'local_cert', '/opt/psa/var/certificates/cert-0x8zHR');
stream_context_set_option($cont, 'ssl', 'local_pk', '/opt/psa/var/certificates/cert-Du9H8N');

Предупреждение: stream_socket_accept (): невозможно установить файл закрытого ключа `/ opt/psa/var/Certificates/cert-Du9H8N 'в ... on line ...

Предупреждение: stream_socket_accept (): не удалось включить шифрование в ... on line ...

Предупреждение: stream_socket_accept (): принять не удалось: Успешно ... в линии ...

Предупреждение: socket_read () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Примечание: неопределенное смещение: 1 in ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Предупреждение: socket_close () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

И с (просто конкатенация cert-0x8zHR с cert-Du9H8N)

$file = dirname(__FILE__, 3) . \DIRECTORY_SEPARATOR . 'fullchain.pem';
stream_context_set_option($cont, 'ssl', 'local_cert', $file);

Предупреждение: stream_socket_accept (): не удалось получить сертификат однорангового узла в ... ...

Предупреждение: stream_socket_accept (): не удалось включить шифрование в ... ...

Предупреждение: stream_socket_accept (): принять не удалось: Успех в ... ...

Предупреждение: socket_read () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Примечание: неопределенное смещение: 1 in ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение задано в ... ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение задано в ... ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение задано в ... ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение задано в ... ...

Предупреждение: socket_write () ожидает, что параметр 1 будет ресурсом, логическое значение задано в ... ...

Предупреждение: socket_close () ожидает, что параметр 1 будет ресурсом, логическое значение указано в ... on line ...

Редактировать 3: Да, действительно, есть ошибка openssl. После третьего предупреждения socket_stream_accept я теперь получаю эту ошибку, просто используя код из примера php doc:

error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch

Я искал эту ошибку в интернете. Говорят, если появляется эта ошибка, я выбрал неверный файл сертификата.

Кроме того, если я играю эту команду:

openssl x509 -noout -in cert-0x8zHR -modulus

Я получил две разные строки для модуля. Но я не знаю почему, ни как это исправить. Оба эти файла используются в конфигурации vhost веб-сервера Apache и работают нормально:

SSLEngine on
SSLVerifyClient none
SSLCertificateFile /opt/psa/var/certificates/cert-0x8zHR
SSLCACertificateFile /opt/psa/var/certificates/cert-Du9H8N

PS: Если вы знаете какой-либо инструмент для локального преобразования в файл pem, пожалуйста, дайте мне знать. Онлайн конвертация невозможна.

16
alpham8

Вы сказали:

Мои сгенерированные файлы сертификатов выглядят так:

Файл/opt/psa/var/сертификаты/cert-0x8zHR:

----- НАЧАТЬ ЗАПРОС СЕРТИФИКАТА ----- некоторые буквы ----- КОНЕЦ ЗАПРОС СЕРТИФИКАТА -----

----- НАЧАТЬ ЧАСТНЫЙ КЛЮЧ ----- несколько букв ----- КОНЕЦ ЧАСТНОГО КЛЮЧА -----

----- НАЧАТЬ СЕРТИФИКАТ ----- несколько букв ----- КОНЕЦ СЕРТИФИКАТ -----

----- НАЧАТЬ СЕРТИФИКАТ ----- несколько букв ----- КОНЕЦ СЕРТИФИКАТ -----

Это не правильно на многих уровнях.

Во-первых, часть "СЕРТИФИКАТ ЗАПРОС" совершенно бесполезна, как только вы получите сертификат. Вы можете просто игнорировать эту часть.

Теперь скопируйте часть "PRIVATE KEY", с заголовками в одном файле. Это ваш закрытый ключ, вы можете использовать его везде, где у вас есть опции "local_pk" или программное обеспечение, запрашивающее ключ.

Тогда у вас есть два сертификата. И еще один в другом файле. Вам нужно будет все это отсортировать. Если бы вы предоставили настоящий контент сертификатов (который является общедоступным), люди могли бы помочь вам лучше. Здесь только с "некоторыми буквами" мы можем только догадываться.

В приведенном выше файле два сертификата могут быть CA и промежуточный. Вы должны скопировать их оба в другой файл, и это будет соответствовать опции "ca_cert".

Я предполагаю, что второй файл - это ваш пробный сертификат (снова только предположение, без ваших данных). Используйте его везде, где у вас есть "local_cert" или вы запрашиваете сертификат. Этот сертификат должен соответствовать файлу закрытого ключа (вы не можете проверить это вручную, вам нужны инструменты).

После того как вы создали все правильные файлы, первый больше не будет использоваться. Я бы рекомендовал использовать лучшую семантику для имени файла, потому что cert-X и cert-Y будут довольно сложными. Вместо этого используйте имя веб-сайта, чтобы у вас были такие файлы, как www.example.com.cert, www.example.com-ca.cert и www.example.com.key, чтобы сразу было понятно, что к чему. Или с каталогом с именем www.example.com/, а затем cert.pem, ca.pem и key.pem внутри каталога. Расширения сами по себе не используются программным обеспечением и, независимо от их содержания, только вы сами можете определить, что имеет смысл.

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

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

4
Patrick Mevzek

Попробуй это:

stream_context_set_option($cont, 'ssl', 'local_cert', '/opt/psa/var/certificates/cert-0x8zHR');
stream_context_set_option($cont, 'ssl', 'ca_file', '/opt/psa/var/certificates/cert-Du9H8N');

Если это не сработает, попробуйте закомментировать вторую строку (ca_flie) и установите verify_peer в false

Каковы сообщения об ошибках в обоих случаях?

Обновление: Кстати, ваши сертификаты уже в формате .pem (в кодировке ASCII-base64).

0
SergeAx

Похоже, OpenSSL не нравится ваш ключ.

Вы можете попробовать позвонить http://php.net/manual/en/function.openssl-error-string.php , чтобы узнать, есть ли дополнительная информация о том, почему?

Или попробуйте различные комбинации файлов ключей в соответствии с этими, возможно, связанными вопросами, которые содержат ту же ошибку:

GL

0
Rich