Android原生網(wǎng)絡(luò)庫HttpURLConnection分析——HTTPS部分

一褒纲、前言

在《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()打開連接

OpenHttpsURLConnection.jpg

在詳細分析之前,需要再看一個類圖結(jié)構(gòu)囚灼,以了解 Http們 與 Https們的關(guān)系骆膝。


HttpsUrlConnectionImpl.jpg

從類圖可以看到祭衩,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ù)雜馁龟,先來看前兩部分:


HttpsGetOutputStream.jpg

(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)握手

HttpsGetOutputStream2.jpg

握手是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ù)來處理。

StatechartHandshake.jpg

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é)多多包含并幫忙指出,非常感謝菜循。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末翘地,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子癌幕,更是在濱河造成了極大的恐慌衙耕,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勺远,死亡現(xiàn)場離奇詭異橙喘,居然都是意外死亡,警方通過查閱死者的電腦和手機胶逢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門厅瞎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饰潜,“玉大人,你說我怎么就攤上這事和簸∨砦恚” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵锁保,是天一觀的道長薯酝。 經(jīng)常有香客問我,道長爽柒,這世上最難降的妖魔是什么吴菠? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮浩村,結(jié)果婚禮上做葵,老公的妹妹穿的比我還像新娘。我一直安慰自己心墅,他們只是感情好酿矢,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著嗓化,像睡著了一般棠涮。 火紅的嫁衣襯著肌膚如雪谬哀。 梳的紋絲不亂的頭發(fā)上刺覆,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音史煎,去河邊找鬼谦屑。 笑死,一個胖子當著我的面吹牛篇梭,可吹牛的內(nèi)容都是我干的氢橙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼恬偷,長吁一口氣:“原來是場噩夢啊……” “哼悍手!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起袍患,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤坦康,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后诡延,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滞欠,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年肆良,在試婚紗的時候發(fā)現(xiàn)自己被綠了筛璧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逸绎。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖夭谤,靈堂內(nèi)的尸體忽然破棺而出棺牧,到底是詐尸還是另有隱情,我是刑警寧澤沮翔,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布陨帆,位于F島的核電站,受9級特大地震影響采蚀,放射性物質(zhì)發(fā)生泄漏疲牵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一榆鼠、第九天 我趴在偏房一處隱蔽的房頂上張望纲爸。 院中可真熱鬧,春花似錦妆够、人聲如沸识啦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽颓哮。三九已至,卻和暖如春鸵荠,著一層夾襖步出監(jiān)牢的瞬間冕茅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工蛹找, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留姨伤,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓庸疾,卻偏偏與公主長得像乍楚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子届慈,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容