はじめに
目的
本章では、本記事の狙いと読み方を簡潔に説明します。対象は、OpenSSLを使っている開発者や、通信の証明書を扱うエンジニアです。SSL_get_peer_certificateに関する仕様や実装上の注意点を、実務で使える形で学べます。
背景と重要性
SSL/TLSでは、通信相手の証明書を確認することが安全な接続の基本です。例えば、ウェブクライアントがサーバーの証明書を取得して検証する場面で、この種の関数を使います。本記事は、その流れと具体的な取り扱い方を丁寧に解説します。
この記事で学べること
- SSL_get_peer_certificateの役割と使い方の概観
- OpenSSL 3.0以降の変更点と代替手段の概要
- 証明書取得時の検証とメモリ管理の注意点
- 実装例やエラー対策、関連APIの参照先
前提知識
基本的なC言語の理解と、SSL/TLSの基礎用語(証明書、検証)の知識があると読みやすくなります。専門用語は必要な箇所で丁寧に補足します。
SSL_get_peer_certificateとは
概要
SSL_get_peer_certificateは、OpenSSLなどのSSL/TLSライブラリで、通信相手がハンドシェイク中に提示した証明書(X509形式)をプログラムから取得するための関数です。TLS接続確立後に相手の証明書を確認・検証したいときに使います。
返り値とメモリ管理
返り値はX509*(証明書へのポインタ)で、相手が証明書を提示していない場合はNULLを返します。取得後はライブラリの仕様により参照カウントが増えるため、不要になったら必ずX509_free()で解放してください。
いつ呼ぶか
TLS/SSLハンドシェイクが完了した直後に呼びます。ハンドシェイク前に呼んでも意味がありません。サーバ証明書だけでなく、相互認証(クライアント証明書)時にも同様に使えます。
簡単な使い方(例)
X509 cert = SSL_get_peer_certificate(ssl);
if (cert) {
/ 表示・検証処理 /
X509_free(cert);
} else {
/ 証明書がない場合の処理 */
}
注意点
証明書チェーン全体や検証ステータスは別APIで取得する必要があります。戻り値のNULLと検証失敗は別事象ですので、両方を確認してください。
OpenSSL 3.0以降の変更点・非推奨化
概要
OpenSSL 3.0から、従来のSSL_get_peer_certificateは非推奨になりました。代わりにSSL_get1_peer_certificateとSSL_get0_peer_certificateが導入され、用途に応じて使い分けます。
両関数の違い(簡潔に)
- SSL_get1_peer_certificate: 参照カウントが+1されます。取得後はX509_freeで解放する必要があります。証明書をSSLオブジェクトの寿命を超えて保持したい場合に使います。
- SSL_get0_peer_certificate: 参照カウントを増やしません。SSLオブジェクトが有効な間だけ参照可能で、解放してはいけません。手元で一時的に検査するだけならこちらが簡潔です。
実装上の注意
ライブラリ(QtやPython拡張など)でビルドエラーを避けるには、OpenSSLのバージョンに応じてAPIを切り替えます。例:
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
cert = SSL_get1_peer_certificate(ssl);
#else
cert = SSL_get_peer_certificate(ssl); // 古いAPI
#endif
推奨方針
- 証明書を保持して後で使うならSSL_get1_peer_certificateを使い、必ずX509_freeで解放してください。
- 一時的な検査のみならSSL_get0_peer_certificateを使い、SSLが生存する範囲で参照してください。
これらを守ると、メモリリークや二重解放といった問題を防げます。
証明書取得時の注意点・検証方法
概要
取得した証明書がそのまま正当とは限りません。アプリ側で検証を必ず行ってください。一般的にはSSL_get_verify_resultなどで検証結果を確認します。
証明書検証の手順(実務向け)
- 接続確立後に証明書を取得
- SSL_get_verify_resultで検証ステータスを確認
- 必要に応じて証明書のフィンガープリントや有効期限を確認
例:ピンニングする場合はサーバ証明書のハッシュを比較します。
クライアント証明書の扱い
クライアント証明書はサーバが要求した場面でのみ送信します。匿名暗号化(サーバだけの証明書で済む場合)では送られません。
証明書チェーンの取得と注意点
- SSL_get_peer_cert_chainは未検証のチェーンを返します。検証前の情報として参照してください。
- 検証済みチェーンが必要な場合はSSL_get0_verified_chainを使います。
実装上の注意
検証は接続後すぐ行い、検証失敗時は接続を切断してください。証明書データのメモリ管理にも注意し、コピーや参照のルールを守ってください。
よくある落とし穴
自己署名証明書や期限切れ、ホスト名不一致の検知漏れです。自動で信頼しない設計にしてください。
ローカル証明書の取得・用途
概要
サーバ側などで、自分自身(ローカル)に設定された証明書を確認したいときに使います。SSL_get_peer_certificateが相手の証明書取得用であるのに対し、ローカル証明書はSSL_get_certificateで参照できます。
取得方法(使い方のイメージ)
関数にSSLコンテキスト(ssl)を渡して呼び出します。返された証明書オブジェクトから主題(subject)や発行者(issuer)、有効期限などを取得して確認できます。例えばSNIや証明書選択のコールバック内で呼ぶと、実際に選ばれた証明書を検査できます。
主な用途
- コールバック内で選択済み証明書をログに残す
- SNIに基づく証明書選択の確認
- デバッグや運用時の状態確認(subject、サブジェクト代替名、期限)
- 必要なら証明書チェーンや鍵との対応をチェック
注意点
内部のポインタを直接開放しないでください。証明書を長期保持する必要がある場面では、参照カウントを増やす(X509_up_ref)か複製(X509_dup)してから使います。また、APIやOpenSSLのバージョン差に注意して呼び出し方を確認してください。
エラー例・他ライブラリとの連携
よくあるエラー例
- ビルド時の「undefined reference to SSL_get_peer_certificate」や「undefined symbol」。
- 実行時にライブラリ読み込みエラー(QtアプリでOpenSSLの別バージョンが使われる等)。
これらはOpenSSLのバージョン差や非推奨APIの影響で発生します。たとえばSSL_get_peer_certificateがOpenSSL 3.0以降で取り扱いが変わり、リンク時に存在しないとエラーになります。
対策(基本)
- コンパイル時にOPENSSL_VERSION_NUMBERやpkg-configでバージョン判定し、条件付きでSSL_get1_peer_certificateへ切替えます。
- 動的にリンクする場合は実行時に正しいlibssl.soを読み込むようパスを調整します。
ライブラリ別のポイント
- Qt: -openssl-linkedでビルドするか、実行環境のOpenSSLを揃えます。バンドルする方法も有効です。
- Python: cryptographyやpyOpenSSLはバイナリホイールとシステムOpenSSLの不整合で問題が出ます。ホイールを使う、または仮想環境で明示的にOpenSSLを揃えます。
代替手段と注意点
- dlsymで関数の有無を確認してフォールバック実装を用意できます。メモリ管理は注意し、SSL_get1_peer_certificateは参照を増やすためX509_freeで解放してください。
実装例とメモリ管理
実装例(C言語)
以下はSSL_get1_peer_certificateで証明書を取得し、利用後に必ずX509_freeで解放する最小例です。
X509 *cert = SSL_get1_peer_certificate(ssl);
if (cert == NULL) {
/* 相手が証明書を送っていない */
} else {
char *subj = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
if (subj) {
printf("Subject: %s\n", subj);
OPENSSL_free(subj);
}
/* 証明書利用処理 */
X509_free(cert); /* 必ず解放 */
}
メモリ管理上の注意点
- SSL_get1_peer_certificateは新しい参照を返します。受け取ったら自分でX509_freeする義務があります。
- X509_get_subject_nameは内部ポインタを返します。直接freeしてはいけません。
- X509_NAME_onelineなどが返す文字列はOPENSSL_freeで解放します。
よくあるミスと対処法
- 解放忘れ:メモリリークになります。早めにfreeを入れてください。
- 二重解放:同じX509を2回X509_freeしないでください。必要ならSSL_get1_peer_certificateで再取得します。
C++での扱い方
unique_ptr cert_guard(cert, X509_free);
とすればスコープで自動解放できます。
関連API・公式ドキュメント
公式ドキュメントの参照先
- OpenSSL公式マニュアル(manページ): 各APIの詳細と使用例が載っています。例: man SSL_CTX_set_verify、man SSL_get_verify_result。
- OpenSSLのソースとリリースノート: 挙動の変更や非推奨情報を確認できます。
主な関連API
- SSL_CTX_set_verify(ctx, mode, callback)
- 証明書検証のポリシーを設定します(例: SSL_VERIFY_PEER)。
- SSL_get_verify_result(ssl)
- 検証結果を取得します。成功はX509_V_OKで判定します。
- SSL_get_peer_cert_chain(ssl)
- サーバ側が送った証明書チェーンを取得します。クライアント証明書と混同しないよう注意してください。
- SSL_get_peer_certificate / SSL_get0_peer_certificates
- OpenSSLのバージョンで挙動が変わります。所有権や参照カウントに注意して扱います。
- X509_free, X509_up_ref
- 証明書オブジェクトのメモリ管理に使います。
使い分けのポイント
- チェーン全体が必要ならSSL_get_peer_cert_chainを使います。
- 単一の証明書だけ必要なら適切な取得APIを確認し、メモリ所有権を明確にします。
ドキュメント参照のコツ
- API名でmanページを検索し、バージョン(OpenSSL 1.1/3.0)を必ず合わせて確認してください。
- 実装例は公式のappsディレクトリやテストコードが参考になります。
例(概念)
- 証明書検証後にSSL_get_verify_resultで結果を確認し、必要ならX509_freeで解放します。
公式ドキュメントを最初に確認する習慣をつけると、安全で互換性のある実装がしやすくなります。












