如何用 C 实现 HTTS 通信

关注开源中国OSC头条号, 获取最新技术资讯 HTTPS是以安全为目标的HTTP通道, 简单讲是HTTP的安全版。即HTTP下加入SSL层, HTTPS的安全基础是SSL, 因此加密的详细内容就需要SSL。Nebula是一个为开发者提供一个快速开发高并发网络服务程序或搭建高并发分布式服务集群的高性能事件驱动网络框架。Nebula作为通用网络框架提供HTTPS支持十分重要, Nebula既可用作https服务器, 又可用作https客户端。本文将结合Nebula框架的https实现详细讲述基于openssl的SSL编程。如果觉得本文对你有用, 帮忙到Nebula的Github或码云给个star, 谢谢。Nebula不仅是一个框架, 还提供了一系列基于这个框架的应用, 目标是打造一个高性能分布式服务集群解决方案。Nebula的主要应用领域:即时通讯(成功应用于一款IM)、消息推送平台、数据实时分析计算(成功案例)等, Bwar还计划基于Nebula开发爬虫应用。 1. SSL加密通信 HTTPS通信是在TCP通信层与HTTP应用层之间增加了SSL层, 如果应用层不是HTTP协议也是可以使用SSL加密通信的, 比如WebSocket协议WS的加上SSL层之后的WSS。Nebula框架可以通过更换Codec达到不修改代码变更通讯协议目的, Nebula增加SSL支持后, 所有Nebula支持的通讯协议都有了SSL加密通讯支持, 基于Nebula的业务代码无须做任何修改。

Socket连接建立后的SSL连接建立过程:

