为什么要使用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. 【Spring学习笔记-MVC-13.2】Spring MVC之多文件上传

    作者:ssslinppp       1. 摘要 前篇文章讲解了单文件上传<[Spring学习笔记-MVC-13]Spring MVC之文件上传>http://www.cnblogs.co ...

  2. appium 3-4-1034等待、日志、性能数据、xpath定位、web driver协议

    1.等待 1.1精确等待 sleep 不推荐 @Test public void testWait1() throws InterruptedException{ day_time(); Thread ...

  3. 拼图游戏js

    实现算法: 1. JavaScript动态生成拼图:通过生成16个div,且除最后一个div不使用背景图片以外,其他div都设置拼图图片为背景.然后通过调整background-position来实现 ...

  4. Opencv 入门学习之图片人脸识别

    读入图片,算法检测,画出矩形框 import cv2 from PIL import Image,ImageDraw import os def detectFaces(image_name): im ...

  5. 用docker搭建php+nginx+laravel的开发环境

    制作镜像 由于官方php:7.2.2-fpm-alpine3.7 镜像不含composer,而单独做一个composer镜像又会依赖php镜像,所以应该在php镜像中添加composer.Docker ...

  6. 一起KVM环境下windows7虚拟机异常死机(BSOD)的问题解决

    先说一下环境: 一.硬件 8台服务器做的超融合架构,软件存储池, 每台服务器是96G内存,两颗Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz,32线程. 每台服务器是 ...

  7. 跳表(skiplist)Python实现

    # coding=utf-8 # 跳表的Python实现 import random # 最高层数设置为4 MAX_LEVEL = 4 def randomLevel(): ""& ...

  8. 并发工具类(二)同步屏障CyclicBarrier

    前言   JDK中为了处理线程之间的同步问题,除了提供锁机制之外,还提供了几个非常有用的并发工具类:CountDownLatch.CyclicBarrier.Semphore.Exchanger.Ph ...

  9. 网络命令ping/netstat/ipconfig/arp/tracert/nbstat

    1.1 Ping命令的使用 ping检测网络故障步骤: ping 127.0.0.1 ping环绕指针检测是否在计算上anzhaung了TCP/IP协议及配置是否正确 ping本机IP这个命令被被送到 ...

  10. 温故而知新-mysql高级管理

    温故而知新-mysql高级管理 1 mysql的一些授权信息都保存在授权表中 授权表是6个 db,user,host,tables_priv,columns_priv,procs_priv 这6个表 ...