【问题标题】:"Unable to read x509 certificate" when making HTTPS calls to PayPal's subscription API对 PayPal 的订阅 API 进行 HTTPS 调用时出现“无法读取 x509 证书”
【发布时间】:2020-07-04 21:09:14
【问题描述】:

我正在将 PayPal 的订阅 API 实施到我的项目中,但是,我收到以下 curl 错误:

array:2 [▼
  "error" => "error_in_reading_cert"
  "error_description" => "Unable to read x509 certificate"
]

请注意,我正在使用 Laravel。这是我的 curl 课程:

<?php

namespace App\Logic\Curl;

class Curl {

    /**
     * Perform new POST request and return decoded JSON response
     *
     * @param $url
     * @param $data
     * @return array
     */
    public function newRequest($url, $data)
    {
        $connection = curl_init($url);

        $clientId = env('services.paypal.client-id');
        $secret = env('services.paypal.secret');

        curl_setopt($connection, CURLOPT_HTTPHEADER, [
                "Content-Type: application/json",
                "Authorization: Basic $clientId:$secret",
            ]
        );

        $options = array(
            CURLOPT_RETURNTRANSFER => true,   // return web page
            CURLOPT_HEADER         => false,  // don't return headers
            CURLOPT_FOLLOWLOCATION => true,   // follow redirects
            CURLOPT_MAXREDIRS      => 10,     // stop after 10 redirects
            CURLOPT_ENCODING       => "",     // handle compressed
            CURLOPT_USERAGENT      => "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36", // name of client
            CURLOPT_AUTOREFERER    => true,   // set referrer on redirect
            CURLOPT_CONNECTTIMEOUT => 120,    // time-out on connect
            CURLOPT_TIMEOUT        => 120,    // time-out on response
        );

        curl_setopt_array($connection, $options);

        curl_setopt($connection, CURLOPT_POSTFIELDS, $data);

        $response = curl_exec($connection);

        if(curl_error($connection)) {
            return curl_error($connection);
        }

        curl_close($connection);

        return $this->decodeResponse($response);
    }

    /**
     * JSON decode the response
     *
     * @param $response
     * @return mixed
     */
    public function decodeResponse($response)
    {
        return json_decode($response, true);
    }

}

这是我的 PayPal 课程:

<?php

namespace App\Logic\Paypal;

use App\Logic\Curl\Curl;
use Exception;

class Paypal {

    public function createProduct()
    {
        $productDetails = [
            "name" => "Feedback Form",
            "description" => "Feedback form as a service.",
            "type" => "SERVICE",
            "category" => "SOFTWARE",
            "home_url" => "https://www.feedback.com/"
        ];

        $url = $this->getApiUrl('createProduct');

        $curl = new Curl();

        return $curl->newRequest($url, $productDetails);
    }

    public function getApiUrl($endpointName) {
        $mode = config('services.paypal.mode');

        $urls = [
            'createProduct' => [
                'live' => 'https://api.paypal.com/v1/catalogs/products',
                'sandbox' => 'https://api.sandbox.paypal.com/v1/catalogs/products'
            ]
        ];

        return $urls[$endpointName][$mode];
    }
}

这是我的 PayPal 控制器,它接收请求:

<?php

namespace App\Http\Controllers;

use App\Logic\Paypal\Paypal;
use App\Setting;

class PaypalController extends Controller
{
    public function bootstrap()
    {
        $setting = Setting::where('name', '=', 'active_plan_id')->first();

        if ($setting) {
            return 'plan already activated';
        }

        $paypal = new Paypal();
        $product = $paypal->createProduct();

        dd($product);
    }
}

以上代码只是尝试根据 PayPal 的订阅文档创建产品。

在网上搜索解决方案时,我在 StackOverflow 上遇到了各种问题 - 其中最有希望的是 this。首先,我尝试了投票最多的解决方案,但它对我不起作用,尽管它有点奇怪。我跟着它进入我的 /etc/php/7.2/apache2/php.ini 并取消注释 curl.cainfo 并用下载证书的绝对路径填充它并重新启动 apache 并没有帮助。然后,我创建了一个 php 信息文件并查看了选项,即使加载的配置文件正是我编辑的那个,我也找不到 curl.cainfo - 根据this curl.cainfo 不会显示在从 PHP7.2 开始的 phpinfo 解决了这个问题。

从同一个 StackOverflow 问题,我也尝试过:

