web-dev-qa-db-ja.com

Android 5.0 Lollipopで失敗したハンドシェイクでHttpClientが失敗する

Android 5.0 LollipopのDefaultHttpClientが壊れているようです。以前のバージョンのAndroidで正常に設定された一部のサイトへの接続を設定できません。

たとえば https://uralsg.megafon.r に接続しようとします

//Create httpclient like in https://stackoverflow.com/questions/18523784/ssl-tls-protocols-and-cipher-suites-with-the-androidhttpclient
HttpClient client = new DefaultHttpClient(manager, params);
HttpGet httpGet = new HttpGet("https://uralsg.megafon.ru");
HttpResponse client = httpclient.execute(httpGet);

このコードはAndroid 2.3-4.4で機能しますが、Android 5.0(デバイスとエミュレーター)では失敗し、接続がピアによって閉じられました。 Android 5.0はこの古いサーバーをTLSv1.2および最新の暗号と接続しようとし、それらをサポートしていないため、これはもちろん理解できます。

OK、サンプルコードを SSL/TLSプロトコルと暗号スイートとAndroidHttpClientを使用して プロトコルと暗号をTLSv1に制限しますおよびSSL_RSA_WITH_RC4_128_MD5。今度は別のエラーで失敗します:

javax.net.ssl.SSLHandshakeException: Handshake failed
caused by 
    error:140943FC:SSL routines:SSL3_READ_BYTES:sslv3 alert bad record mac 
    (external/openssl/ssl/s3_pkt.c:1286 0x7f74c1ef16e0:0x00000003) 
    at com.Android.org.conscrypt.NativeCrypto.SSL_do_handshake

そしてもちろん、このコードはAndroid 2.3-4.4でスムーズに実行されます。

私はwiresharkでトラフィックを調べました:

302 4002.147873000  192.168.156.30  83.149.32.13    TLSv1   138 Client Hello
303 4002.185362000  83.149.32.13    192.168.156.30  TLSv1   133 Server Hello
304 4002.186700000  83.149.32.13    192.168.156.30  TLSv1   1244    Certificate
305 4002.186701000  83.149.32.13    192.168.156.30  TLSv1   63  Server Hello Done
307 4002.188117000  192.168.156.30  83.149.32.13    TLSv1   364 Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message
308 4002.240695000  83.149.32.13    192.168.156.30  TLSv1   61  Alert (Level: Fatal, Description: Bad Record MAC)

接続が確立されたことがわかりますが、暗号化されたハンドシェイクメッセージをデコードできなかったため、サーバーに警告が出されました。

Android 5.0でHttpClientを使用して https://uralsg.megafon.r に接続できませんでした。ストックブラウザはそれを接続します。 Android 2.3-4.4は、このサイトを問題なく接続します。

HttpClientがそのようなサイトに接続できるようにする方法はありますか?これは1つの例にすぎません。Android 5.0とHttpClientで接続できなかったレガシーサーバーがたくさんあると思います。

15
Dmitry Kochin

更新:それはバックエンドのバグであり、Android 5、確かに問題の暗号で。

私も同じ問題を抱えていました。私にとっては、Android 5の(更新された)デフォルトの暗号のセットから選択された暗号TLS_DHE_RSA_WITH_AES_256_GCM_SHA384であることがわかりました。

受け入れ可能な暗号のクライアントリストからそれを削除するとすぐに、接続が再び機能しました。

Android 5の変更ログ の言及:

  • AES-GCM(AEAD)暗号スイートが有効になりました。

これが犯人だと私はかなり確信している。 TLS_DHE_RSA_WITH_AES_256_GCM_SHA384が(サーバーによって)優先されるとすぐに、接続は失敗します。

TLS_DHE_RSA_WITH_AES_128_GCM_SHA256が機能することに注意してください。

私の推測では、Android TLS_DHE_RSA_WITH_AES_256_GCM_SHA384の実装はバグがあるか、または話しているサーバーの1つです。

ソリューション:

  1. サーバーで使用可能な暗号からTLS_DHE_RSA_WITH_AES_256_GCM_SHA384を削除します(アプリの再デプロイは必要ありません)。
  2. クライアントが提供する暗号のリストからTLS_DHE_RSA_WITH_AES_256_GCM_SHA384を削除します(CLIENT_HELLOの間)。

独自のSSLSocketFactoryを実装して呼び出すことで、クライアント側でそれを行うことができます

sslSocket.setEnabledCipherSuites(String[] suites);

sSLSocketの作成時。

edit:これは必ずしもAndroidバグではありません。サーバーの実装に問題がある可能性があります。問題の原因が本当に暗号にある場合は、Androidバグ追跡システム)( https://code.google.com/p/Android/issues/)にコメントを残してください。詳細?id = 816 )。ありがとうございます!

5
stefs

カスタムソケットファクトリでcipherSuitesを変更しようとしましたが、それは役に立ちませんでした。私の場合、ソケットのEnabledProtocolsからTLSv1.1およびTLSv1.2プロトコルを削除する必要がありました。古いサーバーの中には、新しいプロトコルのプロトコルネゴシエーションをうまく処理できないものがあります。 HttpsURLConnectionを使用するときにAndroidによってサーバーに送信された暗号リストをオーバーライドする方法など、カスタムソケットファクトリを作成するさまざまな例があります。 、およびApacheソケット用のもの。それが終わったら、次のAdjustSocketメソッドを呼び出してプロトコルを削除しました。

private void AdjustSocket(Socket socket)
{
    String[] protocols = ((SSLSocket) socket).getSSLParameters().getProtocols();
    ArrayList<String> protocolList = new ArrayList<String>(Arrays.asList(protocols));

    for (int ii = protocolList.size() - 1; ii >= 0; --ii )
        {
        if ((protocolList.get(ii).contains("TLSv1.1")) || (protocolList.get(ii).contains("TLSv1.2")))
            protocolList.remove(ii);
        }

    protocols = protocolList.toArray(new String[protocolList.size()]);
    ((SSLSocket)socket).setEnabledProtocols(protocols);
}
0
Mike M