以Engine替换为出发点解析OpenSSL的SSL连接过程

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

转载于:https://www.cnblogs.com/crunchyou/archive/2013/01/26/2877648.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值