square / okhttp

Square’s meticulous HTTP client for the JVM, Android, and GraalVM.
https://square.github.io/okhttp/
Apache License 2.0
45.87k stars 9.16k forks source link

Handshake failed with self-signed ssl cert with okhhtpps v3 and sdk24 #2669

Closed punksta closed 8 years ago

punksta commented 8 years ago

What kind of issue is this?

javax.net.ssl.SSLHandshakeException: Handshake failed
    at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:390)
    at okhttp3.internal.io.RealConnection.connectTls(RealConnection.java:239)
    at okhttp3.internal.io.RealConnection.establishProtocol(RealConnection.java:196)
    at okhttp3.internal.io.RealConnection.buildConnection(RealConnection.java:171)
    at okhttp3.internal.io.RealConnection.connect(RealConnection.java:111)
    at okhttp3.internal.http.StreamAllocation.findConnection(StreamAllocation.java:187)
    at okhttp3.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:123)
    at okhttp3.internal.http.StreamAllocation.newStream(StreamAllocation.java:93)
    at okhttp3.internal.http.HttpEngine.connect(HttpEngine.java:296)
    at okhttp3.internal.http.HttpEngine.sendRequest(HttpEngine.java:248)
    at okhttp3.RealCall.getResponse(RealCall.java:243)
    at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:201)
    at ru.example.example.tech.modules.NetModule$Companion$TokenInterceptor.intercept(NetModule.kt:156)
    at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:190)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:163)
    at okhttp3.RealCall.execute(RealCall.java:57)
    at retrofit2.OkHttpCall.execute(OkHttpCall.java:174)
    at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$RequestArbiter.request(RxJavaCallAdapterFactory.java:171)
    at rx.internal.operators.OperatorSubscribeOn$1$1$1.request(OperatorSubscribeOn.java:80)
    at rx.Subscriber.setProducer(Subscriber.java:211)
    at rx.internal.operators.OperatorSubscribeOn$1$1.setProducer(OperatorSubscribeOn.java:76)
    at rx.Subscriber.setProducer(Subscriber.java:205)
    at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:152)
    at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:138)
    at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:50)
    at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
    at rx.Observable.unsafeSubscribe(Observable.java:8666)
    at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94)
    at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:220)
    at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
    at java.util.concurrent.FutureTask.run(FutureTask.java:237)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
    at java.lang.Thread.run(Thread.java:818)
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=Failure in SSL library, usually a protocol error
error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol (external/openssl/ssl/s23_clnt.c:795 0xabea4dd4:0x00000000)
    at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
    at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:318)
    ... 36 more

Part of my dagger network module(kotlin)

 @Provides
    @Singleton
    fun provideOkHttpClient(context: Context, config: ApiConfig, token: IApiAuthTokenHolder): OkHttpClient =
            with(OkHttpClient.Builder()
                    .readTimeout(BuildConfig.READ_TIME_OUT, TimeUnit.SECONDS))
            {
                val host = Uri.parse(config.baseUrl()).host

                addInterceptor(TokenInterceptor(token.observe(), host))
                if (config.useSSL()) {
                    val certificatePinner = CertificatePinner.Builder()
                            .add(host, "sha256/${config.certHash()}")
                            .build();

                    certificatePinner(certificatePinner)

                    socketFactory(getSllSocketFactory(context, config.sllCertResource()))
                    hostnameVerifier { hostname, sslSession -> true }

                    val spec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
                            .tlsVersions(TlsVersion.TLS_1_2)
                            .build();
                    connectionSpecs(mutableListOf(spec))
                }
                build()
            }

    @Throws(CertificateException::class, NoSuchAlgorithmException::class,
            IOException::class, KeyManagementException::class, KeyStoreException::class)
    private fun getSllSocketFactory(context: Context, sllResId: Int): SSLSocketFactory {

        val cf = CertificateFactory.getInstance("X.509")
        val cert = context.resources.openRawResource(sllResId)
        val ca: Certificate
        try {
            ca = cf.generateCertificate(cert)
        } finally {
            cert.close()
        }

        val keyStoreType = KeyStore.getDefaultType()
        val keyStore = KeyStore.getInstance(keyStoreType)
        keyStore.load(null, null)
        keyStore.setCertificateEntry("ca", ca)

        val tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
        val tmf = TrustManagerFactory.getInstance(tmfAlgorithm)
        tmf.init(keyStore)

        val sslContext = SSLContext.getInstance("TLS")
        sslContext.init(null, tmf.trustManagers, null)
        return sslContext.socketFactory
    }

    @Provides
    @Singleton
    fun provideApi(client: OkHttpClient,
                   gson: Gson,
                   config: ApiConfig): Api {
        val retrofit = Retrofit.Builder()
                .baseUrl(config.baseUrl())
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
                .client(client)
                .build();

        return retrofit.create(Api::class.java)
    }

connection spec:

Protocol
TLS 1.2
Key Exchange
ECDHE_RSA
Cipher Suite
AES_128_GCM

sdk\libs versions:

compileSdkVersion 24
    buildToolsVersion "24.0.0"
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 24
    }
...
 compile 'com.squareup.okhttp3:okhttp:3.3.1'
 compile 'com.squareup.retrofit2:retrofit:2.1.0'

I use following command to generate hash of public key. Also I use cert.crt for socketFactory

openssl x509 -in cert.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

tested on google emulators for x86 22 and 23 api

swankjesse commented 8 years ago

Really difficult to tell what action we should take here. Could you make a smaller test case, something that has no dependencies on anything other than OkHttp?

punksta commented 8 years ago

After reading okhttp3/recipes/CustomTrust example fixed! instead of

Builder::setSocketFactory(SocketFactory)

I need use

Builder::sslSocketFactory(
        SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) 

so I rewrite getSllSocketFactory to

 @Throws(CertificateException::class, NoSuchAlgorithmException::class,
            IOException::class, KeyManagementException::class, KeyStoreException::class)
    private fun initSllFactory(okhttpClientBuilder: OkHttpClient.Builder, context: Context, certResourceId: Int) {

        val cf = CertificateFactory.getInstance("X.509")
        val cert = context.resources.openRawResource(certResourceId)
        val ca: Certificate
        try {
            ca = cf.generateCertificate(cert)
        } finally {
            cert.close()
        }

        val keyStoreType = KeyStore.getDefaultType()
        val keyStore = KeyStore.getInstance(keyStoreType)
        keyStore.load(null, null)
        keyStore.setCertificateEntry("ca", ca)

        val tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
        val tmf: TrustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm)
        tmf.init(keyStore)

        val sslContext = SSLContext.getInstance("TLS")
        sslContext.init(null, tmf.trustManagers, null)

        okhttpClientBuilder.sslSocketFactory(sslContext.socketFactory, 
                tmf.trustManagers.get(0) as X509TrustManager?)  
    }