【问题标题】:SSL error SSL3_GET_SERVER_CERTIFICATE:certificate verify failedSSL 错误 SSL3_GET_SERVER_CERTIFICATE:证书验证失败
【发布时间】:2015-11-19 14:07:23
【问题描述】:

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

【问题讨论】:

  • 已经试过了.. 我不能locate文件
  • 你得看看细节。有多种可能的原因,但不使用证书中显示的相同域是一种常见的原因。您可以使用curl -v 无错误地访问它吗?另请注意,除非那些版本的 openssl 已反向移植所有安全补丁,否则您可能需要升级。
  • 问题不在于服务器。它有效。它是导致问题的客户端
  • 您使用的是哪个操作系统?

标签: php apache openssl


【解决方案1】:

您下载的文件 (http://curl.haxx.se/ca/cacert.pem) 是来自主要受信任证书颁发机构的根证书捆绑包。你说远程主机有一个自签名的 SSL 证书,所以它没有使用受信任的证书。 openssl.cafile 设置需要指向用于在远程主机上签署 SSL 证书的 CA 证书。 PHP 5.6 比以前的 PHP 版本进行了改进,现在默认验证对等证书和主机名 (http://php.net/manual/en/migration56.openssl.php)

您需要找到在签署 SSL 证书的服务器上生成的 CA 证书并将其复制到此服务器。如果您使用的是自签名证书,则需要将用于签署远程主机 SSL 证书的 CA 证书添加到您要连接的服务器上的受信任存储中,或者使用流上下文将该证书用于每个单独的请求。将其添加到受信任的证书是最简单的解决方案。只需将远程主机的 CA 证书的内容添加到您下载的 cacert.pem 文件的末尾即可。

上一个:

fsockopen 不支持流上下文,因此请改用 stream_socket_client。它返回一个可以与 fsockopen 资源可以使用的所有命令一起使用的资源。

这应该是您问题中的 sn-p 的替代品:

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

}

【讨论】:

  • 它应该单独与fsockopen一起工作。这太复杂了
  • 我不确定您使用的是哪种 Linux 发行版,但请尝试运行 sudo service apache2 restart。可能是在php.ini中加入openssl.cafile这一行后需要重启apache
  • @clarkk 您要连接的主机,该主机的 SSL 证书是您拥有 CA 证书的自签名证书吗? PHP 5.6 现在在使用 fsockopen 时验证 SSL 证书,因此用于签署远程主机 SSL 证书的 CA 证书需要在您的系统上得到信任。我上面的回答只是为请求使用特定证书的另一种方式。
  • 我总是在配置更改后重新启动 apache.. 但是服务器(主机)上的证书是自签名的
  • @clarkk 你能试试我发布的代码看看它是否真的有效吗?因为如果确实如此,那么我们可以解决信任您主机上的 CA 证书的问题,因此 fsockopen 将起作用。如果它根本不起作用,那么还有其他问题。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    请补充

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

    【讨论】:

    • 它在生产环境中为我工作,但在沙盒中它正在工作。能不能给个亮点?
    • 请注意,这是不安全的,因为它告诉 PHP 忽略对等点不是有效匹配的事实。
    • @AlexisWilke :谢谢,我知道,但我找不到其他解决方案。如果你有什么好的想法,请告诉我
    • 首先,我认为您的回答是您说关闭对等验证存在安全风险,因此我发表了评论。至于解决方案,可能是服务器仍然使用已弃用的 SSL3。因此,在升级之前,您不应再访问该服务器...
    • 添加先生的确切位置??
    【解决方案4】:

    如果您使用的是 macOS sierra,PHP 版本有更新。您需要将 Entrust.net 证书颁发机构 (2048) 文件添加到 PHP 代码中。更多信息在这里查看接受的答案Push Notification in PHP using PEM file

    【讨论】:

      【解决方案5】:

      添加

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

      【讨论】:

        【解决方案6】:

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

        由于极简主义的面向 Docker 的基础映像,我缺少 ca-certificates 包和简单的 apt-get install ca-certificates 帮助了我。

        【讨论】:

        • 终于,找了这么久,终于有帮助了!谢谢!
        【解决方案7】:

        就我而言,我在 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 以获取有关做什么和如何做的更多信息。 :)

        【讨论】:

          【解决方案8】:

          您提到证书是自签名的(由您)?那么你有两个选择:

          • 将证书添加到您的信任存储区(从 cURL 网站获取 cacert.pem 不会做任何事情,因为它是自签名的)
          • 不必费心验证证书:您相信自己,不是吗?

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

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

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

          【讨论】:

          • “你相信自己吗?”的第一个解决方案是正确的:将自己添加到信任库。第二个是积极有害的:“你相信自己吗?是的,现在你可以相信任何说他们是你的人!”
          【解决方案9】:

          您是否尝试过使用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 文件,以确保您有权访问它,并确保主机名与证书匹配。

          【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-08-19
          • 2015-03-30
          • 1970-01-01
          • 2020-09-20
          • 2015-01-05
          • 2016-07-28
          • 1970-01-01
          相关资源
          最近更新 更多