• SSL连接建立过程分析(2)


    SSL连接建立过程分析(2)
     
    本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
    msn: yfydz_no1@hotmail.com
    来源:http://yfydz.cublog.cn/

    2.6 SSL_CTX_set_default_passwd_cb[_userdata]()

    这个函数比较简单,就是设置SSL要加载的证书的口令,如果不设置的话加载证书时会出提示符要求输入口令的,这样在程序中使用就比较麻烦,该函数就是预先将口令保存,在读证书时自动使用。

    实现该功能的有两个函数SSL_CTX_set_default_passwd_cb()和SSL_CTX_set_default_passwd_cb_userdata(),前者是定义一个口令回调函数,要获取口令时口令由该函数获取;后者是直接将口令设置好。

    /* ssl/ssl_lib.c */
    void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb)
     {
     ctx->default_passwd_callback=cb;
     }
    void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx,void *u)
     {
     ctx->default_passwd_callback_userdata=u;
     }

    举例:

    static int
    pass_cb(char *buf, int len, int verify, void *password)
    {
        snprintf(buf,len, "123456");
        return strlen(buf);
    }
    SSL_CTX_set_default_passwd_cb(ctx, pass_cb);
    等价于:
    SSL_CTX_set_default_passwd_cb_userdata(ctx, "123456");
     
    2.7 SSL_CTX_use_certificate_file()

    该函数读取证书文件,证书文件通常都进行了加密保护。普及一下,证书文件里肯定是有公钥的,一般没私钥,某些情况会把私钥也包含进去,但那样作太不安全了,原则上私钥是永远不会给别人看到的,就算是进行了加密保护。
    /* ssl/ssl_rsa.c */
    int SSL_use_certificate_file(SSL *ssl, const char *file, int type)
     {
     int j;
     BIO *in;
     int ret=0;
     X509 *x=NULL;
     in=BIO_new(BIO_s_file_internal());
     if (in == NULL)
      {
      SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE,ERR_R_BUF_LIB);
      goto end;
      }
     if (BIO_read_filename(in,file) <= 0)
      {
      SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE,ERR_R_SYS_LIB);
      goto end;
      }
    // 根据证书是PEM还是DER分别读取进行解码
    // DER是二进制格式,PEM则是对DER用BASE64编码的后的文本格式
     if (type == SSL_FILETYPE_ASN1)
      {
      j=ERR_R_ASN1_LIB;
      x=d2i_X509_bio(in,NULL);
      }
     else if (type == SSL_FILETYPE_PEM)
      {
      j=ERR_R_PEM_LIB;
      x=PEM_read_bio_X509(in,NULL,ssl->ctx->default_passwd_callback,ssl->ctx->default_passwd_callback_userdata);
      }
     else
      {
      SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE,SSL_R_BAD_SSL_FILETYPE);
      goto end;
      }
     if (x == NULL)
      {
      SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE,j);
      goto end;
      }
    // 加载解码后后的证书
     ret=SSL_use_certificate(ssl,x);
    end:
     if (x != NULL) X509_free(x);
     if (in != NULL) BIO_free(in);
     return(ret);
     }

    2.8 SSL_CTX_use_PrivateKey_file()

    该函数加载私钥文件,和SSL_CTX_use_certificate_file()是类似的,因为RSA算法的公钥私钥是对称的,刚生成密钥时谁作私钥都行。
    SSL_CTX_use_PrivateKey_file()只加载PEM格式私钥,DER格式的用函数SSL_use_PrivateKey_ASN1()加载。

    /* ssl/ssl_rsa.c */
    int SSL_use_PrivateKey_file(SSL *ssl, const char *file, int type)
     {
     int j,ret=0;
     BIO *in;
     EVP_PKEY *pkey=NULL;
     in=BIO_new(BIO_s_file_internal());
     if (in == NULL)
      {
      SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,ERR_R_BUF_LIB);
      goto end;
      }
     if (BIO_read_filename(in,file) <= 0)
      {
      SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,ERR_R_SYS_LIB);
      goto end;
      }
    // 私钥只支持PEM格式
     if (type == SSL_FILETYPE_PEM)
      {
      j=ERR_R_PEM_LIB;
      pkey=PEM_read_bio_PrivateKey(in,NULL,
       ssl->ctx->default_passwd_callback,ssl->ctx->default_passwd_callback_userdata);
      }
     else
      {
      SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,SSL_R_BAD_SSL_FILETYPE);
      goto end;
      }
     if (pkey == NULL)
      {
      SSLerr(SSL_F_SSL_USE_PRIVATEKEY_FILE,j);
      goto end;
      }
    // 加载私钥
     ret=SSL_use_PrivateKey(ssl,pkey);
     EVP_PKEY_free(pkey);
    end:
     if (in != NULL) BIO_free(in);
     return(ret);
     }

    2.9 SSL_CTX_check_private_key()

    该函数检查所用的公钥私钥是否是匹配的
    int SSL_CTX_check_private_key(SSL_CTX *ctx)
     {
     if ( (ctx == NULL) ||
      (ctx->cert == NULL) ||
      (ctx->cert->key->x509 == NULL))
      {
      SSLerr(SSL_F_SSL_CTX_CHECK_PRIVATE_KEY,SSL_R_NO_CERTIFICATE_ASSIGNED);
      return(0);
      }
     if  (ctx->cert->key->privatekey == NULL)
      {
      SSLerr(SSL_F_SSL_CTX_CHECK_PRIVATE_KEY,SSL_R_NO_PRIVATE_KEY_ASSIGNED);
      return(0);
      }
    // 这才是真正比较函数,在crypto/x509/x509_cmp.c中定义
     return(X509_check_private_key(ctx->cert->key->x509, ctx->cert->key->privatekey));
     }
    2.10 SSL_new
    该函数根据SSL_CTX实现一个SSL结构实例,SSL结构是个很复杂的结构,定义如下:

    /* ssl/ssl.h */
    typedef struct ssl_st SSL;
    struct ssl_st
     {
     /* protocol version
      * (one of SSL2_VERSION, SSL3_VERSION, TLS1_VERSION)
      */
     int version;
     int type; /* SSL_ST_CONNECT or SSL_ST_ACCEPT */
     SSL_METHOD *method; /* SSLv3 */
     /* There are 2 BIO's even though they are normally both the
      * same.  This is so data can be read and written to different
      * handlers */
    #ifndef OPENSSL_NO_BIO
     BIO *rbio; /* used by SSL_read */
     BIO *wbio; /* used by SSL_write */
     BIO *bbio; /* used during session-id reuse to concatenate
          * messages */
    #else
     char *rbio; /* used by SSL_read */
     char *wbio; /* used by SSL_write */
     char *bbio;
    #endif
     /* This holds a variable that indicates what we were doing
      * when a 0 or -1 is returned.  This is needed for
      * non-blocking IO so we know what request needs re-doing when
      * in SSL_accept or SSL_connect */
     int rwstate;
     /* true when we are actually in SSL_accept() or SSL_connect() */
     int in_handshake;
     int (*handshake_func)();
     /* Imagine that here's a boolean member "init" that is
      * switched as soon as SSL_set_{accept/connect}_state
      * is called for the first time, so that "state" and
      * "handshake_func" are properly initialized.  But as
      * handshake_func is == 0 until then, we use this
      * test instead of an "init" member.
      */
     int server; /* are we the server side? - mostly used by SSL_clear*/
     int new_session;/* 1 if we are to use a new session.
                      * 2 if we are a server and are inside a handshake
                      *   (i.e. not just sending a HelloRequest)
                      * NB: For servers, the 'new' session may actually be a previously
                      * cached session or even the previous session unless
                      * SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION is set */
     int quiet_shutdown;/* don't send shutdown packets */
     int shutdown; /* we have shut things down, 0x01 sent, 0x02
        * for received */
     int state; /* where we are */
     int rstate; /* where we are when reading */
     BUF_MEM *init_buf; /* buffer used during init */
     void *init_msg;    /* pointer to handshake message body, set by ssl3_get_message() */
     int init_num;  /* amount read/written */
     int init_off;  /* amount read/written */
     /* used internally to point at a raw packet */
     unsigned char *packet;
     unsigned int packet_length;
     struct ssl2_state_st *s2; /* SSLv2 variables */
     struct ssl3_state_st *s3; /* SSLv3 variables */
     int read_ahead;  /* Read as many input bytes as possible
                        * (for non-blocking reads) */
     /* callback that allows applications to peek at protocol messages */
     void (*msg_callback)(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg);
     void *msg_callback_arg;
     int hit;  /* reusing a previous session */
     int purpose;  /* Purpose setting */
     int trust;  /* Trust setting */
     /* crypto */
     STACK_OF(SSL_CIPHER) *cipher_list;
     STACK_OF(SSL_CIPHER) *cipher_list_by_id;
     /* These are the ones being used, the ones in SSL_SESSION are
      * the ones to be 'copied' into these ones */
     EVP_CIPHER_CTX *enc_read_ctx;  /* cryptographic state */
     const EVP_MD *read_hash;  /* used for mac generation */
    #ifndef OPENSSL_NO_COMP
     COMP_CTX *expand;   /* uncompress */
    #else
     char *expand;
    #endif
     EVP_CIPHER_CTX *enc_write_ctx;  /* cryptographic state */
     const EVP_MD *write_hash;  /* used for mac generation */
    #ifndef OPENSSL_NO_COMP
     COMP_CTX *compress;   /* compression */
    #else
     char *compress; 
    #endif
     /* session info */
     /* client cert? */
     /* This is used to hold the server certificate used */
     struct cert_st /* CERT */ *cert;
     /* the session_id_context is used to ensure sessions are only reused
      * in the appropriate context */
     unsigned int sid_ctx_length;
     unsigned char sid_ctx[SSL_MAX_SID_CTX_LENGTH];
     /* This can also be in the session once a session is established */
     SSL_SESSION *session;
     /* Default generate session ID callback. */
     GEN_SESSION_CB generate_session_id;
     /* Used in SSL2 and SSL3 */
     int verify_mode; /* 0 don't care about verify failure.
         * 1 fail if verify fails */
     int verify_depth;
     int (*verify_callback)(int ok,X509_STORE_CTX *ctx); /* fail if callback returns 0 */
     void (*info_callback)(const SSL *ssl,int type,int val); /* optional informational callback */
     int error;  /* error bytes to be written */
     int error_code;  /* actual code */
    #ifndef OPENSSL_NO_KRB5
     KSSL_CTX *kssl_ctx;     /* Kerberos 5 context */
    #endif /* OPENSSL_NO_KRB5 */
     SSL_CTX *ctx;
     /* set this flag to 1 and a sleep(1) is put into all SSL_read()
      * and SSL_write() calls, good for nbio debuging :-) */
     int debug; 
     /* extra application data */
     long verify_result;
     CRYPTO_EX_DATA ex_data;
     /* for server side, keep the list of CA_dn we can use */
     STACK_OF(X509_NAME) *client_CA;
     int references;
     unsigned long options; /* protocol behaviour */
     unsigned long mode; /* API behaviour */
     long max_cert_list;
     int first_packet;
     int client_version; /* what was passed, used for
         * SSLv3/TLS rollback check */
     };

    /* ssl/ssl_lib.c */
    SSL *SSL_new(SSL_CTX *ctx)
     {
     SSL *s;
    // 一些必要检查
     if (ctx == NULL)
      {
      SSLerr(SSL_F_SSL_NEW,SSL_R_NULL_SSL_CTX);
      return(NULL);
      }
     if (ctx->method == NULL)
      {
      SSLerr(SSL_F_SSL_NEW,SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION);
      return(NULL);
      }
    // 分配SSL实例的空间
     s=(SSL *)OPENSSL_malloc(sizeof(SSL));
     if (s == NULL) goto err;
     memset(s,0,sizeof(SSL));
    // 初始化SSL结构参数
    #ifndef OPENSSL_NO_KRB5
     s->kssl_ctx = kssl_ctx_new();
    #endif /* OPENSSL_NO_KRB5 */
     s->options=ctx->options;
     s->mode=ctx->mode;
     s->max_cert_list=ctx->max_cert_list;
     if (ctx->cert != NULL)
      {
      /* Earlier library versions used to copy the pointer to
       * the CERT, not its contents; only when setting new
       * parameters for the per-SSL copy, ssl_cert_new would be
       * called (and the direct reference to the per-SSL_CTX
       * settings would be lost, but those still were indirectly
       * accessed for various purposes, and for that reason they
       * used to be known as s->ctx->default_cert).
       * Now we don't look at the SSL_CTX's CERT after having
       * duplicated it once. */
      s->cert = ssl_cert_dup(ctx->cert);
      if (s->cert == NULL)
       goto err;
      }
     else
      s->cert=NULL; /* Cannot really happen (see SSL_CTX_new) */
     s->read_ahead=ctx->read_ahead;
     s->msg_callback=ctx->msg_callback;
     s->msg_callback_arg=ctx->msg_callback_arg;
     s->verify_mode=ctx->verify_mode;
     s->verify_depth=ctx->verify_depth;
     s->sid_ctx_length=ctx->sid_ctx_length;
     OPENSSL_assert(s->sid_ctx_length <= sizeof s->sid_ctx);
     memcpy(&s->sid_ctx,&ctx->sid_ctx,sizeof(s->sid_ctx));
     s->verify_callback=ctx->default_verify_callback;
     s->generate_session_id=ctx->generate_session_id;
     s->purpose = ctx->purpose;
     s->trust = ctx->trust;
     s->quiet_shutdown=ctx->quiet_shutdown;
     CRYPTO_add(&ctx->references,1,CRYPTO_LOCK_SSL_CTX);
     s->ctx=ctx;
     s->verify_result=X509_V_OK;
     s->method=ctx->method;
     if (!s->method->ssl_new(s))
      goto err;
     s->references=1;
     s->server=(ctx->method->ssl_accept == ssl_undefined_function)?0:1;
     SSL_clear(s);
     CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL, s, &s->ex_data);
     return(s);
    err:
     if (s != NULL)
      {
      if (s->cert != NULL)
       ssl_cert_free(s->cert);
      if (s->ctx != NULL)
       SSL_CTX_free(s->ctx); /* decrement reference count */
      OPENSSL_free(s);
      }
     SSLerr(SSL_F_SSL_NEW,ERR_R_MALLOC_FAILURE);
     return(NULL);
     }
     

    2.11 SSL_set_fd

    SSL_set_fd()函数将建立的SSL结构与TCP套接字联系,使SSL结构对套接字中的TCP数据进行SSL封装。
    /* ssl/ssl_lib.c */
    int SSL_set_fd(SSL *s,int fd)
     {
     int ret=0;
     BIO *bio=NULL;
    // 建立一个BIO,BIO是OpenSSL提供的用来进行算法封装的处理结构,还可以将多个算法
    // 串联起来,这样可以很方便地实现数据的封装
     bio=BIO_new(BIO_s_socket());
     if (bio == NULL)
      {
      SSLerr(SSL_F_SSL_SET_FD,ERR_R_BUF_LIB);
      goto err;
      }
    // 把套接字和BIO联系
     BIO_set_fd(bio,fd,BIO_NOCLOSE);
    // 把SSL和BIO联系起来,包括读写操作
     SSL_set_bio(s,bio,bio);
     ret=1;
    err:
     return(ret);
     }

    void SSL_set_bio(SSL *s,BIO *rbio,BIO *wbio)
     {
     /* If the output buffering BIO is still in place, remove it
      */
     if (s->bbio != NULL)
      {
      if (s->wbio == s->bbio)
       {
       s->wbio=s->wbio->next_bio;
       s->bbio->next_bio=NULL;
       }
      }
     if ((s->rbio != NULL) && (s->rbio != rbio))
      BIO_free_all(s->rbio);
     if ((s->wbio != NULL) && (s->wbio != wbio) && (s->rbio != s->wbio))
      BIO_free_all(s->wbio);
    // 设置SSL读写BIO
     s->rbio=rbio;
     s->wbio=wbio;
     }

    SSL_set_fd()还有两个类似函数:
    SSL_set_wfd():对写的数据进行SSL封装
    SSL_set_rfd():对都的数据进行SSL封装
    不过一般情况下用得比较少。

    ...... 待续 ......

  • 相关阅读:
    第四季-专题8-Linux系统调用
    第四季-专题7-Linux内核链表
    Python3 运算符
    Python2和Python3有什么区别?
    python常见的PEP8规范
    机器码和字节码
    域名是什么?为什么域名是www开头?
    selenium自动化登录qq邮箱
    xpath+selenium工具爬取腾讯招聘全球岗位需求
    ArrayList
  • 原文地址:https://www.cnblogs.com/adylee/p/1541088.html
Copyright © 2020-2023  润新知