Mac と iPhone でクライアント証明書を使用して mTLS サイトにアクセスする

本稿では、macOS と iPhone でクライアント証明書を使用して内部ネットワークの mTLS サイトにアクセスする方法を記録する。内容には CanoKey PIV、step-ca、Nginx、および iOS システムへの証明書のインストールが含まれる。

1. 環境と認証フロー

現在の環境は以下のとおりである。

  • CA:既存の CX-Homelab-CA Root CA と CX-Homelab-CA Intermediate CA。
  • 証明書の発行:step-ca。
  • Web サーバー:Nginx 1.25.0。
  • テストサイト:https://www.home.arpa。
  • Mac クライアント:CanoKey PIV 9A。
  • iPhone クライアント:PKCS#12 ファイルを介してクライアント証明書と秘密鍵をインポート。

mTLS と通常の HTTPS の違いは、サーバーがクライアント証明書を検証し、クライアントもサーバー証明書を検証する必要がある点である。クライアント証明書には対応する秘密鍵が含まれている必要があり、.crt ファイルだけをインストールしても、通常はクライアント認証を完了できない。

基本フロー如下:

  1. クライアントが HTTPS サイトにアクセスする。
  2. Nginx がクライアント証明書を要求する。
  3. ブラウザーまたはシステムがクライアント ID を選択し、対応する秘密鍵を使用して TLS ハンドシェイクを完了する。
  4. Nginx がクライアント証明書の有効性、証明書チェーン、用途を検証する。
  5. 検証に成功すると、サーバーは保護されたリソースへのアクセスを許可する。

需要注意すべき点として、クライアント証明書とサーバー CA の信頼は別々の問題である。クライアント ID をインストールしただけでは、デバイスがウェブサイトサーバー証明書の発行元であるプライベート CA を信頼できる状態になるわけではない。

2. Mac:CanoKey PIV 9A の使用

2.1 現在の構成

Mac では CanoKey の PIV 9A スロットにクライアント ID を保存している。macOS はスマートカード関連の仕組みを通じて証明書と秘密鍵を認識し、ブラウザーは TLS ハンドシェイク時にその ID を使用する。

現在確認済みの事項は以下のとおりである。

  • CanoKey PIV 9A のクライアント証明書が発行済みである。
  • 証明書に TLS Web Client Authentication 用途が含まれている。
  • macOS 上の Chrome では、CanoKey PIV 9A を使用して mTLS サイトへのアクセスに成功している。

このことから、その Chrome アクセスにおけるクライアント証明書、秘密鍵による署名、およびサーバー側の検証チェーンが正常に機能 Crystallized ことが確認できる。

2.2 使用方法

  1. CanoKey を Mac に挿入する。
  2. システムがスマートカードとその証明書を認識できることを確認する。
  3. Chrome を開く。
  4. https://www.home.arpa にアクセスする。
  5. ブラウザーがクライアント証明書の選択を要求した場合、対応する CanoKey PIV 9A の ID を選択する。
  6. 必要に応じて PIN の入力またはハードウェア操作を行う。
  7. ページが正常に開いたことを確認し、Nginx のログを確認する。

証明書選択画面が表示されない場合も、直ちに証明書を使用できないと判断してはならない。ブラウザーが選択内容を記憶している可能性があるほか、その接続ではサーバーがクライアント証明書を要求していなかった可能性がある。

2.3 トラブルシューティングコマンド

システムが認識しているスマートカードを表示する:

security list-smartcards

スマートカード関連の認証 ID を表示する:

sc_auth identities

スマートカードの Token 状態を表示する:

sudo security smartcards token -l

Chrome ではアクセスできる一方、Safari が次の内容を返す場合:

400 Bad Request
No required SSL certificate was sent

ブラウザーのクライアント証明書に関する動作、macOS によるスマートカードの認識状況、および Nginx が受信した TLS クライアント証明書情報を、それぞれ個別に確認する。

1 つのブラウザーで成功しただけで、別のブラウザーでも必ず同じクライアント ID が使用されるとはみなさない。

3. iPhone:クライアント証明書のインポート

3.1 方式の説明

iPhone では CanoKey とは別のクライアント証明書を使用し、CanoKey 上の秘密鍵を共有しない。

現在採用している方式は以下のとおりである。

  1. Mac 上で iPhone 専用の秘密鍵を生成する。
  2. CSR を生成する。
  3. 既存の step-ca を使用してクライアント証明書を発行する。
  4. 証明書と秘密鍵を暗号化された PKCS#12 ファイル(.p12)にパッケージする。
  5. .p12 を iPhone にインストールする。
  6. システムのクライアント証明書に対応したアプリを使用して mTLS サイトにアクセスする。

この方式では、Root CA を再初期化する必要,也没有新しい RSA Intermediate CA を作成する必要がある。

3.2 Mac での秘密鍵と CSR の生成

専用ディレクトリを作成する:

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

楕円曲線用の秘密鍵を生成する:

openssl ecparam \
  -genkey \
  -name prime256v1 \
  -noout \
  -out iphone.key

CSR を生成する:

openssl req \
  -new \
  -key iphone.key \
  -subj "/CN=CX iPhone 16 Pro Max" \
  -out iphone.csr

ここでは P-256(prime256v1)鍵を使用する。発行前に、CA の証明書テンプレートまたは Provisioner で、クライアント証明書に正しい拡張鍵用途が設定されていることを確認する。

3.3 step-ca による発行

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

上記の有効期間パラメータは、約 1 年間の証明書を两块 bamb 请求することを意味する。実際の有効期間仍然是 CA ポリシーによって制約される。

証明書の内容を確認する:

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

重点的に確認する項目は以下のとおりである。

  • Subject が iPhone 専用の ID になっている。
  • Issuer が想定した Homelab Intermediate CA になっている。
  • 有効期間が想定どおりである。
  • Extended Key Usage に TLS Web Client Authentication が含まれている。

クライアント証明書に clientAuth 用途がない場合は、iPhone に直接インポートする前に、発行設定を修正する。

3.4 PKCS#12 ファイルのエクスポート

openssl pkcs12 \
  -export \
  -inkey iphone.key \
  -in iphone.crt \
  -out iphone.p12

実行時に .p12 のエクスポートパスワードを設定する。インストール時には、通常このパスワードの入力が必要である。

証明書チェーンの設定で、クライアントが Intermediate CA 証明書も同時に送信する必要がある場合は、エクスポートした PKCS#12 に適切な中間証明書が含まれていることを確認し、最終的にインポートされる ID が完全であるかも確認する。Root CA の秘密鍵や Intermediate CA の秘密鍵をクライアントファイルに同梱してはならない。

3.5 iPhone へのインストール

  1. AirDrop などの管理された方法を用いて iphone.p12 を iPhone に転送する。
  2. iPhone 上でファイルを開く。
  3. システムの手順に従い、設定画面からダウンロードしたプロファイルまたは証明書 ID をインストールする。
  4. 手順に従って PKCS#12 パスワードを入力する。
  5. 「設定」の「VPN とデバイス管理」または関連するプロファイル画面で、インストール結果を確認する。iOS のバージョンによって、画面上の文言が多少異なる場合がある。
  6. 公開証明書だけでなく、クライアント証明書と秘密鍵を含む ID がインストールされていることを確認する。

iPhone がウェブサイトで使用しているプライベート CA をまだ信頼していない場合は、Root CA を別途インストールする。手動でインストールしたルート証明書については、iOS の証明書信頼設定で完全信頼を有効にする必要がある。クライアント ID のインストールとサーバー CA の信頼は、それぞれ独立した手順である。

3.6 Safari からのアクセス

  1. iPhone がホームネットワークに接続している、または内部ネットワークの VPN にアクセスできることをを確認する。
  2. Safari で https://www.home.arpa を開く。
  3. サーバーがクライアント証明書を要求したら、システムの手順に従って対応するクライアント ID を選択する。
  4. ページが正常に開いた場合は、Nginx のログを確認し、クライアント証明書の検証に成功していることを確認する。

成功しない場合は、DNS、ネットワーク到達性、サーバー証明書の信頼、クライアント ID のインストール状況、および Nginx のクライアント証明書検証ログを、それぞれ個別に調査する。

4. iPhone 版 Chrome がシステム証明書を直接使用できない理由

iOS 版 Chrome は、macOS 版 Chrome のように、システム設定を通じてインストールされたクライアント証明書 ID を直接使用できない。

