TLSv1.2介绍及Https协议SSL建立过程分析(OpenSSL源码)

TLSv1.2协议

首先明确TLS的作用三个作用
(1)身份认证
通过证书认证来确认对方的身份,防止中间人攻击
(2)数据私密性
使用对称性密钥加密传输的数据,由于密钥只有客户端/服务端有,其他人无法窥探。
(3)数据完整性
使用摘要算法对报文进行计算,收到消息后校验该值防止数据被篡改或丢失。

TLS传输过程

下面是使用wireshark抓包的结果,其中1-4是握手阶段,5是指握手后双方使用商议好的秘钥进行通讯。

2中并列着Server Hello,Certificate等多个类型,是因为这是一个Multiple Handshake Messages,一次性发送多个握手协议包。

传输过程总体描述


(1)客户端提供【客户端随机数、可选算法套件、sessionId】等信息
(2)服务端提供【服务端随机数、选用算法套件、sessionId】等信息
(3)服务端提供证书
(4)服务端与客户端互换算法需要的参数
(5)客户端根据前面提到的随机数和参数生成master secret,确认开始使用指定算法加密,并将握手信息加密传输给服务端,用来校验握手信息、秘钥是否正确
(6)服务端进行与(5)一样的操作
(7)客户端、服务端可以用master secret进行加密通讯

 

传输中的一些参数

cipher suites

每个算法套件是一组算法,以TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256为例。
其中

(1)ECDHE用于协商秘钥 https://blog.csdn.net/mrpre/article/details/78025940
(2)RSA是用于身份验证
(3)AES_128_GCM用于对称加密通讯
(4)SHA256用于生成摘要,验证数据完整性

秘钥协商过程

这里涉及到几个参数,client random,server random,pre master key,master key,其中master key是最终协商出来的秘钥,后续对称加密通讯都是使用master key。

(1)master key是使用伪随机算法,结合client random,server random,pre master key三个随机因素生成的。

(2)client random和server random是客户端和服务端分别生成的随机数,这样增加了随机性。

(3)pre master key也是通过一定规则计算出来的随机数。
我们依旧以TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256为例讲述一下pre master key的生成过程。

第一步,客户端随机生成随机值Ra,计算Pa = Ra * Q(x, y),Q(x, y)为全世界公认的某个椭圆曲线算法的基点。将Pa发送至服务器。
第二步,服务器随机生成随机值Rb,计算Pb - Rb * Q(x, y)。将Pb发送至客户端。
由于算法的不可逆转性,外界无法通过Pa、Pb推算出Ra、Rb
第三步,因为算法保证了Ra * Pb= Rb *Pa,所以客户端计算S = Ra * Pb;服务器计算S = Rb *Pa,提取其中的S的x向量作为密钥(pre master key)

TLS握手过程中服务器和客户端互换参数,就是在交换Pa和Pb,交换后服务器和客户端可根据ECDHE算出同一个pre master key。

身份验证过程

身份验证的过程需要做两个事情:

  • 确认服务器发送过来的证书是合法的
  • 确认服务器拥有私钥

下面就从这两个角度来讲

验证证书合法性
以csdn网站为例,其证书结构如下:

服务端发送证书给客户端时,需要发送整个证书链(这里没有传输根证书,我认为是因为浏览器自带了根证书,并且信任它)

-

每个证书包含了一些基本信息、证书公钥和证书签名。其中证书签名是对证书进行摘要计算,并使用颁发机构的私钥加密生成。

为证实csdn.net证书的合法性,我们需要用GeoTrust RSA CA 2018的公钥证书签名进行解密,解密后与证书的摘要进行对比,一致证明csdn.net证书是没有问题的。
同样的,我们需要证明GeoTrust RSA CA 2018的合法性,使用DigiCert的公钥
DigiCert是浏览器信任的根证书,无需校验。

验证服务器拥有私钥
如果密钥协商过程使用的是RSA算法,则已经验证了服务器拥有私钥
如果密钥协商过程使用的是ECDHE算法,则没有验证服务器的私钥,此时需要在server key exchange时,对数据进行摘要计算并使用私钥加密,如果客户端可以使用公钥正常解密,则证明服务器拥有私钥。

sessionId - 握手重用

在Hello的过程中,有一个sessionId的字段,是用来重用握手信息的。
(1)第一次握手的时候
客户端发送的sessionId为空,服务端会生成sessionId返回给客户端,并将握手信息保存起来。
(2)再次握手的时候
客户端发送【上次的sessionId】,服务端检查到sessionId存在,返回同样的sessionId,然后就可以直接使用上次商定的秘钥进行通讯了。
(3)session过期时
客户端发送【上次的sessionId】,服务端检查到sessionId不存在,则返回新的sessionId,继续正常的握手流程。
 

SSL协议

SSL协议,当前版本为3.1(SSL3.1就是TLS1.0)。它已被广泛地用于Web浏览器与服务器之间的身份认证和加密数据传输.它位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。

SSL协议可分为两层:

SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。

SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。

 

SSL连接建立过程分析

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()将错误信息插入到一个链表中
如关于加密算法的错误信息:
/* 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) <= 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);
 }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值