Ошибка SSL SSL3_GET_SERVER_CERTIFICATE: проверка сертификата не выполнена

После обновления до PHP 5.6 я получаю сообщение об ошибке при попытке подключиться к серверу через fsockopen() ..

Сертификат на сервере (хост) является самозаверяющим

PHP Предупреждение: fsockopen (): операция SSL завершилась неудачно с кодом 1. OpenSSL Сообщения об ошибках: ошибка: 14090086: SSL-процедуры: SSL3_GET_SERVER_CERTIFICATE: проверка сертификата не выполнена

код

 if($fp = fsockopen($host, $port, $errno, $errstr, 20)){ $this->request = 'POST '.substr($this->url, strlen($this->host)).' HTTP/1.1'.$crlf .'Host: '.$this->host.$crlf .'Content-Length: '.$content_length.$crlf .'Connection: Close'.$crlf.$crlf .$body; fwrite($fp, $this->request); while($line = fgets($fp)){ if($line !== false){ $this->response .= $line; } } fclose($fp); } 

Попробовал

 # cd /etc/ssl/certs/ # wget http://curl.haxx.se/ca/cacert.pem 

php.ini

 openssl.cafile = "/etc/ssl/certs/cacert.pem" 

Но сценарий все еще не работает

Обновить

Это работает

 echo file_get_contents("/etc/ssl/certs/cacert.pem"); 

