Как установить список доверенных сертификатов для клиента сокета в PHP?

В контексте IHE Connectathon я хочу сделать сырой сервер сокетов, отвечающий на профиль ATNA, для которого требуются сокеты TLS с сертификатами обоих концов.

Моя проблема, если она суммируется в этом сообщении: https://groups.google.com/d/msg/eu_connectathon/O-VGI_3cltw/ARsElA65ZkkJ

Изменить : Извините, группы Google не являются общедоступными, вот сообщение:

Привет, Флориан,

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

Сообщение означает, что сервер отправил сообщение CertificateRequest в clienmt без значений в поле certificate_authorities.

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

Похоже, вы можете поручить OpenSSL вернуть это значение, вызвав SSL_CTX_set_client_CA_list, например, в DcmTLSTransportLayer :: addTrustedCertificateFile. Я еще не тестировал это с помощью инструментов TLS, но я надеюсь сделать это до начала подключения.

Но моя реализация на PHP не такая же, как у их. Похоже, что в PHP отсутствует опция «Список CA CA set set client», чтобы сообщить клиенту, какой центр сертификации он должен использовать.

$context = stream_context_create(); if ($certificate) { // Server certificate + private key stream_context_set_option($context, 'ssl', 'local_cert', "/path/to/server.pem"); stream_context_set_option($context, 'ssl', 'passphrase', $passphrase); // Client public certificates stream_context_set_option($context, 'ssl', 'cafile', "/path/to/ca.pem"); 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', "TlsTools2"); stream_context_set_option($context, 'ssl', 'capture_peer_cert', true); stream_context_set_option($context, 'ssl', 'capture_peer_cert_chain', true); } $this->__socket = @stream_socket_server("tcp://$address:$port", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context); 

Клиент TLS IHE Gazelle говорит мне: «Сервер запросил сертификат, но список эмитентов не содержит действительных сертификатов».

Сообщения между клиентом и сервером проходят, но тест не в порядке, как «недостаточно безопасный», как сообщает это сообщение.

Вы видите проблему, и у PHP больше вариантов, которых я не видел?

Спасибо за вашу помощь.

Изменить : Как предложил мне @rdlowrey, я просто создал отчет об ошибке: https://bugs.php.net/bug.php?id=69215

Как я упоминал в первоначальном комментарии:

Реализация потокового сервера PHP никогда не использовала SSL_CTX_set_client_CA_list () для зашифрованных потоков.

Это было исправлено вверх по потоку, как указано в соответствующем отчете об ошибке:

https://bugs.php.net/bug.php?id=69215

Это изменение будет отражено после выпуска двоичного файла PHP 5.6.8 (или вы можете создать PHP вручную против текущего источника до этой версии).

Реализация

Используя обновленный двоичный код, примерный код OP будет работать без изменений. Простой пример криптоконтекста для использования на серверах:

 <?php $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '/path/to/my-server-cert.pem', 'passphrase' => 'elephpant', 'cafile' => '/path/to/my-ca-certs.pem', 'verify_peer' => true ]]); 

В приведенном выше примере PHP автоматически использует имена из сертификатов, найденных в файле my-ca-certs.pem упомянутом выше, при отправке списка CA CA в качестве части рукопожатия TLS.

Заметки

При включении проверки "verify_peer" => true в зашифрованных потоках сервера через "verify_peer" => true PHP не будет автоматически активировать проверку имени пэра. Если вы не хотите, чтобы единственный владелец сертификата (с определенным именем) имел доступ к вашему серверу, это именно то, что вы хотите. Это поведение по умолчанию поддерживает более распространенный прецедент, позволяющий любому клиенту, чей сертификат был подписан доверенным ЦС для установления соединений с вашим сервером. Однако, если вы хотите обеспечить проверку имени в зашифрованном сервере, измените приведенный выше пример контекста, как показано ниже:

 <?php $serverCtx = stream_context_create(['ssl' => [ 'local_cert' => '/path/to/my-server-cert.pem', 'passphrase' => 'elephpant', 'cafile' => '/path/to/my-ca-certs.pem', 'verify_peer' => true, 'verify_peer_name' => true, // verify the name on the cert 'peer_name' => 'zanzibar' // ensure the cert's name matches this ]]);