目录
访问网络时,出现错误:
javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xb7eabc88: Failure in SSL library, usually a protocol error error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version (external/openssl/ssl/s23_clnt.c:741 0xa4fb8d5c:0x00000000)
SSLSocket的setEnabledProtocols配置支持TLSv1.1,TLSv1.2协议
错误:java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
可以给客户端安装证书
除了给客户端安装证书外,还有其他的解决办法
1、忽略所有证书验证
2、自定义对证书验证(需要将证书提前放到assert文件夹中)
访问网络时,出现错误:
javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xb7eabc88: Failure in SSL library, usually a protocol error error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version (external/openssl/ssl/s23_clnt.c:741 0xa4fb8d5c:0x00000000)
通过Https通信,客户端与服务端在SSL/TLS层建立安全连接前,涉及到版本协商过程。
Android 4.4默认支持的SSL/TLS版本是SSLv3(1996发布,2015弃用),TLSv1(1996发布)。但TLSv1.1,TLSv1.2(主流)实际上也是在其支持范围内的,需要人为去配置。
SSLSocket的setEnabledProtocols配置支持TLSv1.1,TLSv1.2协议
在okhttp中,建立Tls连接,创建套接字是通过SSLSocketFactory实现的,重写SSLSocketFactory,通过SSLSocket的setEnabledProtocols(String protocols[]),使其支持TLSv1.1,TLSv1.2。最后将其设置给OkHttpClient
SSLSocketFactory factory = new SSLSocketFactoryCompat(context);
// okhttpClient的builder
builder.sslSocketFactory(factory);
public class SSLSocketFactoryCompat extends SSLSocketFactory{
private static final String[] TLS_V12_ONLY = {"TLSv1.2"};
private final SSLSocketFactory delegate;
public SSLSocketFactoryCompat() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, null, null);
delegate = sc.getSocketFactory();
}
public SSLSocketFactoryCompat(SSLSocketFactory delegate) {
if (delegate == null) {
throw new NullPointerException();
}
this.delegate = delegate;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
private Socket enableTls12(Socket socket) {
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 20) {
if (socket instanceof SSLSocket) {
((SSLSocket) socket).setEnabledProtocols(TLS_V12_ONLY);
}
}
return socket;
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTls12(delegate.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException {
return enableTls12(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
return enableTls12(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTls12(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTls12(delegate.createSocket(address, port, localAddress, localPort));
}
}
然而,通过上述配置后,有些url还是无法访问,会出现
错误:java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
这里我遇到的问题是客户端没有对应的根CA证书导致的
可以给客户端安装证书
在浏览器网站上,输入在Android上访问出错的url,点击链接-连接-证书有效-详细信息,证书链汇中选择最上面一个,导出根证书
导出后将文件传入Androidsd卡中,然后在设置中找到安装证书的选项。
或者,在代码中打开安装证书的设置
/**
* 安裝证书
*/
public void installCert(Context context) {
InputStream assetsIn = null;
Intent intent = KeyChain.createInstallIntent();
try {
//获取证书流,注意参数为assets目录文件全名
assetsIn = context.getAssets().open("a.cer");
byte[] cert = new byte[10240];
assetsIn.read(cert);
javax.security.cert.X509Certificate x509 = null;
try {
x509 = javax.security.cert.X509Certificate.getInstance(cert);
//将证书传给系统
intent.putExtra(KeyChain.EXTRA_CERTIFICATE, x509.getEncoded());
} catch (CertificateException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
//此处为给证书设置默认别名,第二个参数可自定义,设置后无需用户输入
intent.putExtra("name", "gz");
startActivity(intent);
}
安装成功后,尝试是否可以访问链接。文章来源:https://www.toymoban.com/news/detail-796575.html
除了给客户端安装证书外,还有其他的解决办法
1、忽略所有证书验证
2、自定义对证书验证(需要将证书提前放到assert文件夹中)
改造一下SSLSocketFactory文章来源地址https://www.toymoban.com/news/detail-796575.html
import android.content.Context;
import android.os.Build;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
public class SSLSocketFactoryCompat extends SSLSocketFactory{
private static final String[] TLS_V12_ONLY = {"TLSv1.1"};
private static final String TAG = "TAG";
private final SSLSocketFactory delegate;
private X509TrustManager trustManager;
private Context context;
public SSLSocketFactoryCompat(Context context) throws KeyManagementException, NoSuchAlgorithmException {
this.context = context;
/*
a.cer是浏览器导出的CA根证书(这里没有用到,另外一个解决方法 安装到设备中的)
j.pem证书中存储了服务器的公钥等信息,并没有经过CA机构的加密,一般是由服务器导出
*/
// delegate = getSslSocketFactoryTrustALL();
delegate = getSslSocketFactorySpecificVerify();
}
// 默认创建代理方式
private SSLSocketFactory getSslSocketFactoryDefault() throws NoSuchAlgorithmException, KeyManagementException {
final SSLSocketFactory delegate;
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, null, null);
delegate = sc.getSocketFactory();
return delegate;
}
// 自定义证书校验
private SSLSocketFactory getSslSocketFactorySpecificVerify() throws KeyManagementException, NoSuchAlgorithmException {
final SSLSocketFactory delegate;
SSLContext sc = SSLContext.getInstance("TLS");
// 自定义证书验证
trustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
if (chain == null || chain.length == 0) {
throw new CertificateException("checkServerTrusted: X509Certificate array is null");
}
if (!(null != authType && authType.equals("ECDHE_RSA"))) {
throw new CertificateException("checkServerTrusted: AuthType is not ECDHE_RSA");
}
//判断证书是否是本地信任列表里颁发的证书(系统默认的验证)
try {
TrustManagerFactory factory = TrustManagerFactory.getInstance("X509");
factory.init((KeyStore) null);
for (TrustManager trustManager : factory.getTrustManagers()) {
((X509TrustManager) trustManager).checkServerTrusted(chain, authType);
}
Log.i("TAG", "checkServerTrusted: 本地");
return;//用系统的证书验证服务器证书,验证通过就不需要继续验证证书信息;也可以注释掉,继续走自己的服务器证书逻辑
} catch (Exception e) {
Log.i(TAG, "checkServerTrusted: " + e.getMessage());
e.printStackTrace();
//注意这个地方不能抛异常,用系统的证书验证服务器证书,没通过就用自己的验证规则
// throw new CertificateException(e);
}
//获取本地证书中的信息
String clientEncoded = "";//公钥
String clientSubject = "";//颁发给
String clientIssUser = "";//颁发机构
try (InputStream inputStream = getAssetFileInputStream(context, "j.pem")) {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate clientCertificate = (X509Certificate) certificateFactory.generateCertificate(inputStream);
clientEncoded = new BigInteger(1, clientCertificate.getPublicKey().getEncoded()).toString(16);
clientSubject = clientCertificate.getSubjectDN().getName();
clientIssUser = clientCertificate.getIssuerDN().getName();
Log.i("TAG", "clientEncoded: " + clientEncoded);
Log.i("TAG", "clientSubject: " + clientSubject);
Log.i("TAG", "clientIssUser: " + clientIssUser);
} catch (Exception e) {
e.printStackTrace();
throw new CertificateException(e);
}
//获取网络中的证书信息
X509Certificate certificate = chain[0];
PublicKey publicKey = certificate.getPublicKey();
String serverEncoded = new BigInteger(1, publicKey.getEncoded()).toString(16);
if (!clientEncoded.equals(serverEncoded)) {
Log.i("TAG", "checkServerTrusted: server's PublicKey is not equals to client's PublicKey");
throw new CertificateException("server's PublicKey is not equals to client's PublicKey");
}
String subject = certificate.getSubjectDN().getName();
if (!clientSubject.equals(subject)) {
Log.i("TAG", "checkServerTrusted: server's SubjectDN is not equals to client's SubjectDN");
throw new CertificateException("server's SubjectDN is not equals to client's SubjectDN");
}
String issuser = certificate.getIssuerDN().getName();
if (!clientIssUser.equals(issuser)) {
Log.i("TAG", "checkServerTrusted: server's IssuerDN is not equals to client's IssuerDN");
throw new CertificateException("server's IssuerDN is not equals to client's IssuerDN");
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
sc.init(null, new TrustManager[]{trustManager}, new SecureRandom());
delegate = sc.getSocketFactory();
return delegate;
}
// 信任所有证书
private SSLSocketFactory getSslSocketFactoryTrustALL() throws NoSuchAlgorithmException, KeyManagementException {
final SSLSocketFactory delegate;
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() {
Log.i("TAG", "getAcceptedIssuers: ");
return new X509Certificate[0];
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom());
delegate = sslContext.getSocketFactory();
return delegate;
}
public SSLSocketFactoryCompat(SSLSocketFactory delegate) {
if (delegate == null) {
throw new NullPointerException();
}
this.delegate = delegate;
}
@Override
public String[] getDefaultCipherSuites() {
Log.i(TAG, "getDefaultCipherSuites: ");
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
Log.i(TAG, "getSupportedCipherSuites: ");
return delegate.getSupportedCipherSuites();
}
private Socket enableTls12(Socket socket) {
Log.i(TAG, "enableTls12: ");
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 20) {
if (socket instanceof SSLSocket) {
((SSLSocket) socket).setEnabledProtocols(TLS_V12_ONLY);
}
}
return socket;
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
Log.i(TAG, "createSocket: ");
return enableTls12(delegate.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException {
Log.i(TAG, "createSocket: ");
return enableTls12(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
Log.i(TAG, "createSocket: ");
return enableTls12(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
Log.i(TAG, "createSocket: ");
return enableTls12(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
Log.i(TAG, "createSocket: ");
return enableTls12(delegate.createSocket(address, port, localAddress, localPort));
}
private static InputStream getAssetFileInputStream(Context context, String assetsFileName) {
try {
return context.getAssets().open(assetsFileName);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
到了这里,关于Android低版本(4.4)okhttp 网络适配的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!