简单聊一聊openssl中session的实现。
通常来说在传输层上面形成了流的概念,即由源IP,目的IP,源端口,目的端口,以及传输层的协议构成了网络中的一条虚拟传输线路,称之为流。由于网络协议是分层的,也就是说每一层都存在着相应的连接概念 ,因此流是传输层区分不同连接的概念,而对于http层来说区分不同http连接的方法可以使用不同的GET请求,不同的cookie,不同的UA等等,可以说是不同的请求头,因为承载什么样的数据其实不是网络协议的所规定的了。
对于高层协议来说,不同会话之间的关系不能够简单的通过流来区分,因为同一条流上面可以承载多个会话,例如同一个tcp流上面可以有多个http连接,同一个tcp流上面也可以由多个ssl会话(重协商)。表示不同会话之间关系,在http协议中使用referer等头域,那么在ssl协议中使用session id等方式。 那么对于ssl层来说,区分不同连接的方法就是不同的client请求,包括版本,加密套件,不同的server hello等。在openssl源码中使用ssl_st来区分不同的会话连接,该结构体主要来进行ssl握手过程以及传输具体的应用数据。
下面简单的说一下session id在openssl源码中是如何实现的。 整体的流程是很简单的: 1,初次会话连接过程在client hello中session id字段为空 。2,服务器生成session id 保存,同时传输给客户端。 3,下一次发出请求的时候client hello带上相应session id ,服务器决定是否复用。
由于session id 是记录在ssl_session_st
结构中,该结构来记录不同的会话信息,如下:
struct ssl_session_st { int ssl_version; /* what ssl version session info is being kept * in here? */ size_t master_key_length;
/* TLSv1.3 early_secret used for external PSKs */
unsigned char early_secret[EVP_MAX_MD_SIZE];
/*
* For <=TLS1.2 this is the master_key. For TLS1.3 this is the resumption
* master secret
*/
unsigned char master_key[TLS13_MAX_RESUMPTION_MASTER_LENGTH];
/* session_id - valid? */
size_t session_id_length;
unsigned char session_id[SSL_MAX_SSL_SESSION_ID_LENGTH];
/*
* this is used to determine whether the session is being reused in the
* appropriate context. It is up to the application to set this, via
* SSL_new
*/
size_t sid_ctx_length;
unsigned char sid_ctx[SSL_MAX_SID_CTX_LENGTH];
ifndef OPENSSL_NO_PSK
char *psk_identity_hint;
char *psk_identity;
endif
/*
* Used to indicate that session resumption is not allowed. Applications
* can also set this bit for a new session via not_resumable_session_cb
* to disable session caching and tickets.
*/
int not_resumable;
/* This is the cert and type for the other end. */
X509 *peer;
int peer_type;
/* Certificate chain peer sent. */
STACK_OF(X509) *peer_chain;
/*
* when app_verify_callback accepts a session where the peer's
* certificate is not ok, we must remember the error for session reuse:
*/
long verify_result; /* only for servers */
CRYPTO_REF_COUNT references;
long timeout;
long time;
unsigned int compress_meth; /* Need to lookup the method */
const SSL_CIPHER *cipher;
unsigned long cipher_id; /* when ASN.1 loaded, this needs to be used to
* load the 'cipher' structure */
STACK_OF(SSL_CIPHER) *ciphers; /* shared ciphers? */
CRYPTO_EX_DATA ex_data; /* application specific data */
/*
* These are used to make removal of session-ids more efficient and to
* implement a maximum cache size.
*/
struct ssl_session_st *prev, *next;
struct {
char *hostname;
ifndef OPENSSL_NO_EC
size_t ecpointformats_len;
unsigned char *ecpointformats; /* peer's list */
endif /* OPENSSL_NO_EC */
size_t supportedgroups_len;
uint16_t *supportedgroups; /* peer's list */
/* RFC4507 info */
unsigned char *tick; /* Session ticket */
size_t ticklen; /* Session ticket length */
/* Session lifetime hint in seconds */
unsigned long tick_lifetime_hint;
uint32_t tick_age_add;
unsigned char *tick_nonce;
size_t tick_nonce_len;
int tick_identity;
/* Max number of bytes that can be sent as early data */
uint32_t max_early_data;
/* The ALPN protocol selected for this session */
unsigned char *alpn_selected;
size_t alpn_selected_len;
} ext;
ifndef OPENSSL_NO_SRP
char *srp_username;
endif
uint32_t flags;
CRYPTO_RWLOCK *lock;
};
openssl使用ssl_session_st
结构来记录不同的会话信息,包括unsigned char master_key[TLS13_MAX_RESUMPTION_MASTER_LENGTH]
主密钥信息,unsigned char session_id[SSL_MAX_SSL_SESSION_ID_LENGTH]
,具体的id字符串,X509 *peer
证书信息,const SSL_CIPHER *cipher
密钥信息等,可以看到这些信息是两个会话之间存在关联的一些要素。
对于ssl_session_st
这种会话消息的建立,openssl提供的函数是ssl_get_new_session
,在openssl源码中,会话的建立有两处,分别位于客户端的处理流程tls_construct_client_hello
函数中以及服务器的处理流程tls_post_process_client_hello
函数中。
在1.1.0f代码中ssl_get_new_session
函数中在生成ssl_session_st
这种结构类型变量的同时,还会生成相应的session id
,在最新的代码中session id
的生成被分离成为一个单独的函数。session id
默认的生成函数为def_generate_session_id
,然后调用相应的随机数生成函数。当然用户也可以设置自己的seession id
生成函数,相应的设置函数为SSL_set_generate_session_id
在生成每一个seession id
之后都会调用相应的SSL_has_matching_session_id
函数来确保全局哈希表中的session id
是唯一的,这个全局哈希表后面在说。ssl_get_prev_session(SSL *s, const PACKET *ext, const PACKET *session_id)
函数是根据一个session id
来获取相应的ssl_session_st
结构体(这个函数有个缺点就是入参为PACKET *
,然后在该函数内部做了转换PACKET_copy_all(session_id, data.session_id,sizeof(data.session_id), &local_len
) 正常来说session_id
是一个字符串数组,应该传输参数为字符串指针,相应的转换在ssl_get_prev_session
之前完成才是最好的,这也是ssl代码写的不太好的原因) 该函数为唯一被调用的位置是在 tls_process_client_hello
函数中,也就是服务器在读取client hello时候决定究竟是新建session还是复用以前的session。
当然对于客户端来和服务器来说说这些session如何进行管理。在tls_finish_handshake
中会调用ssl_update_cache
函数来对客户端和服务器进行更新session相关内容。在ssl_update_cache
函数中会调用SSL_CTX_add_session
函数向全局的哈希表中增加前面所建立的session(这张哈希表是在SSL_CTX_new
函数被调用的时候初始化的)。除此之外还会调用函数SSL_CTX_flush_sessions
来保证客户端每存储255个session,主动老化一次哈希表,确保占用资源不会过多。 除此之外,对于新增的session,还需要调用SSL_SESSION_list_add
函数,将相应的session变量加入到SSL_CTX
变量中的ctx->session_cache_head
以及ctx->session_cache_tail
构成的链表之中。
那么服务器端允许多少个sessions呢?在SSL_CTX_new
中默认了ret->session_cache_size = SSL_SESSION_CACHE_MAX_SIZE_DEFAULT;
表示默认能够缓存的最大数量。当然也可以通过SSL_CTX_ctr
l函数中的SSL_CTRL_SET_SESS_CACHE_SIZE
分支来设置具体的 session_cache_size
以上就是对于openssl session的一些理解。
本文为CSDN村中少年原创文章,转载记得加上小尾巴偶,博主链接这里。