LoginSignup
3
0

More than 3 years have passed since last update.

Android端末にインストールされた認証局でサーバ証明書を検証する

Posted at

目的

タイトル通りだが、方法を見つけるのに苦労したので記載しておく。
これを実装すると、自前のRoot認証局が署名したサーバ証明書でも検証してくれる。
自前のRoot認証局のインストール方法は、「設定」>「セキュリティ」>「SDカードからインストール」をタップし、証明書(PEM形式でよい)を選択する。インストールに成功すると、「設定」>「セキュリティ」>「信頼できる認証情報」の「ユーザー」タブに選択した証明書が表示される。

実装

fun connect() {
    // 端末にインストールされている「信頼できる認証情報」ストレージを取得する
    val caStore = KeyStore.getInstance("AndroidCAStore").apply {
        load(null)
    }
    // デバッグ用: 「信頼できる認証情報」をログ出力する
    caStore.aliases().toList().forEach { alias ->
        Log.d("----", "alias=${alias}, certificate=${caStore.getCertificate(alias)}")
    }
    // SSLハンドシェイク時に受信したサーバ証明書の署名者を、検証してくれるTrustManagerを生成するオブジェクトを作成する
    val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply {
        init(caStore)
    }
    // 上で作成したTrustManagerを使う SSLContext を作成する
    val context: SSLContext = SSLContext.getInstance("TLS").apply {
        init(null, trustManagerFactory.trustManagers, null)
    }
    // HTTPS 通信を行う
    val url = URL("https://example.com/index.html")
    val urlConnection = (url.openConnection() as HttpsURLConnection).apply {
        sslSocketFactory = context.socketFactory
    }
    // デバッグ用: 通信結果をログ出力する
    val inputStream = urlConnection.inputStream
    Log.d("----", InputStreamReader(inputStream).readText())
}

注意点

端末にRoot認証局をインストールし、サーバ証明書を中間認証局(Root認証局で署名)で署名しているにもかかわらず、検証に失敗する場合がある。その時は、下記のように自前のX509TrustManagerを実装し、サーバから受信する証明書チェーンを確認する。

fun connect() {
    val context: SSLContext = SSLContext.getInstance("TLS").apply {
        //        init(null, trustManagerFactory.trustManagers, null)
        init(
            null,
            arrayOf(object : X509TrustManager {
                override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
                }

                override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
                    // デバッグ用: サーバから受信した証明書チェーンをログ出力する
                    Log.d("----", "chain=${Arrays.toString(chain)}")
                }

                override fun getAcceptedIssuers(): Array<X509Certificate> {
                    return emptyArray()
                }
            }),
            null
        )
    }
    // HTTPS 通信を行う
    val url = URL("https://example.com/index.html")
    val urlConnection = (url.openConnection() as HttpsURLConnection).apply {
        sslSocketFactory = context.socketFactory
    }
    // デバッグ用: 通信結果をログ出力する
    val inputStream = urlConnection.inputStream
    Log.d("----", InputStreamReader(inputStream).readText())
}
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0