Android 4.0-4.4设备使用Volley StringRequest遇SSLHandshakeException求助
解决Android 4.x设备上Volley StringRequest的SSL握手失败问题
这问题我之前碰过好多次了,你看到的SSLHandshakeException(具体是SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure),核心原因很明确:Android 4.0到4.4的系统默认不支持TLS 1.2及更高版本的加密协议,而现在几乎所有正规服务器都禁用了不安全的SSLv3、TLS 1.0这些旧协议,只认TLS 1.2+,所以4.x设备发起HTTPS请求时根本没法和服务器完成握手,直接报错。
具体解决方案
要让Volley在4.x设备上正常工作,我们需要自定义Volley的网络栈,强制开启TLS 1.2支持,分三步来:
- 写一个自定义的SSLSocketFactory,强制启用TLS协议
这个类会让Socket优先使用TLS 1.2,同时兼容低版本的TLS协议,确保4.x系统能识别:
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; public class TLSSocketFactory extends SSLSocketFactory { private SSLSocketFactory internalSSLSocketFactory; public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException { SSLContext context = SSLContext.getInstance("TLS"); context.init(null, null, null); internalSSLSocketFactory = context.getSocketFactory(); } @Override public String[] getDefaultCipherSuites() { return internalSSLSocketFactory.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return internalSSLSocketFactory.getSupportedCipherSuites(); } @Override public Socket createSocket() throws IOException { return enableTLSOnSocket(internalSSLSocketFactory.createSocket()); } @Override public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose)); } @Override public Socket createSocket(String host, int port) throws IOException, UnknownHostException { return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)); } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort)); } @Override public Socket createSocket(InetAddress host, int port) throws IOException { return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)); } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort)); } private Socket enableTLSOnSocket(Socket socket) { if(socket != null && (socket instanceof SSLSocket)) { ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2", "TLSv1.1", "TLSv1"}); } return socket; } }
- 自定义HurlStack,把上面的SSLSocketFactory集成进去
这个类负责让Volley的网络请求使用我们自定义的Socket工厂:
import com.android.volley.toolbox.HurlStack; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; public class TLSStack extends HurlStack { private SSLSocketFactory sslSocketFactory; public TLSStack(SSLSocketFactory sslSocketFactory) { this.sslSocketFactory = sslSocketFactory; } @Override protected HttpURLConnection createConnection(URL url) throws IOException { HttpURLConnection connection = super.createConnection(url); if (connection instanceof HttpsURLConnection) { ((HttpsURLConnection) connection).setSSLSocketFactory(sslSocketFactory); } return connection; } }
- 初始化Volley RequestQueue时使用自定义栈
在你创建RequestQueue的地方,替换掉默认的网络栈就行,记得加异常处理,防止初始化失败:
RequestQueue queue; try { TLSSocketFactory tlsSocketFactory = new TLSSocketFactory(); TLSStack tlsStack = new TLSStack(tlsSocketFactory); queue = Volley.newRequestQueue(getApplicationContext(), tlsStack); } catch (KeyManagementException | NoSuchAlgorithmException e) { // 如果初始化失败, fallback到默认栈(虽然4.x可能还是不行,但至少不会崩溃) e.printStackTrace(); queue = Volley.newRequestQueue(getApplicationContext()); } // 接下来正常用你的StringRequest就行 StringRequest stringRequest = new StringRequest(Request.Method.GET, "你的HTTPS接口地址", response -> { // 处理成功响应的逻辑 }, error -> { // 处理错误的逻辑 }); queue.add(stringRequest);
额外提醒
- 如果你的服务器用的是自签名证书,那还得在
TLSSocketFactory的context.init()方法里传入自定义的TrustManager,不然还是会证书验证失败。 - 最好找个Android 4.x的真机或者模拟器测一测,确保生效。
内容的提问来源于stack exchange,提问作者Ashwin H
相关产品推荐
相关产品推荐

