CA証明書とandroidの危うい関係 4
java(SDK)で実装する
Apache HttpClientではなくjavax.net.ssl.HttpsUrlConnectionを使った例も載せておく。
同クラスを使う場合、TrustManagerFactoryとKeyManagerFactoryという二つのファクトリを用意しなくてはならず、若干煩雑になる。
- HttpsUrlConnectionを使用する
URL url = new URL("接続するURL"); HttpURLConnection con = (HttpURLConnection)url.openConnection(); if ( uri.startsWith("https")) { HttpsURLConnection https = (HttpsURLConnection)con; https.setSSLSocketFactory(getSSLSocketFactory(context)); } 〜以降、HttpURLConnectionを使って通信を実行する private static SSLSocketFactory getSSLSocketFactory(Context context) { final Resources resource = context.getResources(); TrustManagerFactory trustManagerFactory; KeyManagerFactory keyManagerFactory; char[] passwdchars = "<パスワード>".toCharArray(); InputStream cacerts_stream = resource.openRawResource(R.raw.cacerts); try { KeyStore tm_keystore = KeyStore.getInstance("BKS"); tm_keystore.load(cacerts_stream, passwdchars); trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(tm_keystore); } finally { cacerts_stream.close(); } try { cacerts_stream = resource.openRawResource(R.raw.cacerts); KeyStore km_keystore = KeyStore.getInstance("BKS"); km_keystore.load(cacerts_stream, passwdchars); keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(km_keystore, passwdchars); } finally { cacerts_stream.close(); } SSLContext sslContext = SSLContext.getInstance("TLSv1"); sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); return sslContext.getSocketFactory(); }
例のごとく例外処理は省いている。
これでHttpClient同様通信ができると思うが、サーバの環境如何によっては、
-
- 接続できたり出来なかったりと不定期又は定期に問題が発生する
- HTTPのレスポンスコードに-1が戻る
等問題が発生する場合がある。
androidは何故か※のHTTP KeepAliveがデフォルトで有効になっており、これが問題になるケースがあるため、アプリケーションの初期化時や通信時に以下のコードでKeepAliveを無効にすることで問題が解決する場合がある。
System.setProperty("http.keepAlive", "false");
前回のエントリから自前の証明書とキーストアを使う例を二通り掲載したが、正直なところ、こんなコーディングなぞしなくても最新のルートCA証明書位はOTA等で端末にプッシュする仕組みを提供すべきだと思うが(それが結果としてセキュリティ強化にもつながる)、いかがですかGoogleさん。
※ソケットは比較的重いリソースなので、なるべく接続を節約したいという意図があるのかもしれない。