macOS と iOS でクライアント証明書を使用して mTLS Websitesにアクセスする

本文では、macOS および iOS デバイスでクライアント証明書を設定し、双方向 TLS(mTLS)を有効にした HTTPS ウェブサイトにアクセスする方法を説明します。例では、プライベート CA、step-ca、Nginx、PKCS#12、および PIV スマートカードを使用します。以下のドメイン名、証明書名、ファイル名、IP アドレスはすべて例なので、実際の値に置き換えてください。

1. mTLS 認証フロー

通常の HTTPS では主にクライアントがサーバー証明書を検証しますが、mTLS ではさらにサーバーがクライアント証明書を検証します。

基本フロー:

  1. クライアントが HTTPS サイトにアクセスする。
  2. Nginx が TLS ハンドシェイク中にクライアント証明書を要求する。
  3. ブラウザーまたはシステムがクライアント ID を選択し、対応する秘密鍵で署名する。
  4. Nginx が構成に基づいてクライアント証明書、証明書チェーン、有効期限を検証する。
  5. 検証に成功すると接続は HTTP レイヤーに入り、サイト側のポリシーによってアクセスが許可されるかどうかが決定される。

クライアント ID とサーバー CA の信頼は、それぞれ独立した構成です。

  • クライアント ID:クライアント証明書およびそれに対応する秘密鍵。サーバーに対して自身を証明するために使用します。
  • サーバー CA の信頼:クライアントがウェブサイト用サーバー証明書の発行者を信頼する CA。HTTPS サーバーの同一性を検証するために使用します。

クライアント証明書をインストールしても、デバイスがプライベート CA を自動的に信頼するようになるわけではありません。

2. macOS:PIV ハードウェアキーの使用

クライアント秘密鍵が PIV 対応ハードウェアキーに保存されている場合は、秘密鍵をハードウェアデバイス内に保持し、秘密鍵ファイルのエクスポートを回避できます。ブラウザーがこの ID を使用できるかどうかは、macOS、スマートカードミドルウェア、鍵デバイス、およびブラウザーのサポート状況に依存します。

一般的な確認手順:

  1. PIV ハードウェアキーを挿入する。
  2. macOS がスマートカードとその証明書を認識できることを確認する。
  3. ブラウザーを開いて mTLS サイトにアクセスする。
  4. ブラウザーがクライアント証明書を要求する場合は、対応する ID を選択する。
  5. 指示に従って PIN を入力する、またはハードウェア上の操作を完了する。
  6. ページの表示結果とサーバーログを照合し、認証に成功したか確認する。

次のコマンドでシステムの状態を確認できます。

security list-smartcards
sc_auth identities
sudo security smartcards token -l

macOS のバージョン、ハードウェア、ミドルウェアによって出力が異なる場合があります。コマンドに出力がないことだけでは、証明書が無効であることを単独で示すことはできません。

3. macOS:CRT、PEM、KEY のキーチェーンへのインポート

すでにクライアント証明書と対応する秘密鍵ファイルがある場合は、PKCS#12 ファイル(.p12)にパッケージ化し、macOS キーチェーンにインポートできます。

例として用意するファイル:

  • client.crt:クライアント証明書。
  • client.key:証明書に対応する秘密鍵。
  • intermediate-ca.crt:中間 CA 証明書。任意。
  • root-ca.crt:ルート CA 証明書。証明書チェーンの検証またはサーバー CA の信頼設定に使用します。

管理されたディレクトリで作業し、秘密鍵や PKCS#12 ファイルをコードリポジトリにコミットしないようにしてください。

3.1 クライアント証明書の確認

openssl x509 -in client.crt -noout \
  -subject -issuer -serial -dates

証明書の用途を確認します。

openssl x509 -in client.crt -noout -ext extendedKeyUsage

証明書には TLS Web Client Authentication が含まれている必要があり、その用途の OID は 1.3.6.1.5.5.7.3.2 です。

証明書と秘密鍵が一致していることを確認します。

openssl x509 -in client.crt -pubkey -noout |
  openssl pkey -pubin -outform DER |
  shasum -a 256

openssl pkey -in client.key -pubout -outform DER |
  shasum -a 256

2 つのコマンドの出力ダイジェストは一致する必要があります。

中間 CA がある場合は、証明書チェーンを検証できます。

openssl verify \
  -CAfile root-ca.crt \
  -untrusted intermediate-ca.crt \
  client.crt

中間 CA がない場合は、次のコマンドを使用します。

openssl verify -CAfile root-ca.crt client.crt

証明書チェーンに複数の中間 CA がある場合は、完全かつ正しい中間証明書チェーンを提供してください。

3.2 macOS キーチェーン互換の PKCS#12 ファイルの生成

