Android 上のアプリから SSL クライアント認証の必要なサーバへアクセスする方法
それから半年、本家の Android 標準ブラウザは現在も未対応のままですが、「SandroB」のようにクライアント認証に独自対応したブラウザも登場し始めています。こうした動きは Android ユーザとしてとても喜ばしいことで、対応環境の拡大や機能面の向上など今後の進化が大いに期待されます。
一方で、たとえば KLab の「VPN-Warp」のような SSL-VPN システム経由での利用が想定されるアプリケーションは Web ブラウザばかりではありません。私の場合、出先から自宅 PC へのアクセスには VNC を VPN-Warp+stone 経由で使っています。VNC ビューワそのものは SSL に対応していませんが、通信に stone を介在させることで所定のアプリ本体が SSL まわりの実装を持っていなくてもそういう使い方が可能となるわけですね。もちろん、人によってはここで言う「所定のアプリ」に、「普段使い慣れているブラウザ」が該当する場合もあるでしょう。
冒頭の記事を書いた時点では Android 用 stone は何かと面倒な CUI ベースでしたが、現在は通常の Android アプリとしてマーケットで公開しています。社内からの声もあり、今回はこのアプリ版に固有の Android キーストアへ証明書をインストールする機能に関する話題に触れながら、stone for Android でクライアント証明書を利用する手順等を紹介します。
■ Android キーストアと Android アプリの間の微妙な関係
Andorid 2.x 以上では [設定]−[現在地情報とセキュリティ]−[SDカードから (または「ストレージからの」) インストール] より、所定の証明書をシステムのキーストアである認証情報ストレージ (Credential Storage) へインストールすることができます。
この操作での一連の流れは Windows 環境上でキーストアへ証明書をインポートする際のそれと似ているため Windows でその手順を経験したことのある人にはこれといって違和感のあるものではないでしょう。しかし、内部的にはとても大きな違いがあります。Android では、上記の手順でキーストアへインストールした証明書を自作のアプリケーションから参照することができないのです。これは以下の理由によるものです。
(※1) たとえば、「mycert.p12」 という 一件のクライアント証明書・秘密鍵のペアと 一件の CA 証明書を含む pkcs12 形式の証明書ファイルを上記の要領で [設定] からインストールすると、キーストアの実体である /data/misc/keystore/ 下には以下のネーミングでファイルが配置される。"1000_" の部分が uid。
- 1000_USRCERT_mycert
- 1000_USRPKEY_mycert
- 1000_CACERT_mycert
また、次の点にも注意が必要です。
- あるアプリの uid は、そのアプリが別のプロセスからのインテント発行をトリガーに起動した場合においてもインテント発行元のプロセスの uid の影響を受けない
(親子プロセスの関係にはならない)
■ stone for Android での証明書インストール対応方法
システムがキーストアを持っているにもかかわらず自作のアプリからそれを利用できないというのはどうにも残念です。stone は証明書をファイルベースで参照することができるためキーストアへの証明書インストールが実用上必須というわけではありませんが、証明書のパスワードをファイルとして端末に配置することのリスクや、証明書パスワードを記憶し管理し続けることのコストを考えればやはりキーストアを利用したいところです。
もちろん、別の対処方法としてアプリ独自のキーストア機構を用意し証明書データの暗号化と復号を自前で処理するといったことも考えられますが、オープンソースの Android ですから、こうした場合、どのように対応すれば実現したい処理をシステム側の事情にうまく馴染ませることができるかを調べてみる価値は大いにありそうです。
大枠で次のように考えました。
「システムアプリのプロセスから証明書インストール処理が呼び出されると具合が悪いのなら、そこで使用されているインストール処理用の最小限のコードを自分のアプリに組み込んでしまえばよいのではないか?」
もっとスマートな方法もあるかもしれませんが、つまりは自作アプリにキーストアへの証明書インストール機能を実装しちゃえということですね。インテント経由で Android システムの証明書インストール機能を呼び出すのではなく 自分の処理の一部として証明書のインストールをハンドリングすれば、そこに反映される uid は確実に自作アプリ自身のプロセスのものとなるはずです。
ソースを調べてみると [設定] からの証明書インストール処理は「CertInstaller」というシリーズのコードに記述されていることがわかりました。
それから最終的に以下の 3 つのディレクトリから 10 本のソースコードをピックアップし、ライセンス規約に準じた形でアプリへの組み込みを行いました。
stone for Android の証明書インストール機能はこのようにして実装しています。
当初の期待通り、組み込んだ「証明書のインストール」機能を使ってインストールした証明書は同アプリの uid 識別とともにキーストアへ配置され stone 実行時に正しく参照することができています。持ち込んだソースの中には削ることの可能なコードもまだ残っていますが、そこにはいずれゆっくり手をつけることにしましょう :-)
■ クライアント証明書をインストールしてみる
上述の通り stone for Android は Android 2.x 以上の環境において Android キーストアへの証明書のインストールと参照に対応しています。ここでは証明書をインストールする手順をざっくりまとめてみます。
アプリケーションメニューの「証明書のインストール」をタップして下さい。
ここで上図 A) のメッセージが表示された場合は下記リンクの図を参考にキーストアにパスワードを設定した上で再度上記の操作を行って下さい。
上図 B) のメッセージはキーストアのパスワード保護が解除されていない場合に表示されます。端末の電源を投入した時点ではキーストアはロックされた状態です。設定ずみのパスワードを投入し「OK」を押下するとロックが解除されます。
キーストアへアクセス可能な状態であれば stone for Android は次のいずれかのディレクトリ下に存在する *.p12, *.pfx 等の証明書ファイルを一覧表示します。
- [外部ストレージディレクトリ]/ (※2)
- [外部ストレージディレクトリ]/download/
(※2) 外部ストレージディレクトリとは 当該環境において android.os.Environment の getExternalStorageDirectory() が指し示すディレクトリであり、伝統的には 「/mnt/sdcard」 や 「/sdcard」 がこれに該当するインストール対象とする証明書ファイル名をタップし、図の手順でキーストアへのインストールを行います。
インストールが完了すると、その証明書を参照する stone パラメータ記述例が GUI のパラメータフィールドに提示されます。リモートホスト名の指定箇所は「SERVERNAME」とダミー表記されており、その他の指定を含めこのサンプルを編集すれば効率がよいでしょう。
なお、サンプルパラメータで上書きされる前の stone パラメータは履歴として保存されており履歴一覧から簡単に呼び戻せます。
■ インストールずみのクライアント証明書を使ってみる
インストールしたクライアント証明書を参照する stone プロセスに SSL 処理一式をまかせ、所定のアプリからリモートサーバへアクセスした例を図に示します。
stone パラメータの文法解説はアプリケーションメニューの [ヘルプ] から参照できます。応用の際などに利用して下さい。
(tanabe)