首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

OpenSSL實戰:從零構建安全的服務端與客戶端通信程序

     編輯:關於C語言

前言

近期深入研究OpenSSL編程,整理出一套完整的服務端與客戶端實作範例。核心步驟包括:使用同一CA簽發伺服器憑證(server.cer,須標註可用於服務端)與客戶端憑證(client.cer),雙向驗證確保通信安全。

服務端完整代碼

#include "openssl/bio.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#include <cutil.h>

#define EXIT_IF_TRUE(x) if (x) \
    do { \
        fprintf(stderr, "Check '%s' is true\n", #x); \
        ERR_print_errors_fp(stderr); \
        exit(2); \
    } while(0)

int main(int argc, char **argv) {
    SSL_CTX *ctx;
    SSL *ssl;
    X509 *client_cert;
    char szBuffer[1024];
    int nLen;
    struct sockaddr_in addr;
    int len;
    int nListenFd, nAcceptFd;

    // 初始化OpenSSL
    cutil_init();
    cutil_log_set_level(LOG_ALL);
    cutil_log_set_stderr(1);
    SSLeay_add_ssl_algorithms();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    ERR_load_BIO_strings();

    // 使用SSL v2/v3協議
    EXIT_IF_TRUE((ctx = SSL_CTX_new(SSLv23_method())) == NULL);

    // 要求驗證對方憑證
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);

    // 載入CA憑證、自身憑證與私鑰
    EXIT_IF_TRUE(!SSL_CTX_load_verify_locations(ctx, "cacert.cer", NULL));
    EXIT_IF_TRUE(SSL_CTX_use_certificate_file(ctx, "server.cer", SSL_FILETYPE_PEM) <= 0);
    EXIT_IF_TRUE(SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM) <= 0);
    EXIT_IF_TRUE(!SSL_CTX_check_private_key(ctx));

    // 建立Socket監聽
    nListenFd = cutil_socket_new(SOCK_STREAM);
    cutil_socket_bind(nListenFd, NULL, 8812, 1);
    memset(&addr, 0, sizeof(addr));
    len = sizeof(addr);
    nAcceptFd = accept(nListenFd, (struct sockaddr *)&addr, (size_t *)&len);
    cutil_log_debug("Accept a connect from [%s:%d]\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));

    // 將連接綁定到SSL
    EXIT_IF_TRUE((ssl = SSL_new(ctx)) == NULL);
    SSL_set_fd(ssl, nAcceptFd);
    EXIT_IF_TRUE(SSL_accept(ssl) != 1);

    // 數據收發
    memset(szBuffer, 0, sizeof(szBuffer));
    nLen = SSL_read(ssl, szBuffer, sizeof(szBuffer));
    fprintf(stderr, "Get Len %d %s ok\n", nLen, szBuffer);
    strcat(szBuffer, " this is from server");
    SSL_write(ssl, szBuffer, strlen(szBuffer));

    // 釋放資源
    SSL_free(ssl);
    SSL_CTX_free(ctx);
    close(nAcceptFd);
    return 0;
}

客戶端完整代碼

#include "openssl/bio.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#include <cutil.h>

#define EXIT_IF_TRUE(x) if (x) \
    do { \
        fprintf(stderr, "Check '%s' is true\n", #x); \
        ERR_print_errors_fp(stderr); \
        exit(2); \
    } while(0)

int main(int argc, char **argv) {
    SSL_METHOD *meth;
    SSL_CTX *ctx;
    SSL *ssl;
    int nFd, nLen;
    char szBuffer[1024];

    // 初始化OpenSSL
    cutil_init();
    cutil_log_set_level(LOG_ALL);
    cutil_log_set_stderr(1);
    SSLeay_add_ssl_algorithms();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    ERR_load_BIO_strings();

    // 使用SSL v2/v3協議
    EXIT_IF_TRUE((ctx = SSL_CTX_new(SSLv23_method())) == NULL);

    // 要求驗證對方憑證
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);

    // 載入CA憑證、客戶端憑證與私鑰
    EXIT_IF_TRUE(!SSL_CTX_load_verify_locations(ctx, "cacert.cer", NULL));
    EXIT_IF_TRUE(SSL_CTX_use_certificate_file(ctx, "client.cer", SSL_FILETYPE_PEM) <= 0);
    EXIT_IF_TRUE(SSL_CTX_use_PrivateKey_file(ctx, "client.key", SSL_FILETYPE_PEM) <= 0);
    EXIT_IF_TRUE(!SSL_CTX_check_private_key(ctx));

    // 建立連接
    nFd = cutil_socket_new(SOCK_STREAM);
    if (cutil_socket_connect(nFd, "127.0.0.1", 8812, 30) < 0) {
        cutil_log_error("連接服務器失敗\n");
        return -1;
    }

    // 將連接綁定到SSL並發起握手
    EXIT_IF_TRUE((ssl = SSL_new(ctx)) == NULL);
    SSL_set_fd(ssl, nFd);
    EXIT_IF_TRUE(SSL_connect(ssl) != 1);

    // 發送數據並接收回應
    sprintf(szBuffer, "this is from client %d", getpid());
    SSL_write(ssl, szBuffer, strlen(szBuffer));

    memset(szBuffer, 0, sizeof(szBuffer));
    nLen = SSL_read(ssl, szBuffer, sizeof(szBuffer));
    fprintf(stderr, "Get Len %d %s ok\n", nLen, szBuffer);

    // 釋放資源
    SSL_free(ssl);
    SSL_CTX_free(ctx);
    close(nFd);
    return 0;
}

關鍵點解析

  • 雙向認證:雙方均載入CA憑證並啟用SSL_VERIFY_PEER,確保通信雙方身份可信。
  • 錯誤處理:宏EXIT_IF_TRUE統一處理OpenSSL調用失敗,輸出詳細錯誤堆棧。
  • 資源管理:嚴格遵循SSL_free、SSL_CTX_free、close順序釋放資源,避免洩漏。

此範例可作為基於OpenSSL安全通信開發的基礎模板。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved