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解密函数.