Visual Studio 2022 の Android エミュレータで HTTPS の API にだけつながらず、モバイル Chrome でも「この接続ではプライバシーが保護されません」と出て詰まる――この現象は、ほぼ例外なく“証明書の信頼”と“エミュレータ特有のネットワーク”が原因です。この記事では、最短で復旧させる手順から、根本理解・恒久対策・落とし穴の回避まで、現場でそのまま使えるレベルで詳しく解説します。
現象と前提の整理
- Visual Studio 2022 付属の Android エミュレータで、アプリが自社 API(HTTPS)へ接続できない。
- エミュレータ内の Chrome で同じ URL を開いても、「この接続ではプライバシーが保護されません(NET::ERR_CERT_AUTHORITY_INVALID など)」が表示され閲覧不可。
- 開発 PC(ホスト)のブラウザでは正常にアクセスできるため、ホスト側ネットワークは正常。
結論から言うと、エミュレータの OS があなたの開発用証明書(CA またはサーバー証明書)を信頼していない、あるいはエミュレータからの到達先(ホストの localhost 相当)を誤っていることが原因です。以降の手順で確実に解消できます。
最短解決フロー(まずはこれだけ)
- 接続先 URL を 10.0.2.2 へ修正(エミュレータからホストの
localhostに到達するための特別アドレス)。例:https://10.0.2.2:7043/ - エミュレータに“信頼できる CA 証明書”をインポート(自己署名や社内 CA を使っているなら必須)。CA を用意できない場合は、開発時のみ
chrome://flags/#allow-insecure-localhostを使う。 - アプリが Android 7.0+ で「ユーザー追加 CA」を既定で信頼しない仕様に該当する場合、debug ビルドの
network_security_configでユーザー CA を明示的に許可する。 - それでもダメなら、日付時刻ズレ・証明書チェーン欠落・SAN/ドメイン不一致・プロキシ/MITM・TLS バージョン不一致を順に点検。
原因を一枚でつかむ:対処一覧
| 対処 | 手順 / 補足 | 効果 | 注意点 |
|---|---|---|---|
| ① エミュレータへ開発用CA証明書をインストール | 1) 開発環境で使っている CA の証明書(.crt/.pem)を用意2) エミュレータへ転送(ドラッグ&ドロップ or adb push rootCA.crt /sdcard/Download/)3) Android 設定 → セキュリティ → 暗号化と認証情報 → CA 証明書をインストール 4) 再起動後、Chrome とアプリを再試行 | 根本解決。以後その CA で署名した全証明書を信頼。 | インポートできるのは CA:TRUE な証明書のみ。単体のサーバー証明書は「CA 証明書」として登録できない。 |
| ② Chrome で一時的に検証緩和(開発限定) | chrome://flags/#allow-insecure-localhost を Enabled。localhost に対する無効証明書を許容(テスト目的の暫定策)。 | 動作確認が即できる。 | 本番・QA 禁止。主に https://localhost を対象にした挙動で、10.0.2.2 には効かないことがある。 |
| ③ HTTP に切替(どうしても HTTPS が整わない場合) | Android 9+ は既定で HTTP をブロック。network_security_config.xml の cleartextTrafficPermitted="true" で許可、または対象ドメインのみ許容。併せて Manifest に android:usesCleartextTraffic="true" を設定。 | 証明書問題を回避して接続可。 | 開発限定。本番は必ず HTTPS。 |
| ④ モバイル OS の証明書要件を理解 | アプリは証明書検証を無効化できない。サーバー証明書はグローバル CA か、手動登録済み CA で署名。 | 設計段階で迷わない。 | アプリでのピン留め(証明書ピンニング)を有効にしている場合は別途更新が必要。 |
| ⑤ 追加トラブルシューティング | 時刻ズレ、ブラウザキャッシュ、Wi‑Fi/プロキシ環境、セキュリティアプリの干渉、SAN/ドメイン不一致、チェーン欠落、SNI/IP アクセスの矛盾を順に確認。 | 原因の切り分けが早い。 | エミュレータの「データを消去」や OS 更新でユーザー CA が消える場合あり。 |
“エミュレータ ↔ ホスト”の正しい到達先:10.0.2.2
標準 Android エミュレータからホストの localhost に接続するには、10.0.2.2 を使います。例えば ASP.NET Core 開発サーバーがホストで https://localhost:7043/ なら、エミュレータからは https://10.0.2.2:7043/ です。
- ホストのファイアウォールで該当ポート(例:7043)が開いているか確認。
- IPv6 や VPN を併用していると名前解決・経路で迷子になることがあるため、一旦切って検証。
最重要:エミュレータに「信頼できる CA」を入れる(恒久策)
自己署名のサーバー証明書を直接インポートしても、Android の「CA 証明書」としては登録できません(CA:TRUE ではないため)。ルート CA か中間 CAをエミュレータに登録し、その CA が署名したサーバー証明書を使うのが正攻法です。
CA を用意する最短ルート(mkcert)
- ホストに mkcert を導入し、
mkcert -installでローカル CA を作成。 - API 用の証明書を発行:例)
mkcert api.dev.local 10.0.2.2 localhost - 生成物(
.pem/.key)をサーバーに設定。 - エミュレータには mkcert のルート CA(例:
rootCA.pem)をインポートする。
mkcert の CA ファイルは通常ユーザーディレクトリ配下(例:%LOCALAPPDATA%\mkcert\rootCA.pem)にあります。
ASP.NET Core / .NET MAUI と dev-certs
dotnet dev-certs https --trust は、Windows/macOS のローカル証明書ストアへ「開発者用(localhost)証明書」を登録するだけで、Android エミュレータには一切反映されません。エミュレータ側には CA を入れる必要があります。既存の localhost.pfx をエミュレータへ入れても CA ではないため、Chrome のエラーは解消しません。
証明書の形式変換(OpenSSL)
エミュレータは .crt/.pem などの X.509 をインポートできます。PFX/P12 の場合は以下のように変換します。
# PFX → サーバー証明書(PEM)
openssl pkcs12 -in server.pfx -clcerts -nokeys -out server.crt
# PFX → 秘密鍵(開発サーバー側で使用;エミュレータへは不要)
openssl pkcs12 -in server.pfx -nocerts -nodes -out server.key
# ルートCA(PEM)を DER(CRT)へ
openssl x509 -in rootCA.pem -outform DER -out rootCA.crt
エミュレータにインポートするのは rootCA.crt(CA)側です。
エミュレータへのインポート手順(UI)
- エミュレータを起動し、CA ファイルをドラッグ&ドロップ、または
adb push rootCA.crt /sdcard/Download/。 - Android の「設定」→「セキュリティ」→「暗号化と認証情報」→「SD カードからインストール」を開く(名称は OS により若干異なる)。
- 種別として 「CA 証明書」を選んで
rootCA.crtを選択。 - インポート後、エミュレータを再起動。
「ユーザー証明書」「VPN とアプリ」などに入れても サーバー証明書の信頼は広がりません。必ず CA 証明書として登録してください。
(発展)システム CA への配置
アプリが「ユーザー追加 CA を信頼しない」場合に備え、エミュレータのシステム CA ストアへ配置する手もあります(開発機限定・自己責任)。Google 純正エミュレータは adb root と adb remount に対応することが多く、以下のように配置できます。
adb root
adb remount
# ハッシュ名へリネーム(OpenSSL の c_rehash 相当)
# 例:ファイル名を 9a5ba575.0 のようにして配置
adb push rootCA.crt /system/etc/security/cacerts/9a5ba575.0
adb shell chmod 644 /system/etc/security/cacerts/9a5ba575.0
adb reboot
これで「ユーザー CA ではなくシステム CA」として認識され、アプリ既定の信頼にも乗ります。ただし OS アップデートやエミュレータの「データを消去」で失われる点には注意。
Android 7.0 以降:ユーザー CA をアプリが既定で信頼しない問題
Android 7.0(API 24)以降、アプリはユーザー追加 CA を既定で信頼しません。Chrome はユーザー CA を使いますが、あなたのアプリ(OkHttp/HttpClient 等)は使わない可能性があります。debug ビルドに限り network_security_config.xml でユーザー CA を許可しましょう。
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<debug-overrides>
<trust-anchors>
<certificates src="user"/> <!-- ユーザー追加 CA を信頼 -->
</trust-anchors>
</debug-overrides>
AndroidManifest.xml(application 要素)でこの設定を参照します。
<application
android:name=".App"
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="false"
... />
本番ビルドでは ユーザー CA を許可しない設定に戻すか、グローバルに信頼される公的 CA で署名した証明書を使います。
Chrome の暫定回避:allow-insecure-localhost
開発中にブラウザだけでも動作を見たいとき、エミュレータの Chrome で chrome://flags/#allow-insecure-localhost を Enabled にすると、localhost 宛の無効証明書を一時的に許容します。ただし次の制約を理解しておきましょう。
- 対象は 原則 localhost。
10.0.2.2には効かない挙動がある。 - テスト目的の暫定策。本番・QA では禁止。
- アプリ側の HTTP クライアントには影響しない。
どうしても HTTPS が整わないときの HTTP 許可(開発限定)
Android 9(API 28)以降は既定で HTTP がブロックされます。開発限定で HTTP 通信を許すには、次のように設定します。
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system"/>
<certificates src="user"/> <!-- 必要なら -->
</trust-anchors>
</base-config>
10.0.2.2
Manifest:
<application
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config" />
繰り返しになりますが、運用環境では必ず HTTPSにしてください。
追加トラブルシューティング ― 原因別チェックリスト
| 症状 | 確認ポイント | 解決のヒント |
|---|---|---|
| NET::ERR_CERT_AUTHORITY_INVALID | CA が未登録/間違い | エミュレータへ CA 証明書をインポート。サーバー証明書のインポートでは直らない。 |
| NET::ERR_CERT_COMMON_NAME_INVALID | SAN/ドメイン不一致 | 証明書の SAN に 10.0.2.2、localhost、使用ドメインを含め再発行。 |
| NET::ERR_CERT_DATE_INVALID | エミュレータの時刻ズレ | 設定 → 日付と時刻 → 自動を ON/OFF して再取得。再起動。 |
| チェーン不完全 | 中間 CA 未送出 | サーバーに中間証明書を連結(fullchain.pem の提供)。 |
| アプリだけ失敗、Chrome は成功 | Android 7+ のユーザー CA 非信頼 | network_security_config の <debug-overrides> で user CA を許可。 |
| IP 直指定で失敗 | SNI/ホスト名不一致 | ドメインでアクセスするか、証明書に IP を SAN 追加。 |
| 会社のプロキシでのみ失敗 | SSL 検査(MITM) | プロキシのルート CA をエミュレータへ入れるか、検査を無効化(社内手続き)。 |
よくある落とし穴と回避策
- 「VPN とアプリ」にサーバー証明書を入れても直らない:そこはクライアント認証用。信頼の拡張は CA 証明書で行う。
- エミュレータの「データを消去」や OS 更新でユーザー CA が消える:スナップショット運用や自動スクリプト(
adb push→ 設定)で再現可能に。 - ASP.NET Core の開発証明書(localhost.pfx)をそのまま配ろうとする:CA ではないのでエミュレータの Chrome 警告は消えない。mkcert などで CA を作成し、サーバー証明書はその CA で再発行。
- OkHttp/HttpClient の検証無効化をコードでやる:セキュリティ観点で非推奨。
network_security_configと CA 管理で解決を。
具体手順:ASP.NET Core(Kestrel)× .NET MAUI の例
- mkcert を導入し、
mkcert -install(ホストに CA を作成)。 - 開発 API 用に証明書作成:
mkcert localhost 10.0.2.2 api.dev.local - Kestrel の
appsettings.Development.jsonで証明書を指すか、起動引数で--cert--keyを指定。 - エミュレータに
rootCA.crtを「CA 証明書」でインポート、再起動。 - アプリの
network_security_config.xmlに<debug-overrides><certificates src="user"/>を記述し、Manifest で参照。 - アプリの接続先を
https://10.0.2.2:<ポート>/に設定。
診断に使えるコマンド集
# 証明書チェーンと SNI の確認
openssl s_client -connect 10.0.2.2:7043 -servername localhost -showcerts
# エミュレータへファイル転送
adb push rootCA.crt /sdcard/Download/
# エミュレータの日時確認(shell)
adb shell date
# エミュレータのプロキシ設定をリセット(必要に応じて)
adb shell settings put global http_proxy :0
CORS と TLS を切り分ける
「TLS 警告が消えたが API が 4xx/5xx」で止まる場合、原因は CORS(オリジン制約)であることが少なくありません。まず TLS が完全に確立しているか(Chrome のアドレスバーが安全表示か、アプリ側で SSL 例外が発生していないか)を確認し、TLS が問題ないなら CORS ポリシーを見直します。
セキュリティと運用のベストプラクティス
- 開発・検証・本番で証明書を分離し、秘密鍵の取り扱いを厳格に。
- mkcert の CA をチームで共有する場合はアクセス制御と鍵の保護を徹底。
- ピンニングを採用するなら、CA/証明書更新の手順を CI/CD に組み込み、失効や更新時のリリース手順を文書化。
- エミュレータの更新やクリーンアップで CA が消える前提で再登録スクリプトを用意。
トラブルを未然に防ぐチェックシート
| 項目 | Yes/No | 備考 |
|---|---|---|
接続先は 10.0.2.2 を使用している | □ | ホストの localhost を指す特別アドレス |
| エミュレータに CA 証明書をインポート済み | □ | CA:TRUE の証明書であること |
| 中間 CA を含むフルチェーンをサーバーが返している | □ | OpenSSL で確認 |
証明書の SAN に使用ホスト(10.0.2.2/ドメイン)が含まれる | □ | CN だけでは不十分 |
アプリの network_security_config を debug で調整 | □ | ユーザー CA を許可 |
| エミュレータの日時が正しい | □ | 自動取得のオン/オフで再同期 |
| プロキシ/MITM 製品の影響を排除 | □ | 社内 CA の登録 or 検査無効化 |
FAQ
Q. サーバー証明書(localhost.pfx)はエミュレータで使えますか? A. いいえ。Android の「CA 証明書」は CA しか登録できません。CA を登録 → その CA で署名したサーバー証明書を使うのが正解です。 Q. Chrome で手動「続行」できないのはなぜ? A. HSTS など厳格なポリシーや重大な検証エラーでは回避操作が出ないことがあります。CA の登録で根本から直すべきです。 Q. 10.0.2.2 以外に方法は? A. ADB のリバースポート(adb reverse tcp:7043 tcp:7043)なども使えます。構成を増やすほど混乱しやすいので、まずは 10.0.2.2 を基本に。 Q. エミュレータを入れ替えたら再発しました。 A. 新しい AVD には CA が入っていません。セットアップ手順(CA インポート)を再実施してください。
まとめ
Android エミュレータで HTTPS API に接続できず「この接続ではプライバシーが保護されません」となる主因は、エミュレータがあなたの開発用証明書を信頼していないことと、エミュレータ固有のホスト到達先(10.0.2.2)を誤っていることです。CA をエミュレータに登録し、debug の network_security_config でユーザー CA を許可、接続先は 10.0.2.2 に統一――この 3 点を押さえれば、エミュレータでも安全に HTTPS で API に接続できるようになります。開発中の暫定回避(Chrome フラグや HTTP 許可)はあくまで短期用途とし、最終的には CA と証明書の正しい管理で本質的に解決しましょう。
付録:実施手順を最小コストで自動化する
新規 AVD を作るたびに CA を入れ直すのは面倒です。次の方針で自動化すると楽になります。
- CA の単一保管:リポジトリに「開発用 CA 証明書(公開鍵のみ)」を格納。
- スクリプト化:
adb push→ インテント呼び出しで認証情報画面を開く(手動タップのみ最後に必要)。 - mkcert の再発行コマンド:対象 SAN(
localhost、10.0.2.2、開発用ドメイン)を README に明記。 - AVD スナップショット:CA 登録済み状態でスナップショットを取り、以後そこから起動。
付録:チェックリスト(コピー&使い回し用)
[ ] エミュレータからの到達先は https://10.0.2.2:<PORT>/ に統一
[ ] mkcert で開発用 CA を作成し、サーバー証明書はその CA で再発行
[ ] エミュレータに CA を「CA 証明書」としてインポート済み
[ ] Android 7+ 対策:debug の network_security_config で user CA を許可
[ ] サーバーは中間 CA を含むフルチェーンを返す(fullchain)
[ ] SAN に 10.0.2.2 / localhost / 開発ドメインを含める
[ ] エミュレータの時刻は自動同期で正しい
[ ] プロキシ/MITM/セキュリティアプリによる干渉なし
[ ] (必要時のみ)HTTP 許可は debug に限定、運用では HTTPS 固定
補足情報(知っておくと役立つポイント)
- ローカルホストのアドレス:エミュレータからホストの
localhostへはhttp(s)://10.0.2.2:<ポート>を使用。 - 自己署名証明書を頻繁に更新する場合の効率化:
mkcertなどでローカル CA を作成し、エミュレータに一度だけ CA 証明書を登録すれば、その CA で発行したすべてのドメイン証明書が自動的に信頼される。 - .NET MAUI / ASP.NET Core を併用している場合:
dotnet dev-certs https --trustはホスト OS のローカルストア登録のみ。エミュレータ向けには本記事の手順で別途 CA をインポート。
アクションプラン(今すぐやる)
mkcert -installで CA 作成 → サーバー証明書を再発行。- エミュレータに
rootCA.crtを「CA 証明書」で登録、再起動。 - 接続先を
https://10.0.2.2:<PORT>/に統一。 - debug の
network_security_configで<certificates src="user"/>を許可。 - OpenSSL でチェーンと SAN を最終確認。

コメント