SSL Session
在SSL协议中,每次Client向Server发送认证请求,Server端都会产生一个SSL回话session.在这个session中包含这个将回话的主要信息,用来匹配该连接。下面的代码是PolarSSL中定义的ssl会话结构:
struct _ssl_session { #if defined(POLARSSL_HAVE_TIME) time_t start; // starting time #endif int ciphersuite; // chosen ciphersuite int compression; // chosen compression size_t length; // session id length unsigned char id[32]; // session identifier unsigned char master[48]; // the master secret #if defined(POLARSSL_X509_CRT_PARSE_C) x509_crt *peer_cert; // peer X.509 cert chain #endif // POLARSSL_X509_CRT_PARSE_C int verify_result; // verification result #if defined(POLARSSL_SSL_SESSION_TICKETS) unsigned char *ticket; // RFC 5077 session ticket size_t ticket_len; // session ticket length uint32_t ticket_lifetime; // ticket lifetime hint #endif // POLARSSL_SSL_SESSION_TICKETS #if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) unsigned char mfl_code; // MaxFragmentLength negotiated by peer #endif // POLARSSL_SSL_MAX_FRAGMENT_LENGTH #if defined(POLARSSL_SSL_TRUNCATED_HMAC) int trunc_hmac; // flag for truncated hmac activation #endif // POLARSSL_SSL_TRUNCATED_HMAC }; typedef struct _ssl_session ssl_session;
上面代码包含ssl_session结构体中的组成,其中有一些成员是由宏定义控制的,这个需要在config.h文件中通过是否定义该宏来控制这个结构体中是否包含该成员。但是ciphersuite, compression, id, master这几项是必须包含的。
SSL Cache
每次SSL协议的握手连接都是比较耗费资源的,出于性能的考虑,在Server端利用缓存技术,将曾经的一些SSL连接参数都放到系统SSL缓存中,待客户端在有效期内再次发起SSL连接请求的时候,Server端在缓存中查找曾经的连接参数,并且加以复用,合理省略了SSL协议中的一些步骤.
下面的代码是每个SSL缓存中每个连接对应的缓存个体:
struct _ssl_cache_entry { #if defined(POLARSSL_HAVE_TIME) time_t timestamp; // entry timestamp #endif ssl_session session; // entry session #if defined(POLARSSL_X509_CRT_PARSE_C) x509_buf peer_cert; // entry peer_cert #endif ssl_cache_entry *next; // chain pointer }; typedef struct _ssl_cache_entry ssl_cache_entry;
下面的代码就是缓存上下文,对应SSL连接中所保留cache中的所有信息:
struct _ssl_cache_context { ssl_cache_entry *chain; // start of the chain int timeout; // cache entry timeout int max_entries; // maximum entries #if defined(POLARSSL_THREADING_C) threading_mutex_t mutex; // mutex, 线程安全 #endif };
后面来简单理顺一下其函数的执行逻辑:
在ssl_cache_init函数中
// 初始化定义失效时间 cache->timeout = SSL_CACHE_DEFAULT_TIMEOUT; // 缓存中存储的最大规模 cache->max_entries = SSL_CACHE_DEFAULT_MAX_ENTRIES; // 选择是否是线程安全 #if defined(POLARSSL_THREADING_C) polarssl_mutex_init( &cache->mutex ); #endif
在ssl_cache_get函数中:
// 超过有效期 #if defined(POLARSSL_HAVE_TIME) if( cache->timeout != 0 && (int) ( t - entry->timestamp ) > cache->timeout ) continue; #endif // 密码套件及压缩和sessionID的长度都要相符 if( session->ciphersuite != entry->session.ciphersuite || session->compression != entry->session.compression || session->length != entry->session.length ) continue; // sessionID相同 if( memcmp( session->id, entry->session.id, entry->session.length ) != 0 ) continue; // 然后从cache中恢复信息 memcpy( session->master, entry->session.master, 48 ); session->verify_result = entry->session.verify_result; // 如果定义了证书解析功能,也需要恢复证书 x509_crt_init( session->peer_cert ); if( x509_crt_parse( session->peer_cert, entry->peer_cert.p, entry->peer_cert.len ) != 0 )
在ssl_cache_set函数中:
// 过期的 if( cache->timeout != 0 && (int) ( t - cur->timestamp ) > cache->timeout ) // 已经缓存过的 if( memcmp( session->id, cur->session.id, cur->session.length ) == 0 ) // 最旧的 if( oldest == 0 || cur->timestamp < oldest ) { oldest = cur->timestamp; old = cur; }
找到这3种的来覆盖当前cache中的缓存。
// 到chain末尾,但是最旧的entry不再最末尾,覆盖它 cur = old; memset( &cur->session, 0, sizeof(ssl_session) ); // 到chain末尾,其他情况的就覆盖第一个,移动到最后 cur = cache->chain; cache->chain = cur->next; memset( cur, 0, sizeof(ssl_cache_entry) ); prv->next = cur;