PHPからSSL通信しようとしたところ、SSL operation failed with code 40. OpenSSL Error messages: not in error state
というエラーで失敗するケースがありました。
環境
とある共有レンタルサーバ群の数台。
PHPも5.2.5とかなり古いものです。
ソースコード
<?php
echo file_get_contents("https://example.herokuapp.com/");
実行結果
Warning: file_get_contents() [function.file-get-contents]: SSL operation failed with code 40. OpenSSL Error messages: not in error state in /****/****/ssl.php on line 2
Warning: file_get_contents() [function.file-get-contents]: Failed to enable crypto in /****/****/ssl.php on line 2
Warning: file_get_contents(https://example.herokuapp.com/) [function.file-get-contents]: failed to open stream: Bad file descriptor in /****/****/ssl.php on line 2
該当サーバでの状況確認
問題切り分けのために該当サーバのコンソールより、wgetやcurlコマンドの実行結果を確認しました。
# wgetコマンドを実行すると次のエラーが発生。
## 但し --no-check-certificate 引数を付ければ通信出来ます。
$ wget https://example.herokuapp.com/
--2015-01-07 15:52:51-- https://example.herokuapp.com/
Resolving example.herokuapp.com... 54.225.214.208
Connecting to example.herokuapp.com|54.225.214.208|:443... connected.
ERROR: The certificate of ‘example.herokuapp.com’ is not trusted.
ERROR: The certificate of ‘example.herokuapp.com’ hasn't got a known issuer.
# curlコマンドを実行すると次のエラーが発生。
## 但し -k 引数を付ければ通信出来ます。
$ curl https://example.herokuapp.com/
curl: (60) SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). The default
bundle is named curl-ca-bundle.crt; you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
# opensslのバージョンは次の通りです
$ openssl
OpenSSL> version
OpenSSL 0.9.8za 5 Jun 2014
OpenSSL> quit
最新のルート証明書ファイル(CA)を入手
本来であればサーバの証明書ファイルをopensshのアップデート等で実施すべきです。
しかし共有レンタルサーバなどでそれが困難な場合は、任意のディレクトリに証明書ファイルを展開します。
それでは、次を参考にCA fileをユーザディレクトリに展開してそれを用いてSSL通信してみましょう。
ref. http://takuya-1st.hatenablog.jp/entry/20110306/1299436282
例に従って、まずはcacert.prmを分割します。
# 任意の証明書ディレクトリを作ります
$ mkdir certs
$ cd certs
# pemファイルをダウンロードします
$ wget http://curl.haxx.se/ca/cacert.pem
# ディレクトリ指定して動作するwget向けに、分割します
$ curl http://curl.haxx.se/ca/cacert.pem \
| awk 'split_after==1{n++;split_after=0}\
/-----END CERTIFICATE-----/ {split_after=1} {print > "cert" n ".pem"}'
# 次のファイルが出来ました
$ ls certs/
cert100.pem cert115.pem cert12.pem cert144.pem cert20.pem cert35.pem cert4.pem cert64.pem cert79.pem cert93.pem
cert101.pem cert116.pem cert130.pem cert145.pem cert21.pem cert36.pem cert50.pem cert65.pem cert7.pem cert94.pem
cert102.pem cert117.pem cert131.pem cert146.pem cert22.pem cert37.pem cert51.pem cert66.pem cert80.pem cert95.pem
cert103.pem cert118.pem cert132.pem cert147.pem cert23.pem cert38.pem cert52.pem cert67.pem cert81.pem cert96.pem
cert104.pem cert119.pem cert133.pem cert148.pem cert24.pem cert39.pem cert53.pem cert68.pem cert82.pem cert97.pem
cert105.pem cert11.pem cert134.pem cert149.pem cert25.pem cert3.pem cert54.pem cert69.pem cert83.pem cert98.pem
cert106.pem cert120.pem cert135.pem cert14.pem cert26.pem cert40.pem cert55.pem cert6.pem cert84.pem cert99.pem
cert107.pem cert121.pem cert136.pem cert150.pem cert27.pem cert41.pem cert56.pem cert70.pem cert85.pem cert9.pem
cert108.pem cert122.pem cert137.pem cert151.pem cert28.pem cert42.pem cert57.pem cert71.pem cert86.pem cert.pem
cert109.pem cert123.pem cert138.pem cert152.pem cert29.pem cert43.pem cert58.pem cert72.pem cert87.pem
cert10.pem cert124.pem cert139.pem cert15.pem cert2.pem cert44.pem cert59.pem cert73.pem cert88.pem
cert110.pem cert125.pem cert13.pem cert16.pem cert30.pem cert45.pem cert5.pem cert74.pem cert89.pem
cert111.pem cert126.pem cert140.pem cert17.pem cert31.pem cert46.pem cert60.pem cert75.pem cert8.pem
cert112.pem cert127.pem cert141.pem cert18.pem cert32.pem cert47.pem cert61.pem cert76.pem cert90.pem
cert113.pem cert128.pem cert142.pem cert19.pem cert33.pem cert48.pem cert62.pem cert77.pem cert91.pem
cert114.pem cert129.pem cert143.pem cert1.pem cert34.pem cert49.pem cert63.pem cert78.pem cert92.pem
その後に新しく用意したCAファイルを参照してリクエストを発行してみましょう。
先ほど失敗していたコマンドが実行できるようになっているはずです。
# wgetは期待通り動きます
$ wget --ca-directory=/path/to/certs https://example.herokuapp.com/
# cacertを指定します。-k や --insecure オプションを使っても通信出来ます。
$ curl --cacert /path/to/certs/cacert.pem https://www.google.co.jp/
$ curl -V
curl 7.16.3 (i686-pc-linux-gnu) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Protocols: tftp ftp telnet dict ldap http file https ftps
Features: Largefile NTLM SSL libz
PHPで最新のルート証明書ファイル(CA)を使う方法
次のページを参考に試してみます。
ref. http://www.docnet.nu/tech-portal/2014/06/26/ssl-and-php-streams-part-1-you-are-doing-it-wrongtm/C0
<?php
// Change this to point to your newly downloaded CA file
$caFile = '/path/to/certs/cacert.pem';
// Create a stream context to pass the relevant SSL information
$context = stream_context_create(array('ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => false,
'cafile' => $caFile,
)));
echo file_get_contents("https://example.herokuapp.com/", false, $context);
しかし次の結果となり、cafile, capath共に上手く行きませんでした。・・・誰か教えてください ><
Warning: file_get_contents() [function.file-get-contents]: SSL operation failed with code 40. OpenSSL Error messages: not in error state in /path/to/ssl.php on line 14
Warning: file_get_contents() [function.file-get-contents]: Failed to enable crypto in /path/to/ssl.php on line 14
Warning: file_get_contents(https://example.herokuapp.com/) [function.file-get-contents]: failed to open stream: Bad file descriptor in /path/to/ssl.php on line 14
なお、curlが使える環境なら回避策がまだあるかもしれません。
ルート証明書ファイルの有効期限を確認する方法
任意のルート証明書ファイルの中身を確認するには次のコマンドを利用します。
$ openssl x509 -text -noout -in 確認対象ファイル.pem
指定ディレクトリ以下のファイルを対象に一括で確認する方法は次の通りです。
$ find /etc/ssl/certs/*.pem -type f -exec echo "--------------------" \; -exec echo {} \; -exec bash -c "openssl x509 -text -noout -in {} | head" \;
このコマンドを、問題の共有レンタルサーバで実行しました。
かなり困る状態ですね。
--------------------
/etc/ssl/certs/eng1.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 0 (0x0)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=CA, ST=ON, L=Toronto, O=BankEngine Inc., OU=Certification Authority Division, CN=bankengine/emailAddress=ca@bankengine.com
Validity
Not Before: Jan 1 00:00:00 1998 GMT
Not After : Jan 17 00:00:00 2038 GMT
Subject: C=CA, ST=ON, L=Toronto, O=BankEngine Inc., OU=Certification Authority Division, CN=bankengine/emailAddress=ca@bankengine.com
--------------------
/etc/ssl/certs/eng2.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 0 (0x0)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=CA, ST=ON, L=Toronto, O=CertEngine Inc., OU=Certification Authority Division, CN=certengine/emailAddress=ca@certengine.com
Validity
Not Before: Jan 1 00:00:00 1998 GMT
Not After : Jan 17 00:00:00 2038 GMT
Subject: C=CA, ST=ON, L=Toronto, O=CertEngine Inc., OU=Certification Authority Division, CN=certengine/emailAddress=ca@certengine.com
--------------------
/etc/ssl/certs/eng3.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 0 (0x0)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=CA, ST=ON, L=Toronto, O=FortEngine Inc., OU=Certification Authority Division, CN=fortengine/emailAddress=ca@fortengine.com
Validity
Not Before: Jan 1 00:00:00 1998 GMT
Not After : Jan 17 00:00:00 2038 GMT
Subject: C=CA, ST=ON, L=Toronto, O=FortEngine Inc., OU=Certification Authority Division, CN=fortengine/emailAddress=ca@fortengine.com
--------------------
/etc/ssl/certs/eng4.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 0 (0x0)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=CA, ST=ON, L=Toronto, O=MailEngine Inc., OU=Certification Authority Division, CN=mailengine/emailAddress=ca@mailengine.com
Validity
Not Before: Jan 1 00:00:00 1998 GMT
Not After : Jan 17 00:00:00 2038 GMT
Subject: C=CA, ST=ON, L=Toronto, O=MailEngine Inc., OU=Certification Authority Division, CN=mailengine/emailAddress=ca@mailengine.com
--------------------
/etc/ssl/certs/eng5.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 0 (0x0)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=CA, ST=ON, L=Toronto, O=TraderEngine Inc., OU=Certification Authority Division, CN=traderengine/emailAddress=ca@traderengine.com
Validity
Not Before: Jan 1 00:00:00 1998 GMT
Not After : Jan 17 00:00:00 2038 GMT
Subject: C=CA, ST=ON, L=Toronto, O=TraderEngine Inc., OU=Certification Authority Division, CN=traderengine/emailAddress=ca@traderengine.com
--------------------
/etc/ssl/certs/Equifax-root1.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 903804111 (0x35def4cf)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
Validity
Not Before: Aug 22 16:41:51 1998 GMT
Not After : Aug 22 16:41:51 2018 GMT
Subject: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
--------------------
/etc/ssl/certs/ICP-Brasil.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4 (0x4)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=BR, O=ICP-Brasil, OU=Instituto Nacional de Tecnologia da Informacao - ITI, L=Brasilia, ST=DF, CN=Autoridade Certificadora Raiz Brasileira
Validity
Not Before: Nov 30 12:58:00 2001 GMT
Not After : Nov 30 23:59:00 2011 GMT
Subject: C=BR, O=ICP-Brasil, OU=Instituto Nacional de Tecnologia da Informacao - ITI, L=Brasilia, ST=DF, CN=Autoridade Certificadora Raiz Brasileira
--------------------
/etc/ssl/certs/RegTP-5R.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 820138 (0xc83aa)
Signature Algorithm: ripemd160WithRSA
Issuer: C=DE, O=Regulierungsbeh\xC8orde f\xC8ur Telekommunikation und Post/0.2.262.1.10.7.20=1, CN=5R-CA 1:PN
Validity
Not Before: Mar 22 08:55:51 2000 GMT
Not After : Mar 22 08:55:51 2005 GMT
Subject: C=DE, O=Regulierungsbeh\xC8orde f\xC8ur Telekommunikation und Post/0.2.262.1.10.7.20=1, CN=5R-CA 1:PN
--------------------
/etc/ssl/certs/RegTP-6R.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 3330445 (0x32d18d)
Signature Algorithm: ripemd160WithRSA
Issuer: C=DE, O=Regulierungsbeh\xC8orde f\xC8ur Telekommunikation und Post/0.2.262.1.10.7.20=1, CN=6R-Ca 1:PN
Validity
Not Before: Feb 1 09:52:17 2001 GMT
Not After : Jun 1 09:52:17 2005 GMT
Subject: C=DE, O=Regulierungsbeh\xC8orde f\xC8ur Telekommunikation und Post/0.2.262.1.10.7.20=1, CN=6R-Ca 1:PN
--------------------
/etc/ssl/certs/thawteCb.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: md5WithRSAEncryption
Issuer: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Server CA/emailAddress=server-certs@thawte.com
Validity
Not Before: Aug 1 00:00:00 1996 GMT
Not After : Dec 31 23:59:59 2020 GMT
Subject: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Server CA/emailAddress=server-certs@thawte.com
--------------------
/etc/ssl/certs/thawteCp.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: md5WithRSAEncryption
Issuer: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com
Validity
Not Before: Aug 1 00:00:00 1996 GMT
Not After : Dec 31 23:59:59 2020 GMT
Subject: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com
--------------------
/etc/ssl/certs/vsign1.pem
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
32:50:33:cf:50:d1:56:f3:5c:81:ad:65:5c:4f:c8:25
Signature Algorithm: md2WithRSAEncryption
Issuer: C=US, O=VeriSign, Inc., OU=Class 1 Public Primary Certification Authority
Validity
Not Before: Jan 29 00:00:00 1996 GMT
Not After : Jan 7 23:59:59 2020 GMT
--------------------
/etc/ssl/certs/vsign3.pem
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
70:ba:e4:1d:10:d9:29:34:b6:38:ca:7b:03:cc:ba:bf
Signature Algorithm: md2WithRSAEncryption
Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
Validity
Not Before: Jan 29 00:00:00 1996 GMT
Not After : Aug 1 23:59:59 2028 GMT
--------------------
/etc/ssl/certs/vsignss.pem
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
02:ad:66:7e:4e:45:fe:5e:57:6f:3c:98:19:5e:dd:c0
Signature Algorithm: md2WithRSAEncryption
Issuer: C=US, O=RSA Data Security, Inc., OU=Secure Server Certification Authority
Validity
Not Before: Nov 9 00:00:00 1994 GMT
Not After : Jan 7 23:59:59 2010 GMT
--------------------
/etc/ssl/certs/wellsfgo.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 971282334 (0x39e4979e)
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, O=Wells Fargo, OU=Wells Fargo Certification Authority, CN=Wells Fargo Root Certificate Authority
Validity
Not Before: Oct 11 16:41:28 2000 GMT
Not After : Jan 14 16:41:28 2021 GMT
Subject: C=US, O=Wells Fargo, OU=Wells Fargo Certification Authority, CN=Wells Fargo Root Certificate Authority
併せて読みたい
-
[PHP][cURL] cURLでSSL(https)のCA証明書警告の回避や設定
http://mio-koduki.blogspot.jp/2012/08/php-curlsslhttpsca.html -
サーバのSSL CA(認証局)証明書が古くてcurl がエラーになる件
http://d.hatena.ne.jp/hogem/20120705/1340284071