preface

A colleague encountered the following error while doing local development:

GuzzleHttp Exception cURL error 60: SSL certificate problem: unable to get local
Copy the code

Development environment:

  • PHP7.2.1
  • Win7
  • phpstudy

I finally found a solution on the Internet, but I don’t know why.

The problem

GuzzleHttp defaults verify = true during initial configuration.

// FILE: guzzlehttp/guzzle/src/Client.php
/**
 * Configures the default options for a client.
 *
 * @param array $config
 * @return void
 */
private function configureDefaults(array $config)
{
    /** * Verify is set to true by default
    $defaults = [
        'allow_redirects' => RedirectMiddleware::$defaultSettings.'http_errors'= >true.'decode_content'= >true.'verify'= >true.'cookies'= >false.'idn_conversion'= >true,]; . }Copy the code

In the following curl configuration, the CURLOPT_SSL_VERIFYHOST and CURLOPT_SSL_VERIFYPEER variables are initialized according to verify.

// FILE: guzzlehttp/guzzle/src/Handler/CurlFactory.php
private function applyHandlerOptions(EasyHandle $easy.array &$conf)
{
    $options = $easy->options;
    if (isset($options['verify']) {if ($options['verify'= = =false) {
            unset($conf[CURLOPT_CAINFO]);
            $conf[CURLOPT_SSL_VERIFYHOST] = 0;
            $conf[CURLOPT_SSL_VERIFYPEER] = false;
        } else {
            $conf[CURLOPT_SSL_VERIFYHOST] = 2;
            $conf[CURLOPT_SSL_VERIFYPEER] = true;
            if (is_string($options['verify']) {// Throw an error if the file/folder/link path is not valid or doesn't exist.
                if(! file_exists($options['verify']) {throw new \InvalidArgumentException(
                        "SSL CA bundle not found: {$options['verify']}"
                    );
                }
                // If it's a directory or a link to a directory use CURLOPT_CAPATH.
                // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO.
                if (is_dir($options['verify']) ||
                    (is_link($options['verify']) && is_dir(readlink($options['verify')))) {$conf[CURLOPT_CAPATH] = $options['verify'];
                } else {
                    $conf[CURLOPT_CAINFO] = $options['verify']; }}}}... }Copy the code

Since CURLOPT_SSL_VERIFYPEER = true and CURLOPT_SSL_VERIFYHOST > 0, the SSL certificate needs to be verified.

The following explanation comes from:portal

CA packages are not present on all system disks; for example, Windows and OS X do not have a common local CA package. When verify is set to true, Guzzle will try to find the appropriate CA packages on your operating system. When using cURL or streams above PHP 5.6, Guzzle will try to find CA packages in the following order:

  • Check if it is set in the php.ini fileopenssl.cafile.
  • Check if it is set in the php.ini filecurl.cainfo.
  • check/etc/pki/tls/certs/ca-bundle.crtDoes it exist (Red Hat.CentOS.Fedora; byca-certificatesPackage provides)
  • check/etc/ssl/certs/ca-certificates.crtDoes it exist (Ubuntu.Debian; byca-certificatesPackage provides)
  • check/usr/local/share/certs/ca-root-nss.crtDoes it exist (FreeBSD; byca_root_nssPackage provides)
  • check/usr/local/etc/openssl/cert.pemWhether there is (OS X; byhomebrewProvide)
  • checkC:\windows\system32\curl-ca-bundle.crtDoes it exist (Windows)
  • checkC: Windows \curl-ca-bundle. CRT exists (Windows)

The results of the query are cached in memory for quick subsequent calls by the same process. However, on some servers such as Apache where each request is in a separate process, you should consider setting the openSSL.cafile environment variable to specify a disk file so that the whole process can be skipped.

The specific code is as follows:

/**
 * Returns the default cacert bundle for the current system.
 *
 * First, the openssl.cafile and curl.cainfo php.ini settings are checked.
 * If those settings are not configured, then the common locations for
 * bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X
 * and Windows are checked. If any of these file locations are found on
 * disk, they will be utilized.
 *
 * Note: the result of this function is cached for subsequent calls.
 *
 * @return string
 * @throws \RuntimeException if no bundle can be found.
 */
function default_ca_bundle()
{
    static $cached = null;
    static $cafiles = [
        // Red Hat, CentOS, Fedora (provided by the ca-certificates package)
        '/etc/pki/tls/certs/ca-bundle.crt'.// Ubuntu, Debian (provided by the ca-certificates package)
        '/etc/ssl/certs/ca-certificates.crt'.// FreeBSD (provided by the ca_root_nss package)
        '/usr/local/share/certs/ca-root-nss.crt'.// SLES 12 (provided by the ca-certificates package)
        '/var/lib/ca-certificates/ca-bundle.pem'.// OS X provided by homebrew (using the default path)
        '/usr/local/etc/openssl/cert.pem'.// Google app engine
        '/etc/ca-certificates.crt'.// Windows?
        'C:\\windows\\system32\\curl-ca-bundle.crt'.'C:\\windows\\curl-ca-bundle.crt',];if ($cached) {
        return $cached;
    }

    if ($ca = ini_get('openssl.cafile')) {
        return $cached = $ca;
    }

    if ($ca = ini_get('curl.cainfo')) {
        return $cached = $ca;
    }

    foreach ($cafiles as $filename) {
        if (file_exists($filename)) {
            return $cached = $filename; }}throw new \RuntimeException(
        <<< EOT No system CA bundle could be found in any of the common system location.php versions earlier than 5.6 are not properly configured to use the system's CA bundle by default. In order to verify peer certificates, you will need to supply the path on disk to a certificate bundle to the 'verify' request option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not need a specific certificate bundle, then Mozilla provides a commonly used CA bundle which can be downloaded here (provided by the maintainer of cURL): https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP ini setting to point to the path to the file, allowing you to omit the 'verify' request option. See http://curl.haxx.se/docs/sslcerts.html for more information. EOT
    );
}
Copy the code

conclusion

In cases where there is no requirement to check the certificate and the GuzzleHttp package is directly referenced, verify = false can be specified at initialization. Instead, keep the certificate where it belongs.