一直以来都是普通的socket read/write,现在终于有基于SSL通道的项目了。所以简单记录了一下OpenSSL的调用流程,便于快速入门。
本文地址:https://segmentfault.com/a/1190000005933931/
Reference
ssl的消息读写以及和tcp语义的异同——(这篇文章很好)
——吐槽一下,这篇文章原作者都说了未经允许不得转载了,结果网上还是一堆抄的,唉,版权意识药丸药丸
OpenSSL流程和函数介绍
初始化
int SSL_Library_init (void);
选择会话协议和创建会话环境
目前支持的会话协议包括:TLSv1.0, SSLv2, SSLv3, SSLv2/v3。
OpenSSL中一个会话的环境称为“CTX”,申请CTX的函数是:
SSL_CTX *SSL_CTX_new (SSL_METHOD *method);
其中method就是会话协议,比如SSLv2/v3的client,则传入函数调用的返回值:
const SSL_METHOD *SSLv23_client_method (void);
接下来是设置CTX的属性,比如制定证书验证方式:
int SSL_CTX_set_verify (SSL_CTX *ctx, int mode, int (*verify_callback), int (X509_STROE_CTX *));
加载CA证书:
SSL_CTX_load_varify_location (SSL_CTX *ctx, const char *Cafile, const char *Capath);
加载用户私钥:
SSL_CTX_use_Private_file (SSL_CTX *ctx, const char *file, int type);
加载用户证书:
SSL_CTX_use_certificate_file (SSL_CTX *ctx, const char *file, int type);
验证私钥和证书是否相等
int SSL_CTX_check_private_key (SSL_CTX *ctx);
建立SSL套接字
SSL *SSL_new (SSL_CTX *ctx);
使用socket绑定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); // 只写
注意:上述三个函数成功时返回TRUE,失败时返回FALSE
完成SSL握手
int SSL_connect (SSL *ssl); // 用于client int SSL_accept (SSL *ssl); // 用于client
从SSL套接字中提取对方的证书信息
X509 *SSL_get_peer_certificate (SSL *ssl);
获取证书所有者的名字:
X509_NAME *X509_get_subject_name (X509 *a);
数据传输
int SSL_read (SSL *ssl, void *buf, int num); int SSL_write (SSL *ssl, const void *buf, int num);
结束SSL通信
关闭SSL套接字
int SSL_shitdown (SSL *ssl);
释放SSL套接字
void SSL_free (SSL *ssl);
释放SSL会话
void SSL_CTX_free (SSL_CTX *ctx);
OpenSSL应用编程框架
下面大致的伪代码说明了调用SSL的一整个流程
Client端
ctx = SSL_CTX_new (SSLv23_client_method()); ssl = SSL_new (ctx); fd = socket (); connect (); SSL_set_fd (ssl, fd); SSL_connect (ssl); SSL_write ();
Server端
ctx = SSL_CTX_new (SSLv23_server_method()); ssl = SSL_new (ctx); fd = socket (); bind (); listen (); accept (); SSL_set_fd (ssl, fd); SSL_accept (ssl); SSL_read ();
注意
OpenSSL库中,各个函数的返回值的格式并不统一(有些用0表示失败,有些用0表示成功),请注意区分
用在OpenSSL的fd不能设置为nonblock,否则在SSL_connect时会失败——感觉这一点限制了OpenSSL与除了libevent之外其他异步I/O库的适配
关于nonblock的问题,感谢@程序猿小何的补充:可以在完成了SSL_connect/accept之后,将fd设置为nonblock