SSL socket 通讯详解
2016-05-04 16:22
197 查看
转载请注明出处
http://blog.csdn.net/pony_maggie/article/details/51315824
作者:小马
最近刚弄完一个ssl socket通讯,整理个笔记。
ssl实现了通过非对称的RSA加解密原理实现密钥的交换。(这里就不细讲RSA的原理了,不明白的可以先补充一下这部分知识)。我们假设A是服务器端,B是客户端。然后还有一个角色,是一个可信任的第三方CA。A首先生成一个RSA公私钥对,然后再由这个可信的第三方用自己的私钥(为了便于描述, 后面叫CA_PRIVATE_KEY)把这个DES密钥连同RSA公钥一起签发一个客户端证书(后面叫CLIENT_CERT),这个证书中同时也包含一段签名。
A会把CA证书(证书中包含CA_PRIVATE_KEY 对应的公钥, 后面叫CA_CERT)连同CLIENT_CERT一起发给客户端,当然客户端也可以由其它途径获取这两个证书(比如由专门的安全设备中导入到本地)。同时RSA私钥(后面叫CLIENT_PRIVATE_KEY)也会一起下发到客户端。
客户端首先用CA_CERT对CLIENT_CERT做验签,以确保数据确实是由A发出来的。如果验签能过,其实就已经说明B已经认证了A的身份。前面说到CLIENT_CERT包含服务器的公钥,B用这个公钥对一个对称的DES密钥加密,然后可以公开发出去,他不用担心这个密钥会被截取,因为只有A才有CLIENT_PRIVATE_KEY,也只有A才能解密出来这个DES密钥。这样就完成了对称密钥的交换。
盗个图,流程基本是下面这样的,
![](https://img-blog.csdn.net/20160504162009646)
大部分时候到这里,SSL通讯前的握手已经完成了,可以进行安全的数据通讯了。不过有时候会有双向认证的需求,也就是A也想认证B。这个时候CLIENT_PRIVATE_KEY就发生作用了,B会用这个私钥自己生成签名,然后发给A来认证。
这些基本就是SSL的原理了。
上面讲到的这些流程,如果用程序实现还是有点复杂的。幸运的是,开源库openssl已经帮我们做了大部分的事情(openssl的实现机制更复杂也更灵活,但基本原理跟上面是一致的)。我们只需要调用一些基本的接口就可以完成SSL socket通讯。
第一步, 初始化
这一步主要是初始化openssl库,创建会话上下文等,
SSL_METHOD *method = NULL;
SSL_library_init ();
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
method = SSLv3_client_method();
g_ctx = SSL_CTX_new(method); /* Create new context */
SSLv3_client_method 是指定ssl要使用的协议。SSL协议由美国 NetScape公司开发的,V1.0版本从没有公开发表过;V2.0版本于1995年2月发布。但是,由于V2.0版本有许多安全漏洞,所以,1996年紧接着就发布了V3.0版本。微软从IE 7开始就已经把浏览器的缺省设置不支持SSL 2.0,但可能是考虑到有些网站还只支持SSL 2.0,所以IE浏览器留了一个可以由用户设置支持SSL 2.0的选项,以便能正常访问只支持SSL 2.0的网站。IE7/IE8支持SSL 3.0和TLS1.0,而IE9还支持TLS1.1和1.2。
在openssl里指定协议很简单,每个协议都有对应的函数,一行代码就可以搞定。
SSL_METHOD* TLSv1_client_method(void); TLSv1.0 协议
SSL_METHOD* SSLv2_client_method(void); SSLv2 协议
SSL_METHOD* SSLv3_client_method(void); SSLv3 协议
SSL_METHOD* SSLv23_client_method(void); SSLv2/v3 协议
SSL_CTX_new创建ssl上下文,这里面很多全局变量要被各个阶段共享。
SSL_load_error_strings(void );
如果想打印出一些方便阅读的调试信息的话,便要在一开始调用此函数.
第二步,加载证书和私钥
前面提到过,证书有CA证书,还是客户端的证书,私钥是客户端私钥。也是几个接口就搞定的事情,
int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile,const char *CApath);
此函数用来便是加载CA证书文件的.
int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type);
加载客户端自己的证书文件.
int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);
加载自己的私钥,以用于签名.
void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *u);
加载私钥时一般要一个验证密码,这个密码是由生成私钥的一方来设定的,客户端得到这个密码后要通过一个接口传入验证:
void SSL_CTX_set_verify(SSL_CTX ctx,int mode,int (*callback)(int, X509_STORE_CTX ));
缺省mode是SSL_VERIFY_NONE,如果想要验证对方的话,便要将此项变成SSL_VERIFY_PEER.SSL/TLS中缺省只验证server,如果没有设置 SSL_VERIFY_PEER的话,客户端连证书都不会发过来.
第三步,建立ssl socket连接
首先要建立普通的socket连接,这个就不多说了,连接成功后返回一个socket描述符,假设名称为socket_fd。然后与ssl进行关联,三个接口就可以完成:
g_ssl = SSL_new(g_ctx);
SSL_set_fd(g_ssl, socket_fd);
SSL_connect(g_ssl);
这样后面所以的发送,接收过程都是基于g_ssl这个ssl的全局字段了。
第四步,发送和接收
收发数据就更简单了,
发送,
SSL_write(g_ssl, pData, bytes_left);
接收,
SSL_read(g_ssl, pData, bytes_left);
当然,一般我们在普通的socket收发都会加一些超时处理机制,对于ssl的收发也是一样的,后面我的示例中会给出来一个较完善的处理过程。
第五步,释放资源
如果不使用了,就要把所有占用的资源都释放掉。
当然首先要把socket关闭,
close(socket_fd);
然后是和ssl相关的资源释放
SSL_CTX_free(g_ctx);
SSL_shutdown(g_ssl);
SSL_free(g_ssl);
ERR_free_strings();
如有错误,请不吝赐教。
http://blog.csdn.net/pony_maggie/article/details/51315824
作者:小马
最近刚弄完一个ssl socket通讯,整理个笔记。
SSL原理
比如 A要和B互相通讯,为了安全他们希望双方发送的数据都是经过加密的。这就要求双方有一个共同的加解密密钥(一般加密都是基于对称加密算法)。如何才能让双方都拥有同一个密钥呢?有人说由一方生成发给另一方不就行了。这样就带来另一个问题,如何保证这个传送的密钥是安全的呢?ssl实现了通过非对称的RSA加解密原理实现密钥的交换。(这里就不细讲RSA的原理了,不明白的可以先补充一下这部分知识)。我们假设A是服务器端,B是客户端。然后还有一个角色,是一个可信任的第三方CA。A首先生成一个RSA公私钥对,然后再由这个可信的第三方用自己的私钥(为了便于描述, 后面叫CA_PRIVATE_KEY)把这个DES密钥连同RSA公钥一起签发一个客户端证书(后面叫CLIENT_CERT),这个证书中同时也包含一段签名。
A会把CA证书(证书中包含CA_PRIVATE_KEY 对应的公钥, 后面叫CA_CERT)连同CLIENT_CERT一起发给客户端,当然客户端也可以由其它途径获取这两个证书(比如由专门的安全设备中导入到本地)。同时RSA私钥(后面叫CLIENT_PRIVATE_KEY)也会一起下发到客户端。
客户端首先用CA_CERT对CLIENT_CERT做验签,以确保数据确实是由A发出来的。如果验签能过,其实就已经说明B已经认证了A的身份。前面说到CLIENT_CERT包含服务器的公钥,B用这个公钥对一个对称的DES密钥加密,然后可以公开发出去,他不用担心这个密钥会被截取,因为只有A才有CLIENT_PRIVATE_KEY,也只有A才能解密出来这个DES密钥。这样就完成了对称密钥的交换。
盗个图,流程基本是下面这样的,
大部分时候到这里,SSL通讯前的握手已经完成了,可以进行安全的数据通讯了。不过有时候会有双向认证的需求,也就是A也想认证B。这个时候CLIENT_PRIVATE_KEY就发生作用了,B会用这个私钥自己生成签名,然后发给A来认证。
这些基本就是SSL的原理了。
上面讲到的这些流程,如果用程序实现还是有点复杂的。幸运的是,开源库openssl已经帮我们做了大部分的事情(openssl的实现机制更复杂也更灵活,但基本原理跟上面是一致的)。我们只需要调用一些基本的接口就可以完成SSL socket通讯。
openssl 与 socket
基于ssl的socket通讯一般分为几个步骤。第一步, 初始化
这一步主要是初始化openssl库,创建会话上下文等,
SSL_METHOD *method = NULL;
SSL_library_init ();
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
method = SSLv3_client_method();
g_ctx = SSL_CTX_new(method); /* Create new context */
SSLv3_client_method 是指定ssl要使用的协议。SSL协议由美国 NetScape公司开发的,V1.0版本从没有公开发表过;V2.0版本于1995年2月发布。但是,由于V2.0版本有许多安全漏洞,所以,1996年紧接着就发布了V3.0版本。微软从IE 7开始就已经把浏览器的缺省设置不支持SSL 2.0,但可能是考虑到有些网站还只支持SSL 2.0,所以IE浏览器留了一个可以由用户设置支持SSL 2.0的选项,以便能正常访问只支持SSL 2.0的网站。IE7/IE8支持SSL 3.0和TLS1.0,而IE9还支持TLS1.1和1.2。
在openssl里指定协议很简单,每个协议都有对应的函数,一行代码就可以搞定。
SSL_METHOD* TLSv1_client_method(void); TLSv1.0 协议
SSL_METHOD* SSLv2_client_method(void); SSLv2 协议
SSL_METHOD* SSLv3_client_method(void); SSLv3 协议
SSL_METHOD* SSLv23_client_method(void); SSLv2/v3 协议
SSL_CTX_new创建ssl上下文,这里面很多全局变量要被各个阶段共享。
SSL_load_error_strings(void );
如果想打印出一些方便阅读的调试信息的话,便要在一开始调用此函数.
第二步,加载证书和私钥
前面提到过,证书有CA证书,还是客户端的证书,私钥是客户端私钥。也是几个接口就搞定的事情,
int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile,const char *CApath);
此函数用来便是加载CA证书文件的.
int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type);
加载客户端自己的证书文件.
int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);
加载自己的私钥,以用于签名.
void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *u);
加载私钥时一般要一个验证密码,这个密码是由生成私钥的一方来设定的,客户端得到这个密码后要通过一个接口传入验证:
void SSL_CTX_set_verify(SSL_CTX ctx,int mode,int (*callback)(int, X509_STORE_CTX ));
缺省mode是SSL_VERIFY_NONE,如果想要验证对方的话,便要将此项变成SSL_VERIFY_PEER.SSL/TLS中缺省只验证server,如果没有设置 SSL_VERIFY_PEER的话,客户端连证书都不会发过来.
第三步,建立ssl socket连接
首先要建立普通的socket连接,这个就不多说了,连接成功后返回一个socket描述符,假设名称为socket_fd。然后与ssl进行关联,三个接口就可以完成:
g_ssl = SSL_new(g_ctx);
SSL_set_fd(g_ssl, socket_fd);
SSL_connect(g_ssl);
这样后面所以的发送,接收过程都是基于g_ssl这个ssl的全局字段了。
第四步,发送和接收
收发数据就更简单了,
发送,
SSL_write(g_ssl, pData, bytes_left);
接收,
SSL_read(g_ssl, pData, bytes_left);
当然,一般我们在普通的socket收发都会加一些超时处理机制,对于ssl的收发也是一样的,后面我的示例中会给出来一个较完善的处理过程。
第五步,释放资源
如果不使用了,就要把所有占用的资源都释放掉。
当然首先要把socket关闭,
close(socket_fd);
然后是和ssl相关的资源释放
SSL_CTX_free(g_ctx);
SSL_shutdown(g_ssl);
SSL_free(g_ssl);
ERR_free_strings();
一个使用示例
初始化,连接等操作都很简单,这里只给出一个ssl发送函数的实现方法,带超时处理的,供大家参考。int socket_send_ssl(const char *data, unsigned int data_len,int time_out) { int iRet = -1; struct timeval tm; tm.tv_sec = time_out; tm.tv_usec = 0; char *pData = data; int bytes_left = data_len; int written_bytes = 0; if((data == NULL) || (data_len <= 0)) { return -2; } iRet = setsockopt(socket_fd,SOL_SOCKET,SO_SNDTIMEO,&tm,sizeof(tm));//设置发送超时 while(bytes_left > 0) { written_bytes = SSL_write(g_ssl, pData, bytes_left); if(written_bytes <= 0) { if(errno == EINTR) { written_bytes = 0; } else { return -1; } } bytes_left -= written_bytes; pData += written_bytes; } return 0; }
如有错误,请不吝赐教。
相关文章推荐
- 图文讲解Java中实现quickSort快速排序算法的方法
- 编程技巧系列(5)字符串格式化常用做法
- Android代码质量检查利器:从代码源头保证代码质量
- 完整的Java简单浏览器
- openfire3.9.1集群配置
- mp4文件格式之fragment mp4
- bootstrap table 服务器端分页--ashx+ajax
- android 网络请求 volley
- 执行顺序
- 输入控制
- windows.location.href 中文汉字乱码
- 实际项目使用例程(一)
- java即时编译器JIT之简单介绍
- Android异步加载
- 自定义View---------->onMeasure()的理解
- 回朔——关于产生01数组
- 导航VC的左右item代码
- Django笔记:1.创建一个项目
- 作业六:团队项目——编写项目的Spec
- UiViewController 里frame和bounds取值有误