PHP-SSL错误SSL3_GET_SERVER_CERTIFICATE:证书验证失败

升级到PHP 5.6后,尝试通过code连接到服务器时收到错误消息。

服务器(主机)上的证书是自签名的

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:证书验证失败

clarkk asked 2020-07-30T19:50:17Z
9个解决方案
31 votes

您下载的文件([http://curl.haxx.se/ca/cacert.pem)]是来自主要受信任证书颁发机构的一系列根证书。 您说远程主机具有自签名SSL证书,因此它不使用受信任的证书。 openssl.cafile设置需要指向用于在远程主机上签名SSL证书的CA证书。 与以前的PHP版本相比,PHP 5.6进行了改进,现在可以默认情况下验证对等证书和主机名([http://php.net/manual/en/migration56.openssl.php)]

您需要找到在签署SSL证书的服务器上生成的CA证书,并将其复制到该服务器上。 唯一的其他选择是禁用验证对等方,但这会破坏SSL安全性。 如果您确实想尝试禁用验证,请尝试使用我以前的回答中的代码来验证此数组:

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

无论哪种方式,如果您使用的是自签名证书,则都需要将用于对远程主机的SSL证书进行签名的CA证书添加到要从其连接的服务器上的受信任存储中,或者使用流上下文来使用 该证书用于每个单独的请求。 将其添加到受信任的证书是最简单的解决方案。 只需将远程主机的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);

}
aecend answered 2020-07-30T19:50:50Z
6 votes

问题出在macOS Sierra中的新PHP版本中

请加

stream_context_set_option($ctx, 'ssl', 'verify_peer', false);
Nguyễn Quang Tuấn answered 2020-07-30T19:51:14Z
4 votes

在使用Docker的Ubuntu 16.04工作期间,我遇到了类似的问题。 在我的情况下,这是Composer的问题,但错误消息(因此也是问题)是相同的。

由于基于Docker的极简主义基本映像,我缺少了ca-certificates程序包,而简单的apt-get install ca-certificates可以帮助我。

Vladimir Posvistelik answered 2020-07-30T19:51:39Z
4 votes

$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";
Himanshu Sharma answered 2020-07-30T19:52:11Z
1 votes

首先,请确保您的防病毒软件不会阻止SSL2。
因为我长时间无法解决问题,所以仅禁用防病毒软件对我有帮助

Igor Shumichenko answered 2020-07-30T19:52:36Z
0 votes

您提到证书是由您自己签名的吗? 然后,您有两种选择:

  • 将证书添加到您的信任库中(从cURL网站获取allow_self_signed不会做任何事情,因为它是自签名的)
  • 不要打扰验证证书:您信任自己,不是吗?

这是PHP中的SSL上下文选项的列表:[https://secure.php.net/manual/en/context.ssl.php]

如果将证书导入到信任库中,请设置allow_self_signed,或者将verify_peer设置为false以跳过验证。

我们信任特定证书的原因是因为我们信任其颁发者。 由于您的证书是自签名的,因此没有客户端会信任该证书,因为签名人(您)不受信任。 如果您在签名证书时创建了自己的CA,则可以将CA添加到您的信任库中。 如果您的证书不包含任何CA,那么您将无法期望任何人连接到您的服务器。

DavidS answered 2020-07-30T19:53:19Z
0 votes

就我而言,我在CentOS 7上,我的php安装指向通过man 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,以获取有关做什么和如何做的更多信息。 :)

n0nag0n answered 2020-07-30T19:53:44Z
0 votes

如果您使用的是macOS sierra,则PHP版本会进行更新。 您需要将Entrust.net证书颁发机构(2048)文件添加到PHP代码中。 更多信息在此处检查接受的答案使用PEM文件在PHP中推送通知

Im Batman answered 2020-07-30T19:54:04Z
-1 votes

您是否尝试使用file_get_contents()方法?

$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);

此外,请尝试对pem文件进行file_get_contents(),以确保您有权访问该文件,并确保主机名与证书匹配。

uri2x answered 2020-07-30T19:54:28Z
translate from https://stackoverflow.com:/questions/32211301/ssl-error-ssl3-get-server-certificatecertificate-verify-failed