zoukankan      html  css  js  c++  java
  • 以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解密函数.

  • 相关阅读:
    JAVA日报
    JAVA日报
    论文爬取(七)
    论文爬取(六)
    论文爬取(五)
    JAVA日报
    JAVA日报
    剑指 Offer 68
    剑指 Offer 68
    剑指 Offer 67. 把字符串转换成整数
  • 原文地址:https://www.cnblogs.com/crunchyou/p/2877648.html
Copyright © 2011-2022 走看看