Chromium の公式ポリシードキュメントでは、Android 版および iOS 版の Chrome は、Chrome 自身が設定したクライアント証明書のみを選択でき、オペレーティングシステムレベルでインストールされた証明書には直接アクセスできないと明確に規定している。

https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/policy/resources/templates/policy_definitions/ContentSettings/AutoSelectCertificateForUrls.yaml

したがって、iPhone のシステムに iphone.p12 をインポートしても、Chrome がそれを使用できるとは限らない。

証明書を再発行する、証明書アルゴリズムを変更する、または .p12 を再度エクスートしても、このアプリ側の権限制限だけを解決することはできない。

現在の方式では以下のとおりである。

  • iPhone Safari:システムにインストールされたクライアント ID を優先して使用する。
  • iPhone Chrome:システムにインストールされたクライアント証明書を直接使用できない。
  • iOS 上のその他のアプリ:クライアント証明書認証を実装しているかどうか、および必要なサーバーアドレスに対応しているかどうかを、個々に確認する。

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

クライアント証明書は秘密ではないが、対応する秘密鍵は適切に保護する必要がある。

注意が必要な項目は以下のとおりである。

  • iphone.key は、iPhone クライアント ID の秘密鍵である。
  • iphone.p12 にはクライアント証明書と秘密鍵の両方が含まれており、機密ファイルとして扱う必要がある。
  • .key や .p12 を公開リポジトリ、ブログの添付ファイル、または信頼できないファイル共有サービスにアップロードしない。
  • 転送が完了したら、Mac の一時ディレクトリ、ダウンロードディレクトリ、およびファイル転送履歴に、不要なコピーが残っていないか確認する。
  • 復旧機能を確保したい場合は、秘密鍵または PKCS#12 ファイルを暗号化してバックアップし、保護 없이長期保存しない。
  • 秘密鍵が漏れた場合は、対応するクライアント証明書を失効する、または信頼しないようにし、鍵と証明書を再生成する。

明確にしておく:本方式の iPhone 秘密鍵は、Mac 上で生成してから iPhone にインポートしたものであり、iPhone の Secure Enclave 内で生成され、エクスポートできないハードウェア拘束型の秘密鍵とは同等ではない。

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

mTLS へのアクセスが失敗した場合は、以下の順序で調査し、すべての問題を証明書のせいだと決めつけないようにする。

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

クライアントが www.home.arpa を解決でき、Nginx の HTTPS ポートへ接続できることを確認する。

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

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

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

クライアント証明書の有効期間、clientAuth 拡張鍵用途、およびクライアント秘密鍵が利用可能かどうかを確認する。

第 4 ステップ:Nginx の検証設定を確認する

ssl_client_certificate が参照する CA ファイルに、正しいクライアント証明書の発行チェーンが含まれていることを確認する。また、ssl_verify_client と ssl_verify_depth の設定が現在の CA 階層に適合していることを確認する。

第 5 ステップ:クライアントが証明書を送信していない場合と、証明書の検証に失敗した場合を区別する

この 2 つのエラーは同一个ではない。

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

Nginx の error log と конкре的なクライアントの動作を組み合わせて判断し、ブラウザーに表示されたエラーページだけに基づいて結論を下さない。

7. 現在の方式のまとめ

プラットフォームクライアント ID現在の状態
macOS ChromeCanoKey PIV 9A成功を確認済み
macOS SafariCanoKey PIV 9Aブラウザーの動作を個別に調査する必要あり
iPhone Safariインポートした PKCS#12 IDシステムの証明書 ID 方式で使用
iPhone Chromeシステムにインストールされた PKCS#12 IDシステム証明書の直接呼び出しには対応していない

現在最も単純な分担は、Mac では CanoKey PIV 9A を使用し、iPhone では独立したクライアント証明書を使用することである。内部ネットワークの mTLS サイトへアクセスする必要がある場合は、対応するクライアント ID をサポートしているブラウザーを優先して使用する。

今後、クライアント証明書認証を Bitwarden で利用する場合は、Bitwarden App 自身がサーバーに接続する際の mTLS と、ブラウザーで通常のウェブサイトにアクセスする際の mTLS を分けて設計する。