| SSL(安全套接字层)是一种保护互联网通信安全的协议,它可以用于网页浏览、电子邮件、即时通讯和其他数据传输方面。SSL 的优点是为您的用户增添了安全保障。如果您的用户通过不安全的互联网连接(如公共的无线网络或未加密的网络)访问 ,他们的帐户可能会更容易受到黑客攻击。安全的连接会通过保护 Cookie 会话来防止黑客攻击。而 Cookie 会话黑客攻击指的是假冒者未经授权地访问 Cookie,并在合法会话仍在运行时夺取其控制权。 ttps协议: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()将错误信息插入到一个链表中 
				如关于加密算法的错误信息: 
				/* 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); } (责任编辑:最模板) | 







 shopex仿银泰商城模板
							人气:783
							shopex仿银泰商城模板
							人气:783
						 Ecmall仿天猫2014多店铺商城
							人气:2515
							Ecmall仿天猫2014多店铺商城
							人气:2515
						 Neoshop经济型外贸综合类
							人气:360
							Neoshop经济型外贸综合类
							人气:360
						 ecshop趣玩模板2010老版
							人气:834
							ecshop趣玩模板2010老版
							人气:834
						 ecshop仿聚美模板免费整站
							人气:3737
							ecshop仿聚美模板免费整站
							人气:3737
						 蓝色包包外贸英文ecshop模
							人气:641
							蓝色包包外贸英文ecshop模
							人气:641