読者です 読者をやめる 読者になる 読者になる

炊きたてのご飯が食べたい

定時に帰れるっていいね。自宅勤務できるっていいね。子どもと炊きたてのご飯が食べられる。アクトインディでは積極的にエンジニアを募集中です。

Android プロジェクトでの Basic 認証 とオレオレ証明書対応

Kotlin Android Basic認証 オレオレ証明書 自己証明書

アクトインディ Advent Calendar 2016 12日目の記事になります。子どもとおでかけ情報アプリ「いこーよ」は、マップ上で簡単におでかけ先を探せる検索アプリです。記事を見てくれたパパさん、ママさん、ぜひ一度使ってみてください^^

iko-yo.net

今回は、デバッグモードでアプリを起動する際に、開発環境サーバーの SSL 証明書がオレオレ証明書を使っていたり Basic 認証をかけていることがあるので、Android アプリでよく利用するライブラリの Basic 認証とオレオレ証明書の対応方法について書きたいと思います。

Kotlin 1.0.5
minSdkVersion 19
targetSdkVersion 25

Retrofit + OkHttp

Basic 認証

OkHttp の Interceptor を作成し Retrofit にセットします。

class BasicAuthenticationInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain?): Response? {
        chain ?: return null
        val credentials = "username:password"
        val basic = "Basic " + Base64.encodeToString(credentials.toByteArray(), Base64.NO_WRAP)
        val request = chain.request().newBuilder()
                        .header("Authorization", basic)
                        .build()
        return chain.proceed(request)
    }
}
val client = OkHttpClient.Builder()
if (BuildConfig.DEBUG) {
    client.addInterceptor(BasicAuthenticationInterceptor())
}

val retrofit = Retrofit.Builder()
        .baseUrl(apiUrl)
        .client(client)
        .build()

オレオレ証明書

OkHttp の sslSocketFactory を利用します

class TrustAllX509TrustManager : X509TrustManager {
    override fun getAcceptedIssuers(): Array<X509Certificate?> = arrayOfNulls(0)
    override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) {
    }

    override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) {
    }

    companion object {
        fun getSslSocketFactory(): SSLSocketFactory {
            val trustAllCerts = arrayOf(TrustAllX509TrustManager())
            val sslContext = SSLContext.getInstance("SSL")
            sslContext.init(null, trustAllCerts, java.security.SecureRandom())
            return sslContext.socketFactory
        }
    }
}
val client = OkHttpClient.Builder()
if (BuildConfig.DEBUG) {
    client.addInterceptor(BasicAuthenticationInterceptor())
            .sslSocketFactory(
                    TrustAllX509TrustManager.getSslSocketFactory(),
                    TrustAllX509TrustManager()
            )
            .hostnameVerifier { s, sslSession -> true }
}

Glide

Basic 認証とオレオレ証明書

GlideModule を継承した CustomGlideModule クラスを作成します。 Glide では通信処理に OkHttpClient が利用できるので、 OkHttp の Interceptor が丸々使えます。 GlideModule の呼び出しは AndroidManifest に定義します。今回は if (BuildConfig.DEBUG) を利用せず debug と release フォルダに同じクラスファイルを作成し debug のみ設定を反映するように書きました。

・AndroidManifest.xml

<meta-data
    android:name=".CustomGlideModule"
    android:value="GlideModule"/>

・debug の CustomGlideModule

class CustomGlideModule : GlideModule {
    override fun applyOptions(context: Context?, builder: GlideBuilder?) {
    }

    override fun registerComponents(context: Context?, glide: Glide?) {
        val client = OkHttpClient.Builder()
                .addInterceptor(BasicAuthenticationInterceptor())
                .sslSocketFactory(
                        TrustAllX509TrustManager.getSslSocketFactory(),
                        TrustAllX509TrustManager()
                )
                .hostnameVerifier { s, sslSession -> true }
                .build()
        glide?.register(GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(client))
    }
}

・release の CustomGlideModule

class CustomGlideModule : GlideModule {
    override fun applyOptions(context: Context?, builder: GlideBuilder?) {
    }

    override fun registerComponents(context: Context?, glide: Glide?) {
    }
}

Picasso

Basic 認証とオレオレ証明書

Picasso も Glide 同様に OkHttp の client を指定することができます。Picasso.setSingletonInstance(picasso) でアプリ起動時に一度だけセットすれば OK です。

class SampleApplication : Application() {
    companion object {
        lateinit var instance: IkomaApplication
    }

    init {
        instance = this
    }

    override fun onCreate() {
        super.onCreate()

        if (BuildConfig.DEBUG) {
            this.setupPicassoForDebug()
        }
    }

    private fun setupPicassoForDebug() {
        val client = OkHttpClient.Builder()
                .addInterceptor(BasicAuthenticationInterceptor())
                .sslSocketFactory(
                        TrustAllX509TrustManager.getSslSocketFactory(),
                        TrustAllX509TrustManager()
                )
                .hostnameVerifier { s, sslSession -> true }
                .build()
        val picasso = Picasso.Builder(this).downloader(OkHttp3Downloader(client)).build()
        try {
            Picasso.setSingletonInstance(picasso)
        } catch (e: IllegalStateException) {
            // No-op
        }
    }
}

WebView

Basic 認証とオレオレ証明書

setWebViewClient の onReceivedHttpAuthRequest に Basic 認証、onReceivedSslError を空の実装にすることで証明書エラーを無視することができます。

class WebViewActivity : AppCompatActivity() {
    private val webView: WebView by bindView(R.id.web_view)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_web_view)

        if (BuildConfig.DEBUG) {
            this.webView.setWebViewClient(object : WebViewClient() {
                override fun onReceivedHttpAuthRequest(view: WebView?, handler: HttpAuthHandler?, host: String?, realm: String?) {
                    handler?.proceed(username, password)
                }

                override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
                    handler?.proceed()
                }
            })
        }
    }
}