android - 在 Android 4.2.2 (API 17) 上进行 API 调用时出现 SSLHandshakeException
问题描述
我按照本教程使用 Volley 进行安全 API 调用。
我的活动
class MainActivity : AppCompatActivity() {
private val SECURE_URL = "https://epas.vercel.app/api/v1/login"
private var requestQueue: RequestQueue? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sendRequest()
}
private fun sendRequest() {
requestQueue = Volley.newRequestQueue(this, HurlStack(null, pinnedSSLSocketFactory()))
val request = StringRequest(Request.Method.POST, SECURE_URL, { response ->
println("_print::Response: $response")
}) { error ->
println("_print::Request failed: $error")
}
requestQueue!!.add(request)
}
private fun pinnedSSLSocketFactory(): SSLSocketFactory? {
try {
return TLSSocketFactory("SX23A4")
} catch (e: KeyManagementException) {
e.printStackTrace()
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
}
return null
}
}
公钥管理器
class PubKeyManager(private val publicKey: String) : X509TrustManager {
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
require(chain.isNotEmpty()) { "checkServerTrusted: X509Certificate is empty" }
// Perform customary SSL/TLS checks
val tmf: TrustManagerFactory
try {
tmf = TrustManagerFactory.getInstance("X509")
tmf.init(null as KeyStore?)
for (trustManager in tmf.trustManagers) {
(trustManager as X509TrustManager).checkServerTrusted(chain, authType)
}
} catch (e: Exception) {
throw CertificateException(e.toString())
}
// Hack ahead: BigInteger and toString(). We know a DER encoded Public
// Key starts with 0x30 (ASN.1 SEQUENCE and CONSTRUCTED), so there is
// no leading 0x00 to drop.
val pubkey = chain[0].publicKey as RSAPublicKey
val encoded = BigInteger(1 /* positive */, pubkey.encoded).toString(16)
// Pin it!
val expected = publicKey.equals(encoded, ignoreCase = true)
// fail if expected public key is different from our public key
if (!expected) throw CertificateException("Not trusted")
}
override fun getAcceptedIssuers(): Array<X509Certificate?> = arrayOfNulls(0)
}
和 TLSSocketFactory
class TLSSocketFactory(publicKey: String?) : SSLSocketFactory() {
private val internalSSLSocketFactory: SSLSocketFactory
override fun getDefaultCipherSuites(): Array<String> =
internalSSLSocketFactory.defaultCipherSuites
override fun getSupportedCipherSuites(): Array<String> =
internalSSLSocketFactory.supportedCipherSuites
@Throws(IOException::class)
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose))
}
@Throws(IOException::class)
override fun createSocket(host: String, port: Int): Socket =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port))
@Throws(IOException::class)
override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort))
@Throws(IOException::class)
override fun createSocket(host: InetAddress, port: Int): Socket =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port))
@Throws(IOException::class)
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort))
private fun enableTLSOnSocket(socket: Socket): Socket {
if (socket is SSLSocket) {
socket.enabledProtocols = addTLS1And2(socket.enabledProtocols)
}
return socket
}
private fun addTLS1And2(currentProtocols: Array<String>): Array<String> {
var hasTLSv1_1 = false
var hasTLSv1_2 = false
val tlsv11 = "TLSv1.1"
val tlsv12 = "TLSv1.2"
val list = ArrayList(listOf(*currentProtocols))
for (protocol in currentProtocols) {
if (protocol == tlsv11) hasTLSv1_1 = true
if (protocol == tlsv12) hasTLSv1_2 = true
}
if (!hasTLSv1_1) list.add(tlsv11)
if (!hasTLSv1_2) list.add(tlsv12)
return list.toTypedArray()
}
init {
val context = SSLContext.getInstance("TLS")
val tm = arrayOf<TrustManager>(PubKeyManager(publicKey!!))
context.init(null, tm, null)
internalSSLSocketFactory = context.socketFactory
}
}
当我在装有 Android 4.2.2 (API 17) 的设备上运行该应用程序时,我收到此错误:
Request failed: com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x516e3e70: Failure in SSL library, usually a protocol error
我也试过改造,我得到了同样的错误。
是否有任何解决方法或推荐的 API 调用方式在使用 volley 或改造的旧设备上调用 MODEL_TLS 服务器?
解决方案
推荐阅读
- jpa - 休眠条件查询中的 DATEDIFF 函数
- c# - Unity:当我释放移动输入时,玩家行为怪异
- google-smart-home - 定制现有的智能错误
- kubernetes - 使用 CLI 部署后,kubeflow UI 显示“无法访问站点”
- python - 在 Python 中使用贪心算法进行活动选择
- python - 正则表达式仅匹配副标题
- pointers - 为什么更改指针副本不会影响初始对象
- python - Keras:对model.predict()进行图像分类的解释
- asp.net-mvc - 添加 DevExpress 控件后,MVC 5 应用程序路由不起作用
- reactjs - 对于有条件地呈现 JSX 的 React 组件,更新周期如何表现?