您的位置:首页 > 理论基础 > 计算机网络

https 研究一

2016-04-05 14:42 751 查看

HTTPS研究一

差不多花了一周的时间,研究了下https,研究的不算深入,顶多算是入门,先做个记录,遗留问题再抽时间研究

SSL

https可以看成是http+ssl或tls,tls它的前身是ssl,这里简单介绍下ssl

位置:ssl介于http和tcp之间

协议:握手协议、记录协议、报警协议

握手协议



协议大致过程如上图所示

阶段一、client通过client hello将client支持的ssl最高version、32字节随机数、密码套件(包括秘钥交换算法、加密算法、散列算法)、压缩算法发送给server、server通过server hello将ssl version、32字节随机数、从client中选择的密码套件、压缩算法发送给client,通过阶段一,client和server双方协商确认了ssl版本、秘钥交换算法、对称加密算法、散列算法、压缩算法、秘钥生成的两个随机数(client和server一边一个)

阶段二、server端发送证书,秘钥交换(基于秘钥交换算法),客户端证书请求(可选,如果server段需要验证client的话,则发送)

阶段三、client端发送证书(可选,如果server段需要的话),client端秘钥交换,server端证书验证(可选,如果需要验证,需要第三方认证机构)

阶段四、两端互相发送改变密码规格值,握手完成

记录协议

握手协议完成之后,可以开始正式的数据传输,通过对称加密算法对传输的数据进行加密,通过散列算法来保证数据的完整性。

报警协议

客户机和服务器发现错误时,向对方发送一个警报消息。如果是致命错误,则算法立即关闭SSL连接

https概述

https可以看做是http+ssl(tls),http本身明文传输,而https却通过ssl解决了3个问题

client对server的信任问题,通过ca证书
数据的加密问题,通过非对称加密交换对称秘钥,再通过对称加密对数据进行加密
数据的完整性问题,通过散列算法保证数据的完整性

https性能调优

服务器端配置HSTS,减少302跳转,其实HSTS的最大作用是防止302 HTTP劫持。HSTS的缺点是浏览器支持率不高,另外配置HSTS后HTTPS很难实时降级成HTTP。
设置ssl session 的共享内存cache. 以nginx为例,它目前只支持session cache的单机多进程共享。如果是前端接入是多服务器架构,这样的session cache是没有作用的,所以需要实现session cache的多机共享机制。可以访问外部session cache server,比如通过redis来实现。nginx配置如下
ssl_session_cache   shared:SSL:10m;
ssl_session_timeout 10m;

配置相同的session ticket key,部署在多个服务器上,这样多个不同的服务器也能产生相同的 session ticket。session ticket的缺点是支持率不广,大概只有40%。而session id是client hello的标准内容,从SSL2.0开始就被全部客户支持。nginx配置如下:ticket.key需要通过 openssl rand 48 > ticket.key 来生成

ssl_session_tickets    on;
ssl_session_ticket_key /usr/local/cdn_cache/nginx/conf/ticket.key;

打开keepalive,keepalive可以保证多个http请求在同一个tcp连接上进行,而不是每个http请求建立一个tcp连接,每建立一个tcp连接都需要一次秘钥交换的过程,非对称加解密是https性能最大的消耗者。nginx配置如下,keepalive是指tcp连接保持的时间,注意这个参数nginx会在每次接收http request之后,重新设置,也就是说nginx会在最后接收http request之后的65s才去释放tcp连接。第二个参数表示每个tcp连接所能接收的最大http request的个数。

keepalive_timeout  65s;
keepalive_requests 1000;

设置ocsp stapling file,这样ocsp的请求就不会发往ca提供的ocsp站点,而是发往网站的webserver。
优先使用ecdhe密钥交换算法,因为它支持PFS(perfect forward secrecy),能够实现false
start。libcurl 7.42.0版本开始支持

启用tcp fast open。Linux内核版本3.7以上
启用spdy或http2,libcurl 7.47.0版本开始支持http2

https nginx server的配置过程

1、编译nginx,./configure时加上--with-http_ssl_module,即./configure --with-http_ssl_module......

