使用OkHttp请求https在android4.4报错javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException
·
对于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
更多推荐


所有评论(0)