为什么要使用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)的更多相关文章

  1. HTTPS IP直连问题小结

    HTTPS IP直连问题小结: https://blog.csdn.net/leelit/article/details/77829196 可以使用OkHttpClient进行相同IP地址,不同DNS ...

  2. fiddler Android下https抓包全攻略

    fiddler Android下https抓包全攻略 fiddler的http.https的抓包功能非常强大,可非常便捷得对包进行断点跟踪和回放,但是普通的配置对于像招商银行.支付宝.陌陌这样的APP ...

  3. 在深谈TCP/IP三步握手&四步挥手原理及衍生问题—长文解剖IP

    如果对网络工程基础不牢,建议通读<细说OSI七层协议模型及OSI参考模型中的数据封装过程?> 下面就是TCP/IP(Transmission Control Protoco/Interne ...

  4. 关于Android的https通讯安全

    原文链接:http://pingguohe.net/2016/02/26/Android-App-secure-ssl.html 起因 前段时间,同事拿着一个代码安全扫描出来的 bug 过来咨询,我一 ...

  5. Android 使用 HTTPS 问题解决(SSLHandshakeException)

    title date categories tags Android 5.0以下TLS1.x SSLHandshakeException 2016-11-30 12:17:02 -0800 Andro ...

  6. TCP/IP三次握手与四次挥手的正确姿势

    0.史上最容易理解的:TCP三次握手,四次挥手 https://cloud.tencent.com/developer/news/257281 A 理解TCP/IP三次握手与四次挥手的正确姿势http ...

  7. https握手失败案例(一)

      OkHttpClient okHttpClient = new OkHttpClient.Builder() .connectTimeout(15, TimeUnit.SECONDS) .read ...

  8. 使用wireshark 抓取 http https tcp ip 协议进行学习

    使用wireshark 抓取 http https tcp ip 协议进行学习 前言 本节使用wireshark工具抓包学习tcp ip http 协议 1. tcp 1.1 tcp三次握手在wire ...

  9. android 通过socket获取IP

    如题<android 通过socket获取IP>: socket.getInetAddress().getHostAddress();

随机推荐

  1. 【EasyUI学习-3】Easyui tabs入门实践

    作者:ssslinppp       1. 摘要 一般我们在设计程序主框架的时候,当点击(子)菜单时,希望相应界面都在tabs页中显示: 在显示的时候,如果之前打开过该界面,则希望重新选中对应的tab ...

  2. bzoj2002 弹飞绵羊

    Description 某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏.游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置 ...

  3. [UE4]射击和直线追踪

    只有有朝向的组件(如character.Controller等)才能动态获取到朝向,在编辑视图中会有一个箭头表示正前方: 如果是没有朝向的组件(如摄像机),则获取到的朝向永远是固定的

  4. [jni]Getting Started

    写一个java应用程序来调用C函数打印“Hello World!" 这个过程包括以下步骤: 1:创建一个申明了native方法的java类(HelloWorld.java): 2:使用jav ...

  5. php mysql_db_query()函数使用介绍

    php mysql_db_query()函数选择一个数据库并在其上执行查询,本文章向大家介绍mysql_db_query()函数的基本使用方法和实例,需要的朋友可以参考一下本文章. mysql_db_ ...

  6. Openstack kvm win7镜像制作

    本文地址http://www.cnblogs.com/tcicy/p/7790956.html 网上找了很多为openstack制作win7镜像的文章,总是不成功 自己写一下,以便大家查看. 我使用c ...

  7. 无法打开物理文件 XXX.mdf"。操作系统错误 5:"5(拒绝访问。)"的解决办法

    附加数据库时报错: 无法打开物理文件 XXX.mdf".操作系统错误 5:"5(拒绝访问.)" 原因是数据库权限无法读取路径下的文件. 解决方案 一: 数据库使用wind ...

  8. Noip2011Mayan游戏

    题目描述 Mayan puzzle是最近流行起来的一个游戏.游戏界面是一个 7 行5 列的棋盘,上面堆放着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上.游戏通关是指在规定 ...

  9. 编写函数digit(num, k),函数功能是:求整数num从右边开始的第k位数字的值,如果num位数不足k位则返回0。

    function digit(num,k){         var knum = 0;         for(var i=1; i<=k; i++){                 knu ...

  10. Jquery学习(二)

    滚轮事件与函数节流 jquery.mousewheel插件使用 jquery中没有鼠标滚轮事件,原生js中的鼠标滚轮事件不兼容,可以使用jquery的滚轮事件插件jquery.mousewheel.j ...