本文詳細記錄從零開始:安裝 OpenSSL、建立私有 CA、簽發伺服器/客戶端憑證、編寫雙向 TLS 認證的 Client/Server 程式碼,至驗證通訊加密效果的完整流程。
perl -v。openssl-0.9.8.tar.gz) 解壓至 D:\openssl-0.9.8。perl Configure VC-WIN32
ms\do_ms
nmake -f ms\ntdll.mak
C:\caroot,將 openssl.cnf 複製至此。PATH 加入 D:\openssl-0.9.8\out32dll;VC++ 專案 Include 加入 D:\openssl-0.9.8\include,Library 加入 D:\openssl-0.9.8\out32dll。在 C:\caroot 依序執行:
openssl genrsa -out private\ca.key -rand private\.rnd -des 2048
openssl req -new -x509 -days 3650 -key private\ca.key -out private\ca.crt -config openssl.cnf
openssl x509 -in private\ca.crt -noout -text
openssl genrsa -out private\server.key 1024
openssl req -new -key private\server.key -out newcerts\server.csr -config openssl.cnf
openssl ca -in newcerts\server.csr -cert private\ca.crt -keyfile private\ca.key -config openssl.cnf -policy policy_anything -out certs\server.crt
openssl x509 -in certs\server.crt -noout -text
openssl genrsa -out private\client.key 1024
openssl req -new -key private\client.key -out newcerts\client.csr -config openssl.cnf
openssl ca -in newcerts\client.csr -cert private\ca.crt -keyfile private\ca.key -config openssl.cnf -policy policy_anything -out certs\client.crt
openssl x509 -in certs\client.crt -noout -text
檔案對照:
ca.crt:CA 根憑證 (雙方皆需信任)server.crt + server.key:伺服器憑證與私鑰client.crt + client.key:客戶端憑證與私鑰// 初始化 SSL 庫
SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_ssl_algorithms();
SSL_CTX* ctx = SSL_CTX_new(TLSv1_server_method());
// 載入 CA 憑證 (驗證客戶端)
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_load_verify_locations(ctx, "ca.crt", NULL);
// 載入伺服器憑證與私鑰
SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM);
SSL_CTX_check_private_key(ctx); // 驗證匹配
// 設定加密套件
SSL_CTX_set_cipher_list(ctx, "RC4-MD5");
// TCP Socket 建立、bind、listen、accept...
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
// ... bind, listen, accept -> client_sock
// SSL 握手
SSL* ssl = SSL_new(ctx);
SSL_set_fd(ssl, client_sock);
if (SSL_accept(ssl) <= 0) { /* 錯誤處理 */ }
// 驗證客戶端憑證
X509* client_cert = SSL_get_peer_certificate(ssl);
if (client_cert) {
char* subject = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0);
char* issuer = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0);
printf("Client Subject: %s\nIssuer: %s\n", subject, issuer);
OPENSSL_free(subject); OPENSSL_free(issuer); X509_free(client_cert);
}
// 加密通訊
SSL_write(ssl, "Welcome!", 8);
char buf[1024];
int len = SSL_read(ssl, buf, sizeof(buf));
buf[len] = 0; printf("Recv: %s\n", buf);
// 清理
SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ctx); closesocket(sock);
SSL_CTX* ctx = SSL_CTX_new(TLSv1_client_method());
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_load_verify_locations(ctx, "ca.crt", NULL);
// 載入客戶端憑證與私鑰 (雙向認證必須)
SSL_CTX_use_certificate_file(ctx, "client.crt", SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(ctx, "client.key", SSL_FILETYPE_PEM);
SSL_CTX_check_private_key(ctx);
// TCP 連線
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
connect(sock, (sockaddr*)&server_addr, sizeof(server_addr));
// SSL 握手
SSL* ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
if (SSL_connect(ssl) <= 0) { /* 錯誤處理 */ }
printf("Connected securely!\n");
// 收發資料...
SSL_read(ssl, buf, sizeof(buf));
SSL_write(ssl, "Hello Server", 12);
// 清理同上
SSL_VERIFY_PEER 並載入 CA;Client 同樣需載入自身憑證/私鑰,Server 才能驗證 Client 身分。RAND_seed 必須提供足夠熵,否則握手卡死。.crt/.key 放在工作目錄,或改用絕對路徑。sa_cli.sin_addr.s_addr 為網路位元組序,需 inet_ntoa 轉換顯示。ERR_print_errors_fp(stderr) 診斷。啟動 Server,再啟動 Client,雙向 TLS 1.0 握手成功,建立加密通道。Server 端顯示 Client 憑證 Subject/Issuer,雙方可安全收發訊息。
完整程式碼與憑證產生腳本可封裝為專案模板,快速應用於需求加密通訊的嵌入式、物聯網、企業內部系統開發。