sudo apt-get install ca-certificates

和:

sudo update-ca-certificates

但它没有帮助。

非常感谢任何帮助。

编辑 1:刚刚在 PayPal 的文档中注意到 here curl 是使用选项 -k 调用的,它甚至允许不安全的连接,我想知道为什么会这样,PayPal 是否在其沙盒 API 上使用自签名证书?

编辑 2:我尝试从 here 下载证书并将 curl.cainfo 指向它,但效果不佳。

编辑 3:我尝试通过添加以下行 $options[CURLOPT_SSL_VERIFYPEER] = false; 来禁用对等证书验证,但我仍然遇到相同的错误

编辑 4:我也尝试添加 curl_setopt($connection, CURLOPT_CAINFO, '/path/to/cacert.pem');,但没有帮助

编辑 5:我也尝试从命令行运行相同的请求,但出现相同的错误,这是输出:

Note: Unnecessary use of -X or --request, POST is already inferred.
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0*   Trying 173.0.82.78...
* TCP_NODELAY set

  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0* Connected to api.sandbox.paypal.com (173.0.82.78) port 443 (#0)

  0     0    0     0    0     0      0      0 --:--:--  0:00:03 --:--:--     0* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ca-certificates
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [85 bytes data]

  0     0    0     0    0     0      0      0 --:--:--  0:00:04 --:--:--     0* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [4162 bytes data]
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
{ [944 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
} [7 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [262 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / AES256-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=US; ST=California; L=San Jose; O=PayPal, Inc.; OU=PayPal Production; CN=api.sandbox.paypal.com
*  start date: Aug 21 00:00:00 2018 GMT
*  expire date: Aug 20 12:00:00 2020 GMT
*  subjectAltName: host "api.sandbox.paypal.com" matched cert's "api.sandbox.paypal.com"
*  issuer: C=US; O=DigiCert Inc; CN=DigiCert Global CA G2
*  SSL certificate verify ok.

  0     0    0     0    0     0      0      0 --:--:--  0:00:05 --:--:--     0} [5 bytes data]
> POST /v1/catalogs/products HTTP/1.1
> Host: api.sandbox.paypal.com
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Type: application/json
> Authorization: Basic client-id:secret
> Content-Length: 205
> 
} [205 bytes data]
* upload completely sent off: 205 out of 205 bytes
{ [5 bytes data]
< HTTP/1.1 401 Unauthorized
< Cache-Control: max-age=0, no-cache, no-store, must-revalidate
< Content-Length: 87
< Content-Type: application/json
< Date: Wed, 25 Mar 2020 09:45:30 GMT
< Paypal-Debug-Id: f3411e0e1c2ab
< 
{ [87 bytes data]

100   292  100    87  100   205     12     30  0:00:07  0:00:06  0:00:01    55
100   292  100    87  100   205     12     30  0:00:07  0:00:06  0:00:01    68
* Connection #0 to host api.sandbox.paypal.com left intact
{"error":"error_in_reading_cert","error_description":"Unable to read x509 certificate"}

编辑 6:这是我尝试过的完整 curl 命令和输出:

curl -v -k POST https://api.sandbox.paypal.com/v1/catalogs/products -H "Content-Type: application/json" -H "Authorization: Basic AW09uZVO_1NUVZXEzlYp1xgiVjweOwnIBl0rMltEK7X1zMhe9fxcPPr_IgwGplL0xSPHQo4lO3cdP27p:EB351ARk-HkEd5OmkV7NGXrUT5V2AU_zN8ZRJ55cWowGUKr845Do0MM5zrqfpCxJECqL59rwcXueQUW2" -d '{"name": "Video Streaming Service","description": "Video streaming service","type": "SERVICE","category": "SOFTWARE","image_url": "https://example.com/streaming.jpg","home_url": "https://example.com/home"}' --cacert /opt/ssl/curl.pem 2>&1 | tee curl.txt

输出:

* Rebuilt URL to: POST/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:03 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:04 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:05 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:06 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:07 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:08 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:09 --:--:--     0* Could not resolve host: POST
* Closing connection 0
curl: (6) Could not resolve host: POST

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0*   Trying 173.0.82.78...
* TCP_NODELAY set

  0     0    0     0    0     0      0      0 --:--:--  0:00:03 --:--:--     0* Connected to api.sandbox.paypal.com (173.0.82.78) port 443 (#1)

  0     0    0     0    0     0      0      0 --:--:--  0:00:03 --:--:--     0* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /opt/ssl/curl.pem
  CApath: /etc/ssl/certs
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [85 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [4162 bytes data]
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
{ [944 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
} [7 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [262 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / AES256-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=US; ST=California; L=San Jose; O=PayPal, Inc.; OU=PayPal Production; CN=api.sandbox.paypal.com
*  start date: Aug 21 00:00:00 2018 GMT
*  expire date: Aug 20 12:00:00 2020 GMT
*  issuer: C=US; O=DigiCert Inc; CN=DigiCert Global CA G2
*  SSL certificate verify ok.

  0     0    0     0    0     0      0      0 --:--:--  0:00:04 --:--:--     0} [5 bytes data]
> POST /v1/catalogs/products HTTP/1.1
> Host: api.sandbox.paypal.com
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Type: application/json
> Authorization: Basic AW09uZVO_1NUVZXEzlYp1xgiVjweOwnIBl0rMltEK7X1zMhe9fxcPPr_IgwGplL0xSPHQo4lO3cdP27p:EB351ARk-HkEd5OmkV7NGXrUT5V2AU_zN8ZRJ55cWowGUKr845Do0MM5zrqfpCxJECqL59rwcXueQUW2
> Content-Length: 205
> 
} [205 bytes data]
* upload completely sent off: 205 out of 205 bytes
{ [5 bytes data]
< HTTP/1.1 401 Unauthorized
< Cache-Control: max-age=0, no-cache, no-store, must-revalidate
< Content-Length: 87
< Content-Type: application/json
< Date: Wed, 25 Mar 2020 15:54:35 GMT
< Paypal-Debug-Id: ae0a3de96fdf5
< 
{ [87 bytes data]

100   292  100    87  100   205     16     39  0:00:05  0:00:05 --:--:--    79
* Connection #1 to host api.sandbox.paypal.com left intact
{"error":"error_in_reading_cert","error_description":"Unable to read x509 certificate"}

编辑 7:我运行相同的 curl 命令,但使用来自不同企业帐户的不同凭据,这是命令和输出:

curl -v -k POST https://api.sandbox.paypal.com/v1/catalogs/products -H "Content-Type: application/json" -H "Authorization: Basic AVx9AFnHHdAvjsRA_t5AXJEdu_XIqC4RgxOvJ_a49r3QZj9eNlSy1gRGRmLIBS52wh1LWi27adQgvwSc:EPCcwShbEMG4O9uoPvoMtbwFc02RT2vo8FayHqU3StskKR3bxx7sxXACEG7Sf-Mwx_taRFhRfp0s79Ox" -d '{"name": "Video Streaming Service","description": "Video streaming service","type": "SERVICE","category": "SOFTWARE","image_url": "https://example.com/streaming.jpg","home_url": "https://example.com/home"}' --cacert /opt/ssl/curl.pem 2>&1 | tee curl.txt

输出:

* Rebuilt URL to: POST/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:03 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:04 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:05 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:06 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:07 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:08 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:09 --:--:--     0* Could not resolve host: POST
* Closing connection 0
curl: (6) Could not resolve host: POST

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0*   Trying 173.0.82.78...
* TCP_NODELAY set

  0     0    0     0    0     0      0      0 --:--:--  0:00:03 --:--:--     0* Connected to api.sandbox.paypal.com (173.0.82.78) port 443 (#1)

  0     0    0     0    0     0      0      0 --:--:--  0:00:03 --:--:--     0* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /opt/ssl/curl.pem
  CApath: /etc/ssl/certs
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [85 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [4162 bytes data]
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
{ [944 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
} [7 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [262 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / AES256-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=US; ST=California; L=San Jose; O=PayPal, Inc.; OU=PayPal Production; CN=api.sandbox.paypal.com
*  start date: Aug 21 00:00:00 2018 GMT
*  expire date: Aug 20 12:00:00 2020 GMT
*  issuer: C=US; O=DigiCert Inc; CN=DigiCert Global CA G2
*  SSL certificate verify ok.

  0     0    0     0    0     0      0      0 --:--:--  0:00:04 --:--:--     0} [5 bytes data]
> POST /v1/catalogs/products HTTP/1.1
> Host: api.sandbox.paypal.com
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Type: application/json
> Authorization: Basic AW09uZVO_1NUVZXEzlYp1xgiVjweOwnIBl0rMltEK7X1zMhe9fxcPPr_IgwGplL0xSPHQo4lO3cdP27p:EB351ARk-HkEd5OmkV7NGXrUT5V2AU_zN8ZRJ55cWowGUKr845Do0MM5zrqfpCxJECqL59rwcXueQUW2
> Content-Length: 205
> 
} [205 bytes data]
* upload completely sent off: 205 out of 205 bytes
{ [5 bytes data]
< HTTP/1.1 401 Unauthorized
< Cache-Control: max-age=0, no-cache, no-store, must-revalidate
< Content-Length: 87
< Content-Type: application/json
< Date: Wed, 25 Mar 2020 15:54:35 GMT
< Paypal-Debug-Id: ae0a3de96fdf5
< 
{ [87 bytes data]

100   292  100    87  100   205     16     39  0:00:05  0:00:05 --:--:--    79
* Connection #1 to host api.sandbox.paypal.com left intact
{"error":"error_in_reading_cert","error_description":"Unable to read x509 certificate"}

【问题讨论】:

    标签: php laravel curl paypal


    【解决方案1】:

    在 client:secret 上使用 base64。

    如果你有:

    clientId: "clientId"
    secret: "mySecret"
    

    比 base64("clientId:mySecret")

    所以正确的标题是

    Authorization: Basic Y2xpZW50SWQ6bXlTZWNyZXQ=
    

    【讨论】:

      【解决方案2】:

      问题在于使用了错误的授权标头,如下所示:

      Authorization: Basic <client-id>:<secret>
      

      即使由于某种原因它在文档中也不起作用,请改用这个:

      Authorization: Bearer <access-token>
      

      感谢 Preston PHX 帮助我解决这个问题。

      【讨论】:

      • 我怎样才能得到&lt;access-token&gt;
      • 我找到了this。但我不明白-uclientId:secret 组合附近是什么意思。
      • 但是-u 在http 请求中是什么意思呢?因为,我不会使用curl(我什至不知道这是什么)。我将使用 ASP.NET。
      • @YaroslavTrofimov 如我发送的链接中所述 - 这是用于身份验证的用户名和密码。在将其实现为 HTTP 请求方面 - 我认为它只是一个标头,但我不是 100% 确定。
      • 如果您使用基本身份验证,则必须通过 base64encode(:) 而不是文档中所述的普通 client_id:secret:id
      【解决方案3】:

      您可以从 https://curl.haxx.se/docs/caextract.html 下载更新的证书颁发机构包

      在其他可能的配置位置中,可以通过将其添加到 curl 选项来传递该 .pem 文件:

      curl_setopt($connection, CURLOPT_CAINFO, '/path/to/cacert.pem');
      

      或者因为您有一系列选项:

      CURLOPT_CAINFO => '/path/to/cacert.pem',
      

      PayPal 不在沙盒中使用自签名证书,但由于某些环境没有配置适当的证书颁发机构,因此在命令行示例中使用了 curl -k(不安全/无验证对等),因为不需要在开发过程中验证对等证书。

      【讨论】:

      • 我尝试通过添加以下行 $options[CURLOPT_SSL_VERIFYPEER] = false; 来禁用对等证书验证,但我仍然遇到相同的错误。有什么想法吗?
      • 我也尝试添加curl_setopt($connection, CURLOPT_CAINFO, '/path/to/cacert.pem');,但没有帮助
      • 这台网络服务器有多久了?检查您的 curl 和 SSL 库版本(通常是 openssl 或 NSS)检查 phpinfo() ...您需要支持 TLSv1.2 的东西
      • Curl 是 7.58.0,openssl 是 1.1.1d
      • 这可能是你的问题。首先专注于获取 oauth2 访问令牌:developer.paypal.com/docs/api/overview/#get-an-access-token 然后,一旦工作正常,将其生成的 access_token 用于您要实现的 API 调用。
      【解决方案4】:

      是的,clientid 和 secret 必须是 base64 编码。

      技巧:curl 在使用“-u”标志时会为您执行此操作。所以你可以用curl ... -u "clientid:secret"代替curl ... -H "Authorization: Basic ${base64_encoded_creds}"

      【讨论】:

        猜你喜欢
        • 2013-05-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-12
        • 1970-01-01
        • 2020-06-27
        • 2019-02-27
        • 1970-01-01
        相关资源
        最近更新 更多