之前在微博上看到一个分享,关于HTTPS的,原文链接The First Few Milliseconds of an HTTPS Connection,加上最近也在学习openssl 的相关知识。所以也进行简单的实验,并整理一些资料,以加深对SSL、TLS的了解。
一,基本概念
SSL是安全套接层(Secure Socket Layer)的缩写,而TLS表示传输层安全(Transport Layer Security)的缩写。SSl最初由网景公司提出,最初目的是为了保护web安全,然而现在用来提高传输层的安全。TLS是IETF基于SSLv3制定的标准,两者基本一致,只有少许的差别。首先我们来看一下SSLv3 /TLS协议在TCP/IP协议栈中的位置,通常我们认为SSLv3 /TLS处于传输层和应用层之间。而将SSLv3/TLS通常又分为握手层和记录层,如下图所示:
1,Handshake 握手协议的基本功能
(1)服务器认证
(2)客户端认证(可选)
(3)算法协商
(4)密钥生成
2,Change Cipher Spec 更改密码规范协议
在安全协商后,服务器和客户端会交互这条消息,来提示使用协商好的安全参数
3,Alert 警告协议
(1)提供报错机制
(2)安全断连机制
二,WireShark 实验
首先来了解一下SSLv3/TLS协议流程,如下图所示,而4,6这两个过程通常不会出现在我们的浏览器访问过程中。下面我们以WireShark来分析浏览器访问https://github.com 来了解一下TLS建链路的过程。
从下图中,可以看出,采用的TLS协议版本为1.1,握手协议为Client hello,主要包含以下信息:32字节的随机数random;Session ID;客户端支持的密码套件Cipher Suites 以及压缩算法Compression Methods。
//RFC 中Random的定义
struct
{
uint32 gmt_unix_time; //格林威治时间
opaque random_bytes[28];
} Random;
//RFC 中Client Hello的定义
struct { ProtocolVersion client_version; Random random; SessionID session_id; CipherSuite cipher_suites<2..2^16-1>; CompressionMethod compression_methods<1..2^8-1>; } ClientHello;
2 Server Hello
服务器端消息定义如下同clientHello的区别是,服务器端选择双方支持的密码套件及压缩算法,在下图中可以看到选择的密码套件为:TLS_RSA_WITH_RC4_128_SHA,而压缩算法为NULL即不支持压缩。
struct {
ProtocolVersion server_version;
Random random;
SessionID session_id;
CipherSuite cipher_suite;
CompressionMethod compression_method;
} ServerHello;
3~6 Send certificate and Server Hello Done
服务气短讲证书链发送给客户端。证书链中证书的顺序是每个证书的签名都由随后证书的公钥进行验证。紧接着是一个Sever Hello Done的消息,因为这边包含可选的认证,发送Server Hello Done表明服务器端hello 信息结束。因为服务器没有要求验证客户端身份故4和6步骤没有。
7~13 key exchange and change cipher spec
至此链路建立
三,Openssl 中相关函数
SSL协议源码位于ssl目录下。它实现了sslv2、sslv3、TLS以及DTLS(Datagram TLS,基于UDP的TLS实现)。ssl实现中,对于每个协议,都有客户端实现(XXX_clnt.c)、服务端实现(XXX_srvr.c)、加密实现(XXX_enc.c)、记录协议实现(XXX_pkt.c)、METHOD方法(XXX_meth.c)、客户端服务端都用到的握手方法实现(XXX_both.c),以及对外提供的函数实现(XXX_lib.c)。
四,Polarssl 中的SSL实例
Polarssl是一个轻量级的ssl 适合用于嵌入式平台中,我简单看了一下其中的代码结构,感觉非常清晰。看其中的代码也便于理解SSL。具体代码可以查看polarssl/programs/ssl 下的几个文件。下面列出一些客户端关键的代码,服务器端可以自己查看代码,理解如何自己建立SSL链路。
//客户端
/*
* 1.1. Load the trusted CA
*/
#if defined(POLARSSL_CERTS_C)
ret = x509parse_crt( &cacert, (const unsigned char *) test_ca_crt,strlen( test_ca_crt ) );
#endif
/*
* 1.2. Load own certificate and private key
*
* (can be skipped if client authentication is not required)
*/
printf( " . Loading the client cert. and key..." );
#if defined(POLARSSL_CERTS_C)
ret = x509parse_crt( &clicert, (const unsigned char *) test_cli_crt,strlen( test_cli_crt ) );
#endif
#if defined(POLARSSL_CERTS_C)
ret = x509parse_key( &rsa, (const unsigned char *) test_cli_key,strlen( test_cli_key ), NULL, 0 );
#endif
/*
* 2. Start the connection
*/
printf( " . Connecting to tcp/%s/%-4d...", opt.server_name, opt.server_port );
if( ( ret = net_connect( &server_fd, opt.server_name,opt.server_port ) ) != 0 )
{
....
}
/*
* 3. Setup stuff
*/
printf( " . Setting up the SSL/TLS structure..." );
if( ( ret = ssl_init( &ssl ) ) != 0 )
{
....
}
ssl_set_endpoint( &ssl, SSL_IS_CLIENT );
ssl_set_authmode( &ssl, opt.auth_mode );
ssl_set_rng( &ssl, ctr_drbg_random, &ctr_drbg );
ssl_set_dbg( &ssl, my_debug, stdout );
ssl_set_bio( &ssl, net_recv, &server_fd,
net_send, &server_fd );
if( opt.force_ciphersuite[0] != DFL_FORCE_CIPHER )
ssl_set_ciphersuites( &ssl, opt.force_ciphersuite );
ssl_set_renegotiation( &ssl, opt.renegotiation );
ssl_legacy_renegotiation( &ssl, opt.allow_legacy );
ssl_set_ca_chain( &ssl, &cacert, NULL, opt.server_name );
ssl_set_own_cert( &ssl, &clicert, &rsa );
ssl_set_hostname( &ssl, opt.server_name );
if( opt.min_version != -1 )
ssl_set_min_version( &ssl, SSL_MAJOR_VERSION_3, opt.min_version );
if( opt.max_version != -1 )
ssl_set_max_version( &ssl, SSL_MAJOR_VERSION_3, opt.max_version );
/*
* 4. Handshake
*/
printf( " . Performing the SSL/TLS handshake..." );
fflush( stdout );
while( ( ret = ssl_handshake( &ssl ) ) != 0 )
{
if( ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE )
{
....
}
}
/*
* 5. Verify the server certificate
*/
printf( " . Verifying peer X.509 certificate..." );
if( ( ret = ssl_get_verify_result( &ssl ) ) != 0 )
{
printf( " failed\n" );
...
printf( "\n" );
}
printf( " . Peer certificate information ...\n" );
x509parse_cert_info( (char *) buf, sizeof( buf ) - 1, " ",ssl_get_peer_cert( &ssl ) );
/*
* 6. Write the GET request
*/
len = sprintf( (char *) buf, GET_REQUEST, opt.request_page );
while( ( ret = ssl_write( &ssl, buf, len ) ) <= 0 )
{
...
}
/*
* 7. Read the HTTP response
*/
printf( " < Read from server:" );
fflush( stdout );
do
{
ret = ssl_read( &ssl, buf, len );
....
}while(1);
ssl_close_notify( &ssl );
}
关键握手协议在ssl_handshake函数中,循环调用ssl_handshake_step,直至握手过程完成。
int ssl_handshake( ssl_context *ssl )
{
int ret = 0;
SSL_DEBUG_MSG( 2, ( "=> handshake" ) );
while( ssl->state != SSL_HANDSHAKE_OVER )
{
ret = ssl_handshake_step( ssl );
if( ret != 0 )
break;
}
SSL_DEBUG_MSG( 2, ( "<= handshake" ) );
return( ret );
}
client端最后调用的是ssl_handshake_client_step函数,在library/ssl_cli.c中,代码注释非常清晰,不再复述。
int ssl_handshake_client_step( ssl_context *ssl )
{
int ret = 0;
if( ssl->state == SSL_HANDSHAKE_OVER )
return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
SSL_DEBUG_MSG( 2, ( "client state: %d", ssl->state ) );
if( ( ret = ssl_flush_output( ssl ) ) != 0 )
return( ret );
switch( ssl->state )
{
case SSL_HELLO_REQUEST:
ssl->state = SSL_CLIENT_HELLO;
break;
/*
* ==> ClientHello
*/
case SSL_CLIENT_HELLO:
ret = ssl_write_client_hello( ssl );
break;
/*
* <== ServerHello
* Certificate
* ( ServerKeyExchange )
* ( CertificateRequest )
* ServerHelloDone
*/
case SSL_SERVER_HELLO:
ret = ssl_parse_server_hello( ssl );
break;
case SSL_SERVER_CERTIFICATE:
ret = ssl_parse_certificate( ssl );
break;
case SSL_SERVER_KEY_EXCHANGE:
ret = ssl_parse_server_key_exchange( ssl );
break;
case SSL_CERTIFICATE_REQUEST:
ret = ssl_parse_certificate_request( ssl );
break;
case SSL_SERVER_HELLO_DONE:
ret = ssl_parse_server_hello_done( ssl );
break;
/*
* ==> ( Certificate/Alert )
* ClientKeyExchange
* ( CertificateVerify )
* ChangeCipherSpec
* Finished
*/
case SSL_CLIENT_CERTIFICATE:
ret = ssl_write_certificate( ssl );
break;
case SSL_CLIENT_KEY_EXCHANGE:
ret = ssl_write_client_key_exchange( ssl );
break;
case SSL_CERTIFICATE_VERIFY:
ret = ssl_write_certificate_verify( ssl );
break;
case SSL_CLIENT_CHANGE_CIPHER_SPEC:
ret = ssl_write_change_cipher_spec( ssl );
break;
case SSL_CLIENT_FINISHED:
ret = ssl_write_finished( ssl );
break;
/*
* <== ChangeCipherSpec
* Finished
*/
case SSL_SERVER_CHANGE_CIPHER_SPEC:
ret = ssl_parse_change_cipher_spec( ssl );
break;
case SSL_SERVER_FINISHED:
ret = ssl_parse_finished( ssl );
break;
case SSL_FLUSH_BUFFERS:
SSL_DEBUG_MSG( 2, ( "handshake: done" ) );
ssl->state = SSL_HANDSHAKE_WRAPUP;
break;
case SSL_HANDSHAKE_WRAPUP:
ssl_handshake_wrapup( ssl );
break;
default:
SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) );
return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
}
return( ret );
}