linuxssl传输加密,使⽤SSL进⾏⽹络加密传输
⼀、概述
在 Acl 的⽹络通信模块中,为了⽀持安全⽹络传输,引⼊了第三⽅ SSL 库,当前⽀持 Polarssl 及其升级版 MbedTLS,Acl 库中通过抽象与封装,⼤⼤简化了 SSL 的使⽤过程(现在开源的 SSL 库使⽤确实太复杂了),以下是在 Acl 库中使⽤ SSL 的特点:
为了不给不使⽤ SSL 功能的⽤户造成编译上的障碍,Acl 库采⽤动态加载 SSL 动态库⽅式,这样在连接时就不必提供 SSL 库(当然,通过设置编译开关,也允许⽤户采⽤静态连接 SSL 库的⽅式);
在 Acl SSL 模块中,分为全局配置类和 IO 通信类,配置类对象只需在程序启动时进⾏创建与初始化,且整个进程中按单例⽅式使⽤;IO 通信类对象与每⼀个 TCP 连接所对应的 socket 进⾏绑定,TCP 连接建⽴时进⾏初始化,进⾏ SSL 握⼿并接管 IO 过程;
Acl SSL 模块⽀持服务端及客户端⽅式,在服务端模块时需要加载数字证书及证书私钥;
Acl SSL 模块⽀持阻塞与⾮阻塞两种通信⽅式,阻塞⽅式还可以⽤在 Acl 协程通信中;
Acl SSL 模块已经应⽤在 Acl HTTP 通信中,从⽽⽅便⽤户编写⽀持 HTTPS/Websocket 的客户端或服务端程序;同时,Acl SSL 模块也给 Acl Redis 模块提供了安全通信功能;
Acl SSL 模块是线程安全的,虽然官⽅提供的 Mbedtls 库中增加⽀持线程安全的编译选项,但其默认情况下却是将此功能关闭的(这真是⼀个坑⼈的地⽅),当你需要打开线程⽀持功能时还必须得要提供线程锁功能(通过函数回调注册⾃⼰的线程锁,好在 Acl 库中有跨平台的线程模块),这应该是 Mbedtls 默认情况下不打开线程⽀持的原因;
在 config.h 中打开了线程安全的编译选项,同时添加了⽤于线程安全的互斥锁头⽂件:threading_alt.h;
Mbedtls 库编译后⽣成了三个库⽂件:libmbedcrypto/libmbedx509/libmbedtls,⽽原来 Polarssl 只⽣成⼀个库⽂件,所以为了⽤户使⽤⽅便,修改了 ⽂件,可以将这三个库⽂件合并成⼀个;
增加了 visualc/VC2012(⽽官⽅仅提供了 VS2010),这样在 Windows 平台下可以使⽤ VS 2012 来编译⽣成 mbedtls 库。
⼆、API 接⼝说明
为了⽀持更加通⽤的 SSL 接⼝,在 Acl SSL 模块中定义了两个基础类:sslbase_conf 和 sslbase_io,其中 ssbase_conf 类对象可以⽤做全局单⼀实例,ssbase_io 类对象⽤来给每⼀个 TCP socket 对象提供安全 IO 通信功能。
2.1、sslbase_conf 类
在 ssbase_conf 类中定义了纯虚⽅法:open,⽤来创建 SSL IO 通信类对象,在当前所⽀持 Polarssl 和 MbedTSL 中的配置类中(分别为:acl::polarssl_conf 和 acl::mbedtls_conf)均实现了该⽅法。下⾯是 open ⽅法的具体说明:
/**
* 纯虚⽅法,创建 SSL IO 对象
* @param nblock {bool} 是否为⾮阻塞模式
* @return {sslbase_io*}
*/
virtual sslbase_io* open(bool nblock) = 0;
在客户端或服务端创建 SSL IO 对象(即:sslbase_io 对象)时调⽤,被⽤来与 TCP socket 进⾏绑定。下⾯是绑定过程:
bool bind_ssl_io(acl::socket_stream& conn, acl::sslbase_conf& ssl_conf)
{
// 创建⼀个阻塞式 SSL IO 对象
bool non_block = false;
acl::sslbase_io* ssl = ssl_conf.open(non_block);
// 将 SSL IO 对象与 TCP 连接流对象进⾏绑定,在绑定过程中会进⾏ SSL 握⼿,
// 如果 SSL 握⼿失败,则返回该 SSL IO 对象,返回 NULL 表⽰绑定成功。
if (conn.setup_hook(ssl) == ssl) {
return false;
} else {
return true;
}
}
其中 acl::sslbase_io 的⽗类为 acl::stream_hook,在acl::stream 流基础类中提供了⽅法setup_hook⽤来注册外部 IO 过程,其中的参数类型为stream_hook ,通过绑定外部 IO 过程,将 SSL IO 过程与 acl 中的流处理 IO 过程进⾏绑定,从⽽使 acl 的 IO 流过程具备了 SSL 安全传输能⼒。
下⾯的⼏个接⼝⽤在服务端进⾏证书及私钥加载过程:
/**
* 添加⼀个服务端/客户端⾃⼰的证书,可以多次调⽤本⽅法加载多个证书
* @param crt_file {const char*} 证书⽂件全路径,⾮空
* @return {bool} 添加证书是否成功
*/
virtual bool add_cert(const char* crt_file);
/**
* 添加服务端/客户端的密钥(每个配置实例只需调⽤⼀次本⽅法)
* @param key_file {const char*} 密钥⽂件全路径,⾮空
* @param key_pass {const char*} 密钥⽂件的密码,没有密钥密码可写 NULL
* @return {bool} 设置是否成功
*/
virtual bool set_key(const char* key_file, const char* key_pass = NULL);
/**
* 当为服务端模式时是否启⽤会话缓存功能,有助于提⾼ SSL 握⼿效率
* @param on {bool} 是否在服务端启⽤会话缓存⽅式
* 注:该函数仅对服务端模式有效
*/
virtual void enable_cache(bool on);
2.2、sslbase_io 类
acl::sslbase_io 类对象与每⼀个 TCP 连接对象 acl::socket_stream 进⾏绑定,使 acl::socket_stream 具备了进⾏ SSL 安全传输的能⼒,在 acl::sslbase_io类中声明了纯虚⽅法handshake,这使之成为纯虚类;另外,acl::sslbase_io 虽然继承于acl::stream_hook类,但并没有实现 acl::stream_hook 中规定的四个纯虚⽅法:open,on_close,read,send,这⼏个虚⽅法也需要 acl::sslbase_io的⼦类来实现,⽬前acl::sslbase_io有两个⼦类acl::polarssl_io及acl::mbedtls_io 分别⽤来⽀持 Polarssl 及 MbedTLS。
下⾯是这⼏个纯虚⽅法的声明:
/**
* ssl 握⼿纯虚⽅法(属于 sslbase_io 类)
* @return {bool} 返回 true 表⽰ SSL 握⼿成功,否则表⽰失败
*/
virtual bool handshake(void) = 0;
下⾯⼏个虚⽅法声明于 acl::stream_hook 类中:
/**
* 读数据接⼝
* @param buf {void*} 读缓冲区地址,读到的数据将存放在该缓冲区中
* @param len {size_t} buf 缓冲区⼤⼩
* @return {int} 读到字节数,当返回值 < 0 时表⽰出错
*/
virtual int read(void* buf, size_t len) = 0;
/**
* 发送数据接⼝
* @param buf {const void*} 发送缓冲区地址
* @param len {size_t} buf 缓冲区中数据的长度(必须 > 0)
* @return {int} 写⼊的数据长度,返回值
*/
virtual int send(const void* buf, size_t len) = 0;
/**
* 在 stream/aio_stream 的 setup_hook 内部将会调⽤ stream_hook::open
* 过程,以便于⼦类对象⽤来初始化⼀些数据及会话
* @param s {ACL_VSTREAM*} 在 setup_hook 内部调⽤该⽅法将创建的流对象
* 作为参数传⼊
* @return {bool} 如果⼦类实例返回 false,则 setup_hook 调⽤失败且会恢复原样
*/
virtual bool open(ACL_VSTREAM* s) = 0;
/**
* 当 stream/aio_stream 流对象关闭前将会回调该函数以便于⼦类实例做⼀些善后⼯作* @param alive {bool} 该连接是否依然正常
* @return {bool}
*/
virtual bool on_close(bool alive) { (void) alive; return true; }
/**
* 当 stream/aio_stream 对象需要释放 stream_hook ⼦类对象时调⽤此⽅法*/
virtual void destroy(void) {}
以上⼏个虚⽅法均可以在 acl::polarssl_io 及 acl::mbedtls_io 中看到被实现。
三、编程⽰例
2.1、服务器模式(使⽤ MbedTLS)
#include
#include "lib_acl.h"
#include "acl_cpp/lib_acl.hpp"
class echo_thread : public acl::thread {
public:
echo_thread(acl::sslbase_conf& ssl_conf, acl::socket_stream* conn)
: ssl_conf_(ssl_conf), conn_(conn) {}
private:
linux安装redis服务acl::sslbase_conf& ssl_conf_;
acl::socket_stream* conn_;
~echo_thread(void) { delete conn_; }
// @override
void* run(void) {
conn_->set_rw_timeout(60);
// 给 socket 安装 SSL IO 过程
if (!setup_ssl()) {
return NULL;
}
do_echo();
delete this;
return NULL;
}
bool setup_ssl(void) {
bool non_block = false;
acl::sslbase_io* ssl = ssl_conf_.open(non_block);
// 对于使⽤ SSL ⽅式的流对象,需要将 SSL IO 流对象注册⾄⽹络
// 连接流对象中,即⽤ ssl io 替换 stream 中默认的底层 IO 过程
if (conn_->setup_hook(ssl) == ssl) {
printf("setup ssl IO hook error!\r\n");
ssl->destroy();
return false;
}
return true;
}
void do_echo(void) {
char buf[4096];
while (true) {
int ret = conn_->read(buf, sizeof(buf), false);
if (ret == -1) {
break;
}
if (conn_->write(buf, ret) == -1) {
break;
}
}
}
};
static void start_server(const acl::string addr, acl::sslbase_conf& ssl_conf) { acl::server_socket ss;
if (!ss.open(addr)) {
printf("listen %s error %s\r\n", addr.c_str(), acl::last_serror());
return;
}
while (true) {
acl::socket_stream* conn = ss.accept();
if (conn == NULL) {
printf("accept error %s\r\n", acl::last_serror());
break;
}
acl::thread* thr = new echo_thread(ssl_conf, conn);
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论