2. OpenSSL API OpenSSL的API很多, 但并不是都会被使用到, 如果需要查看某个API的详细使用方法可以阅读API文档。 2.1 初始化OpenSSL OpenSSL在使用之前, 必须进行相应的初始化工作。在建立SSL连接之前, 要为Client和Server分别指定本次连接采用的协议及其版本, 目前能够使用的协议版本包括SSLv2、SSLv3、SSLv2/v3和TLSv1.0。SSL连接若要正常建立, 则要求Client和Server必须使用相互兼容的协议。 下面是Nebula框架SocketChannelSslImpl::SslInit()函数初始化OpenSSL的代码, 根据OpenSSL的不同版本调用了不同的API进行初始化。 #if OPENSSL_VERSION_NUMBER >= 0x10100003L if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) { pLogger->WriteLog(neb::Logger::ERROR, __FILE__, __LINE__, __FUNCTION__, "OPENSSL_init_ssl() failed!"); return(ERR_SSL_INIT); } /* * OPENSSL_init_ssl() may leave errors in the error queue * while returning success */ ERR_clear_error();#else OPENSSL_config(NULL); SSL_library_init(); // 初始化SSL算法库函数( 加载要用到的算法 ), 调用SSL函数之前必须调用此函数 SSL_load_error_strings(); // 错误信息的初始化 OpenSSL_add_all_algorithms();#endif 2.2 创建CTX CTX是SSL会话环境, 建立连接时使用不同的协议, 其CTX也不一样。创建CTX的相关OpenSSL函数: //客户端、服务端都需要调用SSL_CTX_new(); //申请SSL会话环境//若有验证对方证书的需求, 则需调用SSL_CTX_set_verify(); //指定证书验证方式SSL_CTX_load_verify_location(); //为SSL会话环境加载本应用所信任的CA证书列表//若有加载证书的需求, 则需调用int SSL_CTX_use_certificate_file(); //为SSL会话加载本应用的证书int SSL_CTX_use_certificate_chain_file();//为SSL会话加载本应用的证书所属的证书链int SSL_CTX_use_PrivateKey_file(); //为SSL会话加载本应用的私钥int SSL_CTX_check_private_key(); //验证所加载的私钥和证书是否相匹配 2.3 创建SSL套接字 在创建SSL套接字之前要先创建Socket套接字, 建立TCP连接。创建SSL套接字相关函数: SSL *SSl_new(SSL_CTX *ctx); //创建一个SSL套接字int SSL_set_fd(SSL *ssl, int fd); //以读写模式绑定流套接字int SSL_set_rfd(SSL *ssl, int fd); //以只读模式绑定流套接字int SSL_set_wfd(SSL *ssl, int fd); //以只写模式绑定流套接字 2.4 完成SSL握手 在这一步, 我们需要在普通TCP连接的基础上, 建立SSL连接。与普通流套接字建立连接的过程类似:Client使用函数SSL_connect()【类似于流套接字中用的connect()】发起握手, 而Server使用函数SSL_ accept()【类似于流套接字中用的accept()】对握手进行响应, 从而完成握手过程。两函数原型如下: int SSL_connect(SSL *ssl);int SSL_accept(SSL *ssl); 握手过程完成之后, Client通常会要求Server发送证书信息, 以便对Server进行鉴别。其实现会用到以下两个函数: X509 *SSL_get_peer_certificate(SSL *ssl); //从SSL套接字中获取对方的证书信息X509_NAME *X509_get_subject_name(X509 *a); //得到证书所用者的名字 2.5 数据传输 经过前面的一系列过程后, 就可以进行安全的数据传输了。在数据传输阶段, 需要使用SSL_read( )和SSL_write( )来代替普通流套接字所使用的read( )和write( )函数, 以此完成对SSL套接字的读写操作,两个新函数的原型分别如下: int SSL_read(SSL *ssl,void *buf,int num); //从SSL套接字读取数据int SSL_write(SSL *ssl,const void *buf,int num); //向SSL套接字写入数据 2.6 会话结束 当Client和Server之间的通信过程完成后, 就使用以下函数来释放前面过程中申请的SSL资源: int SSL_shutdown(SSL *ssl); //关闭SSL套接字void SSl_free(SSL *ssl); //释放SSL套接字void SSL_CTX_free(SSL_CTX *ctx); //释放SSL会话环境 3. SSL 和 TLS HTTPS 使用 SSL(Secure Socket Layer) 和 TLS(Transport LayerSecurity)这两个协议。 SSL 技术最初是由浏览器开发商网景通信公司率先倡导的, 开发过 SSL3.0之前的版本。目前主导权已转移到 IETF(Internet Engineering Task Force, Internet 工程任务组)的手中。 IETF 以 SSL3.0 为基准, 后又制定了 TLS1.0、TLS1.1 和 TLS1.2。TSL 是以SSL 为原型开发的协议, 有时会统一称该协议为 SSL。当前主流的版本是SSL3.0 和 TLS1.0。 由于 SSL1.0 协议在设计之初被发现出了问题, 就没有实际投入使用。SSL2.0 也被发现存在问题, 所以很多浏览器直接废除了该协议版本。 4. Nebula中的SSL通讯实现 Nebula框架同时支持SSL服务端应用和SSL客户端应用, 对openssl的初始化只需要初始化一次即可(SslInit()只需调用一次)。Nebula框架的SSL相关代码(包括客户端和服务端的实现)都封装在SocketChannelSslImpl这个类中。Nebula的SSL通信是基于异步非阻塞的socket通信, 并且不使用openssl的BIO(因为没有必要, 代码还更复杂了)。 SocketChannelSslImpl是SocketChannelImpl的派生类, 在SocketChannelImpl常规TCP通信之上增加了SSL通信层, 两个类的调用几乎没有差异。SocketChannelSslImpl类声明如下: class SocketChannelSslImpl : public SocketChannelImpl{public: SocketChannelSslImpl(SocketChannel* pSocketChannel, std::shared_ptr pLogger, int iFd, uint32 ulSeq, ev_tstamp dKeepAlive = 0.0); virtual ~SocketChannelSslImpl(); static int SslInit(std::shared_ptr pLogger); static int SslServerCtxCreate(std::shared_ptr pLogger); static int SslServerCertificate(std::shared_ptr pLogger, const std::string

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181101A1QA9V00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券