在 Retrofit2 上使用 HTTPs 的 SSLProtocolException

Posted

技术标签:

【中文标题】在 Retrofit2 上使用 HTTPs 的 SSLProtocolException【英文标题】:SSLProtocolException with HTTPs on Retrofit2 【发布时间】:2016-09-21 21:25:48 【问题描述】:

我在 android 4.4 手机(特别是 Galaxy S4,虽然我相信这不是手机本身的问题)上遇到了问题。在使用 Retrofit2.Http 时,我在 HTTPS 上连接时收到以下错误:

javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x737510d0: Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x727fb7d0:0x00000000)

我发现一些资源表明这是 Android 4.4(1) 上的一个错误,但给出的示例始终假定使用了 HttpsUrlConnection 类,我没有使用它。

我确实在这里找到了一些关于为连接设置特定 ConnectionSpec 实例的答案,这似乎很有希望,因为它是 Retrofit-specific(2)。不幸的是,这对错误没有影响。我的代码示例(我尝试了 MODERN_TLS 和 COMPATIBLE_TLS):

return new OkHttpClient.Builder()
                .addInterceptor(interceptor)
                .connectionSpecs(Collections.singletonList(ConnectionSpec.MODERN_TLS))
                .build();

切换到“http”确实有效,但这不是最终版本的选项。绝对需要安全、加密的连接,因此任何完全禁用 HTTPS 或盲目接受任何证书的解决方案都不太可能被接受。

提前致谢!

(1)Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: Failure in SSL library, usually a protocol error

(2)Retrofit OkHttp SSLHandshakeException

编辑:

运行 openssl 给出以下结果:

[[my_computer]]$ ./openssl s_client -connect [[server_hidden_by_me]]:443 -tls1


CONNECTED(00000003)
depth=2 /C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/OU=Domain Control Validated/OU=PositiveSSL/CN=[hidden_by_me]
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
 1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
 2 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
---
Server certificate
-----BEGIN CERTIFICATE-----
[snip certificate for brevity]
-----END CERTIFICATE-----
subject=/OU=Domain Control Validated/OU=PositiveSSL/CN=[hidden_by_me]
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
---
No client certificate CA names sent
---
SSL handshake has read 5449 bytes and written 426 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-SEED-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : DHE-RSA-SEED-SHA
    Session-ID: A4D84E2A0A16DB03082172695141BE1BB562F920ED4E4F6A9139733D9CAB7A54
    Session-ID-ctx: 
    Master-Key: 119C10E5CC34297D1717E4AEAB8BF1CAA8BA012C125B10513FBFE0854B6AB0E9E65536F801990CA3C992FEB69ADBE279
    Key-Arg   : None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 5c 21 2c ac 68 a9 ef 71-8c 69 2a 86 0c da 6c cd   \!,.h..q.i*...l.
    [snipped for brevity]

    Start Time: 1464109807
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
---

【问题讨论】:

AFAIK Android 上的所有 HTTP 库(包括 HttpsUrlConnection)都依赖于标准 SSLSocket——它们只会改变配置 SSLSocket 选项的方式。 我试过这个,但没有任何区别。 ***.com/a/34904758/472737 【参考方案1】:

Nexus 7 (Android 4.4.4) 也有类似问题。发现它尝试通过我的服务器上默认未启用的 TLSv1 进行连接。通过启用它修复 =)

您可以使用 openssl 测试您的服务器: openssl s_client -connect google.com:443 -tls1

更新:

此代码在我的设备上强制使用 TLSv1,顺便说一句,它仅支持 SSLv3、TLSv1。用okhttp:3.3.0测试过

ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
        .tlsVersions(TlsVersion.TLS_1_0)
        .allEnabledCipherSuites()
        .build();

OkHttpClient client = new OkHttpClient.Builder()
        .connectionSpecs(Collections.singletonList(spec))
        .build();

Request request = new Request.Builder()
        .url("https://192.168.0.19:44330")
        .build();

try 
    client.newCall(request).execute();
 catch (IOException e) 
    e.printStackTrace();

使用 openssl 服务器查看它真正使用的协议版本,您将需要证书和密钥文件:

openssl s_server -key key.pem -cert cert.cer -accept 44330 -www -msg

希望这会有所帮助!

【讨论】:

我已经用运行该命令的结果编辑了我的问题。在我看来一切正常,但我不完全确定如何解释结果。 该错误表示该错误不是由于缺少 TLS 而是由于未禁用 SSLv3 引起的。如果您的服务器可以从 Internet 访问,请使用 ssllabs.com/ssltest 对其进行测试 您是正确的 - 报告表明 SSLv3 已启用。有没有好办法禁止OkHttp使用? 我应该注意到我没有任何运气禁用 SSLv3。我试过这个(***.com/a/34904758/472737)但没有成功。堆栈跟踪是相同的。 @MarkHerscher 更新了如何强制 TLSv1 的示例答案,您也可以尝试其他版本。

以上是关于在 Retrofit2 上使用 HTTPs 的 SSLProtocolException的主要内容,如果未能解决你的问题,请参考以下文章

如何在Android上通过retrofit2调用带有Cognito Credentials的API网关?

无法使用retrofit2解析api

使用 Retrofit2(kotlin) 的 CURL 请求

HTTP/2 与 OkHttp3 和 Retrofit2

Retrofit2系列:简单的Get请求

Android实战——Retrofit2的使用和封装