android okhttp3 底层,Android okhttp3 SSL握手底层实现追踪
发布日期:2022-02-19 00:02:14 浏览次数:4 分类:技术文章

本文共 5611 字,大约阅读时间需要 18 分钟。

对于https,在tcp三次握手后就会进行ssl的握手,ssl握手的详细过程网上介绍的很多。下面跟踪下okhttp3对于ssl握手的实现过程。

在okhttp3.internal.io.RealConnection#connectSocket中private void connectSocket(int connectTimeout, int readTimeout, int writeTimeout, ConnectionSpecSelector connectionSpecSelector) throws IOException { rawSocket.setSoTimeout(readTimeout); try { Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout); } catch (ConnectException e) { throw new ConnectException("Failed to connect to " + route.socketAddress()); } source = Okio.buffer(Okio.source(rawSocket)); sink = Okio.buffer(Okio.sink(rawSocket)); if (route.address().sslSocketFactory() != null) { connectTls(readTimeout, writeTimeout, connectionSpecSelector); } else { protocol = Protocol.HTTP_1_1; socket = rawSocket; } 。。。 }

首先tcp三次握手:Platform.get().connectSocket

其次获得I/O流:source = Okio.buffer(Okio.source(rawSocket));sink = Okio.buffer(Okio.sink(rawSocket));

然后判断是否需要ssl,如果需要则进行ssl:connectTls(readTimeout, writeTimeout, connectionSpecSelector);

在connectTls中,忽略其他的,ssl握手发生在sslSocket.startHandshake();startHandshake的实现在org.conscrypt.OpenSSLSocketImpl#startHandshake中,

(/libcore/crypto/src/main/java/org/conscrypt/OpenSSLSocketImpl.java)@Override public synchronized void startHandshake() throws IOException { 。。。 int sslSessionNativePointer; try { sslSessionNativePointer = NativeCrypto.SSL_do_handshake(sslNativePointer, socket.getFileDescriptor$(), this, getSoTimeout(), client, npnProtocols,client ? null : alpnProtocols); } catch (CertificateException e) { SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage()); wrapper.initCause(e); throw wrapper; } 。。。 }

调用的是native函数NativeCrypto.SSL_do_handshake(),实现在/libcore/crypto/src/main/native/org_conscrypt_NativeCrypto.cpp中,取出关键部分进行分析static jlong NativeCrypto_SSL_do_handshake(JNIEnv* env, jclass, jlong ssl_address, jobject fdObject,jobject shc, jint timeout_millis, jboolean client_mode, jbyteArray npnProtocols, jbyteArray alpnProtocols) { 。。。 /* * Make socket non-blocking, so SSL_connect SSL_read() and SSL_write() don't hang * forever and we can use select() to find out if the socket is ready. */ if (!setBlocking(fd.get(), false)) { throwSSLExceptionStr(env, "Unable to make socket non blocking"); SSL_clear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake setBlocking => 0", ssl); return 0; } 。。。 ret = 0; while (appData->aliveAndKicking) { errno = 0; if (!appData->setCallbackState(env, shc, fdObject, npnProtocols, alpnProtocols)) { // SocketException thrown by NetFd.isClosed SSL_clear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake setCallbackState => 0", ssl); return 0; } ret = SSL_do_handshake(ssl); appData->clearCallbackState(); // cert_verify_callback threw exception if (env->ExceptionCheck()) { SSL_clear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake exception => 0", ssl); return 0; } // success case if (ret == 1) { break; } // retry case if (errno == EINTR) { continue; } // error case int sslError = SSL_get_error(ssl, ret); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake ret=%d errno=%d sslError=%d timeout_millis=%d", ssl, ret, errno, sslError, timeout_millis); /* * If SSL_do_handshake doesn't succeed due to the socket being * either unreadable or unwritable, we use sslSelect to * wait for it to become ready. If that doesn't happen * before the specified timeout or an error occurs, we * cancel the handshake. Otherwise we try the SSL_connect * again. */ if (sslError == SSL_ERROR_WANT_READ || sslError == SSL_ERROR_WANT_WRITE) { appData->waitingThreads++; int selectResult = sslSelect(env, sslError, fdObject, appData, timeout_millis); if (selectResult == THROWN_EXCEPTION) { // SocketException thrown by NetFd.isClosed SSL_clear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake sslSelect => 0", ssl); return 0; } if (selectResult == -1) { throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_SYSCALL, "handshake error"); SSL_clear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake selectResult == -1 => 0", ssl); return 0; } if (selectResult == 0) { throwSocketTimeoutException(env, "SSL handshake timed out"); SSL_clear(ssl); freeOpenSslErrorState(); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake selectResult == 0 => 0", ssl); return 0; } } else { // ALOGE("Unknown error %d during handshake", error); break; } } // clean error. See SSL_do_handshake(3SSL) man page. if (ret == 0) { /* * The other side closed the socket before the handshake could be * completed, but everything is within the bounds of the TLS protocol. * We still might want to find out the real reason of the failure. */ int sslError = SSL_get_error(ssl, ret); if (sslError == SSL_ERROR_NONE || (sslError == SSL_ERROR_SYSCALL && errno == 0)) { throwSSLExceptionStr(env, "Connection closed by peer"); } else { throwSSLExceptionWithSslErrors(env, ssl, sslError, "SSL handshake terminated"); } SSL_clear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake clean error => 0", ssl); return 0; } // unclean error. See SSL_do_handshake(3SSL) man page. if (ret < 0) { /* * Translate the error and throw exception. We are sure it is an error * at this point. */ int sslError = SSL_get_error(ssl, ret); throwSSLExceptionWithSslErrors(env, ssl, sslError, "SSL handshake aborted"); SSL_clear(ssl); JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake unclean error => 0", ssl); return 0; } 。。。 }

