一褒纲、前言
在《Android原生網(wǎng)絡(luò)庫分析——HTTP部分》一文中分析完了HTTP部分翁狐,當然其中也包含了網(wǎng)絡(luò)庫中絕大部分共有的基礎(chǔ)部分类溢。這一篇中只側(cè)重于HTTPS協(xié)議部分,相關(guān)基礎(chǔ)只會一筆帶過露懒,不了解HTTP基礎(chǔ)的同學(xué)可以先看看前文中的HTTP部分闯冷。
應(yīng)該有不少小伙伴對HTTPS中所涉及到的證書,簽名懈词,RSA加密蛇耀,SSL/TLS等名詞是熟悉的陌生人。沒關(guān)系坎弯,通過對Android原生網(wǎng)絡(luò)庫的分析纺涤,我們不僅要了解這些名詞的意義,還要知道它們是如何應(yīng)用的抠忘。
原則還是不變撩炊,還是以網(wǎng)絡(luò)連接、通信與斷開為線索進行分析崎脉。
二拧咳、HTTPS連接
1.openConnection()打開連接
在詳細分析之前,需要再看一個類圖結(jié)構(gòu)囚灼,以了解 Http們 與 Https們的關(guān)系骆膝。
從類圖可以看到祭衩,HttpsUrlConnectionImpl的繼承樹里包含了HttpUrlConnection,也就是說HttpUrlConnection中的基本功能都是共用的阅签,可能你會覺得這是廢話掐暮。而有一個關(guān)鍵點在于其還有一個子類HttpUrlConnectionDelegate,這個子類又繼承了HttpUrlConnectionImpl,而它的繼承樹里也有HttpUrlConnection愉择,看起來似乎有點亂了劫乱。其實不然,HttpsUrlConnection以及HttpUrlConnection都是抽象類锥涕,它們封裝了各自協(xié)議里最基礎(chǔ)接口以及功能衷戈。而HttpsUrlConnectionImpl及HttpUrlConnectionImpl是各自協(xié)議的具體實現(xiàn)者,并且在HttpsUrlConnectionImpl中的代理類HttpUrlConnectionDelegate其實就指向了HttpUrlConnectionImpl的實例對象层坠,這也體現(xiàn)了Https只需要關(guān)心 TLS/SSL 層的事情就可以了殖妇,其他與Http相關(guān)的事情還是由Http們來完成即可。
好像繞了點破花,那簡單點說就是HttpsUrlConnectionImpl繼承HttpUrlConnection是為了代碼的復(fù)用谦趣,而HttpsUrlConnectionImpl包含HttpUrlConnectionImpl的實例對象則是功能復(fù)用。
理清了Https們與Http們的關(guān)系后就可以后面的流程分析了座每。從時序圖來看前鹅,openConnection()最終就是創(chuàng)建了一個HttpsUrlConnectionImpl的實現(xiàn)來返回給調(diào)用者。而當HttpsUrlConnectionImpl被創(chuàng)建時峭梳,它同時創(chuàng)建好HttpUrlConnectionImpl的代理類舰绘。有一點需要關(guān)注的是,系統(tǒng)如何判斷的協(xié)議是否為Https的葱椭,看如下代碼捂寿,其實很簡單,在setupStreamHandler()中確定Handler的時候確定的孵运。
void setupStreamHandler() {
...... // 省略部分代碼
423
424 // Fall back to a built-in stream handler if the user didn't supply one
425 if (protocol.equals("file")) {
426 streamHandler = new FileHandler();
427 } else if (protocol.equals("ftp")) {
428 streamHandler = new FtpHandler();
429 } else if (protocol.equals("http")) {
430 streamHandler = new HttpHandler();
431 } else if (protocol.equals("https")) {
432 streamHandler = new HttpsHandler();
433 } else if (protocol.equals("jar")) {
434 streamHandler = new JarHandler();
435 }
436 if (streamHandler != null) {
437 streamHandlers.put(protocol, streamHandler);
438 }
439 }
在創(chuàng)建HttpsUrlConnectionImpl實例的過程中秦陋,在其父類HttpsUrlConnection中創(chuàng)建了兩個重要的類,一個是HostnameVerifier治笨,用于握手時驗證主機名的驳概,另一個是SSLSockoetFactory的實例,用于創(chuàng)建SSLSocket的旷赖。
protected HttpsURLConnection(URL url) {
178 super(url);
179 hostnameVerifier = defaultHostnameVerifier;
180 sslSocketFactory = defaultSSLSocketFactory;
181 }
先來看SSLSockoetFactory吧顺又。sslSocketFactory被設(shè)置成了默認defaultSSLSocketFactory,當然杠愧,其也允許用戶自己指定SSLSockoetFactory待榔,比如自簽名的證書就可以自己設(shè)置逞壁。再來看看defaultSSLSocketFactory的初始化流济。
private static SSLSocketFactory defaultSSLSocketFactory = (SSLSocketFactory) SSLSocketFactory
111 .getDefault();
繼續(xù)看SSLSocketFactory#getDefault()
/**
38 * Returns the default {@code SSLSocketFactory} instance. The default is
39 * defined by the security property {@code 'ssl.SocketFactory.provider'}.
40 *
41 * @return the default ssl socket factory instance.
42 */
43 public static synchronized SocketFactory getDefault() {
44 if (defaultSocketFactory != null) { // 單例類的實現(xiàn)
45 return defaultSocketFactory;
46 }
// Security類所提供的以“ssl.SocketFactory.provider”為屬性key的類
47 if (defaultName == null) {
48 defaultName = Security.getProperty("ssl.SocketFactory.provider");
49 if (defaultName != null) {
50 ClassLoader cl = Thread.currentThread().getContextClassLoader();
51 if (cl == null) {
52 cl = ClassLoader.getSystemClassLoader();
53 }
54 try {
55 final Class<?> sfc = Class.forName(defaultName, true, cl);
56 defaultSocketFactory = (SocketFactory) sfc.newInstance();
57 } catch (Exception e) {
58 System.logE("Problem creating " + defaultName, e);
59 }
60 }
61 }
62 // 默認SSLConext返回的對象實例
63 if (defaultSocketFactory == null) {
64 SSLContext context;
65 try {
66 context = SSLContext.getDefault();
67 } catch (NoSuchAlgorithmException e) {
68 context = null;
69 }
70 if (context != null) {
71 defaultSocketFactory = context.getSocketFactory();
72 }
73 }
// 直接new一個DefaultSSLSocketFactory锐锣,但表明其未安裝。
74 if (defaultSocketFactory == null) {
75 // Use internal implementation
76 defaultSocketFactory = new DefaultSSLSocketFactory("No SSLSocketFactory installed");
77 }
78 return defaultSocketFactory;
79 }
從實現(xiàn)上來看會從三個方面的其中之一绳瘟。
(1)通過Security獲取雕憔。與之配套的有一個屬性文件/libcore/luni/src/main/java/java/security/security.properties,其記錄了key=ssl.SocketFactory.provider所對應(yīng)的類名糖声〗锉耍可以說是OpenSSLSocketFactoryImpl,其所在的包為org.apache.harmony.xnet.provider.jsse蘸泻。哦喲琉苇,看來是移植的OpenSSL實現(xiàn)咯,要越陷越深了嗎悦施。先認識到這里并扇,后面會再見面的。
# For regular SSLSockets, we have two implementations:
ssl.SocketFactory.provider=org.apache.harmony.xnet.provider.jsse.OpenSSLSocketFactoryImpl
#ssl.SocketFactory.provider=org.apache.harmony.xnet.provider.jsse.SSLSocketFactoryImpl
(2)通過SSLConext來獲取
public final SSLSocketFactory getSocketFactory() {
228 return spiImpl.engineGetSocketFactory();
229 }
其中spiImpl是SSLContextSpi所定義的抡诞,SSLContextSpi是一個抽象類穷蛹,其具體實現(xiàn)類是SSLContextImpl,其在engineGetSocketFactory方法返回的是SSLServerSocketFactoryImpl的實例昼汗。其包也是org.apache.harmony.xnet.provider.jsse肴熏。恩,其實就是上面屬性文件中被注釋掉的那個顷窒。
(3)在(1)和(2)都沒有結(jié)果時蛙吏,就直接返回默認的類DefaultSSLSocketFactory的實例了。
2.getOutputStream()
相較于HTTP的getOutputStream()蹋肮,這一部分多了個TLS/SSL的握手部分出刷,然后再加上這里涉及到了另外三方開源庫OpenSSL的實現(xiàn),這里至少需要分成三部分來講解坯辩。為了講解不過于復(fù)雜馁龟,先來看前兩部分:
(1)Http的連接
前面的1-14步所完成的工作就是HTTP的TCP三步握手,從而使客戶端與服務(wù)器之間建立起通信連接漆魔。這一點和HTTP的相同坷檩,因此,這里只做簡單的回顧改抡。首先創(chuàng)建的是HttpsUrlConnectionImpl矢炼,這里設(shè)定了默認的端口號為443。然后就是設(shè)定http的相關(guān)協(xié)議請求行阿纤,請求頭參數(shù)等句灌,最后通過HttpEngine#sendRequest()將請求發(fā)出去,
看過《Android原生網(wǎng)絡(luò)庫分析——HTTP部分》的同學(xué)應(yīng)該還記得,經(jīng)過HttpConnection#connect()方法并創(chuàng)建好HttpConnection實例后其實就建立起TCP的連接了胰锌。
(2)發(fā)起Https的握手請求
Https用于連接的幫助類為HttpsEngines骗绕,在其connect()方法里還進一步調(diào)用了自身的makeSslConnection()方法,這個方法是私有的也是https特有的资昧,由這里開始發(fā)起Https的握手協(xié)議酬土。
/**
451 * Attempt to make an https connection. Returns true if a
452 * connection was reused, false otherwise.
453 *
454 * @param tlsTolerant If true, assume server can handle common
455 * TLS extensions and SSL deflate compression. If false, use
456 * an SSL3 only fallback mode without compression.
457 */
458 private boolean makeSslConnection(boolean tlsTolerant) throws IOException {
459 // make an SSL Tunnel on the first message pair of each SSL + proxy connection
460 if (connection == null) {
461 connection = openSocketConnection();
462 if (connection.getAddress().getProxy() != null) {
463 makeTunnel(policy, connection, getRequestHeaders());
464 }
465 }
466
467 // if super.makeConnection returned a connection from the
468 // pool, sslSocket needs to be initialized here. If it is
469 // a new connection, it will be initialized by
470 // getSecureSocket below.
// 這里第一次獲取時會返回 null
471 sslSocket = connection.getSecureSocketIfConnected();
472
473 // we already have an SSL connection,
474 if (sslSocket != null) {
475 return true;
476 }
477 // 帶著前面定義好的 SSLSocketFactory 進一步設(shè)置 sslSocket,這里應(yīng)該是 OpenSSLSocketFactoryImpl
478 connection.setupSecureSocket(enclosing.getSSLSocketFactory(), tlsTolerant);
479 return false;
480 }
這里進一步調(diào)用 HttpConnection#setupSecureSocket()。
/**
186 * Create an {@code SSLSocket} and perform the SSL handshake
187 * (performing certificate validation.
188 *
189 * @param sslSocketFactory Source of new {@code SSLSocket} instances.
190 * @param tlsTolerant If true, assume server can handle common
191 * TLS extensions and SSL deflate compression. If false, use
192 * an SSL3 only fallback mode without compression.
193 */
194 public void setupSecureSocket(SSLSocketFactory sslSocketFactory, boolean tlsTolerant)
195 throws IOException {
196 // create the wrapper over connected socket
// 創(chuàng)建 SslSocket格带,其應(yīng)該為 OpenSSLSocketImpl 的實例
197 unverifiedSocket = (SSLSocket) sslSocketFactory.createSocket(socket,
198 address.uriHost, address.uriPort, true /* autoClose */);
199 // tlsTolerant mimics Chrome's behavior
200 if (tlsTolerant && unverifiedSocket instanceof OpenSSLSocketImpl) {
201 OpenSSLSocketImpl openSslSocket = (OpenSSLSocketImpl) unverifiedSocket;
202 openSslSocket.setUseSessionTickets(true);
203 openSslSocket.setHostname(address.uriHost);
204 // use SSLSocketFactory default enabled protocols
205 } else {
206 unverifiedSocket.setEnabledProtocols(new String [] { "SSLv3" });
207 }
208 // force handshake, which can throw
// 這里會發(fā)起https的握手
209 unverifiedSocket.startHandshake();
210 }
在這個方法創(chuàng)建了 SslSocket 的創(chuàng)建以及通過其發(fā)起了Https的握手撤缴。這里已經(jīng)開始涉及到Native層的操作了,也就是OpenSSL相關(guān)的操作叽唱。后面的東西會涉及到Native的東西屈呕,所以需要點c/c++的基礎(chǔ)語法,一點點就好棺亭。
其實前面已經(jīng)多次提到OpenSSL了凉袱,它是一個TLS/SSL的實現(xiàn),其代碼托管在Giithub上侦铜,有興趣的可以做更深入的了解专甩。在后面的三步握手實現(xiàn)里也會講到其里面的部分實現(xiàn)。這里還是要繼續(xù)分析關(guān)于創(chuàng)建SslScoket以及發(fā)起Https握手前的細節(jié)钉稍。
a.創(chuàng)建SslSocket以及ssl的上下文指針
SslSocket的是OpenSSLSocketImpl的實例涤躲,而sslSocketFactory是OpenSSLSocketFactoryImpl的實現(xiàn)。createSocket有多個版本贡未,根據(jù)上面的代碼种樱,調(diào)用的版本如下。
public Socket createSocket(Socket s, String host, int port, boolean autoClose)
93 throws IOException {
94 return new OpenSSLSocketImplWrapper(s,
95 host,
96 port,
97 autoClose,
98 (SSLParametersImpl) sslParameters.clone());
99 }
OpenSSLSocketImplWrapper是個包裝類俊卤,其繼承自O(shè)penSSLSocketImpl嫩挤,它的構(gòu)造函數(shù)也是有多個版本的。
protected OpenSSLSocketImpl(Socket socket, String host, int port,
160 boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
161 this.socket = socket;
162 this.wrappedHost = host;
163 this.wrappedPort = port;
164 this.autoClose = autoClose;
165 init(sslParameters);
166
167 // this.timeout is not set intentionally.
168 // OpenSSLSocketImplWrapper.getSoTimeout will delegate timeout
169 // to wrapped socket
170 }
171
在創(chuàng)建OpenSSLSocketImpl時消恍,除了socket,host,port這些基本參數(shù)岂昭,還有一個用于SSL的參數(shù)sslParameters,其所屬類為SSLParametersImpl狠怨。在OpenSSLSocketFactoryImpl構(gòu)造時通過調(diào)用SSLParametersImpl#getDefault()對其進行了初始化约啊。
protected static SSLParametersImpl getDefault() throws KeyManagementException {
144 SSLParametersImpl result = defaultParameters;
145 if (result == null) {
146 // single-check idiom
147 defaultParameters = result = new SSLParametersImpl(null,
148 null,
149 null,
150 new ClientSessionContext(),
151 new ServerSessionContext());
152 }
153 return (SSLParametersImpl) result.clone();
154 }
其創(chuàng)建了用于會話的上下文,且包含了client與server的佣赖。這里只以Android客戶端來分析恰矩,那就只關(guān)注ClientSessionContext即可。
public class ClientSessionContext extends AbstractSessionContext {
...
}
abstract class AbstractSessionContext implements SSLSessionContext {
40
41 volatile int maximumSize;
42 volatile int timeout;
43
44 final long sslCtxNativePointer = NativeCrypto.SSL_CTX_new();
ClientSessionContext繼承自AbstractSessionContext憎蛤,而在AbstractSessionContext初始化時就會初始化Native層的上下文指針外傅,且最終把值賦給上層Java的整型變量sslCtxNativePointer。我們把這一過程可以叫做SSL的上下文創(chuàng)建。
那就來看看SSL_CTX_new()的實現(xiàn)萎胰。這里必須要補充一下的是NativeCrypto.java的 jni 層對應(yīng)的文件是 /libcore/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp彬碱。SSL_CTX_new()函數(shù)在其里面的實現(xiàn)如下:
/*
5998 * public static native int SSL_CTX_new();
5999 */
6000static jlong NativeCrypto_SSL_CTX_new(JNIEnv* env, jclass) {
// 創(chuàng)建上下文
6001 Unique_SSL_CTX sslCtx(SSL_CTX_new(SSLv23_method()));
6002 if (sslCtx.get() == NULL) {
6003 throwExceptionIfNecessary(env, "SSL_CTX_new");
6004 return 0;
6005 }
// 設(shè)置上下文選項
6006 SSL_CTX_set_options(sslCtx.get(),
6007 SSL_OP_ALL
6008 // Note: We explicitly do not allow SSLv2 to be used.
6009 | SSL_OP_NO_SSLv2
6010 // We also disable session tickets for better compatibility b/2682876
6011 | SSL_OP_NO_TICKET
6012 // We also disable compression for better compatibility b/2710492 b/2710497
6013 | SSL_OP_NO_COMPRESSION
6014 // Because dhGenerateParameters uses DSA_generate_parameters_ex
6015 | SSL_OP_SINGLE_DH_USE
6016 // Because ecGenerateParameters uses a fixed named curve
6017 | SSL_OP_SINGLE_ECDH_USE);
6018
6019 int mode = SSL_CTX_get_mode(sslCtx.get());
6020 /*
6021 * Turn on "partial write" mode. This means that SSL_write() will
6022 * behave like Posix write() and possibly return after only
6023 * writing a partial buffer. Note: The alternative, perhaps
6024 * surprisingly, is not that SSL_write() always does full writes
6025 * but that it will force you to retry write calls having
6026 * preserved the full state of the original call. (This is icky
6027 * and undesirable.)
6028 */
6029 mode |= SSL_MODE_ENABLE_PARTIAL_WRITE;
6030
6031 // Reuse empty buffers within the SSL_CTX to save memory
6032 mode |= SSL_MODE_RELEASE_BUFFERS;
6033 // 設(shè)置模式
6034 SSL_CTX_set_mode(sslCtx.get(), mode);
6035 // 設(shè)置一系列的 callback
6036 SSL_CTX_set_cert_verify_callback(sslCtx.get(), cert_verify_callback, NULL);
6037 SSL_CTX_set_info_callback(sslCtx.get(), info_callback);
6038 SSL_CTX_set_client_cert_cb(sslCtx.get(), client_cert_cb);
6039 SSL_CTX_set_tmp_rsa_callback(sslCtx.get(), tmp_rsa_callback);
6040 SSL_CTX_set_tmp_dh_callback(sslCtx.get(), tmp_dh_callback);
6041 SSL_CTX_set_tmp_ecdh_callback(sslCtx.get(), tmp_ecdh_callback);
6042
6043 JNI_TRACE("NativeCrypto_SSL_CTX_new => %p", sslCtx.get());
6044 return (jlong) sslCtx.release();
6045}
這里又調(diào)用了另一個SSL_CTX_new()函數(shù),它的實現(xiàn)在 ssl_lib.c 文件中,其將創(chuàng)建用于會話的上下文指針 SSL_CTX*奥洼,其是typedef定義,即ssl_ctx_st晚胡。而這里還要注意其傳入?yún)?shù)為SSLv23_method()灵奖,這是一個函數(shù)指針,其返回值是SSL_METHOD估盘,而SSL_METHOD其是結(jié)構(gòu)體 ssl_method_st瓷患,其定義如下。
/* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
409struct ssl_method_st
410 {
411 int version;
412 int (*ssl_new)(SSL *s);
413 void (*ssl_clear)(SSL *s);
414 void (*ssl_free)(SSL *s);
415 int (*ssl_accept)(SSL *s);
416 int (*ssl_connect)(SSL *s);
417 int (*ssl_read)(SSL *s,void *buf,int len);
418 int (*ssl_peek)(SSL *s,void *buf,int len);
419 int (*ssl_write)(SSL *s,const void *buf,int len);
420 int (*ssl_shutdown)(SSL *s);
421 int (*ssl_renegotiate)(SSL *s);
422 int (*ssl_renegotiate_check)(SSL *s);
423 long (*ssl_get_message)(SSL *s, int st1, int stn, int mt, long
424 max, int *ok);
425 int (*ssl_read_bytes)(SSL *s, int type, unsigned char *buf, int len,
426 int peek);
427 int (*ssl_write_bytes)(SSL *s, int type, const void *buf_, int len);
428 int (*ssl_dispatch_alert)(SSL *s);
429 long (*ssl_ctrl)(SSL *s,int cmd,long larg,void *parg);
430 long (*ssl_ctx_ctrl)(SSL_CTX *ctx,int cmd,long larg,void *parg);
431 const SSL_CIPHER *(*get_cipher_by_char)(const unsigned char *ptr);
432 int (*put_cipher_by_char)(const SSL_CIPHER *cipher,unsigned char *ptr);
433 int (*ssl_pending)(const SSL *s);
434 int (*num_ciphers)(void);
435 const SSL_CIPHER *(*get_cipher)(unsigned ncipher);
436 const struct ssl_method_st *(*get_ssl_method)(int version);
437 long (*get_timeout)(void);
438 struct ssl3_enc_method *ssl3_enc; /* Extra SSLv3/TLS stuff */
439 int (*ssl_version)(void);
440 long (*ssl_callback_ctrl)(SSL *s, int cb_id, void (*fp)(void));
441 long (*ssl_ctx_callback_ctrl)(SSL_CTX *s, int cb_id, void (*fp)(void));
442 };
上面的定義比較長遣妥,只作了解即可擅编,在后面的過程中會慢慢熟悉的。當然箫踩,其實從這個定義其上可以看到 TLS/SSL 連接或者認證的一些影子了爱态。
而對于SSLv23_method()本身來說,既然是一個函數(shù)指針境钟,那么一定有其實現(xiàn)的地方锦担。
IMPLEMENT_ssl23_meth_func(SSLv23_method,
89 ssl23_accept,
90 ssl23_connect,
91 ssl23_get_method)
由一個宏來實現(xiàn)的,這是 C 語言的慣用技巧慨削。還是來看看吧洞渔。
#define IMPLEMENT_ssl23_meth_func(func_name, s_accept, s_connect, s_get_meth) \
702const SSL_METHOD *func_name(void) \
703 { \
704 static const SSL_METHOD func_name##_data= { \
705 TLS1_2_VERSION, \
706 tls1_new, \
707 tls1_clear, \
708 tls1_free, \
709 s_accept, \
710 s_connect, \
711 ssl23_read, \
712 ssl23_peek, \
713 ssl23_write, \
714 ssl_undefined_function, \
715 ssl_undefined_function, \
716 ssl_ok, \
717 ssl3_get_message, \
718 ssl3_read_bytes, \
719 ssl3_write_bytes, \
720 ssl3_dispatch_alert, \
721 ssl3_ctrl, \
722 ssl3_ctx_ctrl, \
723 ssl23_get_cipher_by_char, \
724 ssl23_put_cipher_by_char, \
725 ssl_undefined_const_function, \
726 ssl23_num_ciphers, \
727 ssl23_get_cipher, \
728 s_get_meth, \
729 ssl23_default_timeout, \
730 &ssl3_undef_enc_method, \
731 ssl_undefined_void_function, \
732 ssl3_callback_ctrl, \
733 ssl3_ctx_callback_ctrl, \
734 }; \
735 return &func_name##_data; \
736 }
也就是說最終定義了一個類型為ssl_method_st的 SSLv23_method_data 的變量,它的每一項參數(shù)也將賦值結(jié)構(gòu)體中ssl_method_st的每一個成員缚态。另外要注意上面的參數(shù)ssl23_accept和ssl23_connect磁椒。對于服務(wù)端需要 s_accept參數(shù),而客戶端則只需要ssl23_connect玫芦。同時浆熔,也可以看出,這兩個也是函數(shù)指針桥帆,其中ssl23_connect的實現(xiàn)在 s23_clnt.c 文件中蘸拔,其實現(xiàn)后面再說。
至于上下文ssl_ctx_st环葵,其也是一個結(jié)構(gòu)體调窍,其定義也是相當?shù)拈L,這里就先不貼出來了张遭,后面如果有需要再看情況邓萨。
這里花費很大的力氣就是為了弄清楚這個參數(shù) SSLv23_method的定義,因為這是一個很至關(guān)重要的參數(shù),其決定了用于連接的協(xié)議以及版本為TLS1_2_VERSION缔恳,以及將來用于連接的 ssl23_connect宝剖。
而關(guān)于 ssl_lib.c 中SSL_CTX_new()的實現(xiàn),也比較長歉甚,也不貼了万细。只要知道函數(shù)指針SSLv23_method會賦值為 SSL_CTX指針的 method 的成員變量即可。
b.發(fā)起Https握手
終于要握手了纸泄,即調(diào)用OpenSSLSocketImpl#startHandshake()赖钞。
(3)握手
握手是https里面最核心的部分,握手過程中最核心的是傳遞加密方法聘裁、密鑰以及證書驗證等雪营。先來看一看握手的上層Java代碼。前方高能衡便,又是非常長的一段代碼献起。為了簡單,可以不用理會整個完整的代碼镣陕,對著時序圖看看關(guān)鍵注釋即可谴餐。
/**
242 * Starts a TLS/SSL handshake on this connection using some native methods
243 * from the OpenSSL library. It can negotiate new encryption keys, change
244 * cipher suites, or initiate a new session. The certificate chain is
245 * verified if the correspondent property in java.Security is set. All
246 * listeners are notified at the end of the TLS/SSL handshake.
247 */
248 @Override public synchronized void startHandshake() throws IOException {
249 synchronized (handshakeLock) {
250 checkOpen();// 狀態(tài)檢查
251 if (!handshakeStarted) {
252 handshakeStarted = true;
253 } else {
254 return;
255 }
256 }
257
258 // note that this modifies the global seed, not something specific to the connection
259 final int seedLengthInBytes = NativeCrypto.RAND_SEED_LENGTH_IN_BYTES; // RAND_SEED_LENGTH_IN_BYTES = 1024
// 獲取或者生成用于加密的隨機數(shù)
260 final SecureRandom secureRandom = sslParameters.getSecureRandomMember();
261 if (secureRandom == null) {
262 NativeCrypto.RAND_load_file("/dev/urandom", seedLengthInBytes);
263 } else {
264 NativeCrypto.RAND_seed(secureRandom.generateSeed(seedLengthInBytes));
265 }
266
// 這里是客戶端,所以為 true
267 final boolean client = sslParameters.getUseClientMode();
268
// 從客戶端獲取上下文指針
269 final long sslCtxNativePointer = (client) ?
270 sslParameters.getClientSessionContext().sslCtxNativePointer :
271 sslParameters.getServerSessionContext().sslCtxNativePointer;
272
273 this.sslNativePointer = 0;
274 boolean exception = true;
275 try {
// 創(chuàng)建 SSL 指針
276 sslNativePointer = NativeCrypto.SSL_new(sslCtxNativePointer);
277 guard.open("close");
278
// npn 協(xié)議是什么協(xié)議?ALPN(Application-Layer Protocol Negotiation)也是 TLS 層的擴展呆抑,用于協(xié)商應(yīng)用層使用的協(xié)議总寒。它的前身是 NPN,最初用于支持 Google SPDY 協(xié)議(現(xiàn)已標準化為 HTTP/2)理肺。
279 if (npnProtocols != null) {
280 NativeCrypto.SSL_CTX_enable_npn(sslCtxNativePointer);
281 }
282
283 // setup server certificates and private keys.
284 // clients will receive a call back to request certificates.
// 服務(wù)器端相關(guān)內(nèi)容摄闸,主要就是設(shè)置證書以及私鑰,只做了解即可妹萨。
285 if (!client) {
286 Set<String> keyTypes = new HashSet<String>();
287 for (String enabledCipherSuite : enabledCipherSuites) {
288 if (enabledCipherSuite.equals(NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
289 continue;
290 }
291 String keyType = CipherSuite.getByName(enabledCipherSuite).getServerKeyType();
292 if (keyType != null) {
293 keyTypes.add(keyType);
294 }
295 }
296 for (String keyType : keyTypes) {
297 try {
298 setCertificate(sslParameters.getKeyManager().chooseServerAlias(keyType,
299 null,
300 this));
301 } catch (CertificateEncodingException e) {
302 throw new IOException(e);
303 }
304 }
305 }
306
// 使能協(xié)議年枕,這里默認是SSLv3、TLS
307 NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols);
// 設(shè)置密碼組乎完,如 md5,sha等
308 NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites);
309 if (useSessionTickets) {
310 NativeCrypto.SSL_clear_options(sslNativePointer, NativeCrypto.SSL_OP_NO_TICKET);
311 }
312 if (hostname != null) {
313 NativeCrypto.SSL_set_tlsext_host_name(sslNativePointer, hostname);
314 }
315
316 boolean enableSessionCreation = sslParameters.getEnableSessionCreation();
317 if (!enableSessionCreation) {
318 NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer,
319 enableSessionCreation);
320 }
321
322 AbstractSessionContext sessionContext;
323 OpenSSLSessionImpl sessionToReuse;
324 if (client) {
325 // look for client session to reuse
326 ClientSessionContext clientSessionContext = sslParameters.getClientSessionContext();
327 sessionContext = clientSessionContext;
328 sessionToReuse = getCachedClientSession(clientSessionContext);
329 if (sessionToReuse != null) {
330 NativeCrypto.SSL_set_session(sslNativePointer,
331 sessionToReuse.sslSessionNativePointer);
332 }
333 } else {
334 sessionContext = sslParameters.getServerSessionContext();
335 sessionToReuse = null;
336 }
337
338 // setup peer certificate verification
// 設(shè)置證書熏兄,目前只是服務(wù)端
339 if (client) {
340 // TODO support for anonymous cipher would require us to
341 // conditionally use SSL_VERIFY_NONE
342 } else {
343 // needing client auth takes priority...
344 boolean certRequested;
345 if (sslParameters.getNeedClientAuth()) {
346 NativeCrypto.SSL_set_verify(sslNativePointer,
347 NativeCrypto.SSL_VERIFY_PEER
348 | NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
349 certRequested = true;
350 // ... over just wanting it...
351 } else if (sslParameters.getWantClientAuth()) {
352 NativeCrypto.SSL_set_verify(sslNativePointer,
353 NativeCrypto.SSL_VERIFY_PEER);
354 certRequested = true;
355 // ... and it defaults properly so don't call SSL_set_verify in the common case.
356 } else {
357 certRequested = false;
358 }
359
360 if (certRequested) {
361 X509TrustManager trustManager = sslParameters.getTrustManager();
362 X509Certificate[] issuers = trustManager.getAcceptedIssuers();
363 if (issuers != null && issuers.length != 0) {
364 byte[][] issuersBytes;
365 try {
366 issuersBytes = NativeCrypto.encodeIssuerX509Principals(issuers);
367 } catch (CertificateEncodingException e) {
368 throw new IOException("Problem encoding principals", e);
369 }
// 下發(fā)證書鏈給客戶端
370 NativeCrypto.SSL_set_client_CA_list(sslNativePointer, issuersBytes);
371 }
372 }
373 }
374
375 // Temporarily use a different timeout for the handshake process
// 握手時間
376 int savedReadTimeoutMilliseconds = getSoTimeout();
377 int savedWriteTimeoutMilliseconds = getSoWriteTimeout();
378 if (handshakeTimeoutMilliseconds >= 0) {
379 setSoTimeout(handshakeTimeoutMilliseconds);
380 setSoWriteTimeout(handshakeTimeoutMilliseconds);
381 }
382
383 // TLS Channel ID
// 設(shè)置 channel id
384 if (client) {
385 // Client-side TLS Channel ID
386 if (channelIdPrivateKey != null) {
387 NativeCrypto.SSL_set1_tls_channel_id(sslNativePointer, channelIdPrivateKey);
388 }
389 } else {
390 // Server-side TLS Channel ID
391 if (channelIdEnabled) {
392 NativeCrypto.SSL_enable_tls_channel_id(sslNativePointer);
393 }
394 }
395
396 int sslSessionNativePointer;
397 try {
// 握手
398 sslSessionNativePointer = NativeCrypto.SSL_do_handshake(sslNativePointer,
399 socket.getFileDescriptor$(), this, getSoTimeout(), client, npnProtocols);
400 } catch (CertificateException e) {
401 SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage());
402 wrapper.initCause(e);
403 throw wrapper;
404 }
405 byte[] sessionId = NativeCrypto.SSL_SESSION_session_id(sslSessionNativePointer);
406 if (sessionToReuse != null && Arrays.equals(sessionToReuse.getId(), sessionId)) {
407 this.sslSession = sessionToReuse;
408 sslSession.lastAccessedTime = System.currentTimeMillis();
409 NativeCrypto.SSL_SESSION_free(sslSessionNativePointer);
410 } else {
411 if (!enableSessionCreation) {
412 // Should have been prevented by NativeCrypto.SSL_set_session_creation_enabled
413 throw new IllegalStateException("SSL Session may not be created");
414 }
// 證書鏈處理
415 X509Certificate[] localCertificates
416 = createCertChain(NativeCrypto.SSL_get_certificate(sslNativePointer));
417 X509Certificate[] peerCertificates
418 = createCertChain(NativeCrypto.SSL_get_peer_cert_chain(sslNativePointer));
419 this.sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates,
420 peerCertificates, getPeerHostName(), getPeerPort(), sessionContext);
421 // if not, putSession later in handshakeCompleted() callback
422 if (handshakeCompleted) {
423 sessionContext.putSession(sslSession);
424 }
425 }
426
427 // Restore the original timeout now that the handshake is complete
428 if (handshakeTimeoutMilliseconds >= 0) {
429 setSoTimeout(savedReadTimeoutMilliseconds);
430 setSoWriteTimeout(savedWriteTimeoutMilliseconds);
431 }
432
433 // if not, notifyHandshakeCompletedListeners later in handshakeCompleted() callback
// 完成握手并發(fā)出通知
434 if (handshakeCompleted) {
435 notifyHandshakeCompletedListeners();
436 }
437
438 exception = false;
439 } catch (SSLProtocolException e) {
440 throw new SSLHandshakeException(e);
441 } finally {
442 // on exceptional exit, treat the socket as closed
443 if (exception) {
444 close();
445 }
446 }
447 }
這是一段超長的代碼,還是那句話树姨,不要畏難摩桶,長歸長,一句一句分析下來就短了帽揪。當然其步驟也是比較多的硝清,如下,大概有 12 步转晰。簡單的就不做展開了芦拿,復(fù)雜的就做更深的了解
a.獲取或者生成用于加密的隨機數(shù)
b.從客戶端獲取上下文指針
c.創(chuàng)建 SSL 指針
調(diào)用SSL_new()函數(shù)將創(chuàng)建指針SSL*士飒,即ssl_st。雖然很重要蔗崎,但沒有什么特別關(guān)鍵的就不展開了酵幕。
d.使能協(xié)議
e.設(shè)置密碼套件
public static String[] getDefaultCipherSuites() {
712 return new String[] {
713 "SSL_RSA_WITH_RC4_128_MD5",
714 "SSL_RSA_WITH_RC4_128_SHA",
715 "TLS_RSA_WITH_AES_128_CBC_SHA",
716 "TLS_RSA_WITH_AES_256_CBC_SHA",
717 "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
718 "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
719 "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
720 "TLS_ECDH_RSA_WITH_RC4_128_SHA",
721 "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
722 "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
723 "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
724 "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
725 "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
726 "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
727 "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
728 "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
729 "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
730 "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
731 "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
732 "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
733 "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
734 "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
735 "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
736 "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
737 "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
738 "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
739 "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
740 "SSL_RSA_WITH_DES_CBC_SHA",
741 "SSL_DHE_RSA_WITH_DES_CBC_SHA",
742 "SSL_DHE_DSS_WITH_DES_CBC_SHA",
743 "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
744 "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
745 "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
746 "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
747 TLS_EMPTY_RENEGOTIATION_INFO_SCSV
748 };
749 }
f.設(shè)置hostname
g.創(chuàng)建會話
會話創(chuàng)建時,做了一個很重要的工作就是給 SSL 指針中handshake_func賦值缓苛。
s->handshake_func=meth->ssl_connect
這個 meth 就是上下文的 meth芳撒,而 ssl_connect 根據(jù)前面的分析,其指向的 s23_clnt.c 中的 ssl23_connect() 函數(shù)未桥。
h.設(shè)置握手時間
i.設(shè)置頻道id
j.握手
以下是一次握手過程所需要經(jīng)歷的狀態(tài)笔刹,每個狀態(tài)基本對應(yīng)一個函數(shù)來處理。
int ssl23_connect(SSL *s)
146 {
147 BUF_MEM *buf=NULL;
148 unsigned long Time=(unsigned long)time(NULL);
149 void (*cb)(const SSL *ssl,int type,int val)=NULL;
150 int ret= -1;
151 int new_state,state;
152
153 RAND_add(&Time,sizeof(Time),0);
154 ERR_clear_error();
155 clear_sys_error();
156
157 if (s->info_callback != NULL)
158 cb=s->info_callback;
159 else if (s->ctx->info_callback != NULL)
160 cb=s->ctx->info_callback;
161
162 s->in_handshake++;// 計數(shù)加 1
// SSL 初始化钢属,其 state 將為 SSL_ST_BEFORE|SSL_ST_CONNECT
163 if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s);
164
165 for (;;)
166 {
167 state=s->state;
168
169 switch(s->state)
170 {
171 case SSL_ST_BEFORE:
172 case SSL_ST_CONNECT:
173 case SSL_ST_BEFORE|SSL_ST_CONNECT: // 第一次循環(huán)時進入
174 case SSL_ST_OK|SSL_ST_CONNECT:
175
176 if (s->session != NULL)
177 {
178 SSLerr(SSL_F_SSL23_CONNECT,SSL_R_SSL23_DOING_SESSION_ID_REUSE);
179 ret= -1;
180 goto end;
181 }
182 s->server=0;
// 通知回調(diào)函數(shù),開始握手了
183 if (cb != NULL) cb(s,SSL_CB_HANDSHAKE_START,1);
184
185 /* s->version=TLS1_VERSION; */
186 s->type=SSL_ST_CONNECT;
187
188 if (s->init_buf == NULL)
189 {
190 if ((buf=BUF_MEM_new()) == NULL)
191 {
192 ret= -1;
193 goto end;
194 }
195 if (!BUF_MEM_grow(buf,SSL3_RT_MAX_PLAIN_LENGTH))
196 {
197 ret= -1;
198 goto end;
199 }
200 s->init_buf=buf;
201 buf=NULL;
202 }
203
204 if (!ssl3_setup_buffers(s)) { ret= -1; goto end; }
205
206 ssl3_init_finished_mac(s);
207 // 設(shè)置狀態(tài)為 SSL23_ST_CW_CLNT_HELLO_A门躯,下次循環(huán)迭代就會進入 case SSL23_ST_CW_CLNT_HELLO_A.
208 s->state=SSL23_ST_CW_CLNT_HELLO_A;
209 s->ctx->stats.sess_connect++;
210 s->init_num=0;
211 break;
212
213 case SSL23_ST_CW_CLNT_HELLO_A:// 第二次循環(huán)
214 case SSL23_ST_CW_CLNT_HELLO_B:
215
216 s->shutdown=0;
// 發(fā)送出 hello
217 ret=ssl23_client_hello(s);
218 if (ret <= 0) goto end;
// 置狀態(tài)為 SSL23_ST_CR_SRVR_HELLO_A
219 s->state=SSL23_ST_CR_SRVR_HELLO_A;
220 s->init_num=0;
221
222 break;
223
224 case SSL23_ST_CR_SRVR_HELLO_A:// 第三次循環(huán)
225 case SSL23_ST_CR_SRVR_HELLO_B:
// 獲取服務(wù)器的的 hello
226 ret=ssl23_get_server_hello(s);
227 if (ret >= 0) cb=NULL;
228 goto end;
229 /* break; */
230
231 default:
232 SSLerr(SSL_F_SSL23_CONNECT,SSL_R_UNKNOWN_STATE);
233 ret= -1;
234 goto end;
235 /* break; */
236 }
237
238 if (s->debug) { (void)BIO_flush(s->wbio); }
239
240 if ((cb != NULL) && (s->state != state))
241 {
242 new_state=s->state;
243 s->state=state;
244 cb(s,SSL_CB_CONNECT_LOOP,1);
245 s->state=new_state;
246 }
247 }
248end:
249 s->in_handshake--;
250 if (buf != NULL)
251 BUF_MEM_free(buf);
252 if (cb != NULL)
253 cb(s,SSL_CB_CONNECT_EXIT,ret);
254 return(ret);
255 }
其主要流程是 ssl 初始化 ——> 客戶端發(fā)送 Hello ——> 獲取服務(wù)端的 Hello淆党。
客戶端發(fā)送hello
static int ssl23_client_hello(SSL *s)
273 {
.......
// 隨機數(shù)
357 p=s->s3->client_random;
358 Time=(unsigned long)time(NULL); /* Time */
359 l2n(Time,p);
......
401 // 版本號
402 s->client_version = version;
403
404 if (ssl2_compat)
405 {
406 /* create SSL 2.0 compatible Client Hello */
407
408 /* two byte record header will be written last */
.......
464 }
465 else
466 {
467 /* create Client Hello in SSL 3.0/TLS 1.0 format */
468
469 /* do the record header (5 bytes) and handshake message header (4 bytes) last */
470 d = p = &(buf[9]);
471
472 *(p++) = version_major;
473 *(p++) = version_minor;
474
475 /* Random stuff */
476 memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE);
477 p += SSL3_RANDOM_SIZE;
478
479 /* Session ID (zero since there is no reuse) */
480 *(p++) = 0;
481 // 所支持的 ciphers 套件
482 /* Ciphers supported (using SSL 3.0/TLS 1.0 format) */
483 i=ssl_cipher_list_to_bytes(s,SSL_get_ciphers(s),&(p[2]),ssl3_put_cipher_by_char);
498 s2n(i,p);
499 p+=i;
500
.......
534
535 /* fill in 4-byte handshake header */
536 d=&(buf[5]);
// 消息類型
537 *(d++)=SSL3_MT_CLIENT_HELLO;
538 l2n3(l,d);
539
540 l += 4;
541
565 ssl3_finish_mac(s,&(buf[5]), s->init_num - 5);
566 }
567 // 置為 SSL23_ST_CW_CLNT_HELLO_B
568 s->state=SSL23_ST_CW_CLNT_HELLO_B;
569 s->init_off=0;
570 }
571
572 /* SSL3_ST_CW_CLNT_HELLO_B */
// 寫入數(shù)據(jù)
573 ret = ssl23_write_bytes(s);
574
575 if ((ret >= 2) && s->msg_callback)
576 {
577 /* Client Hello has been sent; tell msg_callback */
578 // 回調(diào)
579 if (ssl2_compat)
580 s->msg_callback(1, SSL2_VERSION, 0, s->init_buf->data+2, ret-2, s, s->msg_callback_arg);
581 else
582 s->msg_callback(1, version, SSL3_RT_HANDSHAKE, s->init_buf->data+5, ret-5, s, s->msg_callback_arg);
583 }
584
585 return ret;
586 }
客戶端發(fā)送 hello ,主要是發(fā)送了隨機數(shù)讶凉,支持的密鑰算法染乌,SSL/TLS的版本號,當然還有消息類型SSL3_MT_CLIENT_HELLO懂讯。服務(wù)端在收到SSL3_MT_CLIENT_HELLO時會作出回應(yīng)荷憋,也就是發(fā)送服務(wù)端的HELLO。服務(wù)端會在 s23_srvr.c 的 ssl23_get_client_hello 中處理褐望。這里只關(guān)注 Android 端勒庄,因此不展開服務(wù)端的。我們只要知道服務(wù)端會從 SSL3_MT_CLIENT_HELLO 傳過來的 Support Ciphers 里確定一份加密套件瘫里,這個套件決定了后續(xù)加密和生成摘要時具體使用哪些算法实蔽,另外還會生成一份隨機數(shù) Random2。
獲取服務(wù)端的 Hello
static int ssl23_get_server_hello(SSL *s)
589 {
......
// 讀取數(shù)據(jù)
595 n=ssl23_read_bytes(s,7);
596
597 if (n != 7) return(n);
598 p=s->packet;
599
600 memcpy(buf,p,n);
601
// SSL2_MT_SERVER_HELLO消息
602 if ((p[0] & 0x80) && (p[2] == SSL2_MT_SERVER_HELLO) &&
603 (p[5] == 0x00) && (p[6] == 0x02))
604 {
......
672 }
673 else if (p[1] == SSL3_VERSION_MAJOR &&
674 p[2] <= TLS1_2_VERSION_MINOR &&
675 ((p[0] == SSL3_RT_HANDSHAKE && p[5] == SSL3_MT_SERVER_HELLO) ||
676 (p[0] == SSL3_RT_ALERT && p[3] == 0 && p[4] == 2)))
677 {
678 /* we have sslv3 or tls1 (server hello or alert) */
679 // 服務(wù)端選定的協(xié)議版本
680 if ((p[2] == SSL3_VERSION_MINOR) &&
......
693 }
694 else if ((p[2] == TLS1_VERSION_MINOR) &&
695 !(s->options & SSL_OP_NO_TLSv1))
696 {
697 ......
699 }
700 else if ((p[2] == TLS1_1_VERSION_MINOR) &&
701 !(s->options & SSL_OP_NO_TLSv1_1))
702 {
703 ......
705 }
706 else if ((p[2] == TLS1_2_VERSION_MINOR) &&
707 !(s->options & SSL_OP_NO_TLSv1_2))
708 {//假設(shè)是這個版本吧谨读,因為現(xiàn)在主要用的就是這個版本了
709 s->version=TLS1_2_VERSION;
// 這里的 method 被重新定義了為 TLSv1_2_client_method,它也是一個宏實現(xiàn)局装,其最重要和 ssl_connect 指向了 ssl3_connect.
710 s->method=TLSv1_2_client_method();
711 }
712 else
713 {
714 SSLerr(SSL_F_SSL23_GET_SERVER_HELLO,SSL_R_UNSUPPORTED_PROTOCOL);
715 goto err;
716 }
717
718 if (p[0] == SSL3_RT_ALERT && p[5] != SSL3_AL_WARNING)
719 {
720 /* fatal alert */
721
722 void (*cb)(const SSL *ssl,int type,int val)=NULL;
723 int j;
724
725 if (s->info_callback != NULL)
726 cb=s->info_callback;
727 else if (s->ctx->info_callback != NULL)
728 cb=s->ctx->info_callback;
729
730 i=p[5];
731 if (cb != NULL)
732 {
733 j=(i<<8)|p[6];
734 cb(s,SSL_CB_READ_ALERT,j);
735 }
736
737 if (s->msg_callback)
738 s->msg_callback(0, s->version, SSL3_RT_ALERT, p+5, 2, s, s->msg_callback_arg);
739
740 s->rwstate=SSL_NOTHING;
741 SSLerr(SSL_F_SSL23_GET_SERVER_HELLO,SSL_AD_REASON_OFFSET+p[6]);
742 goto err;
743 }
744
745 if (!ssl_init_wbio_buffer(s,1)) goto err;
746
747 /* we are in this state */
// 置狀態(tài)為
748 s->state=SSL3_ST_CR_SRVR_HELLO_A;
749
......
761 改變 handshake_func 的值為 s->method->ssl_connect
762 s->handshake_func=s->method->ssl_connect;
763 }
764 else
765 {
766 SSLerr(SSL_F_SSL23_GET_SERVER_HELLO,SSL_R_UNKNOWN_PROTOCOL);
767 goto err;
768 }
782 // 再調(diào)用 SSL_connect
783 return(SSL_connect(s));
784err:
785 return(-1);
786 }
這一段代碼只讀了 7 個字節(jié),主要就是確定了協(xié)議版本以及用于執(zhí)行繼續(xù)握手的handshake_fun的指向劳殖。根據(jù)代碼里的分析此時是指向的是 ssl3_connect铐尚。另外,此時SSL的狀態(tài)為 SSL3_ST_CR_SRVR_HELLO_A哆姻。在 ssl3_connect中宣增,對于 SSL3_ST_CR_SRVR_HELLO_A 的處理是調(diào)用 ssl3_get_server_hello()
int ssl3_get_server_hello(SSL *s)
892 {
......
948 /* load the server hello data */
949 /* load the server random */
// 獲取服務(wù)器的隨機數(shù)
950 memcpy(s->s3->server_random,p,SSL3_RANDOM_SIZE);
951 p+=SSL3_RANDOM_SIZE;
......
// 獲取加密算法
1016 c=ssl_get_cipher_by_char(s,p);
.......
//
1032 p+=ssl_put_cipher_by_char(s,NULL,NULL);
.....
1146 return(1);
1147f_err:
1148 ssl3_send_alert(s,SSL3_AL_FATAL,al);
1149err:
1150 return(-1);
1151 }
證書校驗
ssl3_get_server_hello()函數(shù)主要是讀取了從服務(wù)器傳遞過來的隨機數(shù)以及所選擇的加密算法。此時就有兩個隨機數(shù)了矛缨。
ssl3_get_server_hello() 執(zhí)行完成后统舀,就會將 s->state 的置為 SSL3_ST_CR_CERT_A匆骗。對于SSL3_ST_CR_CERT_A的狀態(tài)調(diào)用的是 ssl3_get_server_certificate()函數(shù)
int ssl3_get_server_certificate(SSL *s)
1154 {
1165 n=s->method->ssl_get_message(s,
1166 SSL3_ST_CR_CERT_A,
1167 SSL3_ST_CR_CERT_B,
1168 -1,
1169 s->max_cert_list,
1170 &ok);
1171
1203 for (nc=0; nc<llen; )
1204 {
1205 ......
1214 x=d2i_X509(NULL,&q,l);
1215 ......
1227 if (!sk_X509_push(sk,x))
1228 {
1229 ......
1231 }
1232 ......
1235 }
1236 // 校驗證書
1237 i=ssl_verify_cert_chain(s,sk);
......
// 取出公鑰
1263
1264 pkey=X509_get_pubkey(x);
1265
1336 return(ret);
1337 }
1338
主要就是檢驗證書,并獲取公鑰誉简。
握手的后續(xù)流程中對于雙向驗證可能還要發(fā)送DH參數(shù)碉就,以及客戶端的證書給服務(wù)器進行校驗。接收服務(wù)器的 Hello Done消息等闷串,這些就一筆帶過了瓮钥。
在 ssl3_send_client_key_exchange() 函數(shù)會生成第三個隨機數(shù),然后會用服務(wù)器下發(fā)的證書中RSA的公鑰對其進行加密烹吵,并傳遞給服務(wù)器碉熄。自此客戶端與服務(wù)器雙方都有三個隨機數(shù),而利用這三個隨機數(shù)就可以各自生成對稱加密算法的密鑰肋拔,最后通過這個對稱加密算法來進行通信锈津。
k.獲取證書構(gòu)建 OpenSSLSeesionImpl
l.上層完成握手并發(fā)出通知
三、總結(jié)
1.SSL/TLS 是位于傳輸層與應(yīng)用層之間的凉蜂,其主要的職責(zé)就是完成握手協(xié)議以及實現(xiàn)數(shù)據(jù)的加解密傳輸琼梆。
2.Android 中的 SSL/TLS 是移植的 OpenSSL的實現(xiàn),一個重磅的開源庫窿吩,想要深了解的同學(xué)不妨看看其Github
3.握手是在 TCP 連接建立起來之后才進行的茎杂,這也應(yīng)證了第1點其確實應(yīng)該處于應(yīng)用層與傳輸層之間。
4.握手的核心流程是:
客戶端hello(隨機數(shù)1纫雁,密鑰套件煌往,協(xié)議版本) -> 服務(wù)端Hello(選擇的密鑰,協(xié)議版本轧邪,隨機數(shù)2刽脖,并下發(fā)證書) -> 服務(wù)端完成Hello -> 客戶端校驗證書 -> 客戶端生成隨機數(shù)3并用公鑰加密 -> 完成握手 -> 客戶端與服務(wù)端各自用三個隨機數(shù)生成對稱密鑰進行加密通信
文章前后共持續(xù)了半個月左右才完成,然而由于水平有限忌愚,文章中難免存在紕漏以及錯誤之處曾棕,還請各位看到文章的同學(xué)多多包含并幫忙指出,非常感謝菜循。