我一直试图从我在服务器上创建的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正在工作。由于这个问题的专门性;我找不到很多有用的信息。你们有人遇到过这样的东西吗?谢谢。


当前回答

修复了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

其他回答

这是一个非常有用的链接:

http://php.net/manual/en/migration56.openssl.php

一份官方文档,描述了在PHP 5.6中对open ssl所做的更改 从这里我了解到我应该将另一个参数设置为false:“verify_peer_name”=>false

注意:这具有非常重要的安全影响。禁用验证可能会允许MITM攻击者使用无效的证书来窃听请求。虽然在本地开发中这样做可能有用,但在生产中应该使用其他方法。

所以我的工作代码是这样的:

<?php
$arrContextOptions=array(
    "ssl"=>array(
        "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_create($arrContextOptions));

echo $response; ?>

你不应该只是关闭验证。你应该下载一个证书包,也许curl包可以?

然后你只需要把它放在你的web服务器上,给运行php的用户读取文件的权限。然后这段代码应该为您工作:

$arrContextOptions= [
    'ssl' => [
        'cafile' => '/path/to/bundle/cacert.pem',
        'verify_peer'=> true,
        'verify_peer_name'=> true,
    ],
];

$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_create($arrContextOptions)
);

希望您尝试访问的站点的根证书在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上进行了测试和工作

为我工作,我使用的是PHP 5.6。应该启用Openssl扩展,并且在调用谷歌映射API verify_peer时使false 下面的代码是为我工作。

<?php
$arrContextOptions=array(
    "ssl"=>array(
         "verify_peer"=>false,
         "verify_peer_name"=>false,
    ),
);  
$url = "https://maps.googleapis.com/maps/api/geocode/json?latlng="
      . $latitude
      . ","
      . $longitude
      . "&sensor=false&key="
      . Yii::$app->params['GOOGLE_API_KEY'];

$data = file_get_contents($url, false, stream_context_create($arrContextOptions));

echo $data;
?>

对于类似于以下错误

[11-5-2017 19:19:13美国/芝加哥]PHP警告:file_get_contents(): SSL操作失败与代码1。OpenSSL错误消息: SSL例程:ssl3_get_server_certificate:证书验证失败

您是否检查了openssl引用的证书和目录的权限?

你可以这样做

var_dump(openssl_get_cert_locations());

得到类似的结果

array(8) {
  ["default_cert_file"]=>
  string(21) "/usr/lib/ssl/cert.pem"
  ["default_cert_file_env"]=>
  string(13) "SSL_CERT_FILE"
  ["default_cert_dir"]=>
  string(18) "/usr/lib/ssl/certs"
  ["default_cert_dir_env"]=>
  string(12) "SSL_CERT_DIR"
  ["default_private_dir"]=>
  string(20) "/usr/lib/ssl/private"
  ["default_default_cert_area"]=>
  string(12) "/usr/lib/ssl"
  ["ini_cafile"]=>
  string(0) ""
  ["ini_capath"]=>
  string(0) ""
}

这个问题让我沮丧了一段时间,直到我意识到我的“certs”文件夹有700个权限,而它应该有755个权限。请记住,这不是密钥文件夹,而是证书文件夹。我建议阅读这个关于ssl权限的链接。

我曾经做过

chmod 755 certs

问题解决了,至少对我来说是这样。