前面說了一大堆使用OpenSSL進(jìn)行加密操作的東西,今天開始說說上層的
SSL通信了,從基本的程序結(jié)構(gòu)談起。SSL的理論就不說了,自己找RFC的
TLS文檔看去。SSL通信程序的基本結(jié)構(gòu)和普通的客戶機(jī)/服務(wù)器模式相差不大。今天比較偷懶,把錯(cuò)誤處理一并省略^_^
先看客戶端。
#include <openssl/ssl.h>#include <openssl/x509.h>#include <stdio.h>#include <sys/socket.h>#include <arpa/inet.h>int main(int argc, char** argv){char buffer[256] = {0};int socket_client = 0;struct sockaddr_in socket_address;SSL* ssl = NULL;SSL_CTX* ssl_ctx = NULL;SSL_METHOD* ssl_method = NULL;X509* server_cert = NULL;// 初始化SSL庫SSL_library_init();
// 設(shè)置客戶端使用的SSL版本ssl_method = SSLv3_client_method();
// 創(chuàng)建SSL上下文環(huán)境
// 每個(gè)進(jìn)程只需維護(hù)一個(gè)SSL_CTX結(jié)構(gòu)體ssl_ctx = SSL_CTX_new(ssl_method);// 建立TCP連接// 這一部分全部使用標(biāo)準(zhǔn)socket函數(shù)socket_client = socket(AF_INET, SOCK_STREAM, 0);socket_address.sin_family = AF_INET;socket_address.sin_addr.s_addr = inet_addr("127.0.0.1");socket_address.sin_port = htons(1234);connect(socket_client, (struct sockaddr*)&socket_address, sizeof(socket_address));// 創(chuàng)建維護(hù)當(dāng)前連接信息的SSL結(jié)構(gòu)體ssl = SSL_new(ssl_ctx);// 將SSL綁定到套接字上SSL_set_fd(ssl, socket_client);// 建立SSL連接SSL_connect(ssl);
// 獲取和釋放服務(wù)器端證書
// 關(guān)于證書的驗(yàn)證放在下一次討論
server_cert = SSL_get_peer_certificate(ssl);X509_free(server_cert);// 與服務(wù)器端進(jìn)行通信SSL_write(ssl, "hello, world!", sizeof("hello, world!"));SSL_read(ssl, buffer, 255);printf("received: %s\n", buffer);// 斷開SSL連接SSL_shutdown(ssl);// 釋放當(dāng)前連接SSL結(jié)構(gòu)體SSL_free(ssl);// 斷開TCP連接close(socket_client);// 釋放SSL上下文SSL_CTX_free(ssl_ctx);return 0;}再看服務(wù)器端。
#include <openssl/ssl.h>#include <openssl/x509.h>#include <stdio.h>#include <sys/socket.h>#include <arpa/inet.h>int main(){int socket_server = 0;int socket_client = 0;struct sockaddr_in sa_serv, sa_cli;char buffer[256] = {0};SSL* ssl = NULL;SSL_CTX* ssl_ctx = NULL;SSL_METHOD* ssl_method = NULL;X509* client_cert = NULL;// 初始化SSL庫SSL_library_init();
// 設(shè)置客戶端使用的SSL版本ssl_method = SSLv23_server_method();// 創(chuàng)建SSL上下文環(huán)境
// 每個(gè)進(jìn)程只需維護(hù)一個(gè)SSL_CTX結(jié)構(gòu)體ssl_ctx = SSL_CTX_new(ssl_method);
// 讀取證書文件
SSL_CTX_use_certificate_file(ssl_ctx, "ca-cert.pem", SSL_FILETYPE_PEM);// 讀取密鑰文件SSL_CTX_use_PrivateKey_file(ssl_ctx, "ca-cert.pem", SSL_FILETYPE_PEM);// 驗(yàn)證密鑰是否與證書一致SSL_CTX_check_private_key(ssl_ctx);// 建立TCP服務(wù)器端、開始監(jiān)聽并接受客戶端連接請(qǐng)求// 這一部分全部使用標(biāo)準(zhǔn)socket函數(shù)socket_server = socket(AF_INET, SOCK_STREAM, 0);sa_serv.sin_family = AF_INET;sa_serv.sin_addr.s_addr = INADDR_ANY;sa_serv.sin_port = htons(1234);bind(socket_server, (struct sockaddr*)&sa_serv, sizeof(sa_serv));listen(socket_server, 10);while (socket_client = accept(socket_server, NULL, NULL)){// 創(chuàng)建當(dāng)前連接的SSL結(jié)構(gòu)體ssl = SSL_new(ssl_ctx);// 將SSL綁定到套接字上SSL_set_fd(ssl, socket_client);// 接受SSL連接SSL_accept(ssl);// 獲取和釋放客戶端證書
// 這一步是可選的
client_cert = SSL_get_peer_certificate(ssl);X509_free(server_cert);// 與客戶端進(jìn)行通信SSL_read(ssl, buffer, 255);printf("received: %s\n", buffer);SSL_write(ssl, "reply", 5);// 斷開SSL連接SSL_shutdown(ssl);// 斷開與客戶端的TCP連接close(socket_client);// 釋放當(dāng)前連接SSL結(jié)構(gòu)體SSL_free(ssl);}// 停止TCP監(jiān)聽close(socket_server);// 釋放SSL上下文SSL_CTX_free(ssl_ctx);
return 0;}