(1)设置SSL为非阻塞模式

(2)在while里调用openssl的SSL_do_handshake进行握手,返回后检查是否握手成功了,成功则直接跳出while

(3)如果不成功,则检查是否是因为socket不能读写造成的

(4)如果是因为socket此时不能读写,则调用sellect进行等待,此次握手成功则返回,此次失败则继续while,即回到(2)

(5)如果不是因为socket读写的原因,则握手失败,跳出while,进行失败原因判断

转自:https://blog.csdn.net/hello2mao/article/details/53201974

转载地址:https://blog.csdn.net/weixin_34236334/article/details/117627908 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:阿卡迪亚大学计算机专业好考吗,阿卡迪亚大学很烂吗
下一篇:android+gallery+优化,Android--从系统Camera和Gallery获取图片优化

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年03月12日 10时57分28秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

c#背单词小程序视频_C#用timer实现背单词小程序 2019-04-21
24v开关电源维修技巧_【电视技术】液晶电视电源板十个维修经验分享 2019-04-21
laravel comment显示到页面最上面了_使用 Laravel 快速开发API接口,新手必读 2019-04-21
echart实现3d地图_orbslam_2生成稀疏点云地图的保存与加载的实现 邹鹏程 2019.9.15... 2019-04-21
bash 不是内部或外部命令_python学习笔记6-pip命令不是内部命令问题 2019-04-21
管道的另一端上无任何进程。_别被忽悠入坑!信号贴贴上就能信号满格?对手机信号无任何改善... 2019-04-21
mysql无法写数据库_求助,为何我的数据不能写入数据库 2019-04-21
ssh 两个mysql数据库_ssh连接两个数据库(转) 2019-04-21
mysql 双向链表_23张图!万字详解「链表」,从小白到大佬! 2019-04-21
mysql 常量命名规则_详解Java编程规约(命名风格、常量定义、代码格式) 2019-04-21
pomelo mysql_全文索引 - Pomelo.EFCore.MySql 2019-04-21
如何打开git命令窗口_win10系统如何将右键菜单中“在此处打开powershell窗口”调整为“在此处打开命令窗口”?... 2019-04-21
rtsp 华为_华为多实例生成树RSTP配置详解 2019-04-21
ewb交通灯报告和文件_基于ewb平台的交通灯电路设计.doc 2019-04-21
mysql中$使用_在MySQL中使用序列的简单教程 2019-04-21
mysql alter auto increment_将MySQL列更改为AUTO_INCREMENT 2019-04-21
mysql+err+1067_MySQL 5.7 Invalid default value for 'CREATE_TIME'报错的解决方法 2019-04-21
程序中mysql添加用户_MySQL添加用户的两种方法 2019-04-21
简述mysql安装过程_mysql安装的过程 2019-04-21
后端接口重定向_不用再等后端的接口啦!这个开源项目花 2 分钟就能模拟出后端接口... 2019-04-21