Как читать веб-сокеты сертификатов 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
линия …

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

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

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

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

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

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

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

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

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

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

И с

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 / сертификаты / cert-Du9H8N ‘в … on line …

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

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

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

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

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

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

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

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

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

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

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

И с (просто конкатенация 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 будет ресурсным, логическим
дано в … на линии …

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

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

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

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

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

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

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

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

Изменить 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

Решение

Вы сказали:

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

Файл / 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

Другие решения

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

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

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

GL

0

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

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
По вопросам рекламы [email protected]