一部の PKCS#12 ファイルは、正しいパスワードを指定しても、暗号化パラメータの互換性問題によってキーチェーンにインポートできないことがあります。互換性のため、従来の PKCS#12 暗号化パラメータを明示的に指定してエクスポートできます。

中間 CA 証明書がある場合:

umask 077

openssl pkcs12 -export \
  -inkey client.key \
  -in client.crt \
  -certfile intermediate-ca.crt \
  -name "Client Certificate" \
  -keypbe PBE-SHA1-3DES \
  -certpbe PBE-SHA1-3DES \
  -macalg sha1 \
  -out client-macos.p12

中間 CA がない場合は、-certfile intermediate-ca.crt の行を削除します。

実行時に空でないエクスポートパスワードを設定してください。調査上の混乱を少ない passwords の文字エンコーディング差異を減らすため、まず英字と数字だけを使用することをお勧めします。

ファイルを検証します。

openssl pkcs12 -info -in client-macos.p12 -noout

先ほど設定したパスワードを入力します。OpenSSL でファイルを読み取れても、キーチェーンがインポートを拒否する場合は、まず PKCS#12 の暗号化パラメータの互換性を確認してください。

ここでの SHA-1 と 3DES は、PKCS#12 ファイルのラップ形式の互換性のためにのみ使用されます。TLS 接続で古いアルゴリズムを有効にすべきという意味ではありません。また、クライアント証明書の公開鍵、秘密鍵、または拡張鍵用途を変更するものではありません。

3.3 キーチェーンへのインポート

キーチェーンアクセスを開きます。

open -a "Keychain Access"

その後、次の操作を行います。

  1. login キーチェーンを選択します。
  2. 「ファイル → 項目をインポート」を選択します。
  3. client-macos.p12 を選択します。
  4. エクスポートパスワードを入力します。
  5. 「マイ証明書」で対応する ID を見つけます。
  6. 証明書を展開し、その下に対応する秘密鍵が存在することを確認します。

コマンドラインからもインポートできます。

security import ./client-macos.p12 \
  -k "$HOME/Library/Keychains/login.keychain-db" \
  -f pkcs12

SSL クライアント認証に使用できる ID を確認します。

security find-identity -v -p ssl-client \
  "$HOME/Library/Keychains/login.keychain-db"

有効な ID が見つからない場合は、証明書と秘密鍵が一致しているか、証明書が有効か、およびクライアント認証用途を備えているかを確認してください。

4. サーバー CA の信頼構成

ウェブサイトがプライベート CA で署名したサーバー証明書を使用する場合、クライアントは正しいルート CA も信頼する必要があります。

まず、ルート証明書の情報とフィンガープリントを確認します。

openssl x509 -in root-ca.crt -noout \
  -subject -issuer -fingerprint -sha256

出所とフィンガープリントを確認してから、ルート CA を対象デバイスの証明書信頼ストアにインポートします。システム全体の信頼が必要する場合は、macOS の System キーチェーンにインストールし、実際の用途に応じて信頼を構成できます。通常は管理者権限が必要です。

クライアント証明書をルート CA として扱ったり、CA の秘密鍵をクライアントデバイスにインストールしたりしないでください。サーバーは、必要な中間証明書を正しく送信する必要があります。

5. curl による mTLS の検証

ブラウザーでテストする前に、curl を使用して証明書、秘密鍵、およびサーバー構成を検証できます。

curl -v \
  --cert client.crt \
  --key client.key \
  --cacert root-ca.crt \
  https://mtls.example.test/

サーバー IP を指定しながら、正しいホスト名と SNI を維持する必要がある場合は、次のコマンドを使用します。

curl -v \
  --resolve mtls.example.test:443:192.0.2.10 \
  --cert client.crt \
  --key client.key \
  --cacert root-ca.crt \
  https://mtls.example.test/

例として挙げた IP アドレス 192.0.2.10 は文書専用のアドレスなので、実際のサーバー IP に置き換えてください。

サーバーがクライアントに中間 CA 証明書の送信を要求する場合は、クライアント証明書と中間証明書を結合できます。

cat client.crt intermediate-ca.crt > client-chain.pem

その後、--cert client.crt を --cert client-chain.pem に置き換えます。

常见結果:

  • HTTP 200:リクエストが成功しました。通常は TLS とアプリケーションリクエストの両方が成功しています。
  • HTTP 401/403:TLS は完了している可能性がありますが、アプリケーション層の認可ポリシーによって拒否されました。
  • certificate required:サーバーはクライアント証明書を要求しましたが、利用可能な証明書が送信されませんでした。
  • unknown ca:サーバーがクライアント証明書の発行 CA を信頼していない可能性があります。
  • certificate verify failed:サーバー証明書のチェーン、ルート CA、およびドメイン名を確認してください。

