AndroidでTLS1.2のサーバーにhttps接続したときjavax.net.ssl.SSLProtocolExceptionになって通信できない場合の対処方法

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以下の場合サポートされていないため通信できなさそう。**
* [Qualys SSL Labs - Projects / User Agent Capabilities](https://www.ssllabs.com/ssltest/clients.html)

#エラー内容
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を設定したもの。

```java
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
```java
String URL = "アクセスしたいURL";
OkHttpClient okHttpClient = getFixTlsVersionsClient();

RestAdapter restAdapter = new RestAdapter.Builder()
        .setClient(new OkClient(okHttpClient))
        .setEndpoint(URL).build();
```

##Picasso
```java
OkHttpClient okHttpClient = getFixTlsVersionsClient();
Picasso picasso = new Picasso.Builder(context).downloader(new OkHttpDownloader(okHttpClient)).build();
picasso.with(context).load("画像のURL").into(imageView);
```

以下の方法でグローバルインスタンスに一度定義すれば、毎回呼び出すときに定義する必要がなくなる。

```java
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を追加してやればいい。

```java
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;
}
```

#参考
* [OkHttp](http://square.github.io/okhttp/)
* [Picasso (Picasso 2.5.2 API)](https://square.github.io/picasso/javadoc/com/squareup/picasso/Picasso.html)
* [ssl - Android Enable TLSv1.2 in OKHttp - Stack Overflow](http://stackoverflow.com/questions/29249630/android-enable-tlsv1-2-in-okhttp)