1. ホーム
  2. php

[解決済み] SSLエラー SSL3_GET_SERVER_CERTIFICATE:証明書の検証に失敗しました。

2022-01-29 11:16:19

質問

PHP5.6にアップグレードした後、サーバーに接続しようとするとエラーが発生します。 fsockopen() ..

サーバー(ホスト)の証明書は自己署名されている

PHP Warning: fsockopen(): SSL 操作はコード 1 で失敗しました。 OpenSSL エラーメッセージ。 error: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 Warning: stream_socket_client(): SSL 操作はコード 1 で失敗しました。OpenSSL のエラーメッセージです。 error:14090086:SSLルーチン:SSL3_GET_SERVER_CERTIFICATE:証明書の検証に失敗しました。

解決方法は?

ダウンロードしたファイル( 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証明書を、接続元のサーバー上の信頼できるストアに追加するか、ストリームコンテキストを使用して、個々のリクエストにその証明書を使用する必要があります。信頼できる証明書に追加することは、最も簡単な解決策です。ダウンロードした cacert.pem ファイルの末尾にリモートホストの CA 証明書の内容を追加するだけです。

前の記事

fsockopenはストリームコンテキストをサポートしていないので、代わりにstream_socket_clientを使用します。これは、fsockopenのリソースが使用できるすべてのコマンドで使用できるリソースを返します。

これは、あなたの質問にあるスニペットの置き換えになるはずです。

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

}