Android &引用;javax.net.ssl.SSLHandshakeException:握手失败;即使在添加自定义TrustManager和证书固定之后

Android &引用;javax.net.ssl.SSLHandshakeException:握手失败;即使在添加自定义TrustManager和证书固定之后,android,kotlin,retrofit2,sslhandshakeexception,x509trustmanager,Android,Kotlin,Retrofit2,Sslhandshakeexception,X509trustmanager,我计划使用Jamendo API下载音乐,但在连接到API时引发了以下错误 javax.net.ssl.SSLHandshakeException: Handshake failed at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:286) at okhttp3.internal.connecti

我计划使用Jamendo API下载音乐,但在连接到API时引发了以下错误

javax.net.ssl.SSLHandshakeException: Handshake failed
        at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:286)
        at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:351)
        at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:310)
        at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:178)
        at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:236)
        at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:109)
        at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:77)
        at okhttp3.internal.connection.Transmitter.newExchange$okhttp(Transmitter.kt:162)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:35)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:82)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
        at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:84)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:71)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
        at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.kt:184)
        at okhttp3.RealCall.execute(RealCall.kt:66)
        at com.example.musicplayer.utils.CertificatePinningKt.certificatePinning(CertificatePinning.kt:26)
        at com.example.musicplayer.fragments.HomeFragment$onActivityCreated$1$1.invokeSuspend(HomeFragment.kt:42)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:740)
     Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xedb6ee48: Failure in SSL library, usually a protocol error
    error:100000f0:SSL routines:OPENSSL_internal:UNSUPPORTED_PROTOCOL (external/boringssl/src/ssl/handshake_client.cc:576 0xe5faba43:0x00000000)
        at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
        at com.android.org.conscrypt.NativeSsl.doHandshake(NativeSsl.java:375)
        at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:224)
            ... 27 more
然后我知道Android不知道某些CA证书,解决方法是启用TrustManager不验证https请求上的任何证书,如的已接受答案,或者添加自定义TrustManager以接受我需要与之通信的服务器的CA证书,我采取了后一种方法

!)首先,我使用以下命令检查了Jamendo的服务器信息

$ openssl s_client -connect jamendo.com:443 | openssl x509 -noout -subject -issuer
导致

深度=3 C=美国,O=“Go Daddy Group,Inc.”,OU=Go Daddy 2级认证机构 验证返回:1
深度=2 C=美国,ST=亚利桑那州,L=斯科茨代尔,O=“GoDaddy.com,Inc.”,CN=根证书颁发机构-G2 验证返回:1
深度=1 C=美国,ST=亚利桑那州,L=斯科茨代尔,O=“GoDaddy.com,Inc.”,OU=,CN=Go Daddy安全证书颁发机构-G2 验证返回:1
深度=0 C=LU,L=Luxembour,O=Jamendo SA,CN=Jamendo.com 验证返回:1
subject=/C=LU/L=luxembour/O=Jamendo SA/CN=.Jamendo.com issuer=/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com,Inc./OU=Daddy安全证书颁发机构-G2

看起来Jamendo正在使用GoDaddy的托管服务,所以我转到了他们的,在上面的输出中,使用的证书来自G2组,所以我下载了输出中提到的根证书和中间证书(前两个)

2)和然后我尝试创建自定义的SSLSocketFactory和TrustManager,这导致了相同的错误。该方法如以下代码所示

3)然后我了解了证书固定,并想尝试一下,通过运行以下命令获取下载证书的base64编码

openssl x509 -in cert.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
基于的已接受答案,然后尝试运行以下代码,但错误仍然存在

fun certificatePinning() {
    val hostname = "api.jamendo.com/v3.0/playlists/?client_id=c7668145&format=jsonpretty&namesearch=cool"
    val sha256base64_hash1 = "sha256/Ko8tivDrEjiY90yGasP6ZpBU4jwXvHqVvQI0GS3GNdA="
    val sha256base64_hash2 = "sha256/8Rw90Ej3Ttt8RRkrg+WYDS9n7IS03bk5bjP/UXPtaY8="


    val certificatePinner = CertificatePinner.Builder()
        .add(hostname, sha256base64_hash1)
        .add(hostname, sha256base64_hash2)
        .build()
    val client = OkHttpClient.Builder()
        .certificatePinner(certificatePinner)
        .build()

    val request = Request.Builder()
        .url("https://$hostname")
        .build()
    client.newCall(request).execute()
}

如果有人能给我指出正确的方向,那将是一个很大的帮助。

握手问题是由于Jamendo API使用旧的不推荐的TLS协议版本(1.0)而不支持新的协议版本:
* *


旁注:我肯定会选择自定义TrustManager实现,这只有在端点使用自签名证书时才有意义。作为一项基本检查,我将通过尝试直接在手机/模拟器浏览器上打开Jamendo URL来验证您的Android系统TrustStore是否正常工作,以查看您是否遇到任何问题?固定提供了额外的保护,但不能解决您看到的基本握手问题

检查您的“TLS”支持。我面临着这个问题。在我的应用程序中,我使用翻新库。因此,请尝试将“COMPATIBLE_TLS”配置添加到OkHttpClient,如:

OkHttpClient client = new OkHttpClient();
List<ConnectionSpec> connectionSpecs = new ArrayList<>();
connectionSpecs.add(ConnectionSpec.COMPATIBLE_TLS);
client.setConnectionSpecs(connectionSpecs);
...

在访问api.jamendo.com/v3.0/playlists/?client_id=c7668145&format=jsonpretty&namesearch=cool的默认手机浏览器和chromeOk浏览器时,我得到了一个JSON响应。这很好,在讨论中有些偏离了方向。因此,根据您的第一次stacktrace,握手失败,这既不是固定问题,也不是信任问题。Jamendo似乎不支持最新的TLS协议/密码:。感谢您提供到sslabs的链接。你是对的,他们只支持TLSV1.0,正如这里所写的,所有主要的互联网公司都会反对TLSV1.0。此外,正如我在他们的中间文章中发现的那样,改装已经停止了对TLSV1.0的支持。感谢您指出stacktrace中的“协议错误”。如果您可以通过包含这些链接和添加您的评论来修改您的答案,那么这将是正确的答案,并将帮助人们了解更多有关此问题的信息,因为在这方面不存在此类问题。
fun certificatePinning() {
    val hostname = "api.jamendo.com/v3.0/playlists/?client_id=c7668145&format=jsonpretty&namesearch=cool"
    val sha256base64_hash1 = "sha256/Ko8tivDrEjiY90yGasP6ZpBU4jwXvHqVvQI0GS3GNdA="
    val sha256base64_hash2 = "sha256/8Rw90Ej3Ttt8RRkrg+WYDS9n7IS03bk5bjP/UXPtaY8="


    val certificatePinner = CertificatePinner.Builder()
        .add(hostname, sha256base64_hash1)
        .add(hostname, sha256base64_hash2)
        .build()
    val client = OkHttpClient.Builder()
        .certificatePinner(certificatePinner)
        .build()

    val request = Request.Builder()
        .url("https://$hostname")
        .build()
    client.newCall(request).execute()
}
OkHttpClient client = new OkHttpClient();
List<ConnectionSpec> connectionSpecs = new ArrayList<>();
connectionSpecs.add(ConnectionSpec.COMPATIBLE_TLS);
client.setConnectionSpecs(connectionSpecs);
...
implementation 'com.squareup.okhttp:okhttp:2.7.5'