我一直试图从我在服务器上创建的PHP页面访问这个特定的REST服务。我把问题缩小到这两行。我的PHP页面是这样的:

<?php
$response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json");

echo $response; ?>

页面在第2行结束,出现以下错误:

Warning: file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed in ...php on line 2 Warning: file_get_contents(): Failed to enable crypto in ...php on line 2 Warning: file_get_contents(https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json): failed to open stream: operation failed in ...php on line 2

我们使用的是Gentoo服务器。我们最近升级到了PHP 5.6版本。这个问题是在升级之后出现的。

当我用https://www.google.com这样的地址替换REST服务时,我发现;我的页面工作得很好。

在前面的尝试中,我设置“verify_peer”=>false,并将其作为参数传递给file_get_contents,如这里所述:file_get_contents忽略verify_peer=>false?但正如作者所指出的;这没什么区别。

我问过我们的一个服务器管理员,php.ini文件中是否存在这些行:

扩展= php_openssl.dll allow_url_fopen = On

他告诉我,因为我们在Gentoo上,所以openssl是在我们构建的时候编译的;在php。ini文件中没有设置。

我还确认allow_url_fopen正在工作。由于这个问题的专门性;我找不到很多有用的信息。你们有人遇到过这样的东西吗?谢谢。


当前回答

XAMPP和OSX上的PHP 7有同样的错误。

上面提到的https://stackoverflow.com/的答案很好,但它并没有完全解决我的问题。我必须提供完整的证书链才能使file_get_contents()再次工作。我就是这么做的:

获取根证书/中间证书

首先,我必须弄清楚根证书和中间证书。

最方便的方法可能是使用像ssl-shopper这样的在线cert工具

在那里我找到了三个证书,一个服务器证书和两个链证书(一个是根证书,另一个显然是中间证书)。

我所要做的就是在网上把它们都找出来。在我的例子中,这是根:

thawte DV SSL SHA256 CA

它指向他的网址thawte.com。所以我只是把这个证书放入一个文本文件,并为中间做了同样的事情。完成了。

获取主机证书

接下来我要做的是下载我的服务器证书。在Linux或OS X上,可以用openssl来完成:

openssl s_client -showcerts -connect whatsyoururl.de:443 </dev/null 2>/dev/null|openssl x509 -outform PEM > /tmp/whatsyoururl.de.cert

现在把他们聚在一起

现在把它们都合并到一个文件中。(也许把它们放在一个文件夹里比较好,我只是把它们合并到一个文件中)。你可以这样做:

cat /tmp/thawteRoot.crt > /tmp/chain.crt
cat /tmp/thawteIntermediate.crt >> /tmp/chain.crt
cat /tmp/tmp/whatsyoururl.de.cert >> /tmp/chain.crt

告诉PHP在哪里找到链

这里有一个方便的函数openssl_get_cert_locations(),它将告诉您PHP在哪里查找cert文件。还有这个参数,它将告诉file_get_contents()在哪里查找cert文件。也许两种方法都可行。我更喜欢参数方式。(与上述解决方案相比)。

这就是我的php代码

$arrContextOptions=array(
    "ssl"=>array(
        "cafile" => "/Applications/XAMPP/xamppfiles/share/openssl/certs/chain.pem",
        "verify_peer"=> true,
        "verify_peer_name"=> true,
    ),
);

$response = file_get_contents($myHttpsURL, 0, stream_context_create($arrContextOptions));

这是所有。File_get_contents()又开始工作了。没有CURL,希望没有安全缺陷。

其他回答

在将php更新到php5.6后,在centOS上成为这个问题的受害者,我找到了一个适合我的解决方案。

在默认情况下,使用此命令获取放置证书的正确目录

php -r "print_r(openssl_get_cert_locations()['default_cert_file']);"

然后使用它来获取证书,并将其放在从上面的代码中找到的默认位置

wget http://curl.haxx.se/ca/cacert.pem -O <default location>

如果您的PHP版本是5,请尝试在终端中输入以下命令来安装cURL:

sudo apt-get install php5-curl

修复了macos 12.4 / Mamp 6.6 / Homebrew 3.5.2 / Openssl@3

终端

检查版本

openssl version -a

我的手指指向:

...
OPENSSLDIR: "/opt/homebrew/etc/openssl@3"
...

所以我查看了homebrew的dir /opt/homebrew/etc/openssl@3,找到了cert.pem,并确保我的Mamp当前版本的php的php.ini文件指向了homebrew正确的openssl版本的cert.pem

添加到php.ini

openssl.cafile=/opt/homebrew/etc/openssl@3/cert.pem

XAMPP和OSX上的PHP 7有同样的错误。

上面提到的https://stackoverflow.com/的答案很好,但它并没有完全解决我的问题。我必须提供完整的证书链才能使file_get_contents()再次工作。我就是这么做的:

获取根证书/中间证书

首先,我必须弄清楚根证书和中间证书。

最方便的方法可能是使用像ssl-shopper这样的在线cert工具

在那里我找到了三个证书,一个服务器证书和两个链证书(一个是根证书,另一个显然是中间证书)。

我所要做的就是在网上把它们都找出来。在我的例子中,这是根:

thawte DV SSL SHA256 CA

它指向他的网址thawte.com。所以我只是把这个证书放入一个文本文件,并为中间做了同样的事情。完成了。

获取主机证书

接下来我要做的是下载我的服务器证书。在Linux或OS X上,可以用openssl来完成:

openssl s_client -showcerts -connect whatsyoururl.de:443 </dev/null 2>/dev/null|openssl x509 -outform PEM > /tmp/whatsyoururl.de.cert

现在把他们聚在一起

现在把它们都合并到一个文件中。(也许把它们放在一个文件夹里比较好,我只是把它们合并到一个文件中)。你可以这样做:

cat /tmp/thawteRoot.crt > /tmp/chain.crt
cat /tmp/thawteIntermediate.crt >> /tmp/chain.crt
cat /tmp/tmp/whatsyoururl.de.cert >> /tmp/chain.crt

告诉PHP在哪里找到链

这里有一个方便的函数openssl_get_cert_locations(),它将告诉您PHP在哪里查找cert文件。还有这个参数,它将告诉file_get_contents()在哪里查找cert文件。也许两种方法都可行。我更喜欢参数方式。(与上述解决方案相比)。

这就是我的php代码

$arrContextOptions=array(
    "ssl"=>array(
        "cafile" => "/Applications/XAMPP/xamppfiles/share/openssl/certs/chain.pem",
        "verify_peer"=> true,
        "verify_peer_name"=> true,
    ),
);

$response = file_get_contents($myHttpsURL, 0, stream_context_create($arrContextOptions));

这是所有。File_get_contents()又开始工作了。没有CURL,希望没有安全缺陷。

<?php
    $stream_context = stream_context_create([
        "ssl" => [
            "verify_peer" => false,
            "verify_peer_name" => false
        ]
    ]);  
    
    $response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json", false, $stream_context);
    
    echo $response;
?>

刚刚测试了PHP 7.2,运行良好。

编辑:也在PHP 7.1上进行了测试和工作