Ошибка 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: сбой проверки сертификата
10 ответов
Файл, который вы загрузили ( http://curl.haxx.se/ca/cacert.pem), представляет собой пакет корневых сертификатов основных доверенных центров сертификации. Вы сказали, что удаленный хост имеет самозаверяющий сертификат SSL, поэтому он не использует доверенный сертификат. openssl.cafile
параметр должен указывать на сертификат CA, который использовался для подписи сертификата SSL на удаленном хосте. PHP 5.6 был улучшен по сравнению с предыдущими версиями PHP, чтобы теперь проверять сертификаты пиров и имена хостов по умолчанию ( http://php.net/manual/en/migration56.openssl.php)
Вам нужно найти сертификат CA, сгенерированный на сервере, который подписал сертификат SSL, и скопировать его на этот сервер. Единственный другой вариант - отключить проверку однорангового узла, но это нарушает безопасность SSL. Если вы действительно хотите попробовать отключить проверку, попробуйте этот массив с кодом из моего предыдущего ответа:
$contextOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false
)
);
В любом случае, если вы используете самозаверяющие сертификаты, вам нужно добавить сертификат CA, который использовался для подписи SSL-сертификата удаленного хоста, в доверенное хранилище на сервере, к которому вы подключаетесь, ИЛИ использовать потоковые контексты для использования. этот сертификат для каждого отдельного запроса. Добавление его в доверенные сертификаты является самым простым решением. Просто добавьте содержимое сертификата 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);
}
С аналогичной проблемой я столкнулся при работе с Ubuntu 16.04 при использовании Docker. В моем случае это была проблема с Composer, но сообщение об ошибке (и, следовательно, проблема) было то же самое.
Из-за минималистического Docker-ориентированного базового образа, который я пропустил ca-certificates
пакет и простой apt-get install ca-certificates
помог мне.
Проблема в новой версии PHP в macOS Sierra
Пожалуйста, добавьте
stream_context_set_option($ctx, 'ssl', 'verify_peer', false);
Добавлять
$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/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
для получения дополнительной информации о том, что делать и как это сделать.:)
Я использовал следующий скрипт, чтобы проверить проблему
<?php
$url = "mail.example.com";// your host which has issue
$orignal_parse = parse_url($url, PHP_URL_HOST);
$get = stream_context_create(array("ssl" => array("capture_peer_cert" => TRUE)));
$read = stream_socket_client("ssl://".$url.":993", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $get);
if (!$read) {
// ssl connection failed for some reason
// could be a certificate error or failure to connect on port 443
echo "Failed to connect to site. Error {$errno}: {$errstr}\n";
} else {
$cert = stream_context_get_params($read);
$certinfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']);
var_dump($certinfo);
}
?>
Я получал ошибку ниже
PHP Warning: stream_socket_client(): SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed
Обновил сертификаты ЦС, а затем запустил переустановку, которая помогла
yum reinstall ca-certificates
Прежде всего, убедитесь, что антивирусное программное обеспечение не блокирует SSL2.
Потому что долго не мог решить проблему и мне помогло только отключение антивируса
Вы упоминаете сертификат самозаверяющий (вами)? Тогда у вас есть два варианта:
- добавьте сертификат в ваше доверенное хранилище
cacert.pem
с сайта cURL ничего не сделаю, так как он самоподписан) - не надо проверять сертификат: ты доверяешь себе, не так ли?
Вот список параметров контекста SSL в PHP: https://secure.php.net/manual/en/context.ssl.php
Задавать allow_self_signed
если вы импортируете сертификат в доверенное хранилище или установите verify_peer
false, чтобы пропустить проверку.
Причина, по которой мы доверяем конкретному сертификату, заключается в том, что мы доверяем его эмитенту. Поскольку ваш сертификат самоподписан, ни один клиент не будет доверять этому сертификату, поскольку подписавшему (вам) не доверяют. Если вы создали свой собственный CA при подписании сертификата, вы можете добавить CA в ваше хранилище доверенных сертификатов. Если ваш сертификат не содержит ЦС, вы не можете ожидать, что кто-нибудь подключится к вашему серверу.
Если вы используете MacOS Sierra, есть обновление в версии PHP. Вам необходимо добавить файл Entrust.net Certificate Authority (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: убедитесь, что у вас есть разрешения на доступ к нему, и убедитесь, что имя хоста соответствует сертификату.