本文共 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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!