博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[算法] 【一段破代码,对SSL异步编程感兴趣的同学有福了, 欢迎高手指正使用错误】...
阅读量:4936 次
发布时间:2019-06-11

本文共 6014 字,大约阅读时间需要 20 分钟。

SSL技术是很常用的, 无处不在, 在你SSH的时候, 在你HTTPS的时候, 在任何一款想兼具身份认证与加密通信的应用中, 你可以随处看到它的身影.

本人之前只是用用脚本, SSL接口都被透明化了, 而且还是阻塞接口, 或者会使用Libevent支持的SSL体验一番异步的感觉, 也曾经读过Https的源码, 当时并没有在SSL方面引起重视, 直到我希望把Https引入到我的开源Http Server中, 终于有机会来编码实现异步SSL啦.
要理解与正确的编程实践异步SSL, 需要对Feed工作模式的原理有一定的理解, 还需要对网络事件编程有掌握, 最后最重要的还是对SSL握手的过程有清晰的认识, 对SSL的原理有清晰的认识, 这样才不至于在编程时出现逻辑问题, 我们要清楚的明白每一步调用的目的与作用, 而不是胡猜或者模仿. <ignore_js_op>

SSLHandShake.png (48.16 KB, 下载次数: 15)

2012-11-15 14:12 上传

 

代码说明:可以通过浏览器访问或者使用curl/wget进行访问, 记住使用https访问指定端口. 逻辑说明: 1, SSL握手阶段, 很明显的使用了feed模式, 调用链:read -> bio_write(rbio) -> ssl_accept, 即将读到的数据feed给ssl_accept使用. feed后根据bio_pending(wbio)确定ssl是否产生了一些待回复的握手数据, 如果有, 我们注册写事件帮ssl发送出去, 此时调用链为: bio_read -> write 2, 数据交互阶段, 注意SSL握手阶段是绝对不可能交互普通数据的, 因为交互普通数据依赖于SSL彻底完成后获得的对称密钥. 在SSL完全结束前, 是不可能有正常数据到来的, 客户端与服务端是默契的一问一答, 直到SSL握手结束, 我们可以看一下握手交互时序图来看一下这个关系, 客户端在接受到服务端最后一次MAC应答之前是不会操作普通数据的, 服务端在接收到客户端发来的MAC后进入了最后一步握手输出阶段, 在最后一步时我们送出MAC兵彻底进入了数据交互阶段, 这些逻辑在代码里有所体现.

