Android使用HTTPS进行IP直连握手失败问题(okHttp)
为什么要使用ip直连这种方式去请求我们的服务器呢?这其实和国内运营伤有关,运营商有时为了利益会将你的域名劫持换成他人的域名,为了防止这种情况的发生通用的解决办法要么联系运营商要么就只能使用ip直连了。普遍大家目前使用的都是okHttp,这里就以okHttp为例子。其实非常简单只需要设置一下两个方法就行:
OkHttpClient.Builder builder = new OkHttpClient.Builder();
....
String domain = ....;
builder.sslSocketFactory(new TlsSniSocketFactory(domain), new SSLUtil.TrustAllManager())
.hostnameVerifier(new TrueHostnameVerifier(domain));
通过调用sslSocketFactory()方法传入两个参数一个是:SSLSocket,还有一个x509TrustManager。我们来看看第一个参数是如何实现的:
public class TlsSniSocketFactory extends SSLSocketFactory {
private final String TAG = TlsSniSocketFactory.class.getSimpleName();
HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
String peerHost; public TlsSniSocketFactory(String peerHost) {
this.peerHost = peerHost;
} public TlsSniSocketFactory() {
} @Override
public Socket createSocket() {
return null;
} @Override
public Socket createSocket(String host, int port) {
return null;
} @Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) {
return null;
} @Override
public Socket createSocket(InetAddress host, int port) {
return null;
} @Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) {
return null;
} // TLS layer
@Override
public String[] getDefaultCipherSuites() {
return new String[0];
} @Override
public String[] getSupportedCipherSuites() {
return new String[0];
} @Override
public Socket createSocket(Socket plainSocket, String host, int port, boolean autoClose) throws IOException {
if (TextUtils.isEmpty(peerHost)) {
peerHost = host;
peerHost = ....;
} Log.i(TAG, "customized createSocket. host: " + peerHost);
InetAddress address = plainSocket.getInetAddress();
if (autoClose) {
// we don't need the plainSocket
plainSocket.close();
}
// create and connect SSL socket, but don't do hostname/certificate verification yet
SSLCertificateSocketFactory sslSocketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0);
SSLSocket ssl = (SSLSocket) sslSocketFactory.createSocket(address, port);
// enable TLSv1.1/1.2 if available
ssl.setEnabledProtocols(ssl.getSupportedProtocols());
// set up SNI before the handshake
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Log.i(TAG, "Setting SNI hostname");
sslSocketFactory.setHostname(ssl, peerHost);
} else {
Log.d(TAG, "No documented SNI support on Android <4.2, trying with reflection");
try {
java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class);
setHostnameMethod.invoke(ssl, peerHost);
} catch (Exception e) {
Log.w(TAG, "SNI not useable", e);
}
}
// verify hostname and certificate
SSLSession session = ssl.getSession();
if (!hostnameVerifier.verify(peerHost, session))
throw new SSLPeerUnverifiedException("Cannot verify hostname: " + peerHost);
Log.i(TAG, "Established " + session.getProtocol() + " connection with " + session.getPeerHost() +
" using " + session.getCipherSuite());
return ssl;
}
}
为了防止获取不到domain将外围的domain塞入,将这个domain塞入返回我们的ssl。x509TrustManagerx信任了所有的证书,当然正常情况下应该使用和后端约定好的证书,代码如下:
public class SSLUtil {
/**
* 默认信任所有的证书
* TODO 最好加上证书认证,主流App都有自己的证书
*
* @return
*/
@SuppressLint("TrulyRandom")
public static SSLSocketFactory createSSLSocketFactory() { SSLSocketFactory sSLSocketFactory = null; try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, new TrustManager[]{new TrustAllManager()},
new SecureRandom());
sSLSocketFactory = sc.getSocketFactory();
} catch (Exception e) {
} return sSLSocketFactory;
} public static class TrustAllManager implements X509TrustManager { @Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
} @Override
public void checkClientTrusted(X509Certificate[] chain, String authType) { } @Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
} public static class TrustAllHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
}
最后https需要校验我的domain是否是服务器提供的domain:
public class TrueHostnameVerifier implements HostnameVerifier {
public String domain; public TrueHostnameVerifier(String domain) {
this.domain = domain;
}
public TrueHostnameVerifier() {
} @Override
public boolean verify(String hostname, SSLSession session) {
if(TextUtils.isEmpty(domain)) {
domain = ...;
}
return HttpsURLConnection.getDefaultHostnameVerifier().verify(domain, session);
}
}
以上代码可以直接拷贝,省略号代码具体代码可能需要你自己去实现。希望对你有所帮助。
Android使用HTTPS进行IP直连握手失败问题(okHttp)的更多相关文章
- HTTPS IP直连问题小结
HTTPS IP直连问题小结: https://blog.csdn.net/leelit/article/details/77829196 可以使用OkHttpClient进行相同IP地址,不同DNS ...
- fiddler Android下https抓包全攻略
fiddler Android下https抓包全攻略 fiddler的http.https的抓包功能非常强大,可非常便捷得对包进行断点跟踪和回放,但是普通的配置对于像招商银行.支付宝.陌陌这样的APP ...
- 在深谈TCP/IP三步握手&四步挥手原理及衍生问题—长文解剖IP
如果对网络工程基础不牢,建议通读<细说OSI七层协议模型及OSI参考模型中的数据封装过程?> 下面就是TCP/IP(Transmission Control Protoco/Interne ...
- 关于Android的https通讯安全
原文链接:http://pingguohe.net/2016/02/26/Android-App-secure-ssl.html 起因 前段时间,同事拿着一个代码安全扫描出来的 bug 过来咨询,我一 ...
- Android 使用 HTTPS 问题解决(SSLHandshakeException)
title date categories tags Android 5.0以下TLS1.x SSLHandshakeException 2016-11-30 12:17:02 -0800 Andro ...
- TCP/IP三次握手与四次挥手的正确姿势
0.史上最容易理解的:TCP三次握手,四次挥手 https://cloud.tencent.com/developer/news/257281 A 理解TCP/IP三次握手与四次挥手的正确姿势http ...
- https握手失败案例(一)
OkHttpClient okHttpClient = new OkHttpClient.Builder() .connectTimeout(15, TimeUnit.SECONDS) .read ...
- 使用wireshark 抓取 http https tcp ip 协议进行学习
使用wireshark 抓取 http https tcp ip 协议进行学习 前言 本节使用wireshark工具抓包学习tcp ip http 协议 1. tcp 1.1 tcp三次握手在wire ...
- android 通过socket获取IP
如题<android 通过socket获取IP>: socket.getInetAddress().getHostAddress();
随机推荐
- 解决Qt Creator下 undefined reference to 'qmain(int,char**)'的问题
Qt Creator运行以下程序: #include <QTextStream> #include <QList> int main(void) { QTextStream o ...
- js引用类型之valueof和tostring(三)
一.摘要: <javascript高级程序设计第三版>一书中单独有一章对js的引用类型(Object.Array.RegExp.Function:基本包装类型:Boolean.Number ...
- bzoj2458 最小三角形
Description Xaviera现在遇到了一个有趣的问题.平面上有N个点,Xaviera想找出周长最小的三角形.由于点非常多,分布也非常乱,所以Xaviera想请你来解决这个问题.为了减小问题的 ...
- php 表单提交方法
1.收集 HTML 表单提交的数据 ,PHP $_REQUEST 用于收集 HTML 表单提交的数据. <!DOCTYPE html><html><body> &l ...
- oracle 年龄计算 岁 月 天
select trunc(months/12) || '岁' || trunc(mod(months, 12)) || '月' || trunc(sysdate - add_months( ...
- 第10课 C++中的新成员
1. 动态内存分配 (1)C++通过new关键字进行动态内存申请,是以类型为单位来申请空间大小的 (2)delete关键字用于内存释放 ▲注意释放数组时要加[],否则只释放这个数组中的第1个元素. [ ...
- Docker集群管理(二)—— docker+swarm+etcd+shipyard
引言 前一篇介绍如何简单的搭建一个可视化管理的docker集群,本篇将在此基础之上引入etcd发现服务. 目的 使用etcd发现服务解决swarm内置发现服务的不稳定问题.etcd采用raft算法,这 ...
- java编译器
编译: .java变成.class 前端编译 Sun javac Eclipse ECJ .class变成机器码 运行期编译 等HostSpot VM c1,c2 .jav ...
- sorted()&enumerate()
d = {1:2,3:1,44:5,4:5,7:8}l = d.items() #转换为列表print(l) # dict_items([(1, 2), (3, 1), (44, 5), (4, 5 ...
- 0_Simple__simpleP2P
使用 P2P 特性在 GPU 之间传输.读写数据. ▶ 源代码.包括 P2P 使用前的各项检查,设备之间的数据互拷,主机和设备之间数据传输和相互访问. #include <stdlib.h> ...