SSL连接建立过程分析(1) 您所在的位置:网站首页 tls建立连接流程 SSL连接建立过程分析(1)

SSL连接建立过程分析(1)

2024-07-05 09:36| 来源: 网络整理| 查看: 265

Https协议:SSL建立过程分析

web访问的两种方式:

http协议,我们一般情况下是通过它访问web,因为它不要求太多的安全机制,使用起来也简单,很多web站点也只支持这种方式下的访问.

https协议(Hypertext Transfer Protocol over Secure Socket Layer),对于安全性要求比较高的情况,可以通过它访问web,比如工商银行https://www.icbc.com.cn/icbc/(当然也可以通过http协议访问,只是没那么安全了).其安全基础是SSL协议.

SSL协议,当前版本为3.1(SSL3.1就是TLS1.0)。它已被广泛地用于Web浏览器与服务器之间的身份认证和加密数据传输.它位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层: SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。 SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。

为了了解详细过程,可以通过网络抓包工具(Commview,Iris)分析https协议,SSL连接建立过程中,数据包交换情况.

数据包分析过程用到的几个图.

图,SSL Protocol Stack

 

图.SSL Record Format

 

图.SSL Record Protocol Payload

 

图.Handshake Protocol Action

 

 

它们来之.Cryptography and Network Security Principles and Practices, Fourth Edition-Chapter 17. Web Security-17.2. Secure Socket Layer and Transport Layer Security(密码学与网络安全 原理与实践第四版,17章web安全,17.2节,SSL与TLS)具体细节参考本书.

下面跟踪握手过程(图Handshake Protocol Action)中,数据包的交换.

以为https方式访问www.sun.com为例子,一般大型公司,银行的web都支持https访问,如工商银行,sun,微软,IBM.

在IE中输入:https://wwww.sun.com,因为这是https协议,所以在实际访问web前,会建立SSL连接.

通过Commview抓包工具,过滤443端口(一般情况下,HTTPS使用端口443,HTTP使用端口80)可以得到数据包.

数据包大致情况和(图Handshake Protocol Action)对应.

 

SSL连接建立过程分析(1)