2、安装openssl

#yum install openssl
#yum install openssl-devel


3、生成server端ca证书

#cd /usr/local/cdn_cache/nginx/conf
#openssl genrsa -des3 -out server.key 1024
#openssl req -new -key server.key -out server.csr
#openssl rsa -in server.key -out server_nopwd.key
#openssl x509 -req -days 365 -in server.csr -signkey server_nopwd.key -out server.crt


4、nginx配置文件

init_by_lua 'cjson = require "cjson"';

ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m;
ssl_session_tickets on; ssl_session_ticket_key /usr/local/cdn_cache/nginx/conf/ticket.key;
server {
listen 443;
ssl on;
ssl_certificate /usr/local/cdn_cache/nginx/conf/server.crt;
ssl_certificate_key /usr/local/cdn_cache/nginx/conf/server_nopwd.key;

location /shen_post {
content_by_lua '
ngx.req.read_body()
local data = cjson.decode(ngx.req.get_body_data())
local chnlid = data.chnlid
local bps = data.bps
local name = data.name
ret = {}
ret["cid"] = chnlid
ret["bps"] = bps
ret["name"] = name
ngx.say(cjson.encode(ret))
';
}

location /shen_get {
content_by_lua '
ret = {}
ret["cid"] = "10000"
ret["bps"] = 500
ret["name"] = "xxxx"
ngx.say(cjson.encode(ret))
';
}
}


https client

通过libevent+libcurl谢了一个https client,代码如下:

#include <unistd.h>
#include <event.h>
#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>

#define inter_t 10*1000

#ifndef false
#define false 0
#endif

#ifndef true
#define true 1
#endif

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#ifndef NULL
#define NULL 0
#endif

typedef unsigned char bool;

struct timeval tv_get;
struct event ev_get;

struct timeval tv_post;
struct event ev_post;

struct event_base* base = NULL;

typedef struct private_ {
CURL * curl;
CURLM * multi_handle;
bool data_valid;
int 	running_count;
}private_t;

static size_t
handle_recv_header(void *ptr, size_t size, size_t nmemb, void *private) {
size_t total    = size * nmemb;
printf("handle_recv_header---%s\n", ptr);

return total;
}

static size_t
handle_recv_data(void *ptr, size_t size, size_t nmemb, void *private) {
private_t * pri = (private_t *)private;

size_t total 	= size * nmemb;
pri->data_valid = true;
printf("handle_recv_data---%s\n", ptr);

return total;
}

size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp) {
printf("%s\n", buffer);
return size * nmemb;
}

void get_timeout_cb(int fd, short event, void *arg) {
private_t * pri = (private_t *)arg;
while(1) {
pri->data_valid = false;

CURLMcode curl_rc = curl_multi_perform(pri->multi_handle, &(pri->running_count));
if((curl_rc != CURLM_CALL_MULTI_PERFORM && curl_rc != CURLM_OK)) {
printf("curl_multi_perform err happened, jump out loop, curl_rc=%d\n", curl_rc);
break;
}

if (false == pri->data_valid) {
break;
}
}

if (pri->running_count) {
printf("get_timeout_cb, running_count[%d]\n", pri->running_count);
evtimer_add(&ev_get, &tv_get);
return;
}

sleep(15);

curl_multi_remove_handle(pri->multi_handle, pri->curl);
curl_multi_add_handle(pri->multi_handle, pri->curl);
evtimer_add(&ev_get, &tv_get);
return;
}

void post_timeout_cb(int fd, short event, void *arg) {
private_t * pri = (private_t *)arg;
CURLMcode curl_rc = curl_multi_perform(pri->multi_handle, &(pri->running_count));
if((curl_rc != CURLM_CALL_MULTI_PERFORM && curl_rc != CURLM_OK)) {
printf("curl_multi_perform err happened, jump out loop, curl_rc=%d\n", curl_rc);
return;
}

if (pri->running_count) {
printf("post_timeout_cb, running_count[%d]\n", pri->running_count);
evtimer_add(&ev_post, &tv_post);
return;
}

curl_multi_remove_handle(pri->multi_handle, pri->curl);
curl_multi_add_handle(pri->multi_handle, pri->curl);
evtimer_add(&ev_post, &tv_post);
return;
}

