SSL连接建立过程分析



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 <= 0) {
              frintf(stderr, "Use cert fail, status=%d\n", status);
              goto bad;
      }
// 加载私钥文件
      if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) <= 0) {
              fprintf(stderr, "Use private key fail\n");
              goto bad;
      }
// 检查证书和私钥是否匹配
      if (!SSL_CTX_check_private_key(ctx)) {
              fprintf("Private key does not match the certificate public key\n");
              goto bad;
      }
      fprintf("Cert and key OK\n");
      return ctx;
bad:
      SSL_CTX_free (ctx);
      return NULL;
}
1.2 建立SSL新连接
服务器:
// 建立SSL
ssl = SSL_new (ctx);
// 将SSL与TCP socket连接
SSL_set_fd (ssl, sd);
//接受新SSL连接
err = SSL_accept (ssl);
客户端:
// 建立SSL
ssl = SSL_new (ctx);
// 将SSL与TCP socket连接
SSL_set_fd (ssl, sd);
// SSL连接
err = SSL_connect (ssl);

服务器的SSL_accept()和客户端的SSL_connect()函数共同完成SSL的握手协商过程。
 
1.3 SSL通信
和普通的read()/write()调用一样,用下面的函数完成数据的SSL发送和接收,函数输入数据是明文,SSL自动将数据封装进SSL中:
读/接收:SSL_read()
写/发送:SSL_write()
1.4 SSL释放
SSL释放很简单:
  SSL_free (ssl);
 
2. SSL实现分析
以下SSL源代码取自openssl-0.9.7b。

2.1 SSL_load_error_strings
该函数加载错误字符串信息:
void SSL_load_error_strings(void)
  {
#ifndef OPENSSL_NO_ERR
  ERR_load_crypto_strings();
  ERR_load_SSL_strings();
#endif
  }
最后将会进入函数:
static void err_load_strings(int lib, ERR_STRING_DATA *str)
  {
  while (str->error)
    {
    str->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()将错误信息插入到一个链表中
如关于加密算法的错误信息:

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算法:

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());
  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());
  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 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结构:


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; // 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_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_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_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的方法基本数据定义

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上下文结构,该结构定义为:

struct ssl_ctx_st
  {
  SSL_METHOD *method;
  STACK_OF(SSL_CIPHER) *cipher_list;
 
  STACK_OF(SSL_CIPHER) *cipher_list_by_id;
  struct x509_store_st *cert_store;
  struct lhash_st *sessions; 
 
  unsigned long session_cache_size;
  struct ssl_session_st *session_cache_head;
  struct ssl_session_st *session_cache_tail;
 
  int session_cache_mode;
 
  long session_timeout;
 
  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; 
    int sess_connect_renegotiate;
    int sess_connect_good; 
    int sess_accept; 
    int sess_accept_renegotiate;
    int sess_accept_good; 
    int sess_miss;   
    int sess_timeout; 
    int sess_cache_full; 
    int sess_hit;   
    int sess_cb_hit; 
    } stats;
  int references;
 
  int (*app_verify_callback)(X509_STORE_CTX *, void *);
  void *app_verify_arg;
 
 
  pem_password_cb *default_passwd_callback;
 
  void *default_passwd_callback_userdata;
 
  int (*client_cert_cb)(SSL *ssl, X509 **x509, EVP_PKEY **pkey);
  CRYPTO_EX_DATA ex_data;
  const EVP_MD *rsa_md5;
  const EVP_MD *md5; 
  const EVP_MD *sha1;   
  STACK_OF(X509) *extra_certs;
  STACK_OF(SSL_COMP) *comp_methods;

 
  void (*info_callback)(const SSL *ssl,int type,int val);
 
  STACK_OF(X509_NAME) *client_CA;

 
  unsigned long options;
  unsigned long mode;
  long max_cert_list;
  struct cert_st *cert;
  int read_ahead;
 
  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);
 
  GEN_SESSION_CB generate_session_id;
  int purpose;   
  int trust;   
  int quiet_shutdown;
  };

typedef struct ssl_ctx_st SSL_CTX;

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;
 
  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->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;
  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) <= 0)
    {
    SSLerr(SSL_F_SSL_CTX_NEW,SSL_R_LIBRARY_HAS_NO_CIPHERS);
    goto err2;
    }
 
// 定义上下文结构中HASH算法
  if ((ret->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);
  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值