1. 应用程序接口 1.1 SSL初始化 SSL_CTX* InitSSL(int server, char *cert, char *key, char *pw) {     SSL_CTX* ctx;     SSL_METHOD *meth;     int status; // 算法初始化   // 加载SSL错误信息     SSL_load_error_strings(); // 添加SSL的加密/HASH算法     SSLeay_add_ssl_algorithms(); // 服务器还是客户端     If(server)  meth = SSLv23_server_method();     else  meth = SSLv23_client_method(); // 建立新的SSL上下文     ctx = SSL_CTX_new (meth);     if(!ctx) return NULL; // 设置证书文件的口令     SSL_CTX_set_default_passwd_cb_userdata(ctx, pw); //加载本地证书文件     status=SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_ASN1);     if (status error|=ERR_PACK(lib,0,0);   ERRFN(err_set_item)(str);   str++;   }  } 其中: #define ERR_PACK(l,f,r)  (((((unsigned long)l)&0xffL)*0x1000000)| /     ((((unsigned long)f)&0xfffL)*0x1000)| /     ((((unsigned long)r)&0xfffL))) #define ERRFN(a) err_fns->cb_##a ERRFN(err_set_item)(str)的实际函数实现为: static ERR_STRING_DATA *int_err_set_item(ERR_STRING_DATA *d)  {  ERR_STRING_DATA *p;  LHASH *hash;  err_fns_check();  hash = ERRFN(err_get)(1);  if (!hash)   return NULL;  CRYPTO_w_lock(CRYPTO_LOCK_ERR);  p = (ERR_STRING_DATA *)lh_insert(hash, d);  CRYPTO_w_unlock(CRYPTO_LOCK_ERR);  return p;  } Lh_insert()将错误信息插入到一个链表中 如关于加密算法的错误信息: /* crypto/err/err.c */ static ERR_STRING_DATA ERR_str_functs[]= …… static ERR_STRING_DATA ERR_str_libraries[]= …… static ERR_STRING_DATA ERR_str_reasons[]= ……   2.2 SSLeay_add_ssl_algorithms() 这实际是个宏: #define OpenSSL_add_ssl_algorithms()    SSL_library_init() #define SSLeay_add_ssl_algorithms() SSL_library_init() 实际函数为SSL_library_init(),函数比较简单,就是加载各种加密和HASH算法: /* ssl/ssl_algs.c */ int SSL_library_init(void)  { #ifndef OPENSSL_NO_DES  EVP_add_cipher(EVP_des_cbc());  EVP_add_cipher(EVP_des_ede3_cbc()); #endif #ifndef OPENSSL_NO_IDEA  EVP_add_cipher(EVP_idea_cbc()); #endif #ifndef OPENSSL_NO_RC4  EVP_add_cipher(EVP_rc4()); #endif  #ifndef OPENSSL_NO_RC2  EVP_add_cipher(EVP_rc2_cbc()); #endif #ifndef OPENSSL_NO_AES  EVP_add_cipher(EVP_aes_128_cbc());  EVP_add_cipher(EVP_aes_192_cbc());  EVP_add_cipher(EVP_aes_256_cbc()); #endif #ifndef OPENSSL_NO_MD2  EVP_add_digest(EVP_md2()); #endif #ifndef OPENSSL_NO_MD5  EVP_add_digest(EVP_md5());  EVP_add_digest_alias(SN_md5,"ssl2-md5");  EVP_add_digest_alias(SN_md5,"ssl3-md5"); #endif #ifndef OPENSSL_NO_SHA  EVP_add_digest(EVP_sha1()); /* RSA with sha1 */  EVP_add_digest_alias(SN_sha1,"ssl3-sha1");  EVP_add_digest_alias(SN_sha1WithRSAEncryption,SN_sha1WithRSA); #endif #if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_DSA)  EVP_add_digest(EVP_dss1()); /* DSA with sha1 */  EVP_add_digest_alias(SN_dsaWithSHA1,SN_dsaWithSHA1_2);  EVP_add_digest_alias(SN_dsaWithSHA1,"DSS1");  EVP_add_digest_alias(SN_dsaWithSHA1,"dss1"); #endif  /* If you want support for phased out ciphers, add the following */ #if 0  EVP_add_digest(EVP_sha());  EVP_add_digest(EVP_dss()); #endif  return(1);  } 2.3 SSL23_server_method() 建立服务器端的方法库,这是个通用函数,可动态选择SSL协议。如果想固定协议,可以只用SSLv2_server_method(), SSLv3_server_method() 等函数来初始化,该函数返回一个SSL_METHOD结构: /* ssl/ssl.h */ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ typedef struct ssl_method_st  {  int version; // 版本号  int (*ssl_new)(SSL *s); // 建立新SSL  void (*ssl_clear)(SSL *s); // 清除SSL  void (*ssl_free)(SSL *s);  // 释放SSL  int (*ssl_accept)(SSL *s); // 服务器接受SSL连接  int (*ssl_connect)(SSL *s); // 客户端的SSL连接  int (*ssl_read)(SSL *s,void *buf,int len); // SSL读  int (*ssl_peek)(SSL *s,void *buf,int len); // SSL查看数据  int (*ssl_write)(SSL *s,const void *buf,int len); // SSL写  int (*ssl_shutdown)(SSL *s); // SSL半关闭  int (*ssl_renegotiate)(SSL *s); // SSL重协商  int (*ssl_renegotiate_check)(SSL *s); // SSL重协商检查  long (*ssl_ctrl)(SSL *s,int cmd,long larg,void *parg); // SSL控制  long (*ssl_ctx_ctrl)(SSL_CTX *ctx,int cmd,long larg,void *parg); //SSL上下文控制  SSL_CIPHER *(*get_cipher_by_char)(const unsigned char *ptr); // 通过名称获取SSL的算法  int (*put_cipher_by_char)(const SSL_CIPHER *cipher,unsigned char *ptr);  int (*ssl_pending)(SSL *s);  int (*num_ciphers)(void); // 算法数  SSL_CIPHER *(*get_cipher)(unsigned ncipher); // 获取算法  struct ssl_method_st *(*get_ssl_method)(int version);  long (*get_timeout)(void); // 超时  struct ssl3_enc_method *ssl3_enc; /* Extra SSLv3/TLS stuff */ // SSL3加密  int (*ssl_version)(); // SSL版本  long (*ssl_callback_ctrl)(SSL *s, int cb_id, void (*fp)()); // SSL控制回调函数  long (*ssl_ctx_callback_ctrl)(SSL_CTX *s, int cb_id, void (*fp)()); //SSL上下文控制回调函数  } SSL_METHOD; /* ssl/s23_srvr.c */ SSL_METHOD *SSLv23_server_method(void)  {  static int init=1; // 静态量,每个进程只初始化一次  static SSL_METHOD SSLv23_server_data;  if (init)   {   CRYPTO_w_lock(CRYPTO_LOCK_SSL_METHOD);   if (init)    { // ssl23的基本方法结构    memcpy((char *)&SSLv23_server_data,     (char *)sslv23_base_method(),sizeof(SSL_METHOD)); // 服务器,所以要定义accept方法    SSLv23_server_data.ssl_accept=ssl23_accept; // 根据SSL的版本设置SSL的具体方法函数    SSLv23_server_data.get_ssl_method=ssl23_get_server_method;    init=0;    }   CRYPTO_w_unlock(CRYPTO_LOCK_SSL_METHOD);   }  return(&SSLv23_server_data);  } static SSL_METHOD *ssl23_get_server_method(int ver)  { #ifndef OPENSSL_NO_SSL2  if (ver == SSL2_VERSION)   return(SSLv2_server_method()); #endif  if (ver == SSL3_VERSION)   return(SSLv3_server_method());  else if (ver == TLS1_VERSION)   return(TLSv1_server_method()); // 随着TLS1.1(RFC4346)的推出,估计不久将出现TLSv1_1_server_method()  else   return(NULL);  } // SSL23的方法基本数据定义 /* ssl/s23_lib.c */ SSL_METHOD *sslv23_base_method(void)  {  return(&SSLv23_data);  } static SSL_METHOD SSLv23_data= {  TLS1_VERSION,  tls1_new,  tls1_clear,  tls1_free,  ssl_undefined_function,  ssl_undefined_function,  ssl23_read,  ssl23_peek,  ssl23_write,  ssl_undefined_function,  ssl_undefined_function,  ssl_ok,  ssl3_ctrl,  ssl3_ctx_ctrl,  ssl23_get_cipher_by_char,  ssl23_put_cipher_by_char,  ssl_undefined_function,  ssl23_num_ciphers,  ssl23_get_cipher,  ssl_bad_method,  ssl23_default_timeout,  &ssl3_undef_enc_method,  ssl_undefined_function,  ssl3_callback_ctrl,  ssl3_ctx_callback_ctrl,  }; 以SSL3的服务器方法函数为例,其他方法类似: /* ssl/s3_srvr.c */ SSL_METHOD *SSLv3_server_method(void)  {  static int init=1;  static SSL_METHOD SSLv3_server_data; // 只初始化一次  if (init)   {   CRYPTO_w_lock(CRYPTO_LOCK_SSL_METHOD);   if (init)    { // ssl3的基本方法结构    memcpy((char *)&SSLv3_server_data,(char *)sslv3_base_method(),     sizeof(SSL_METHOD)); // ssl3的接受方法    SSLv3_server_data.ssl_accept=ssl3_accept; // ssl3获取服务器的方法函数    SSLv3_server_data.get_ssl_method=ssl3_get_server_method;    init=0;    }       CRYPTO_w_unlock(CRYPTO_LOCK_SSL_METHOD);   }  return(&SSLv3_server_data);  } // SSL3的方法基本数据定义 /* ssl/s3_lib.c */ static SSL_METHOD SSLv3_data= {  SSL3_VERSION,  ssl3_new,  ssl3_clear,  ssl3_free,  ssl_undefined_function,  ssl_undefined_function,  ssl3_read,  ssl3_peek,  ssl3_write,  ssl3_shutdown,  ssl3_renegotiate,  ssl3_renegotiate_check,  ssl3_ctrl,  ssl3_ctx_ctrl,  ssl3_get_cipher_by_char,  ssl3_put_cipher_by_char,  ssl3_pending,  ssl3_num_ciphers,  ssl3_get_cipher,  ssl_bad_method,  ssl3_default_timeout,  &SSLv3_enc_data,  ssl_undefined_function,  ssl3_callback_ctrl,  ssl3_ctx_callback_ctrl,  };   2.4 SSL23_client_method() 和服务器端的其实是相同的,只是不定义结构中的ssl_accept而是定义ssl_connnect: SSL_METHOD *SSLv23_client_method(void)  {  static int init=1;  static SSL_METHOD SSLv23_client_data;  if (init)   {   CRYPTO_w_lock(CRYPTO_LOCK_SSL_METHOD);   if (init)    {    memcpy((char *)&SSLv23_client_data,     (char *)sslv23_base_method(),sizeof(SSL_METHOD));    SSLv23_client_data.ssl_connect=ssl23_connect;    SSLv23_client_data.get_ssl_method=ssl23_get_client_method;    init=0;    }   CRYPTO_w_unlock(CRYPTO_LOCK_SSL_METHOD);   }  return(&SSLv23_client_data);  }   2.5 SSL_CTX_new () 该函数根据SSL方法获取一个SSL上下文结构,该结构定义为: /* ssl/ssl.h */ struct ssl_ctx_st  {  SSL_METHOD *method;  STACK_OF(SSL_CIPHER) *cipher_list;  /* same as above but sorted for lookup */  STACK_OF(SSL_CIPHER) *cipher_list_by_id;  struct x509_store_st /* X509_STORE */ *cert_store;  struct lhash_st /* LHASH */ *sessions; /* a set of SSL_SESSIONs */  /* Most session-ids that will be cached, default is   * SSL_SESSION_CACHE_MAX_SIZE_DEFAULT. 0 is unlimited. */  unsigned long session_cache_size;  struct ssl_session_st *session_cache_head;  struct ssl_session_st *session_cache_tail;  /* This can have one of 2 values, ored together,   * SSL_SESS_CACHE_CLIENT,   * SSL_SESS_CACHE_SERVER,   * Default is SSL_SESSION_CACHE_SERVER, which means only   * SSL_accept which cache SSL_SESSIONS. */  int session_cache_mode;  /* If timeout is not 0, it is the default timeout value set   * when SSL_new() is called.  This has been put in to make   * life easier to set things up */  long session_timeout;  /* If this callback is not null, it will be called each   * time a session id is added to the cache.  If this function   * returns 1, it means that the callback will do a   * SSL_SESSION_free() when it has finished using it.  Otherwise,   * on 0, it means the callback has finished with it.   * If remove_session_cb is not null, it will be called when   * a session-id is removed from the cache.  After the call,   * OpenSSL will SSL_SESSION_free() it. */  int (*new_session_cb)(struct ssl_st *ssl,SSL_SESSION *sess);  void (*remove_session_cb)(struct ssl_ctx_st *ctx,SSL_SESSION *sess);  SSL_SESSION *(*get_session_cb)(struct ssl_st *ssl,   unsigned char *data,int len,int *copy);  struct   {   int sess_connect; /* SSL new conn - started */   int sess_connect_renegotiate;/* SSL reneg - requested */   int sess_connect_good; /* SSL new conne/reneg - finished */   int sess_accept; /* SSL new accept - started */   int sess_accept_renegotiate;/* SSL reneg - requested */   int sess_accept_good; /* SSL accept/reneg - finished */   int sess_miss;  /* session lookup misses  */   int sess_timeout; /* reuse attempt on timeouted session */   int sess_cache_full; /* session removed due to full cache */   int sess_hit;  /* session reuse actually done */   int sess_cb_hit; /* session-id that was not       * in the cache was       * passed back via the callback.  This       * indicates that the application is       * supplying session-id's from other       * processes - spooky :-) */   } stats;  int references;  /* if defined, these override the X509_verify_cert() calls */  int (*app_verify_callback)(X509_STORE_CTX *, void *);  void *app_verify_arg;  /* before OpenSSL 0.9.7, 'app_verify_arg' was ignored   * ('app_verify_callback' was called with just one argument) */  /* Default password callback. */  pem_password_cb *default_passwd_callback;  /* Default password callback user data. */  void *default_passwd_callback_userdata;  /* get client cert callback */  int (*client_cert_cb)(SSL *ssl, X509 **x509, EVP_PKEY **pkey);  CRYPTO_EX_DATA ex_data;  const EVP_MD *rsa_md5;/* For SSLv2 - name is 'ssl2-md5' */  const EVP_MD *md5; /* For SSLv3/TLSv1 'ssl3-md5' */  const EVP_MD *sha1;   /* For SSLv3/TLSv1 'ssl3->sha1' */  STACK_OF(X509) *extra_certs;  STACK_OF(SSL_COMP) *comp_methods; /* stack of SSL_COMP, SSLv3/TLSv1 */  /* Default values used when no per-SSL value is defined follow */  void (*info_callback)(const SSL *ssl,int type,int val); /* used if SSL's info_callback is NULL */  /* what we put in client cert requests */  STACK_OF(X509_NAME) *client_CA;  /* Default values to use in SSL structures follow (these are copied by SSL_new) */  unsigned long options;  unsigned long mode;  long max_cert_list;  struct cert_st /* CERT */ *cert;  int read_ahead;  /* 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 verify_mode;  int verify_depth;  unsigned int sid_ctx_length;  unsigned char sid_ctx[SSL_MAX_SID_CTX_LENGTH];  int (*default_verify_callback)(int ok,X509_STORE_CTX *ctx); /* called 'verify_callback' in the SSL */  /* Default generate session ID callback. */  GEN_SESSION_CB generate_session_id;  int purpose;  /* Purpose setting */  int trust;  /* Trust setting */  int quiet_shutdown;  }; typedef struct ssl_ctx_st SSL_CTX; /* ssl/ssl_lib.h */ SSL_CTX *SSL_CTX_new(SSL_METHOD *meth)  {  SSL_CTX *ret=NULL;    if (meth == NULL)   {   SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_NULL_SSL_METHOD_PASSED);   return(NULL);   }  if (SSL_get_ex_data_X509_STORE_CTX_idx() < 0)   {   SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_X509_VERIFICATION_SETUP_PROBLEMS);   goto err;   } // 分配上下文的内存空间  ret=(SSL_CTX *)OPENSSL_malloc(sizeof(SSL_CTX));  if (ret == NULL)   goto err;  memset(ret,0,sizeof(SSL_CTX));   // 初始化上下文的结构参数  ret->method=meth;  ret->cert_store=NULL;  ret->session_cache_mode=SSL_SESS_CACHE_SERVER;  ret->session_cache_size=SSL_SESSION_CACHE_MAX_SIZE_DEFAULT;  ret->session_cache_head=NULL;  ret->session_cache_tail=NULL;  /* We take the system default */  ret->session_timeout=meth->get_timeout();  ret->new_session_cb=0;  ret->remove_session_cb=0;  ret->get_session_cb=0;  ret->generate_session_id=0;  memset((char *)&ret->stats,0,sizeof(ret->stats));  ret->references=1;  ret->quiet_shutdown=0; /* ret->cipher=NULL;*/ /* ret->s2->challenge=NULL;  ret->master_key=NULL;  ret->key_arg=NULL;  ret->s2->conn_id=NULL; */  ret->info_callback=NULL;  ret->app_verify_callback=0;  ret->app_verify_arg=NULL;  ret->max_cert_list=SSL_MAX_CERT_LIST_DEFAULT;  ret->read_ahead=0;  ret->msg_callback=0;  ret->msg_callback_arg=NULL;  ret->verify_mode=SSL_VERIFY_NONE;  ret->verify_depth=-1; /* Don't impose a limit (but x509_lu.c does) */  ret->sid_ctx_length=0;  ret->default_verify_callback=NULL;  if ((ret->cert=ssl_cert_new()) == NULL)   goto err;  ret->default_passwd_callback=0;  ret->default_passwd_callback_userdata=NULL;  ret->client_cert_cb=0;  ret->sessions=lh_new(LHASH_HASH_FN(SSL_SESSION_hash),    LHASH_COMP_FN(SSL_SESSION_cmp));  if (ret->sessions == NULL) goto err;  ret->cert_store=X509_STORE_new();  if (ret->cert_store == NULL) goto err;   // 建立加密算法链表  ssl_create_cipher_list(ret->method,   &ret->cipher_list,&ret->cipher_list_by_id,   SSL_DEFAULT_CIPHER_LIST);  if (ret->cipher_list == NULL      || sk_SSL_CIPHER_num(ret->cipher_list) rsa_md5=EVP_get_digestbyname("ssl2-md5")) == NULL)   {   SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_UNABLE_TO_LOAD_SSL2_MD5_ROUTINES);   goto err2;   }  if ((ret->md5=EVP_get_digestbyname("ssl3-md5")) == NULL)   {   SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES);   goto err2;   }  if ((ret->sha1=EVP_get_digestbyname("ssl3-sha1")) == NULL)   {   SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES);   goto err2;   }  if ((ret->client_CA=sk_X509_NAME_new_null()) == NULL)   goto err;    CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_CTX, ret, &ret->ex_data);  ret->extra_certs=NULL; // 压缩算法  ret->comp_methods=SSL_COMP_get_compression_methods();  return(ret); err:  SSLerr(SSL_F_SSL_CTX_NEW,ERR_R_MALLOC_FAILURE); err2:  if (ret != NULL) SSL_CTX_free(ret);  return(NULL);  } ...待续...

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有