自己署名、あるいはAndroidにデフォルトでは含まれていないルート証明書で署名された証明書使ったSSLサイトをAndroid Emulatorで閲覧すると、ブラウザがゼロから起動する度に「信頼できない証明書云々」のポップアップが表示される。
少なくとも 2.1 - 2.3 では、ブラウザの機能としてルート証明書を永続的に証明書ストアに追加する事は出来ない。
しかしAndroid SDKのEmulator上であれば、adb(Android Debug Bridge)からroot権限でファイルシステム上の証明書ストアを直接取得・保存することが出来る。
これにより、Androidにデフォルトでは含まれていない証明書をEmulator上に追加することが出来る。
参考:
基本方針としては
/system/etc/security/cacerts.bks
これをEmulatorから取り出し、JREのkeytoolを使って証明書を追加してEmulatorに戻す。
検証バージョン:
2.1-update1 (API Level 7) revision 2 2.2 (API Level 8) revision 2 2.3.3 (API Level 10) revision 1
cacerts.bksは "/system/" 配下にあり、このディレクトリはQEMUをベースとしているEmulatorから見ると "system.img" に含まれている。ここでAndroid SDKに含まれている system.img の「原本」を直接操作すると、Android Virtual Device (AVD)の全てでその証明書が追加されることになる。
操作に失敗した時を想定し、予めsystem.imgのバックアップをしておくとよい。
system.imgは以下のフォルダにある。
(SDK)/platforms/android-(バージョン)/images/system.img
ターゲットとなるAVDのバージョンは
(HOME)/.android/avd/(AVD名).ini
を見ると
target=android-7
という設定があるので、そこで知ることが出来る。
もしも特定のAVDでのみ証明書を追加したい場合は、system.imgを
(HOME)/.android/avd/(AVD名).avd/
以下にコピーすればよい。
デフォルトでは "/system" の空き容量が0で起動してしまう。これでは証明書を追加してサイズが増加したcacerts.bksをAVDにアップロードできない。
例:
> adb shell df ... /system: 73600K total, 73600K used, 0K available (block size 4096) ^^^^^^^^^^^^ ...
この問題を解決するためには emulator コマンドの"-partition-size" オプションを指定する。なおこのオプションはAndroid SDKの公式ドキュメントには記載されていないため、将来も利用出来るかは不明。恐らくqemu自体の機能と思われる。
> emulator -avd AVD名 -partition-size 128
→ "/system" が128MBでmountされる。
> adb shell df ... /system: 127104K total, 74636K used, 52468K available (block size 4096) ^^^^^^^^^^^^^^^^ ...
参考:
adbの"pull"コマンドを使ってホストマシンにコピーする。
> adb pull /system/etc/security/cacerts.bks
cacerts.bksに追加するためには http://bouncycastle.org/ の提供するjarファイルが必要。
http://bouncycastle.org/download/bcprov-jdk16-141.jar
ダウンロードしたjarファイルを
(使用するJRE)/lib/ext/
以下に配置する。
keytoolの使い方についてはJDKのドキュメントを参照。
BurpSuiteの生成するPortSwiggerのルート証明書を追加する例:(事前にWebブラウザ経由などで .crt ファイルを取り出しておくこと)
keytool -keystore cacerts.bks -storetype BKS \ -provider org.bouncycastle.jce.provider.BouncyCastleProvider \ -storepass changeit \ -importcert -trustcacerts -alias PortSwiggerCA -file PortSwiggerCA.crt
デフォルトでは "/system" が書き込み禁止でmountされている。書き込み可能にするためには adb の "remount" コマンドを実行する。
> adb remount
続いて adb の "push" コマンドで cacerts.bks をアップロードする。
> adb push cacerts.bks /system/etc/security/cacerts.bks
以上が任意の証明書を証明書ストアに追加する手順となる。
BurpSuiteの"proxy" > "options" > "proxy listeners" のProxy設定で、
server SSL certificate: -> generate CA-signed per-host certificates
となっている(デフォルト)場合に
> emulator -avd xxyy -http-proxy 127.0.0.1:8080
などでBurpSuiteのProxyを通していると、HTTPSで「ホスト名が証明書と異なります」という警告が表示される。
これはAndroid EmulatorのベースとなっているQEMUの動作が原因で、ホスト名ではなくIPアドレスをProxyに渡してくる。このためBurp側ではIPアドレスで証明書を生成する。これによりEmulator上のブラウザからみるとリクエストしたURLと、提示された証明書のホスト名が異なってしまい、上記の警告が表示されてしまう。
CONNECTリクエストの引数で、普通はホスト名を渡してくるものだが、
QEMUは自身で名前解決を行った後のIPアドレスを渡してくる。
この問題の対処方法として、BurpSuiteの場合、
generate a CA-signed certificate with a specific hostname:
でホスト名を決め打ちで証明書を生成する方法があり得る。
特定のホスト間のHTTPSしかテストしないのであれば、これを使えば問題ないだろう。他のホストへのHTTPSリクエストでは「ホスト名が異なる」警告が表示されてしまうが、テストと割り切って許容する。
もしもBurpのデフォルトのProxy(8080)にこれを設定するのが煩わしければ、別のポート番号を使ってそのホスト名専用のProxyを追加するとよい。対象となるホストのHTTPS通信をテストするときのみ、そのProxyを使ってAVDを起動すれば良い。