iOS9でTLS1.2が必須になったため、APIサーバーのTLSを1.2必須に変更にしたところ、AndroidにてRetrofitとPicassoがjavax.net.ssl.SSLProtocolExceptionを出して通信できなくなってしまい、何とか対応したのでそのメモ。
iOS9対応で今後頻発しそうな気もするので対処方法を残しておきます。
追記:サーバーの設定を変更したところ再びjavax.net.ssl.SSLProtocolExceptionが発生しました、どうやらTLS1.2以外に有効にしてあったTlsVersionで通信ができていた模様。
サーバー側が完全にTLS1.2以外を通さない設定の場合Android 4.3以下の場合サポートされていないため通信できなさそう。
#エラー内容
HTC HTL21 Android 4.1.1(API16)にてエラー。
ASUS Nexus 7 Android 5.1.1 (API22)ではエラーは発生しなかったので古い端末で発生する問題だと思われます。
D/Retrofit: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x5d95bcd0: Failure in SSL library, usually a protocol error
D/Retrofit: error:14077102:SSL routines:SSL23_GET_SERVER_HELLO:unsupported protocol (external/openssl/ssl/s23_clnt.c:714 0x5c685bbc:0x00000000)
D/Retrofit: at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:412)
D/Retrofit: at com.squareup.okhttp.Connection.connectTls(Connection.java:235)
D/Retrofit: at com.squareup.okhttp.Connection.connectSocket(Connection.java:199)
D/Retrofit: at com.squareup.okhttp.Connection.connect(Connection.java:172)
D/Retrofit: at com.squareup.okhttp.Connection.connectAndSetOwner(Connection.java:367)
D/Retrofit: at com.squareup.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:128)
D/Retrofit: at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:328)
D/Retrofit: at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:245)
D/Retrofit: at com.squareup.okhttp.Call.getResponse(Call.java:267)
#対処方法
OkHttpClientにTLS1.2を設定したものをそれぞれの通信時に渡してやればいい。
以下はtlsVersionsにTLS1.2を設定したもの。
public static OkHttpClient getFixTlsVersionsClient() {
OkHttpClient okHttpClient = new OkHttpClient();
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.build();
okHttpClient.setConnectionSpecs(Collections.singletonList(spec));
return okHttpClient;
}
##Retrofit
String URL = "アクセスしたいURL";
OkHttpClient okHttpClient = getFixTlsVersionsClient();
RestAdapter restAdapter = new RestAdapter.Builder()
.setClient(new OkClient(okHttpClient))
.setEndpoint(URL).build();
##Picasso
OkHttpClient okHttpClient = getFixTlsVersionsClient();
Picasso picasso = new Picasso.Builder(context).downloader(new OkHttpDownloader(okHttpClient)).build();
picasso.with(context).load("画像のURL").into(imageView);
以下の方法でグローバルインスタンスに一度定義すれば、毎回呼び出すときに定義する必要がなくなる。
OkHttpClient okHttpClient = getFixTlsVersionsClient();
Picasso picasso = new Picasso.Builder(context).downloader(new OkHttpDownloader(okHttpClient)).build();
Picasso.setSingletonInstance(picasso);
##TLS1.2以外も対応したい
TLS1.1やTLS1.0や(POODLEの問題があったため使われないと思うが)SSL3.0も同時に有効にしたい場合は、TlsVersion.TLS.TLS_1_2の後にTlsVersion.TLS_1_1, TlsVersion.TLS_1_0, TlsVersion.SSL_3_0を追加してやればいい。
public static OkHttpClient getFixTlsVersionsClient() {
OkHttpClient okHttpClient = new OkHttpClient();
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0, TlsVersion.SSL_3_0)
.build();
okHttpClient.setConnectionSpecs(Collections.singletonList(spec));
return okHttpClient;
}
#参考