int curl_get(char * url) {
private_t * pri = calloc(1, sizeof(private_t));

CURL * curl = curl_easy_init();
if (NULL == curl) {
printf("failed in curl_easy_init.\n");
return -1;
}

CURLM * multi_handle = curl_multi_init();
if (NULL == multi_handle) {
curl_easy_cleanup(curl);
printf("failed in curl_multi_init\n");
return -1;
}

curl_easy_setopt(curl, CURLOPT_URL, url);

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handle_recv_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)pri);

curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, handle_recv_header);
curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)pri);

//curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, on_set_sock_fd);
//curl_easy_setopt(curl, CURLOPT_SOCKOPTDATA, (void *)pri);

curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 10);
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 10);

curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
CURLcode xx = curl_easy_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, 1L);
if (CURLE_OK != xx) {
printf("xxxxxx\n");
}

if (curl_multi_add_handle(multi_handle, curl) != CURLM_OK) {
printf("failed in curl_multi_add_handle. ");
curl_easy_cleanup(curl);
curl_multi_cleanup(multi_handle);
return -1;
}

pri->curl          = curl;
pri->multi_handle  = multi_handle;
pri->running_count = 1;

tv_get.tv_sec = 0;
tv_get.tv_usec = inter_t;

//event_set(&ev_appget, -1, EV_PERSIST|EV_TIMEOUT, timeout_cb, pri);
evtimer_set(&ev_get, get_timeout_cb, pri);
event_base_set(base, &ev_get);
evtimer_add(&ev_get, &tv_get);
}

int curl_post(char * url) {
private_t * pri = calloc(1, sizeof(private_t));

char * post_content = "{\
\"chnlid\":\"shen\",\
\"bps\":555,\
\"name\":\"shen\"\
}";

CURL * curl = curl_easy_init();
CURLM * multi_handle = curl_multi_init();
//char *list = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256";
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_content);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
//curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, list);

curl_multi_add_handle(multi_handle, curl);

pri->curl          = curl;
pri->multi_handle  = multi_handle;
pri->running_count = 1;

tv_post.tv_sec = 0;
tv_post.tv_usec = inter_t;

evtimer_set(&ev_post, post_timeout_cb, pri);
event_base_set(base, &ev_post);
evtimer_add(&ev_post, &tv_post);

}

int main(int argc, char *argv[]) {
base = event_base_new();

if (0 == strcmp("get", argv[1])) {
curl_get(argv[2]);
}
else if (0 == strcmp("post", argv[1])) {
curl_post(argv[2]);
}

event_base_dispatch(base);
return 0;
}


编译:gcc -levent -lcurl -g -o lt lbe_curl.c

执行:./lt get "https://10.10.73.18/shen_get"或./lt post "https://10.10.73.18/shen_post"

遗留问题

ssl和tls协议的差异点,主要是tls具体有哪些改进点
ssl非对称加密算法的种类和算法实现细节,不同算法对性能的影响
ssl对称加密种类和实现细节
session cache、session ticket对性能的优化程度
http2协议细节

参考资料

ssl:
http://www.cnblogs.com/zhuqil/archive/2012/10/06/ssl_detail.html http://vincent.bernat.im/en/blog/2011-ssl-perfect-forward-secrecy.html https://www.openssl.org/docs/manmaster/apps/ciphers.html
https性能调优:

http://blog.httpwatch.com/2009/01/15/https-performance-tuning/(强烈推荐)


http://www.mahaixiang.cn/seoyjy/1422.html http://blog.jobbole.com/78042/ http://www.admin10000.com/document/6236.html
http2:

http://chimera.labs.oreilly.com/books/1230000000545/ch12.html(强烈推荐)
http://io.upyun.com/2015/05/13/http2/?utm_source=tuicool&utm_medium=referral
nginx配置:

http://nginx.org/en/docs/http/configuring_https_servers.html(强烈推荐)

http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_tickets(强烈推荐)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: