エラーメッセージ
Peer certificate subjectAltName did not match expected name `api.example.com'
別の表記
- Warning: file_get_contents(): Peer certificate subjectAltName did not match expected name `api.example.com'
PHP は、想定する名前の前にバッククォート、後ろにアポストロフィを付けて示します。SAN 拡張のない証明書では、代わりに「Peer certificate CN=`…' did not match expected CN=`…'」が出力されます。ext-curl ベースのクライアント(Guzzle)は、同じ状況を cURL のエラー 60 として、「no alternative certificate subject name matches target hostname」というメッセージとともに報告します。
PHP streams (openssl stream wrapper) — verify_peer_name check
このエラーの意味
証明書チェーンの検証には成功しましたが、証明書のサブジェクト代替名(Subject Alternative Name)のどれも、PHP が想定する名前(URL のホスト、または peer_name コンテキストオプション)と一致しません。PHP のストリームラッパーは、ハンドシェイクの後にこのチェックを独自に行って接続を拒否し、その後に「Failed to enable crypto」が続きます。
発生する理由
証明書にホスト名が含まれていない
よくあるURL で、証明書の発行対象ではない名前が使われています。
IP アドレスや内部の別名で接続している
よくあるIP アドレスで設定されたデータベース、LDAP、SMTP の接続でよく見られます。
別の名前で SMTP リレーに接続している
ときどきメール送信ライブラリに「localhost」やプロバイダーの別名を設定しているのに、サーバーは自身の公開名の証明書を提示している場合です。
修正方法
- 1.
SAN のリストとホストを比較する
メッセージには想定されるホストが示されています。証明書の SAN を見れば、どの名前が対象になっているかがわかります。
bash echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null \ | openssl x509 -noout -ext subjectAltName - 2.
アドレスで接続するときは peer_name を設定する
ssl コンテキストオプションの peer_name を使うと、ソケットは IP アドレスに接続したまま、どの名前を検証するかを PHP に指定できます。
php // Connect to an address but verify the certificate's real name $context = stream_context_create(['ssl' => ['peer_name' => 'mail.example.com']]); $socket = stream_socket_client('tls://10.0.0.25:465', $errno, $errstr, 10, STREAM_CLIENT_CONNECT, $context); - 3.
DSN には証明書のホスト名を使う
メール送信ライブラリやクライアントには、サーバーの証明書に記載されている名前を設定してください。
text # Mail libraries (PHPMailer, Symfony Mailer): use the SMTP host name from the certificate MAILER_DSN=smtp://user:pass@mail.example.com:587
関連するエラー
SSL operation failed with code 1. OpenSSL Error messages:PHP streams (file_get_contents, fopen, stream_socket_client, SoapClient, mail libraries)curl: (60) SSL: no alternative certificate subject name matches target hostname…curl (OpenSSL backend)x509: certificate is valid for example.com, not api.example.comGo crypto/x509