SSL通讯模型为标准的C/S结构,除了在TCP层之上进行传输之外,与一般的通讯没有什么明显的区别。在这里,我们主要介绍如何使用OpenSSL进行安全通讯的程序设计。关于OpenSSL的一些详细的信息请参考OpenSSL的官方主页http://www.openssl.org。
在使用OpenSSL前,必须先对OpenSSL进行初始化,以下的三个函数任选其一:
SSL_library_init(void);
OpenSSL_add_ssl_algorithms();
SSLeay_add_ssl_algorithms();
事实上后面的两个函数只是第一个函数的宏。
如果要使用OpenSSL的出错信息,使用SSL_load_error_strings(void)进行错误信息的初始化。以后可以使用void ERR_print_errors_fp(FILE* fp)打印SSL的错误信息,也可以使用文献[2]中的printf("Error: %s/n", ERR_reason_error_string(ERR_get_error()))方法打印信息。
一次SSL连接会话一般要先申请一个SSL环境,基本的过程是:
1. SSL_METHOD* meth=TLSv1_client_method();创建本次会话连接所使用的协议,如果是客户端可以使用
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_METHOD* TLSv1_server_method(void);
SSL_METHOD* SSLv2_server_method(void);
SSL_METHOD* SSLv3_server_method(void);
SSL_METHOD* SSLv23_server_method(void);
需要注意的是客户端和服务器需要使用相同的协议。
2. 申请SSL会话的环境CTX,使用不同的协议进行会话,其环境也是不同的。申请SSL会话环境的OpenSSL函数是
SSL_CTX* SSL_CTX_new(SSL_METHOD*);
参数就是前面我们申请的SSL通讯方式。返回当前的SSL连接环境的指针。然后根据自己的需要设置CTX的属性,典型的是设置SSL握手阶段证书的验证方式和加载自己的证书。
void SSL_CTX_set_verify(SSL_CTX*, int, int* (int, X509_STORE_CTX*));
设置证书验证的方式。第一个参数是当前的CTX指针,第二个是验证方式,如果是要验证对方的话,就使用SSL_VERIFY_PEER。不需要的话,使用SSL_VERIFY_NONE.一般情况下,客户端需要验证对方,而服务器不需要。第三个参数是处理验证的回调函数,如果没有特殊的需要,使用空指针就可以了。
void SSL_CTX_load_verify_locations(SSL_CTX*, const char*, const char*);
加载证书,第一个参数同上,参数二是证书文件的名称,参数三是证书文件的路径;
int SSL_CTX_use_certificate_file(SSL_CTX* ctx, const char* file,int type);
加载本地的证书;type指明证书文件的结构类型;失败返回-1
int SSL_CTX_use_PrivateKey_file(SSL_CTX* ctx,const char* file,int type);
加载自己的私钥;type参数指明私钥文件的结构类型;失败返回-1,加载了证书和文件之后,就可以验证私钥和证书是否相符:
int SSL_CTX_check_private_key(SSL_CTX*);
3. 既然SSL使用TCP协议,当然需要把SSLattach到已经连接的套接字上了:
SSL* SSL_new(SSL_CTX*);申请一个SSL套节字;
int SSL_set_rfd(SSL*, int);绑定只读套接字
int SSL_set_wfd(SSL*, int);绑定只写套接字
int SSL_set_fd(SSL*, int);绑定读写套接字
绑定成功返回1,失败返回0;
4. 接下来就是SSL握手的动作了
int SSL_connect(SSL*);
失败返回-1
5. 握手成功之后,就可以进行通讯了,使用SSL_read和SS_write读写SSL套接字代替传统的read、write:
int SSL_read(SSL* ssl, char* buf, int num);
int SSL_write(SSL* ssl, char* buf, int num);
如果是服务器,则使用SSL_accept代替传统的accept调用
int SSL_accept(SSL* ssl);
6. 通讯结束,需要释放前面申请的SSL资源
int SSL_shutdown(SSL* ssl);关闭SSL套接字;
void SSL_free(ssl);释放SSL套接字;
void SSL_CTX_free(ctx);释放SSL环境;