SSL_new
SSL_new沒有做什么實質性的工作
1 分配SSL結構的存儲空間, 初始SSL結構中的成員, 成員初始化也比較好理解; 其中有些是復制SSL_CTX的;
其中一個重要的就是分配SSL3_STATE結構;
2 SSL結構是整個SSL通信過程需要使用的類似Win32編程過程中的handle一樣的東東, 自始至終都再用.
SSL結構有點復雜: 包含了SSL3_STATE, SSL_CTX和SSL_SESSION結構;
因為細節沒有一一列舉, 這里將SSL_new並做了初始化后的結構拷貝粘貼在此, 就可以看到, SSL_CTX的
初始化已經在前面解讀過了:
- s 0x015fb750
version 0x00000300
type 0x00000000
- method 0x0027f268 SSLv3_client_data
version 0x00000300
ssl_new 0x002115a5 _ssl3_new
ssl_clear 0x00211145 _ssl3_clear
ssl_free 0x0021146a _ssl3_free
ssl_accept 0x002113d9 _ssl_undefined_function
ssl_connect 0x002115aa _ssl3_connect
ssl_read 0x0021169a _ssl3_read
ssl_peek 0x00211474 _ssl3_peek
ssl_write 0x002110c3 _ssl3_write
ssl_shutdown 0x002111fe _ssl3_shutdown
ssl_renegotiate 0x002114a6 _ssl3_renegotiate
ssl_renegotiate_check 0x0021155f _ssl3_renegotiate_check
ssl_get_message 0x002114d3 _ssl3_get_message
ssl_read_bytes 0x0021159b _ssl3_read_bytes
ssl_write_bytes 0x00211749 _ssl3_write_bytes
ssl_dispatch_alert 0x00211131 _ssl3_dispatch_alert
ssl_ctrl 0x0021147e _ssl3_ctrl
ssl_ctx_ctrl 0x0021151e _ssl3_ctx_ctrl
get_cipher_by_char 0x00211037 _ssl3_get_cipher_by_char
put_cipher_by_char 0x002116bd _ssl3_put_cipher_by_char
ssl_pending 0x00211582 _ssl3_pending
num_ciphers 0x002110eb _ssl3_num_ciphers
get_cipher 0x0021162c _ssl3_get_cipher
get_ssl_method 0x0022b380 ssl3_get_client_method(int)
get_timeout 0x00232ab0 ssl3_default_timeout(void)
- ssl3_enc 0x0027d5d8 SSLv3_enc_data
enc 0x002115c3 _ssl3_enc
mac 0x002115f5 _ssl3_mac
setup_key_block 0x002116e0 _ssl3_setup_key_block
generate_master_secret 0x002113b6 _ssl3_generate_master_secret
change_cipher_state 0x0021119f _ssl3_change_cipher_state
final_finish_mac 0x00211631 _ssl3_final_finish_mac
finish_mac_length 0x00000024
cert_verify_mac 0x0021163b _ssl3_cert_verify_mac
+ client_finished_label 0x00273b8c "CLNT"
client_finished_label_len 0x00000004
+ server_finished_label 0x00273b84 "SRVR"
server_finished_label_len 0x00000004
alert_value 0x00211343 _ssl3_alert_code
ssl_version 0x0021145b _ssl_undefined_void_function
ssl_callback_ctrl 0x002110ff _ssl3_callback_ctrl
ssl_ctx_callback_ctrl 0x0021129e _ssl3_ctx_callback_ctrl
+ rbio 0x00000000
+ wbio 0x00000000
+ bbio 0x00000000
rwstate 0x00000001
in_handshake 0x00000000
handshake_func 0x00000000
server 0x00000000
new_session 0x00000000
quiet_shutdown 0x00000000
shutdown 0x00000000
state 0x00005000
rstate 0x000000f0
+ init_buf 0x00000000
init_msg 0x00000000
init_num 0x00000000
init_off 0x00000000
+ packet 0x00000000 ""
packet_length 0x00000000
+ s2 0x00000000
- s3 0x015fbbf8
flags 0x00000000
delay_buf_pop_ret 0x00000000
+ read_sequence 0x015fbc00 ""
+ read_mac_secret 0x015fbc08 ""
+ write_sequence 0x015fbc48 ""
+ write_mac_secret 0x015fbc50 ""
+ server_random 0x015fbc90 ""
+ client_random 0x015fbcb0 ""
need_empty_fragments 0x00000000
empty_fragment_done 0x00000000
+ rbuf {...}
+ wbuf {...}
+ rrec {...}
+ wrec {...}
+ alert_fragment 0x015fbd48 ""
alert_fragment_len 0x00000000
+ handshake_fragment 0x015fbd50 ""
handshake_fragment_len 0x00000000
wnum 0x00000000
wpend_tot 0x00000000
wpend_type 0x00000000
wpend_ret 0x00000000
+ wpend_buf 0x00000000 ""
+ finish_dgst1 {...}
+ finish_dgst2 {...}
change_cipher_spec 0x00000000
warn_alert 0x00000000
fatal_alert 0x00000000
alert_dispatch 0x00000000
+ send_alert 0x015fbd9c ""
renegotiate 0x00000000
total_renegotiations 0x00000000
num_renegotiations 0x00000000
in_read_app_data 0x00000000
+ tmp {...}
+ d1 0x00000000
read_ahead 0x00000000
msg_callback 0x00000000
msg_callback_arg 0x00000000
hit 0x00000000
+ param 0x015fbbc0
+ cipher_list 0x00000000
+ cipher_list_by_id 0x00000000
+ enc_read_ctx 0x00000000
+ read_hash 0x00000000
+ expand 0x00000000
+ enc_write_ctx 0x00000000
+ write_hash 0x00000000
+ compress 0x00000000
+ cert 0x015fb870
sid_ctx_length 0x00000000
+ sid_ctx 0x015fb7ec ""
+ session 0x00000000
generate_session_id 0x00000000
verify_mode 0x00000000
verify_callback 0x00000000
info_callback 0x00000000
error 0x00000000
error_code 0x00000000
- ctx 0x015f71e0
+ method 0x0027f268 SSLv3_client_data
+ cipher_list 0x015f7bd8
+ cipher_list_by_id 0x015f7620
+ cert_store 0x015f7438
+ sessions 0x015f7368
session_cache_size 0x00005000
+ session_cache_head 0x00000000
+ session_cache_tail 0x00000000
session_cache_mode 0x00000002
session_timeout 0x00001c20
new_session_cb 0x00000000
remove_session_cb 0x00000000
get_session_cb 0x00000000
+ stats {...}
references 0x00000002
app_verify_callback 0x00000000
app_verify_arg 0x00000000
default_passwd_callback 0x00000000
default_passwd_callback_userdata 0x00000000
client_cert_cb 0x00000000
app_gen_cookie_cb 0x00000000
app_verify_cookie_cb 0x00000000
+ ex_data {...}
- rsa_md5 0x00602310 md5_md
type 0x00000004
pkey_type 0x00000008
md_size 0x00000010
flags 0x00000000
init 0x004cc470 init(env_md_ctx_st *)
update 0x004cc4a0 update(env_md_ctx_st *, const void *, unsigned int)
final 0x004cc4d0 final(env_md_ctx_st *, unsigned char *)
copy 0x00000000
cleanup 0x00000000
sign 0x0043317f _RSA_sign
verify 0x004310a0 _RSA_verify
+ required_pkey_type 0x0060233c
block_size 0x00000040
ctx_size 0x00000060
- md5 0x00602310 md5_md
type 0x00000004
pkey_type 0x00000008
md_size 0x00000010
flags 0x00000000
init 0x004cc470 init(env_md_ctx_st *)
update 0x004cc4a0 update(env_md_ctx_st *, const void *, unsigned int)
final 0x004cc4d0 final(env_md_ctx_st *, unsigned char *)
copy 0x00000000
cleanup 0x00000000
sign 0x0043317f _RSA_sign
verify 0x004310a0 _RSA_verify
+ required_pkey_type 0x0060233c
block_size 0x00000040
ctx_size 0x00000060
- sha1 0x00602470 sha1_md
type 0x00000040
pkey_type 0x00000041
md_size 0x00000014
flags 0x00000000
init 0x004cc700 init(env_md_ctx_st *)
update 0x004cc730 update(env_md_ctx_st *, const void *, unsigned int)
final 0x004cc760 final(env_md_ctx_st *, unsigned char *)
copy 0x00000000
cleanup 0x00000000
sign 0x0043317f _RSA_sign
verify 0x004310a0 _RSA_verify
+ required_pkey_type 0x0060249c
block_size 0x00000040
ctx_size 0x00000064
+ extra_certs 0x00000000
+ comp_methods 0x006e3a48
info_callback 0x00000000
+ client_CA 0x015f7720
options 0x00000000
mode 0x00000000
max_cert_list 0x00019000
- cert 0x015f72f0
- key 0x015f7318
- x509 0x015f8cb8
- cert_info 0x015f8d30
+ version 0x015f7b30
+ serialNumber 0x015f8d70
+ signature 0x006e9798
+ issuer 0x015f8d98
+ validity 0x006e4070
+ subject 0x015f7980
- key 0x015f79d0
+ algor 0x015f79f8
- public_key 0x015f7a18
length 0x0000008c
type 0x00000003
+ data 0x015fa410 "0亯亖"
flags 0x00000008
- pkey 0x015f8ee8
type 0x00000006
save_type 0x00000006
references 0x00000001
- pkey {...}
+ ptr 0x015f8f18 ""
- rsa 0x015f8f18
pad 0x00000000
version 0x00000000
+ meth 0x0063e958 rsa_pkcs1_eay_meth
engine 0x00000000
+ n 0x015f9008
+ e 0x015f90d8
+ d 0x00000000
+ p 0x00000000
+ q 0x00000000
+ dmp1 0x00000000
+ dmq1 0x00000000
+ iqmp 0x00000000
+ ex_data {...}
references 0x00000001
flags 0x00000006
+ _method_mod_n 0x00000000
+ _method_mod_p 0x00000000
+ _method_mod_q 0x00000000
+ bignum_data 0x00000000 ""
blinding 0x00000000
mt_blinding 0x00000000
+ dsa 0x015f8f18
+ dh 0x015f8f18
ec 0x015f8f18
save_parameters 0x00000001
+ attributes 0x00000000
+ issuerUID 0x00000000
+ subjectUID 0x00000000
+ extensions 0x015fa208
+ sig_alg 0x015f7a40
+ signature 0x015f7a60
valid 0x00000000
references 0x00000002
+ name 0x015fa868 "/C=CN/ST=Chongqing/O=YZ/OU=YZ/CN=sslsocketclient/emailAddress=sslsocketclient@yunzhen.com"
+ ex_data {...}
ex_pathlen 0xffffffff
ex_pcpathlen 0x00000000
ex_flags 0x00000000
ex_kusage 0x00000000
ex_xkusage 0x00000000
ex_nscert 0x00000000
+ skid 0x00000000
+ akid 0x00000000
policy_cache 0x00000000
+ sha1_hash 0x015f8cfc ""
+ aux 0x00000000
- privatekey 0x015f9128
type 0x00000006
save_type 0x00000006
references 0x00000003
- pkey {...}
+ ptr 0x015faf00 ""
- rsa 0x015faf00
pad 0x00000000
version 0x00000000
+ meth 0x0063e958 rsa_pkcs1_eay_meth
engine 0x00000000
+ n 0x015f9158
+ e 0x015faf70
+ d 0x015f9288
+ p 0x015f9350
+ q 0x015f93e0
+ dmp1 0x015f9470
+ dmq1 0x015fafc0
+ iqmp 0x015fb048
+ ex_data {...}
references 0x00000001
flags 0x00000006
+ _method_mod_n 0x00000000
+ _method_mod_p 0x00000000
+ _method_mod_q 0x00000000
+ bignum_data 0x00000000 ""
blinding 0x00000000
mt_blinding 0x00000000
+ dsa 0x015faf00
+ dh 0x015faf00
ec 0x015faf00
save_parameters 0x00000001
+ attributes 0x00000000
valid 0x00000000
mask 0x00000000
export_mask 0x00000000
+ rsa_tmp 0x00000000
rsa_tmp_cb 0x00000000
+ dh_tmp 0x00000000
dh_tmp_cb 0x00000000
ecdh_tmp 0x00000000
ecdh_tmp_cb 0x00000000
+ pkeys 0x015f7318
references 0x00000001
read_ahead 0x00000000
msg_callback 0x00000000
msg_callback_arg 0x00000000
verify_mode 0x00000000
sid_ctx_length 0x00000000
+ sid_ctx 0x015f72a8 ""
default_verify_callback 0x00000000
generate_session_id 0x00000000
+ param 0x015f76e8
quiet_shutdown 0x00000000
debug 0x00000000
verify_result 0x00000000
- ex_data {...}
+ sk 0x00000000
dummy 0x00000000
+ client_CA 0x00000000
references 0x00000001
options 0x00000000
mode 0x00000000
max_cert_list 0x00019000
first_packet 0x00000000
client_version 0x00000300
//////////////////////////////////////////////////////////
接下來是SSL_set_fd, 將要通信的socket設置給SSL結構
眾所周知, OpenSSL封裝了了一套稱為BIO的統一stream I/O機制,
stream包括stdin, stdout, stderr, FILE, socket等, 以上是常用的,
其實BIO的功能很強大, 還不止這些. 挺好用;
SSL_set_fd就是先構建一個socket類型的BIO, 然后設置fd. BIO->num就是fd
記住, 以后在觀察socket時, 可以看這個成員的值.
將此BIO指針分別賦值給SSL->rbio和SSL->wbio;
return 1表示成功, 0表示失敗, OpenSSL的返回值很多都是采用這種風格.
//////////////////////////////////////////////////////////
接下來就是SSL_connect, 馬上進入SSL的握手過程.
SSL_connect過程分為幾步完成, 應該有個狀態管理, SS::state應該是記錄狀態的.
1 第一步
初始狀態(client): (SSL* s)
s->server=0;
s->shutdown=0;
s->state=SSL_ST_CONNECT|SSL_ST_BEFORE;
s->handshake_func=s->method->ssl_connect;
SSL_connect連接調用的是SSL_METHOD::ssl_connect函數.
1) 生成隨機數, 這里第一次用到了與engine有關系的東西;
首先調用的RAND_add, 看起來RAND_METHOD::add回調似乎有必要關注;
這應該是設置隨機種子; 這里面用到了SHA1_MD. 不管它, 沒有時間去理解他的
隨機數實現算法.
2) ERR_clear_error: 清空該線程相關的ERR_STATE表
clear_sys_error: Win32 SetLastError(0), !defined(WIN32) errno = 0
這里看到了OpenSSL的error管理機制, 記得不久前, 想自己實現一個lasterror機制
才發現簡單幾句代碼沒有辦法實現, Windows的GetLastError是線程隔離的, 應該與
OpenSSL在這里實現的機制差不多. 而OpenSSL的ERROR還要強大一些, 還會反饋調用
堆棧, 最大16層, 到時候有空了一定看一下: crypto\err\err.c
ERR_clear_error大意是從一個與線程id相關的hash表, 返回ERR_STATE指針; 如果
沒有, 就分配一個新的, 放到hash表中.
3) s->in_handshake++; // 還不知道是干什么
前面已經看到, s->state設置為SSL_ST_CONNECT|SSL_ST_BEFORE
這里比較!(s->state & SSL_ST_INIT) || s->state & SSL_ST_BEFORE) SSL_clear
#define SSL_ST_INIT (SSL_ST_CONNECT|SSL_ST_ACCEPT)
這里執行的是一些清理工作, 當SSL斷開或者shutdown后, 重新調用SSL_connect時, 需要
檢測標志再次清理.
4) 檢測版本標志, 是不是SSLV3, 否則出錯.
設置s->type = SSL_ST_CONNECT;
分配s->init_buf. max_len:0x5558, 初始數據:0, 長度:SSL3_RT_MAX_PLAIN_LENGTH
5) ssl3_setup_buffers: 分配讀寫buffer
分配s->s3->rbuf, s->s3->wbuf, 這里可以看到buf長度為:
rbuf len= 0x4805, wbuf len = 0x490a.
做過測試, ssl write一次最大長度為16K, 也就是0x4000. 與這里的buffer分配有關.
我們可以在ssl3.h中找到
#define SSL3_RT_MAX_PLAIN_LENGTH 16384 // 正好16K
這里讀寫buffer為什么要長一點呢:
#define SSL3_RT_MAX_COMPRESSED_LENGTH (1024+SSL3_RT_MAX_PLAIN_LENGTH)
#define SSL3_RT_MAX_ENCRYPTED_LENGTH (1024+SSL3_RT_MAX_COMPRESSED_LENGTH)
#define SSL3_RT_MAX_PACKET_SIZE (SSL3_RT_MAX_ENCRYPTED_LENGTH+SSL3_RT_HEADER_LENGTH)
在分配寫buffer時, SSL3_RT_MAX_PACKET_SIZE+256. 哈哈. 我也經常這么干.
然后分s->bbio, 不知道, 看注釋/* used during session-id reuse to concatenate messages */
6) ssl3_init_finished_mac
這個函數就兩個語句:
EVP_DigestInit_ex(&(s->s3->finish_dgst1),s->ctx->md5, NULL);
EVP_DigestInit_ex(&(s->s3->finish_dgst2),s->ctx->sha1, NULL);
EVP_DigestInit_ex用到了Engine的EVP_MD的init回調, 當這里卻強制將Engine設置為空了.
意味着在初始化是, 就要將Engine植入, 否則我們的Engine怎么得到調用呢?
***********************************
結論: 還得依靠ENGINE_set_default_digests來設置EVP_MD. - 正確
***********************************
OpenSSL 1.0.1c中, 該函數的實現, 已經完全不同. 不過無論是1.0.1c還0.9.8a版本, 在調用
EVP_DigestInit_ex時, 都在engine參數位置填了NULL. 看來上面的結論沒有問題;
在EVP_DigestInit_ex內部, 會調用ENGINE_get_digest_engine獲得EVP_MD, 這是根據nid在
digest table中去選. 如果我們調用ENGINE_set_default_digests的話, 會在digest table
中找到我們設置的EVP_MD, 就不會調用Engine中實現的MD回調. 而不會調默認的MD回調了.
這里, 順便注意到了一個問題, 看下面的代碼, 這是在EVP_DigestInit_ex中的代碼:
const EVP_MD *d = ENGINE_get_digest(impl, type->type);
if(!d)
{
/* Same comment from evp_enc.c */
EVPerr(EVP_F_EVP_DIGESTINIT_EX,EVP_R_INITIALIZATION_ERROR);
return 0;
}
這里看起來設置了Engine, 仿佛就要實現Engine所列出的全部函數一樣. 黑老子一大跳.
仔細看ENGINE_set_default_digests的代碼, 設置default engine時, 其實只是設置某一方面的
比如digest, cipher, rsa等, 而且是根據nid來在一個全局ENGINE_PILE hash表中進行檢索的.
比如, digest, 就是在tb_digest.c中申明的全局變量:
static ENGINE_TABLE *digest_table = NULL;
struct st_engine_table
{
LHASH_OF(ENGINE_PILE) piles;
};
可以在crypto\engine目錄可以看到, 有很多tb_打頭的文件, 每個文件中, 都有一個ENGINE_TABLE
類型的全局變量個, 作用跟*digest_table一樣.
而且, 如果找不到Engine的話, 就會調用對應的確實回調函數來替代; 這下終於放心了!!!!!!!
7) 變換狀態:
s->state=SSL3_ST_CW_CLNT_HELLO_A;
#define SSL3_ST_CW_CLNT_HELLO_A (0x110|SSL_ST_CONNECT)
8) 狀態管理
在寫代碼時, 經常遇到要執行一系列步驟, 每個步驟都有自己的狀態, 不知道C代碼的狀態管理
"模式", 經常都寫得很"散", 狀態設置到處都是, 一不小心就搞錯. 雖然很少出錯, 當寫這部分
代碼時, 確費盡"心血", 精力高度集中. 而且維護起來比較麻煩;
OpenSSL在這里用了一個for(;;)+swithe(state), 將狀態管理統一在一處, 值得學習;
初始時, switch case SSL_ST_CONNECT:..., 執行初始化, 設置狀態; 進入下次循環, 如此.
汗!!!!
9) 開始向server say hello了.
這里要用到s->init_buf, 參見前面4)步:
分配s->init_buf. max_len:0x5558, 初始數據:0, 長度:SSL3_RT_MAX_PLAIN_LENGTH
首先建立session: ssl_get_new_session
調用: SSL_SESSION_new, 新建session, 除了設置session timeout外, 其他沒什么;
注意SSL_CTX中也有一個timeout, 這里session中也有一個timeout.
ssl_ctx->timeout: 7200, session->timeout: 60 *5 + 4. 不同, new完成后, 這個
session->timeout = ssl_ctx->timeout; // 7200. 2小時為准;
session_id長度, 32字節; 這里還沒有session_id, session_id是和server協商后server
發給client的.
發送的數據:
0~3 - 空置
4~5 - ssl->version, 4=version >> 8: 03, 5=ver&0xff:00
6~37 - random, s->s3->client_random, 產生隨機數時, 只產生了28bytes, 拷貝了32bytes
38 - s->session->session_id_length:0
41~42 ciphers by bytes, 沒兩個字節, 表示一個算法, 由算法ID取最低2個字節. 共1B個算法
39 00 38 00 35 00 16 00 13 00 0A 00 33 00 32 00 2F 00
07 00 66 00 05 00 04 00 63 00 62 00 61 00 15 00 12 00
09 00 65 00 64 00 60 00 14 00 11 00 08 00 06 00 03
95 - s->ctx->comp_methods 壓縮算法的個數, 但為0. 最后放到95位置時, 硬性+1
96 - NULL表示結束
長度: 不算0~3的空置, len = 93, 0x5d
最后填0~3, 0: SSL3_MT_CLIENT_HELLO -- 1
bigendian: len, 占3個字節: 00 00 5d
10) 轉換狀態: s->state=SSL3_ST_CW_CLNT_HELLO_B;
設置s->init_num = 97, s->init_off = 0
ssl3_do_write(s, SSL3_RT_HANDSHAKE); // type - SSL3_RT_HANDSHAKE
->call ssl3_write_bytes(s,type,&s->init_buf->data[s->init_off],s->init_num)
寫完成后, 如果type == SSL3_RT_HANDSHAKE
ssl3_finish_mac(s,(unsigned char *)&s->init_buf->data[s->init_off],ret);
ssl3_finish_mac 只做了digestupdate, 還沒有digestfinal.
11) client say hello 完成. 狀態轉換:
s->state=SSL3_ST_CR_SRVR_HELLO_A;
12 獲得ssl3_get_server_hello, 獲得server端返回的hello信息,server返回的hello信息中,
包含了session_id和選定的算法byte(0x35 AES256_SHA)
server在第一次say hello時, 已經選定算法了, ssl3_choose_cipher這個函數實現了此功能
枚舉client所有的ssl_clipher.
然后用server的cert
通過調用:
ssl_set_cert_masks(cert, client_ssl_cipher);
來決定一個mask和export mask - 簡寫emask
- cert 0x0182ca68
+ key 0x0182ca90--|
valid 0x00000000 |
mask 0x00000000 |
export_mask 0x00000000 |
+ rsa_tmp 0x00000000 |
rsa_tmp_cb 0x00000000 |
+ dh_tmp 0x00000000 |
dh_tmp_cb 0x00000000 |
ecdh_tmp 0x00000000 |
ecdh_tmp_cb 0x00000000 |
+ pkeys 0x0182ca90<-|
references 0x00000001
prio = clnt;
allow = srvr;
dh_dsa 0
dh_rsa 0
dh_tmp 0
dsa_sign 0
mask 0
rsa_enc 1
rsa_enc_export 1
rsa_sign 0
rsa_tmp 0
rsa_tmp_export 0
// 只有SSL_PKEY_RSA_ENC有數據, 其他的沒有數據
#define SSL_C_EXPORT_PKEYLENGTH(cipher) cipher->algo_strength & 0x00000008 ? 512 : 1024;
kl=SSL_C_EXPORT_PKEYLENGTH(cipher);
cpk= &(c->pkeys[SSL_PKEY_RSA_ENC]);
rsa_enc= (cpk->x509 != NULL && cpk->privatekey != NULL);
rsa_enc_export=(rsa_enc && EVP_PKEY_size(cpk->privatekey)*8 <= kl);
rsa_tmp=(c->rsa_tmp != NULL || c->rsa_tmp_cb != NULL);
rsa_tmp_export=(c->rsa_tmp_cb != NULL || (rsa_tmp && RSA_size(c->rsa_tmp)*8 <= kl));
if (rsa_enc || (rsa_tmp && rsa_sign))
mask|=SSL_kRSA;
if (rsa_enc_export || (rsa_tmp_export && (rsa_sign || rsa_enc)))
emask|=SSL_kRSA;
// #define SSL_kRSA 0x00000001L /* RSA key exchange */
if (rsa_enc || rsa_sign)
{
mask|=SSL_aRSA;
emask|=SSL_aRSA;
}
// #define SSL_aRSA 0x00000100L /* Authenticate with RSA */
#define SSL_aNULL 0x00000800L /* no Authenticate, ADH */
mask|=SSL_aNULL; // mask: 0x00000901
emask|=SSL_aNULL; // emask: 0x00000901
c->mask=mask;
c->export_mask=emask;
c->valid=1;
//#define SSL_MKEY_MASK 0x000000FFL // key exchange
//#define SSL_AUTH_MASK 0x00007F00L // authenticate
//#define SSL_ENC_MASK 0x043F8000L // cipher encode, symmetric
//#define SSL_MAC_MASK 0x00c00000L // hash algorithm
//#define SSL_SSL_MASK 0x03000000L // ssl version
alg=c->algorithms&(SSL_MKEY_MASK|SSL_AUTH_MASK);
#define SSL_EXPORT 0x00000002L
#define SSL_IS_EXPORT(a) ((a)&SSL_EXPORT)
#define SSL_C_IS_EXPORT(c) SSL_IS_EXPORT((c)->algo_strength)
當alg
if(SSL_C_IS_EXPORT(c))
ok=((alg & emask) == alg)?1:0;
else
ok=((alg & mask) == alg)?1:0;
因為是循環枚舉, OpenSSL最先發現cipher->name == AES256_SHA, id == 0x03000035的算法
滿足. AES256_SHA算法ID的宏定義為TLS1_CK_RSA_WITH_AES_256_SHA, 根據選擇過程.
首先計算mask和emask, 依據是證書中的密鑰交換算法和認證算法. 以及密鑰長度 小於等於kl, kl
的計算方式見前面ssl_set_cert_masks.
typedef struct ssl_cipher_st {
int valid;
const char *name; /* text name */
unsigned long id; /* id, 4 bytes, first is version */
unsigned long algorithms; /* what ciphers are used */
unsigned long algo_strength; /* strength and export flags */
unsigned long algorithm2; /* Extra flags */
int strength_bits; /* Number of bits really used */
int alg_bits; /* Number of bits for algorithm */
unsigned long mask; /* used for matching */
unsigned long mask_strength; /* also used for matching */
} SSL_CIPHER;
{
1, /* valid */
TLS1_TXT_RSA_WITH_AES_256_SHA, /* text name */
TLS1_CK_RSA_WITH_AES_256_SHA, /* id, 4 bytes, first is version */
SSL_kRSA|SSL_aRSA|SSL_AES|SSL_SHA |SSL_TLSV1, /* what ciphers are used */
SSL_NOT_EXP|SSL_HIGH, // 0x81 /* strength and export flags */
0, /* Extra flags */
256, /* Number of bits really used */
256, /* Number of bits for algorithm */
SSL_ALL_CIPHERS, /* used for matching */
SSL_ALL_STRENGTHS, /* also used for matching */
},
kl = 1024
{
1,
TLS1_TXT_RSA_WITH_AES_128_SHA,
TLS1_CK_RSA_WITH_AES_128_SHA,
SSL_kRSA|SSL_aRSA|SSL_AES|SSL_SHA |SSL_TLSV1,
SSL_NOT_EXP|SSL_MEDIUM, // 0x41
0,
128,
128,
SSL_ALL_CIPHERS,
SSL_ALL_STRENGTHS,
},
OpenSSL的策略是最先找到的匹配, 修改ok的值, 繼續枚舉, 中間發現:SSL3_CK_RSA_DES_192_CBC3_SHA
也滿足條件, DES不是我們支持的. 讓其繼續循環. 循環到第8個時, 找到了AES128_SHA. ok!!!!!
在server支持的cipherlist中查找, 找到, choose_cipher完成.
看來我們的解決方案只有修改OpenSSL代碼了.
修改ssl_set_cert_masks不合適, 涉及點比較多. 直接修改: ssl3_choose_cipher
//////////////////////////////////////////////////////////////////
增加條件限定:
(cipher->algorithms & SSL_ENC_MASK) & SSL_AES -> 將條件限定在AES
(cipher->algorithms & SSL_MAC_MASK) & SSL_SHA -> 將條件限定在SHA1
cipher->alg_bits == 128
這三個條件下來, 就能夠確保選中我們的算法了.
///////////////////////////////////////////////////////////////////
哇哦! 發現一個函數, 可以搞定:SSL_CTX_set_cipher_list(ctx, "AES128-SHA");
rule_str不用填哪個帶"!-+@"的晦澀的語法. 直接填算法名稱即可, 算法名稱在ssl3.h和tls1.h中定義.
后來還發現apps\s_client.c挺有參考價值.
找到自己想要的東西了, 接下來就沒詳細跟蹤了.
先回頭寫Engine, 有時間再來將這個流程搞通並梳理清除.
大概是以下的流程:
接下來是驗證證書. client會將證書鏈以DER的編碼格式傳遞到server端, server端在驗證了client端的
證書后. 接下來會獲得
在接下來是協商在以后通信過程中要用到的對稱算法, 對稱算法的user key計算比較復雜, 不過好在
我們不需要關心.
再接下來就是finish mac了. 用master_key, ssl3_pad_1和2做hash, 先做MD5, 然后在MD5的基礎
上做SHA1. finish mac是干什么的, 沒詳細探索.
我們可以從這一堆的SSL狀態宏定義中看到SSL_connect的處理順序:
/* write to server */
#define SSL3_ST_CW_CLNT_HELLO_A (0x110|SSL_ST_CONNECT)
#define SSL3_ST_CW_CLNT_HELLO_B (0x111|SSL_ST_CONNECT)
/* read from server */
#define SSL3_ST_CR_SRVR_HELLO_A (0x120|SSL_ST_CONNECT)
#define SSL3_ST_CR_SRVR_HELLO_B (0x121|SSL_ST_CONNECT)
#define DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A (0x126|SSL_ST_CONNECT)
#define DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B (0x127|SSL_ST_CONNECT)
#define SSL3_ST_CR_CERT_A (0x130|SSL_ST_CONNECT)
#define SSL3_ST_CR_CERT_B (0x131|SSL_ST_CONNECT)
#define SSL3_ST_CR_KEY_EXCH_A (0x140|SSL_ST_CONNECT)
#define SSL3_ST_CR_KEY_EXCH_B (0x141|SSL_ST_CONNECT)
#define SSL3_ST_CR_CERT_REQ_A (0x150|SSL_ST_CONNECT)
#define SSL3_ST_CR_CERT_REQ_B (0x151|SSL_ST_CONNECT)
#define SSL3_ST_CR_SRVR_DONE_A (0x160|SSL_ST_CONNECT)
#define SSL3_ST_CR_SRVR_DONE_B (0x161|SSL_ST_CONNECT)
/* write to server */
#define SSL3_ST_CW_CERT_A (0x170|SSL_ST_CONNECT)
#define SSL3_ST_CW_CERT_B (0x171|SSL_ST_CONNECT)
#define SSL3_ST_CW_CERT_C (0x172|SSL_ST_CONNECT)
#define SSL3_ST_CW_CERT_D (0x173|SSL_ST_CONNECT)
#define SSL3_ST_CW_KEY_EXCH_A (0x180|SSL_ST_CONNECT)
#define SSL3_ST_CW_KEY_EXCH_B (0x181|SSL_ST_CONNECT)
#define SSL3_ST_CW_CERT_VRFY_A (0x190|SSL_ST_CONNECT)
#define SSL3_ST_CW_CERT_VRFY_B (0x191|SSL_ST_CONNECT)
#define SSL3_ST_CW_CHANGE_A (0x1A0|SSL_ST_CONNECT)
#define SSL3_ST_CW_CHANGE_B (0x1A1|SSL_ST_CONNECT)
#define SSL3_ST_CW_FINISHED_A (0x1B0|SSL_ST_CONNECT)
#define SSL3_ST_CW_FINISHED_B (0x1B1|SSL_ST_CONNECT)
/* read from server */
#define SSL3_ST_CR_CHANGE_A (0x1C0|SSL_ST_CONNECT)
#define SSL3_ST_CR_CHANGE_B (0x1C1|SSL_ST_CONNECT)
#define SSL3_ST_CR_FINISHED_A (0x1D0|SSL_ST_CONNECT)
#define SSL3_ST_CR_FINISHED_B (0x1D1|SSL_ST_CONNECT)
/* server */
/* extra state */
#define SSL3_ST_SW_FLUSH (0x100|SSL_ST_ACCEPT)
/* read from client */
/* Do not change the number values, they do matter */
#define SSL3_ST_SR_CLNT_HELLO_A (0x110|SSL_ST_ACCEPT)
#define SSL3_ST_SR_CLNT_HELLO_B (0x111|SSL_ST_ACCEPT)
#define SSL3_ST_SR_CLNT_HELLO_C (0x112|SSL_ST_ACCEPT)
/* write to client */
#define DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A (0x113|SSL_ST_ACCEPT)
#define DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B (0x114|SSL_ST_ACCEPT)
#define SSL3_ST_SW_HELLO_REQ_A (0x120|SSL_ST_ACCEPT)
#define SSL3_ST_SW_HELLO_REQ_B (0x121|SSL_ST_ACCEPT)
#define SSL3_ST_SW_HELLO_REQ_C (0x122|SSL_ST_ACCEPT)
#define SSL3_ST_SW_SRVR_HELLO_A (0x130|SSL_ST_ACCEPT)
#define SSL3_ST_SW_SRVR_HELLO_B (0x131|SSL_ST_ACCEPT)
#define SSL3_ST_SW_CERT_A (0x140|SSL_ST_ACCEPT)
#define SSL3_ST_SW_CERT_B (0x141|SSL_ST_ACCEPT)
#define SSL3_ST_SW_KEY_EXCH_A (0x150|SSL_ST_ACCEPT)
#define SSL3_ST_SW_KEY_EXCH_B (0x151|SSL_ST_ACCEPT)
#define SSL3_ST_SW_CERT_REQ_A (0x160|SSL_ST_ACCEPT)
#define SSL3_ST_SW_CERT_REQ_B (0x161|SSL_ST_ACCEPT)
#define SSL3_ST_SW_SRVR_DONE_A (0x170|SSL_ST_ACCEPT)
#define SSL3_ST_SW_SRVR_DONE_B (0x171|SSL_ST_ACCEPT)
/* read from client */
#define SSL3_ST_SR_CERT_A (0x180|SSL_ST_ACCEPT)
#define SSL3_ST_SR_CERT_B (0x181|SSL_ST_ACCEPT)
#define SSL3_ST_SR_KEY_EXCH_A (0x190|SSL_ST_ACCEPT)
#define SSL3_ST_SR_KEY_EXCH_B (0x191|SSL_ST_ACCEPT)
#define SSL3_ST_SR_CERT_VRFY_A (0x1A0|SSL_ST_ACCEPT)
#define SSL3_ST_SR_CERT_VRFY_B (0x1A1|SSL_ST_ACCEPT)
#define SSL3_ST_SR_CHANGE_A (0x1B0|SSL_ST_ACCEPT)
#define SSL3_ST_SR_CHANGE_B (0x1B1|SSL_ST_ACCEPT)
#define SSL3_ST_SR_FINISHED_A (0x1C0|SSL_ST_ACCEPT)
#define SSL3_ST_SR_FINISHED_B (0x1C1|SSL_ST_ACCEPT)
/* write to client */
#define SSL3_ST_SW_CHANGE_A (0x1D0|SSL_ST_ACCEPT)
#define SSL3_ST_SW_CHANGE_B (0x1D1|SSL_ST_ACCEPT)
#define SSL3_ST_SW_FINISHED_A (0x1E0|SSL_ST_ACCEPT)
#define SSL3_ST_SW_FINISHED_B (0x1E1|SSL_ST_ACCEPT)
/////////////////////////////////////////////////////
還好沒有放棄跟蹤, 在對server端的trace執行過程中, server端調用ssl3_get_client_key_exchange
時, 在收到client送來的協商密鑰后, 會要用到s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey進行
解密. 調用函數為: RSA_private_decrypt. 因為client端生成exchange key后, 會用server端的pubkey
進行加密, 然后發送給server, 所以, 此時當然要解密;
在加密前, 會判斷s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey是否為空. 所以, 我們在使用硬件
Engine時, 還是要調用SSL_CTX_use_PrivateKey, 只是這里我們的Private的EVP_Key完全可用只包含
公鑰的RSA代替. RSA_private_decrypt內部會調用rsa->meth->rsa_priv_dec來解密. 此時應該執行的
是我們Engine的RSA解密函數.