クライアントの出力と Nginx のログを照合して判断してください。HTTP ステータスコードだけでは、mTLS の各段階が正しいことを完全に証明できません。

6. macOS の Chrome と Safari でのテスト

Chrome

  1. クライアント ID がインストールされ、キーチェーンに一致する秘密鍵が存在することを確認します。
  2. Chrome を完全に終了してから開き直します。
  3. mTLS サイトにアクセスします。
  4. ブラウザーがクライアント証明書を要求する場合は、正しい ID を選択します。
  5. ページの表示結果とサーバーログを確認します。

証明書が表示されない場合は、証明書の用途、秘密鍵の利用可否、およびサーバーが現在の TLS ハンドシェイクでクライアント証明書を要求しているかどうかを確認してください。

Safari

同じ URL を個別にテストします。Safari と Chrome では証明書の選択動作が異なる可能性があるため、一方のブラウザーで成功|other браウザーでも同じ ID が使用されるとは仮定しないでください。

「必要なクライアント証明書が送信されなかった」などのエラーが発生した場合は、ブラウザーが正しい ID を選択して送信したか、システムがその ID を認識しているか、サーバーがそのハンドシェイクでクライアント証明書を受信したかを確認してください。

7. iOS:クライアント ID のインストール

iPhone では、専用のクライアント証明書を利用できます。一般的な方法として、管理された環境で専用秘密鍵と CSR を生成し、既存の CA にクライアント証明書を発行してから、PKCS#12 ファイルをエクスポートして iOS にインストールします。

7.1 秘密鍵と CSR の生成

mkdir -p ~/ios-mtls
cd ~/ios-mtls
umask 077

P-256 秘密鍵を生成します。

openssl ecparam \
  -genkey \
  -name prime256v1 \
  -noout \
  -out ios-client.key

CSR を生成します。

openssl req \
  -new \
  -key ios-client.key \
  -subj "/CN=Mobile Client" \
  -out ios-client.csr

実際のデバイスと証明書管理規定に応じて、適切な ID 名を設定してください。

7.2 step-ca による発行

step ca sign \
  ios-client.csr \
  ios-client.crt \
  --not-after=8760h

実際の有効期間は CA ポリシーに制約されます。発行後に証明書を確認します。

openssl x509 \
  -in ios-client.crt \
  -noout \
  -subject \
  -issuer \
  -dates \
  -ext extendedKeyUsage

発行者、有効期限、およびクライアント認証用途が期待どおりであることを確認してください。clientAuth がない場合は、まず CA の発行ポリシーを修正してください。

7.3 PKCS#12 のエクスポート

中間 CA 証明書がある場合:

openssl pkcs12 -export \
  -inkey ios-client.key \
  -in ios-client.crt \
  -certfile intermediate-ca.crt \
  -name "Mobile Client" \
  -keypbe PBE-SHA1-3DES \
  -certpbe PBE-SHA1-3DES \
  -macalg sha1 \
  -out ios-client.p12

中間 CA がない場合は、-certfile intermediate-ca.crt パラメータを削除します。

ファイルに空でないパスワードを設定し、ローカルで検証します。

openssl pkcs12 -info -in ios-client.p12 -noout

ルート CA または中間 CA の秘密鍵をクライアントファイルにパッケージしないでください。PKCS#12 には、そのクライアント ID に必要な秘密鍵、クライアント証明書、および必要な証明書チェーンのみを含めるべきです。

7.4 iPhone へのインストール

  1. 管理された方法で .p12 ファイルを iPhone に転送します。
  2. ファイルを開き、システムの指示に従って証明書 ID または構成プロファイルをインストールします。
  3. PKCS#12 のエクスポートパスワードを入力します。
  4. 「設定」App の関連プロファイルまたはデバイス管理画面で、インストール結果を確認します。
  5. インストールされた ID が、クライアント証明書と秘密鍵を含み、証明書部分だけになっていないことを確認します。

iOS のバージョンによって、画面上の文言が多少異なる場合があります。ウェブサイトがプライベート CA で署名したサーバー証明書を使用する場合は、ルート CA も別途インストールし、システムの要件に従って信頼する必要があります。

7.5 Safari によるアクセス

  1. iPhone がホームネットワークに接続済み、または社内ネットワークへの VPN にアクセス可能であることを確認します。
  2. Safari で mTLS サイトを開きます。
  3. システムまたはブラウザーがクライアント ID を要求する場合は、対応する証明書を選択します。
  4. ページの表示結果とサーバーログを確認します。