在代码中, SSL_accept意味着客户端MAC被接受, 此时SSL会在wbio中pending上最后一步要发送给客户端的MAC, 我们进行了状态的记录, 在MAC送给客户端后, 我们立即将HTTP应答送出而不等待客户端HTTP请求, 因为我不想解析与理解HTTP协议, 所以在这时候发送是最佳的时机, 并且一定是附带了connection:close来让客户端主动的关闭掉, 因为我们没有主动关闭客户端的逻辑. 在我们消耗掉wbio中的pending MAC后, 客户端发送HTTP请求到来, 服务端读事件将会检测ssl握手完成(因为我们的wbio pending的MAC已经被消耗掉了), 于是我们接下来的数据都被当作普通交互数据, 经历调用链: read -> bio_write -> ssl_read -> print to screen.
我不确定SSL的使用方法一定是完全正确的, 但至少目前运行还没有出过问题, 欢迎大家批评指正, 感兴趣的同学又有福了.(PS:懒得弄证书和prikey的同学可以直接拷走.) SSL基础知识与握手流程观看:

 

 

  • #include <iostream>
  • #include <string>
  • #include <openssl/ssl.h>
  • #include <assert.h>
  • #include <unistd.h>
  • #include <string.h>
  • #include <openssl/err.h>
  • #include <sys/types.h>
  • #include <sys/socket.h>
  • #include <netinet/in.h>
  • #include "event2/event.h"
  • #include "event2/util.h"
  • #define CERT_FILE_PATH "cacert.pem"
  • #define PRIVATE_FILE_PATH "prvtkey.pem"
  • #define TRACE(fmt, ...) do{fprintf(stderr, fmt"\n", ##__VA_ARGS__);}while(0)
  • struct event_base *srv_base;
  • int lis_fd;
  • struct event *lisev;
  • std::string ok_res = "HTTP/1.1 200 OK\r\nConnection:close\r\nContent-Length:2\r\n\r\nok";
  • struct Client
  • {
  •     int         m_fd;
  •     std::string m_tmpbuf;
  •     std::string m_outbuf;
  •    
  •     int         m_done;
  •     SSL        *m_ssl;
  •     SSL_CTX    *m_ctx;
  •     BIO        *m_rbio;
  •     BIO        *m_wbio;
  •     struct event *m_rev;
  •     struct event *m_wev;
  • };
  • void CliCallback(int cli_fd, short event, void *userdata);
  • Client* InitClient(int fd)
  • {
  •     Client *client = new Client();
  •    
  • #define M(mem) client->m_##mem
  •     M(fd) = fd;
  •     evutil_make_socket_nonblocking(fd);
  •     M(ctx) = SSL_CTX_new(SSLv23_server_method());
  •     SSL_CTX_use_certificate_file(M(ctx), CERT_FILE_PATH, SSL_FILETYPE_PEM);
  •     SSL_CTX_use_PrivateKey_file(M(ctx), PRIVATE_FILE_PATH, SSL_FILETYPE_PEM);
  •     M(ssl) = SSL_new(M(ctx));
  •     M(rbio) = BIO_new(BIO_s_mem());
  •     M(wbio) = BIO_new(BIO_s_mem());
  •     SSL_set_bio(M(ssl), M(rbio), M(wbio));
  •     SSL_set_accept_state(M(ssl));
  •    
  •     M(tmpbuf).reserve(1024 * 1024);
  •     M(rev) = event_new(srv_base, fd, EV_READ | EV_PERSIST, CliCallback, client);
  •     M(wev) = event_new(srv_base, fd, EV_WRITE| EV_PERSIST, CliCallback, client);
  •     event_add(M(rev), NULL);
  •     return client;
  • }
  • void FreeClient(Client *client)
  • {
  •     if (client)
  •     {
  •         SSL_free(M(ssl));
  •         SSL_CTX_free(M(ctx));
  •         event_free(M(rev));
  •         event_free(M(wev));
  •         close(M(fd));
  •         delete client;
  •     }
  • }
  • void LisCallback(int lis_fd, short event, void *userdata)
  • {
  •     int cli_fd = accept(lis_fd, NULL, NULL);
  •     if (cli_fd > 0)
  •     {
  •         Client *client = InitClient(cli_fd);
  •         TRACE("New Client Comes");
  •     }
  • }
  • void CliCallback(int cli_fd, short event, void *userdata)
  • {
  •     Client *client = (Client *)userdata;
  •     if (event & EV_READ)
  •     {
  •         int nb = read(cli_fd, &M(tmpbuf[0]), M(tmpbuf).capacity());
  •         TRACE("read %d bytes", nb);
  •         if (nb == -1)
  •         {
  •             if (errno != EAGAIN)
  •                 return FreeClient(client);
  •         }
  •         else if (nb == 0)
  •         {
  •             TRACE("Client Leave");
  •             return FreeClient(client);
  •         }
  •         else
  •         {
  •             /* feed bio */
  •             assert(BIO_write(M(rbio), &M(tmpbuf[0]), nb) == nb);
  •             
  •             if (!SSL_is_init_finished(M(ssl)))
  •             {
  •                 TRACE("ssl is not finished");
  •                 int ac = SSL_accept(M(ssl));
  •                 if (ac < 0)
  •                 {
  •                     int err = SSL_get_error(M(ssl), ac);
  •                     if (err != SSL_ERROR_WANT_READ)
  •                     {
  •                         return FreeClient(client);
  •                     }
  •                     TRACE("SSL_accept wants more");
  •                 }
  •                 if (ac > 0)
  •                 {
  •                     /*ssl reach the last step, send back the MAC*/
  •                     TRACE("SSL_accept done");
  •                     M(done) = 1;
  •                 }
  •                 if (ac == 0)
  •                 {
  •                     TRACE("ac = 0");
  •                 }
  •                 int np = BIO_pending(M(wbio));
  •                 if (np)
  •                 {
  •                     TRACE("BIO_pending=%d", np);
  •                     event_add(M(wev), NULL);
  •                 }
  •             }
  •             else
  •             {
  •                 int decb;
  •                 while ((decb = SSL_read(M(ssl), &M(tmpbuf[0]), M(tmpbuf).capacity())) > 0)
  •                 {
  •                     TRACE("ssl read %d dec bytes", decb);
  •                     TRACE("%.*s", decb, &M(tmpbuf[0]));
  •                 }
  •                 if (decb < 0)
  •                 {
  •                     int err = SSL_get_error(M(ssl), decb);
  •                     if (err != SSL_ERROR_WANT_READ)
  •                     {
  •                         return FreeClient(client);
  •                     }
  •                     TRACE("SSL_read wants more");
  •                 }
  •             }
  •         }
  •     }
  •     if (event & EV_WRITE)
  •     {
  •         int nenc;
  •         while ((nenc = BIO_read(M(wbio), &M(tmpbuf[0]), M(tmpbuf).capacity())) > 0)
  •         {
  •             TRACE("BIO_read %d enc bytes to send off", nenc);
  •             M(outbuf).append(&M(tmpbuf[0]), nenc);
  •         }
  •         
  •         int nb = write(M(fd), M(outbuf).c_str(), M(outbuf).size());
  •         TRACE("write out %d bytes", nb);
  •         if (nb == -1)
  •         {
  •             if (errno != EAGAIN)
  •             {
  •                 return FreeClient(client);
  •             }
  •         }
  •         else
  •         {
  •             M(outbuf).erase(0, nb);
  •         }
  •         if (!M(outbuf).size())
  •         {
  •             if (M(done) == 1) //ssl last step finish
  •             {
  •                 SSL_write(M(ssl), ok_res.c_str(), ok_res.size()); /*ssl is totally done, do response*/
  •                 M(done) = 2;  //begin ordinary communication with symmetric-key
  •             }
  •             else if (M(done) == 2) /* response has been send out */
  •             {
  •                 return FreeClient(client);
  •             }
  •             else
  •             {
  •                 event_del(M(wev));
  •             }
  •         }
  •     }
  • }
  • int main(int argc, char* const argv[])
  • {
  •     SSL_library_init();
  •     OpenSSL_add_ssl_algorithms();
  •     SSL_load_error_strings();
  •     ERR_load_crypto_strings();
  •    
  •     int reuse = 1;
  •     srv_base = event_base_new();     
  •     lis_fd = socket(AF_INET, SOCK_STREAM, 0);
  •     evutil_make_socket_nonblocking(lis_fd);
  •     setsockopt(lis_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
  •     struct sockaddr_in lis_addr;
  •     lis_addr.sin_family = AF_INET;
  •     lis_addr.sin_port = htons(18888);
  •     lis_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  •     bind(lis_fd, (struct sockaddr*)&lis_addr, sizeof(lis_addr));
  •     listen(lis_fd, 5);
  •     lisev = event_new(srv_base, lis_fd, EV_READ | EV_PERSIST, LisCallback, NULL);
  •     event_add(lisev, NULL);
  •     event_base_dispatch(srv_base);
  •     return 0;
  • }

 

转载于:https://www.cnblogs.com/zmqblog/p/6502045.html

你可能感兴趣的文章
Perl oop链接数据库
查看>>
安卓开发16:Spinner 下拉列表控件
查看>>
参数数据自动生成app架构设计【一】
查看>>
网络虚拟化我眼中的OpenFlow
查看>>
多线程笔记1
查看>>
[leetcode] 3. Longest Substring Without Repeating Characters
查看>>
06 Frequently Asked Questions (FAQ) 常见问题解答 (常见问题)
查看>>
itemController.java
查看>>
获取判断IE版本 TypeError: Cannot read property 'msie' of undefined
查看>>
tcpreplay安装使用
查看>>
用systemtap对sysbench IO测试结果的分析1
查看>>
自增锁
查看>>
ps命令学习
查看>>
关于proteus仿真的串口问题
查看>>
逆向工程
查看>>
[NOI2018] 归程 可持久化并查集
查看>>
python--数据结构列表
查看>>
Flask-Moment本地化日期和时间
查看>>
(四)语音识别测试案例
查看>>
oldboy第四天学习
查看>>