对于HTTPS的请求我们一般都是采取忽略的策略,一般而言直接实现X509TrustManager接口,简单的以SSLContext获取SSLSocketFactory就可以的了,实测6.0以上还未遇到过什么奇葩的问题,6.0以下的,正常情况下也不会有什么问题,但最近偶然发现一客户提供的https地址请求居然报错了,抛出了javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException的错误,百思不得其解,搜罗百度才发现原来OKHttp在android4.4中存在这样的错误,特在此做个记录!
对于解决OkHttp请求部分的https在Android4.4报javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException的错误的方法,就是重写SSLSocketFactory,代码如下:
class SSLSocketFactoryCompat : SSLSocketFactory { private var defaultFactory: SSLSocketFactory? = null companion object{ var protocols: Array<String>? = null var cipherSuites:Array<String>? = null init { try { var socket = SSLSocketFactory.getDefault ().createSocket() as SSLSocket if (socket != null) { var protocols:LinkedList<String> = LinkedList() for (protocol in socket.supportedProtocols){ if (!protocol.toUpperCase().contains("SSL")){ protocols.add(protocol) } } SSLSocketFactoryCompat.protocols = protocols.toArray(arrayOfNulls(protocols.size)) /* set up reasonable cipher suites */ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { // choose known secure cipher suites var allowedCiphers = arrayOf(// TLS 1.2 "TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECHDE_RSA_WITH_AES_128_GCM_SHA256", // maximum interoperability "TLS_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA", // additionally "TLS_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA") var availableCiphers:Array<String> = socket.supportedCipherSuites // take all allowed ciphers that are available and put them into preferredCiphers val preferredCiphers = hashSetOf<String>() preferredCiphers.addAll(allowedCiphers) preferredCiphers.retainAll(availableCiphers) /* For maximum security, preferredCiphers should *replace* enabled ciphers (thus disabling * ciphers which are enabled by default, but have become unsecure), but I guess for * the security level of DAVdroid and maximum compatibility, disabling of insecure * ciphers should be a server-side task */ // add preferred ciphers to enabled ciphers var enabledCiphers = preferredCiphers enabledCiphers.addAll(socket.enabledCipherSuites) SSLSocketFactoryCompat.cipherSuites = enabledCiphers.toArray(arrayOfNulls(enabledCiphers.size)) } } } catch (e:IOException) { e.printStackTrace() } } } constructor(tm: X509TrustManager?){ try { val sslContext = SSLContext.getInstance("TLS") sslContext.init(null, if (tm != null) arrayOf<X509TrustManager>(tm) else null, null) defaultFactory = sslContext.getSocketFactory() } catch (e: GeneralSecurityException) { throw AssertionError() // The system has no TLS. Just give up. } } private fun upgradeTLS(ssl: SSLSocket) { // Android 5.0+ (API level21) provides reasonable default settings // but it still allows SSLv3 // https://developer.android.com/about/versions/android-5.0-changes.html#ssl if (protocols != null) { ssl.enabledProtocols = protocols } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && cipherSuites != null) { ssl.enabledCipherSuites = cipherSuites } } override fun getDefaultCipherSuites(): Array<String> { return cipherSuites!! } override fun getSupportedCipherSuites(): Array<String> { return cipherSuites!! } @Throws(IOException::class) override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket { val ssl = defaultFactory?.createSocket(s, host, port, autoClose) if (ssl is SSLSocket) upgradeTLS(ssl as SSLSocket) return ssl!! } @Throws(IOException::class, UnknownHostException::class) override fun createSocket(host: String, port: Int): Socket { val ssl = defaultFactory?.createSocket(host, port) if (ssl is SSLSocket) upgradeTLS(ssl as SSLSocket) return ssl!! } @Throws(IOException::class, UnknownHostException::class) override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket { val ssl = defaultFactory?.createSocket(host, port, localHost, localPort) if (ssl is SSLSocket) upgradeTLS(ssl as SSLSocket) return ssl!! } @Throws(IOException::class) override fun createSocket(host: InetAddress, port: Int): Socket { val ssl = defaultFactory?.createSocket(host, port) if (ssl is SSLSocket) upgradeTLS(ssl as SSLSocket) return ssl!! } @Throws(IOException::class) override fun createSocket( address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int ): Socket { val ssl = defaultFactory?.createSocket(address, port, localAddress, localPort) if (ssl is SSLSocket) upgradeTLS(ssl as SSLSocket) return ssl!! } }
再实现X509TrustManager接口:
class HttpsTrustManager: X509TrustManager { override fun checkClientTrusted(chain: Array<X509Certificate?>, authType: String?) { } override fun checkServerTrusted(chain: Array<X509Certificate?>, authType: String?) { } override fun getAcceptedIssuers(): Array<X509Certificate?> { return arrayOfNulls(0) } } class TrustAllHostnameVerifier : HostnameVerifier{ override fun verify(hostname: String?, session: SSLSession?): Boolean { return true } } private fun createSSLSocketFactory(): SSLSocketFactory { return SSLSocketFactoryCompat(HttpsTrustManager()) }
调用:
sslSocketFactory(createSSLSocketFactory(),HttpsTrustManager())//ssl hostnameVerifier(TrustAllHostnameVerifier())//忽略ssl