обновление 2

 $contextOptions = array( 'ssl' => array( 'verify_peer' => true, // You could skip all of the trouble by changing this to false, but it's WAY uncool for security reasons. 'cafile' => '/etc/ssl/certs/cacert.pem', //'CN_match' => 'example.com', // Change this to your certificates Common Name (or just comment this line out if not needed) 'ciphers' => 'HIGH:!SSLv2:!SSLv3', 'disable_compression' => true, ) ); $context = stream_context_create($contextOptions); $fp = stream_socket_client("{$host}:{$port}", $errno, $errstr, 20, STREAM_CLIENT_CONNECT, $context); 

ошибка

PHP Предупреждение: stream_socket_client (): операция SSL завершилась неудачно с кодом 1. OpenSSL Сообщения об ошибках: ошибка: 14090086: процедуры SSL: SSL3_GET_SERVER_CERTIFICATE: проверка сертификата не выполнена

Загруженный файл ( http://curl.haxx.se/ca/cacert.pem ) представляет собой набор корневых сертификатов из основных доверенных сертификационных центров. Вы сказали, что удаленный хост имеет самоподписанный сертификат SSL, поэтому он не использовал доверенный сертификат. Параметр openssl.cafile должен указывать на сертификат CA, который использовался для подписи SSL-сертификата на удаленном хосте. PHP 5.6 был улучшен по сравнению с предыдущими версиями PHP, чтобы теперь проверить равноправные сертификаты и имена хостов по умолчанию ( http://php.net/manual/en/migration56.openssl.php )

Вам нужно будет найти сертификат ЦС, который был создан на сервере, который подписал сертификат SSL и скопировал его на этот сервер. Единственный другой вариант – отключить проверку партнера, но это побеждает безопасность SSL. Если вы хотите попробовать отключить проверку, попробуйте этот массив с кодом из моего предыдущего ответа:

 $contextOptions = array( 'ssl' => array( 'verify_peer' => false, 'verify_peer_name' => false ) ); 

В любом случае, если вы используете самозаверяющие сертификаты, вам нужно будет добавить сертификат CA, который использовался для подписи сертификата SSL удаленного хоста в доверенное хранилище на сервере, с которого вы подключаетесь, из контекстов использования OR, чтобы использовать этот сертификат для каждого отдельного запроса. Добавление его в доверенные сертификаты является самым простым решением. Просто добавьте содержимое CA сертификата удаленного хоста в конец загруженного файла cacert.pem.

Предыдущая:

fsockopen не поддерживает потоковые контексты, поэтому вместо этого используйте stream_socket_client. Он возвращает ресурс, который можно использовать со всеми командами, которые могут использовать ресурсы fsockopen.

Это должно быть сокращение замены фрагмента, который у вас есть в вашем вопросе:

 <?php $contextOptions = array( 'ssl' => array( 'verify_peer' => true, // You could skip all of the trouble by changing this to false, but it's WAY uncool for security reasons. 'cafile' => '/etc/ssl/certs/cacert.pem', 'CN_match' => 'example.com', // Change this to your certificates Common Name (or just comment this line out if not needed) 'ciphers' => 'HIGH:!SSLv2:!SSLv3', 'disable_compression' => true, ) ); $context = stream_context_create($contextOptions); $fp = stream_socket_client("tcp://{$host}:{$port}", $errno, $errstr, 20, STREAM_CLIENT_CONNECT, $context); if (!$fp) { echo "$errstr ({$errno})<br />\n"; }else{ $this->request = 'POST '.substr($this->url, strlen($this->host)).' HTTP/1.1'.$crlf .'Host: '.$this->host.$crlf .'Content-Length: '.$content_length.$crlf .'Connection: Close'.$crlf.$crlf .$body; fwrite($fp, $this->request); while (!feof($fp)) { $this->response .= fgets($fp); } fclose($fp); } 

Проблема заключается в новой версии PHP в macOS Sierra

Пожалуйста, добавьте

 stream_context_set_option($ctx, 'ssl', 'verify_peer', false); 

Вы упомянули, что сертификат самоподписан (вами)? Тогда у вас есть два варианта:

  • добавьте сертификат в свой магазин доверия (получение cacert.pem с веб-сайта cURL ничего не сделает, поскольку оно самозаверяется)
  • не утруждайте себя проверкой сертификата: вы доверяете себе, не так ли?

Ниже приведен список параметров контекста SSL в PHP: https://secure.php.net/manual/en/context.ssl.php

Установите allow_self_signed если вы импортируете свой сертификат в хранилище доверия или установите для параметра verify_peer значение false, чтобы пропустить проверку.

Причина, по которой мы доверяем конкретному сертификату, заключается в том, что мы доверяем его эмитенту . Поскольку ваш сертификат самоподписан, ни один клиент не будет доверять сертификату, так как подписчику (вам) не доверяют. Если вы создали свой собственный ЦС при подписании сертификата, вы можете добавить ЦС в свой магазин доверия. Если ваш сертификат не содержит CA, то вы не можете ожидать, что кто-либо подключится к вашему серверу.

Я столкнулся с подобной проблемой во время работы с Ubuntu 16.04 с помощью Docker. В моем случае это была проблема с Composer, но сообщение об ошибке (и, следовательно, проблема) было таким же.

Из-за минималистского ориентированного на Docker базового образа у меня отсутствовал пакет apt-get install ca-certificates мне помогли простые apt-get install ca-certificates .

Добавить

 $mail->SMTPOptions = array( 'ssl' => array( 'verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true )); 

до

 mail->send() 

и заменить

 require "mailer/class.phpmailer.php"; 

с

 require "mailer/PHPMailerAutoload.php"; 

В моем случае я был на CentOS 7, и моя установка php указывала на сертификат, который генерируется через update-ca-trust . /etc/pki/tls/cert.pem ссылка была /etc/pki/tls/cert.pem указывающая на /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem . Это был только тестовый сервер, и я хотел, чтобы мой самоподписанный сертификат работал правильно. Так что в моем случае …

 # My root ca-trust folder was here. I coped the .crt file to this location # and renamed it to a .pem /etc/pki/ca-trust/source/anchors/self-signed-cert.pem # Then run this command and it will regenerate the certs for you and # include your self signed cert file. update-ca-trust 

Затем некоторые мои вызовы api начали работать, так как теперь мне доверяют сертификаты. Кроме того, если ваше ca-trust будет обновляться через yum или что-то еще, это приведет к восстановлению ваших корневых сертификатов и по-прежнему будет включать ваш собственный сертификат. Запустите man update-ca-trust для получения дополнительной информации о том, что делать и как это сделать. 🙂

Если вы используете macOS sierra, в версии PHP есть обновление. вам необходимо, чтобы файл сертификата Entrust.net (2048) был добавлен в код PHP. более подробная информация проверена принятым ответом здесь Push Notification в PHP с использованием файла PEM

Пробовали ли вы использовать метод stream_context_set_option() ?

 $context = stream_context_create(); $result = stream_context_set_option($context, 'ssl', 'local_cert', '/etc/ssl/certs/cacert.pem'); $fp = fsockopen($host, $port, $errno, $errstr, 20, $context); 

Кроме того, попробуйте файл file_get_contents() для файла pem, чтобы убедиться, что у вас есть разрешения на его доступ, и убедитесь, что имя хоста соответствует сертификату.