アクセスできない場合は、DNS、ネットワーク到達性、サーバー証明書の信頼、クライアント ID のインストール状況、およびサーバー側の検証ログを個別に調査してください。

8. iOS のブラウザーとアプリの互換性

iOS 上のブラウザーやその他のアプリが、システムにインストールされたクライアント証明書を使用できるかどうかは、アプリの実装、OS インターフェース、および具体的なバージョンに依存します。証明書がインストールされているという理由だけで、すべてのアプリで利用できると断定することはできません。

対象アプリを 1 つずつ検証することをお勧めします。

  • Safari:まずシステムクライアント ID が利用可能か検証します。
  • Chrome:現行バージョンのクライアント証明書サポートを個別にテストします。
  • その他のアプリ:mTLS のサポート、システム ID へのアクセス可否、および対象サーバーアドレスのサポートを確認します。

あるアプリからシステム ID を呼び出せない場合、証明書を再発行したり鍵アルゴリズムを変更したりするだけでは、通常はアプリケーション層のアクセス制限を解消できません。

9. 証明書と秘密鍵のセキュリティ

クライアント証明書は通常公開できますが、対応する秘密鍵は保護しなければなりません。

  • .key ファイルと .p12 ファイルは、どちらも機密情報として扱う必要があります。
  • 秘密鍵や PKCS#12 ファイルを、公開リポジトリ、ブログへの添付ファイル、または信頼できないファイル共有サービスにコミットまたはアップロードしないでください。
  • 転送が完了したら、一時ディレクトリ、ダウンロードディレクトリ、およびファイル転送記録に不要なコピーが残っていないか確認してください。
  • バックアップする場合は、暗号化して安全に保管してください。
  • 秘密鍵が漏れた場合は、対応するクライアント証明書を失効するか信頼を停止し、鍵と証明書を再生成してください。
  • 秘密鍵をハードウェアキーに保存している場合は、秘密鍵のエクスポートを可能な限り避けてください。

Mac から秘密鍵をスマートフォンにインポートする方法は、スマートフォン内の安全なハードウェア上で生成され、エクスポートできないハードウェア拘束型の秘密鍵を利用するものとは異なります。

10. トラブルシューティングの順序

第 1 ステップ:ネットワークと DNS の確認

クライアントが対象ドメイン名を解決でき、Nginx の HTTPS ポートに接続できることを確認します。

第 2 ステップ:サーバー証明書の信頼確認

クライアントが、ウェブサイト用サーバー証明書の発行者を信頼していること、およびサーバー証明書のドメイン名、有効期限、証明書チェーンが正しいことを確認します。

第 3 ステップ:クライアント ID の確認

クライアント証明書が有効で、clientAuth 用途を含み、対応する秘密鍵が利用できることを確認します。macOS キーチェーン方式では、次のコマンドを実行できます。

security find-identity -v -p ssl-client \
  "$HOME/Library/Keychains/login.keychain-db"

第 4 ステップ:Nginx の確認

ssl_client_certificate が参照する CA ファイルに、クライアント証明書の発行に用いた正しい CA チェーンが含まれているか確認し、ssl_verify_client と ssl_verify_depth が実際の CA 階層と一致していることを確認します。

第 5 ステップ:証明書が送信されない場合と検証失敗の区別

  • No required SSL certificate was sent:通常、その TLS ハンドシェイクでは、サーバーが要求したクライアント証明書が受信されなかったことを示します。
  • クライアント証明書の検証失敗:証明書チェーン、有効期限、用途、またはサーバー側の信頼構成が関係している可能性があります。

Nginx の error log とクライアントの実際の動作を照合して判断し、ブラウザーのエラーページだけを根拠に結論を下さないでください。

11. 方式のまとめ

プラットフォームクライアント ID の方式注意事項
macOS ChromePIV ハードウェアキーまたはキーチェーンの ID証明書選択とハードウェアサポートを個別に検証する
macOS SafariPIV ハードウェアキーまたはキーチェーンの IDChrome と完全に同じ動作とは仮定しない
iOS Safariシステムにインストールした PKCS#12 IDサーバー CA の信頼を別途確認する
iOS Chromeアプリおよび OS バージョンのサポート状況に依存対象バージョンで必ず実際にテストする

推奨方法は、Mac ではまず秘密鍵を PIV ハードウェアキーに保存することです。ソフトウェア ID が必要な場合は、互換性のある PKCS#12 ファイルをキーチェーンにインポートします。iPhone では専用クライアント ID を使用し、対象アプリごとに検証してください。

将来、パスワードマネージャーなどのネイティブアプリに mTLS を有効化する必要がある場合は、そのアプリ自身のネットワークリクエスト認証と、ブラウザーでウェブサイトにアクセスするときの mTLS を別々に設計してください。