zoukankan      html  css  js  c++  java
  • [原]OpenSSL SSL连接初始化部分解析


    SSL_CTX_new: 分配并初始化SSL_CTX结构,
    1 很重要的就是load cipher_list
    2 设置ssl_session timeout时间默认为7200秒
    3 初始化client_CA STACK
    4 初始化EVP_MD, rsa_md5, md5, sha1
    5 初始化ex_data.
    6 初始化comp_methods

    重点解析load cipher_list, 因为在替换engine时, 要指定我们支持的算法;

    关键函数: ssl_create_cipher_list

    预设EVP_CIPHER算法
    ssl_cipher_methods - 全局空数组, 元素类型: EVP_CIPHER *, 容量:9
    0, 1, 2 3 4, 7, 8 - 只用到了7个
    7 - SN_aes_128_cbc
    8 - SN_aes_256_cbc

    预设EVP_MD算法
    ssl_digest_methods - 全局空数组, 元素类型: EVP_MD *, 容量:2
    0 - sm_md5
    1 - SN_sha1(fmcpc supported)

    所有evp_cipher和evp_md是放在一个hash列表中, 根据名称检索得到的

    根据一个static int init_ciphers全局标志, 来决定是否load_cipher_list
    其过程用CRYPTO_w_lock进行lock, 线程安全

    完了后, 会调用: ssl_cipher_get_disabled
    首先是硬设置一些被disable的算法:
    默认编译下:
    10000000101000
    SSL_kFZA - 0x8 | SSL_kKRB5 - 0x20 |SSL_aKRB5 - 0x2000
    然后检测ssl_cipher_methods和ssl_digest_methods表中, 哪些指针为空, 如果为
    空, 将该算法对应的disable mask位设置为1
    SSL_eFZA - 0x100000
    最终mask == 100000010000000101000 - 0x00102028
    如果我们强行改ssl代码, 应该可以首先disable其他算法, 只保留
    AES-SHA1, 但这种方法视乎不妥.

    然后获取num_of_ciphers, 这个num_of_ciphers非常简单, 就是一个全局的
    sizeof(ssl3_ciphers)/sizeof(SSL_CIPHER)
    ssl3_ciphers在s3_lib.c中定义, 全局, 定义时初始化. 原属为SSL_CIPHER
    SSL_CIPHER 的每个算法指定了:
    密钥交换算法, 非对称算法, 对称算法及模式, hash算法, 如:
    TLS1_TXT_RSA_WITH_AES_128_SHA/TLS1_TXT_DH_RSA_WITH_AES_128_SHA
    加密强度, used(前面是disable)掩码, 算法位数等;
    这里得到的num_of_cipher长度为0x48(72)个.

    然后将预定义的SSL_CIPHERS从全局数组中取出来, 和disable_mask按位比较, 去掉不适用的算法.
    算法被放到一个CIPHER_ORDER结构中, 有个active标志, 应该特别注意, 这个标志初始化被置为0
    后面还要根据rulestr, strength, mask来决定, 哪些算法应该保留, 保留的被放到cipherstack中
    这个过程看起来比较复杂, 没有仔细解读.

    这里还应该注意一个宏: KSSL_DEBUG, 如果在nt_dll.mak中定义该宏, 应该所有算法会被打印出来.
    这是OpenSSL作者自己定义的宏.

    实际上, 被CIPHER_ORDER数组的算法有0x3c(60)个(很多了). CIPHER_ORDER内部有个prev和next
    在加入完成后, 按照顺序排成一个双向链表, 并返回头和尾的指针.

    最后有1B(27)个算法被选中, 与我们dump出来的server_cipher_list的个数相同. 算法列表:
    DHE-RSA-AES256-SHA
    DHE-DSS-AES256-SHA
    AES256-SHA <<<<<<<<<<< 调试后发现使用的这个算法.
    EDH-RSA-DES-CBC3-SHA
    EDH-DSS-DES-CBC3-SHA
    DES-CBC3-SHA
    DHE-RSA-AES128-SHA
    DHE-DSS-AES128-SHA
    AES128-SHA  >>>>>>>>>>>>> 因为加密卡只支持这两种算法, 所以, 这里应该用这个.
    IDEA-CBC-SHA
    DHE-DSS-RC4-SHA
    RC4-SHA
    RC4-MD5
    EXP1024-DHE-DSS-DES-CBC-SHA
    EXP1024-DES-CBC-SHA
    EXP1024-RC2-CBC-MD5
    EDH-RSA-DES-CBC-SHA
    EDH-DSS-DES-CBC-SHA
    DES-CBC-SHA
    EXP1024-DHE-DSS-RC4-SHA
    EXP1024-RC4-SHA
    EXP1024-RC4-MD5
    EXP-EDH-RSA-DES-CBC-SHA
    EXP-EDH-DSS-DES-CBC-SHA
    EXP-DES-CBC-SHA
    EXP-RC2-CBC-MD5
    EXP-RC4-MD5

    SHA - 是指用SHA1
    AES128 - 使用的模式是CBC
    这里有两种算法必须支持:
    NID_md5 和 NID_sha1必须支持, 不能屏蔽掉, 否则会报错.


    /////////////////////////////////////////////
    接下来是使用client证书
    //int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type)
    SSL_CTX_use_certificate_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM)
    返回值, <= 0表示错误.
    过程:
    1 读取client证书, 证书存放格式: pem (der编码的BASE64表示)
      PEM_read_bio_X509, 解析为X509格式
    2 SSL_CTX_use_certificate
      2.1 ssl_set_cert
      证书中, pub_key存放位置: X509::cert_info::key, 格式:X509_PUBKEY
      可以使用函数: X509_PUBKEY_get获得, 返回EVP_KEY, 比较关键了.
      (key = x->cert_info->key)
      type=OBJ_obj2nid(key->algor->algorithm); type:6, 对应NID_rsaEncryption
      key->algor->algorithm是NID_rsaEncryption对应的OBJECT DER编码
      然后调用EVP_PKEY_new, 这些都调不到engine中. go on -->

      der = key->public_key->data存放的是rsa public key的der编码.
      length = key->public_key->length
      调用: d2i_PublicKey(type, &ret, &p, (long)j), ret为EVP_KEY类型
     
      ret->save_type=type; // ret类型EVP_KEY
      ret->type=EVP_PKEY_type(type);
      这里直接ret->pkey.rsa=d2i_RSAPublicKey(NULL, der,length);
     
      // 从这里看, 已经得到EVP_KEY了, 但并没有调用与Engine相关的函数

      然后将得到EVP_KEY指针存放在x->cert_info->key->pkey中, 并返回该指针
      这里我们记录下整个x509结构的内容:
        -    x    0x019e8cb8
        -    cert_info    0x019e8d30
        +    version    0x019e7b30
        +    serialNumber    0x019e8d70
        +    signature    0x007c9798
        +    issuer    0x019e8d98
        +    validity    0x007c4070
        +    subject    0x019e7980
        -    key    0x019e79d0
        +    algor    0x019e79f8
        +    public_key    0x019e7a18
        -    pkey    0x019e8ee8
            type    0x00000006
            save_type    0x00000006
            references    0x00000002
        -    pkey    {...}
        +    ptr    0x019e8f18 ""
        -    rsa    0x019e8f18
            pad    0x00000000
            version    0x00000000
        +    meth    0x0063e958 rsa_pkcs1_eay_meth
            engine    0x00000000
        +    n    0x019e9008
        +    e    0x019e90d8
        +    d    0x00000000
        +    p    0x00000000
        +    q    0x00000000
        +    dmp1    0x00000000
        +    dmq1    0x00000000
        +    iqmp    0x00000000
        -    ex_data    {...}
        +    sk    0x00000000
            dummy    0xbaadf00d
            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    0x019e8f18
        +    dh    0x019e8f18
            ec    0x019e8f18
            save_parameters    0x00000001
        +    attributes    0x00000000
        +    issuerUID    0x00000000
        +    subjectUID    0x00000000
        +    extensions    0x019ea208
        +    sig_alg    0x019e7a40
        +    signature    0x019e7a60
            valid    0x00000000
            references    0x00000001
        +    name    0x019ea868 "/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    0x019e8cfc ""
        +    aux    0x00000000
        可以看到, evp_pkey中的rsa的结构, 只有n, e, 没有私钥的部分. 合理, 构造只具有公钥部分的rsa完全没问题.
       
        然后会检测证书中是否包含有私钥, 如果有私钥, 会将私钥部分拷贝到x->cert_info->key->pkey中.
        在使用硬件加密卡时, 证书存放在加密卡的存储器中, 证书一般不包含私钥, key pair另外存放在保密区域.
        所以, 这里不会出现这种情况. 这里说一下https通信, 我们在生成测试证书来搭建https通信时, 会
        将私钥捆绑在个人证书上, 然后导入人证书存放区域, 靠密码保护, 与此不同. (没有研究使用usb key的情况)
        因为ms的证书管理器没有单独导入key pair的地方(可以运行certmgr来看一下, 确实没有单独管理私钥的地方).
        https通信时, 一般需要用到P12格式的个人证书, 这个P12个人证书是将私钥与X509证书捆绑在一起的.
        openssl参考命令: openssl pkcs12 -export -clcerts -in crunch.cer -inkey crunchkey.pem -out crunch.p12

        然后会执行EVP_KEY_free, 把我吓一跳, 怎么会就free了呢, 才想起前面在返回EVP_PKEY前, EVP_PKEY
        的引用计数被加了一次. 原来这里只是减少引用计数而已;

        然后将cert->pkeys[i == 0].x509 = 传入的x509证书
        最后将cert->key指向&pkeys[i == 0].
        对了, 这个i是ssl_cert_type获得的结果, 如果是rsa证书, i为0, dsa对应2, ecc对应5, 其他的有错误

        ////////////////////////////////////////////

        接下来是需要调用到SSL_CTX_use_PrivateKey, 前面看到因为在加载证书时, 因为证书上没有捆绑私钥, 所以
        这里要单独执行SSL_CTX_use_PrivateKey.
        首先是调用PEM_read_bio_PrivateKey读取私钥证书. 然后再调用SSL_CTX_use_PrivateKey.
        key可能是PEM格式的, 也可能是直接存为DER格式的, 都可以, 只是读取key文件的函数要变一下;
        SSL_CTX_use_PrivateKey(SSL_CTX*, EVP_KEY*);
        SSL_CTX_use_PrivateKey内部主要调用ssl_set_pkey(CERT*, EVP_KEY*);
        在调用X509_get_pubkey, 获得的是前面提到的cert->pkes[i== 0].x509->cert_info->key->pkey
        然后调用EVP_PKEY_copy_parameters, 这个函数误导我了, 以为这里就是拷贝私钥部分, 只有当私钥type类型
        为EVP_PKEY_EC或EVP_PKEY_DSA时, 这个函数才拷贝私钥部分; RSA类型的在后面.
       
        读取的privatekey实际上是一个keypair, 直接将其赋值给了cert->pkeys[i == 0].privatekey.
        而在校验pubkey和privatekey时, 实际上是比较keypair中的公钥部分与pubkey中的公钥部分是否相等.

    OK, SSL连接的初始化工作已经做完了. 下面开始socket连接, 接下来就是SSL的握手部分了.

  • 相关阅读:
    java 支付宝即时到帐提交订单dome
    C#字符串string的常用使用方法(转载)
    UltraEdit 回车符替换空格
    java 七牛上传图片到服务器(采用的html5 压缩 传输base64方式)
    c# 多播委托
    c# 泛型委托
    c# 求数组的最大值
    c# 匿名函数和lamda表达式语法
    匿名函数和lamda表达式
    导出Excel之Epplus使用教程4(其他设置)
  • 原文地址:https://www.cnblogs.com/crunchyou/p/2872722.html
Copyright © 2011-2022 走看看