Android 9(API 28)中HttpsURLConnection失效,抛出SSLPeerUnverifiedException
嘿,这个问题我之前帮不少开发者排查过,Android 9(API 28)下用HttpsURLConnection触发SSLPeerUnverifiedException,大多和系统默认安全配置变化或者证书信任逻辑有关,我给你拆解下核心原因和对应的解决办法:
常见原因1:Android 9默认限制了TLS版本
Android 9开始,系统默认只允许使用TLS 1.2及以上版本的加密协议,如果你的服务器还在使用旧的TLS 1.0/1.1,就会触发这个异常。
临时兼容方案(不推荐生产环境)
如果暂时没法升级服务器,可以通过自定义SSLSocketFactory让HttpsURLConnection支持旧版本协议:
// 创建支持多版本TLS的SSL上下文 SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); sslContext.init(null, null, new SecureRandom()); // 包装原生SSLSocketFactory,启用旧协议 SSLSocketFactory socketFactory = new SSLSocketFactoryCompat(sslContext.getSocketFactory()); // 配置到HttpsURLConnection HttpsURLConnection connection = (HttpsURLConnection) obj.openConnection(); connection.setSSLSocketFactory(socketFactory); // 后续操作... connection.connect();
对应的SSLSocketFactoryCompat实现:
public class SSLSocketFactoryCompat extends SSLSocketFactory { private final SSLSocketFactory delegate; public SSLSocketFactoryCompat(SSLSocketFactory delegate) { this.delegate = delegate; } @Override public String[] getDefaultCipherSuites() { return delegate.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return delegate.getSupportedCipherSuites(); } @Override public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { return enableOldTlsProtocols((SSLSocket) delegate.createSocket(s, host, port, autoClose)); } @Override public Socket createSocket(String host, int port) throws IOException, UnknownHostException { return enableOldTlsProtocols((SSLSocket) delegate.createSocket(host, port)); } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { return enableOldTlsProtocols((SSLSocket) delegate.createSocket(host, port, localHost, localPort)); } @Override public Socket createSocket(InetAddress host, int port) throws IOException { return enableOldTlsProtocols((SSLSocket) delegate.createSocket(host, port)); } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { return enableOldTlsProtocols((SSLSocket) delegate.createSocket(address, port, localAddress, localPort)); } private Socket enableOldTlsProtocols(SSLSocket socket) { // 启用TLS 1.0、1.1、1.2 socket.setEnabledProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"}); return socket; } }
注意:这个方案只是临时过渡,强烈建议优先升级服务器到TLS 1.2+,旧协议存在安全漏洞。
常见原因2:服务器证书不被系统信任
如果服务器用的是自签名证书,或者证书链不完整(缺少中间证书),Android系统会拒绝信任该证书,从而触发异常。
测试环境临时方案(绝对禁止生产环境使用)
如果只是本地测试,可以临时跳过证书验证(会完全失去HTTPS的安全防护,仅用于测试):
// 创建信任所有证书的TrustManager TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {} @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {} @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } }; // 初始化SSL上下文 SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new SecureRandom()); // 配置HttpsURLConnection HttpsURLConnection connection = (HttpsURLConnection) obj.openConnection(); connection.setSSLSocketFactory(sslContext.getSocketFactory()); // 跳过主机名验证 connection.setHostnameVerifier((hostname, session) -> true); connection.connect();
生产环境正确方案:导入自定义证书信任库
把服务器的合法证书(.crt/.pem格式)放到app/src/main/res/raw目录,然后通过自定义TrustManager让应用信任该证书:
// 加载raw目录下的证书文件 InputStream certInputStream = getResources().openRawResource(R.raw.your_server_cert); // 解析证书 CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate) cf.generateCertificate(certInputStream); certInputStream.close(); // 创建KeyStore并添加证书 KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, null); keyStore.setCertificateEntry("server_cert", cert); // 初始化TrustManagerFactory TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(keyStore); // 创建SSL上下文 SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), new SecureRandom()); // 配置到HttpsURLConnection HttpsURLConnection connection = (HttpsURLConnection) obj.openConnection(); connection.setSSLSocketFactory(sslContext.getSocketFactory()); connection.connect();
总结
优先推荐的解决顺序是:
- 升级服务器到TLS 1.2+,确保证书链完整且被权威CA签发
- 生产环境使用自定义证书信任库方案
- 仅测试环境使用跳过验证的临时方案
内容的提问来源于stack exchange,提问作者Hosam Odeh
相关产品推荐
相关产品推荐

