Linux SSLSocket C语言如何实现安全通信?

99ANYc3cd6
预计阅读时长 23 分钟
位置: 首页 C语言 正文

整体概念

在 Linux 中,普通的 TCP 套接字 (socket(), connect(), accept(), send(), recv()) 是不加密的,所有数据都以明文形式传输,SSL/TLS 协议在 TCP 之上增加了一个加密层,确保数据在传输过程中是加密的,从而防止窃听和数据篡改。

linux sslsocket c语言
(图片来源网络,侵删)

OpenSSL 是目前最流行的开源 SSL/TLS 工具库,它提供了丰富的 API 来实现加密通信。

我们将分两部分介绍:

  1. SSL/TLS 服务器端
  2. SSL/TLS 客户端

准备工作:安装 OpenSSL 开发库

在开始编码之前,确保你的系统上安装了 OpenSSL 的开发头文件和库。

# 对于 Debian/Ubuntu 系统
sudo apt-get update
sudo apt-get install libssl-dev
# 对于 CentOS/RHEL/Fedora 系统
sudo yum install openssl-devel

第一部分:SSL/TLS 服务器端

服务器端需要加载自己的证书和私钥,用于向客户端证明自己的身份。

linux sslsocket c语言
(图片来源网络,侵删)

准备证书和私钥

服务器必须有一个证书和一个与之匹配的私钥,你可以使用 OpenSSL 命令行工具来生成一个自签名的证书(仅用于测试,生产环境应使用由受信任的 CA 签发的证书)。

# 创建一个用于存放文件的目录
mkdir -p ~/ssl_server
cd ~/ssl_server
# 生成一个 2048 位的 RSA 私钥
openssl genrsa -out server.key 2048
# 使用私钥创建一个证书签名请求 (CSR)
# 你会被要求输入一些信息,如国家、城市、组织等,这些可以随意填写。
# 最重要的是 "Common Name",它应该是你的服务器域名或 IP 地址(127.0.0.1)。
openssl req -new -key server.key -out server.csr
# 使用私钥和 CSR 生成自签名证书
# -days 3650 表示证书有效期为 10 年
openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt
# 删除不再需要的 CSR 文件
rm server.csr

你的 ~/ssl_server 目录下应该有 server.keyserver.crt 两个文件。

C 语言服务器端代码 (ssl_server.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
// 初始化 OpenSSL
void init_openssl() {
    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();
}
// 清理 OpenSSL
void cleanup_openssl() {
    EVP_cleanup();
}
// 创建 SSL 上下文
SSL_CTX *create_context() {
    const SSL_METHOD *method;
    SSL_CTX *ctx;
    // 使用 SSLv23_method 允许协商使用最高版本的协议
    // 也可以使用 TLS_server_method() 强制使用 TLS
    method = SSLv23_server_method();
    ctx = SSL_CTX_new(method);
    if (!ctx) {
        perror("Unable to create SSL context");
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }
    return ctx;
}
// 配置 SSL 上下文,加载证书和私钥
void configure_context(SSL_CTX *ctx) {
    // 加载证书
    if (SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }
    // 加载私钥
    if (SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }
    // 验证私钥是否与证书匹配
    if (!SSL_CTX_check_private_key(ctx)) {
        fprintf(stderr, "Private key does not match the public certificate\n");
        exit(EXIT_FAILURE);
    }
}
int main() {
    int sock;
    struct sockaddr_in addr;
    SSL_CTX *ctx;
    SSL *ssl;
    int client;
    // 1. 初始化 OpenSSL
    init_openssl();
    // 2. 创建 SSL 上下文
    ctx = create_context();
    configure_context(ctx);
    // 3. 创建 TCP 套接字
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("Unable to create socket");
        exit(EXIT_FAILURE);
    }
    addr.sin_family = AF_INET;
    addr.sin_port = htons(4433); // 使用一个非标准端口,避免与 HTTPS 冲突
    addr.sin_addr.s_addr = INADDR_ANY;
    // 4. 绑定套接字
    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("Unable to bind");
        exit(EXIT_FAILURE);
    }
    // 5. 开始监听
    if (listen(sock, 10) < 0) {
        perror("Unable to listen");
        exit(EXIT_FAILURE);
    }
    printf("Server is listening on port 4433...\n");
    // 6. 接受客户端连接
    client = accept(sock, (struct sockaddr*)&addr, (socklen_t*)&sizeof(addr));
    if (client < 0) {
        perror("Unable to accept");
        exit(EXIT_FAILURE);
    }
    // 7. 创建新的 SSL 连接对象
    ssl = SSL_new(ctx);
    SSL_set_fd(ssl, client);
    // 8. 执行 SSL 握手
    if (SSL_accept(ssl) <= 0) {
        ERR_print_errors_fp(stderr);
    } else {
        char reply[1024] = 0;
        printf("SSL connection established.\n");
        // 9. 读取客户端数据
        int bytes = SSL_read(ssl, reply, sizeof(reply) - 1);
        if (bytes > 0) {
            reply[bytes] = '\0';
            printf("Client message: %s\n", reply);
        }
        // 10. 发送响应数据
        char *response = "Hello from SSL Server!";
        SSL_write(ssl, response, strlen(response));
    }
    // 11. 清理
    SSL_shutdown(ssl);
    SSL_free(ssl);
    close(client);
    SSL_CTX_free(ctx);
    close(sock);
    // 12. 清理 OpenSSL
    cleanup_openssl();
    return 0;
}

编译和运行服务器

将上述代码保存为 ssl_server.c,并确保它与 server.crtserver.key 在同一目录下。

# 编译
# -lssl 和 -lcrypto 是必须的,链接 OpenSSL 的 SSL 和加密库
# -g 用于调试,可以打印出 OpenSSL 的错误信息
gcc -g ssl_server.c -o ssl_server -lssl -lcrypto
# 运行
./ssl_server

现在服务器正在监听 4433 端口,等待客户端连接。

linux sslsocket c语言
(图片来源网络,侵删)

第二部分:SSL/TLS 客户端

客户端需要连接到服务器,并验证服务器的证书(可选,但推荐)。

C 语言客户端代码 (ssl_client.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
// 初始化 OpenSSL
void init_openssl() {
    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();
}
// 清理 OpenSSL
void cleanup_openssl() {
    EVP_cleanup();
}
// 创建 SSL 上下文
SSL_CTX *create_context() {
    const SSL_METHOD *method;
    SSL_CTX *ctx;
    method = SSLv23_client_method(); // 客户端使用客户端方法
    ctx = SSL_CTX_new(method);
    if (!ctx) {
        perror("Unable to create SSL context");
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }
    return ctx;
}
int main() {
    int sock;
    struct sockaddr_in addr;
    SSL *ssl;
    SSL_CTX *ctx;
    // 1. 初始化 OpenSSL
    init_openssl();
    // 2. 创建 SSL 上下文
    ctx = create_context();
    // 3. 创建 TCP 套接字
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("Unable to create socket");
        exit(EXIT_FAILURE);
    }
    addr.sin_family = AF_INET;
    addr.sin_port = htons(4433);
    // 将 IP 地址转换为网络字节序
    if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) <= 0) {
        perror("
-- 展开阅读全文 --
头像
会员ID如何显示地址年龄dede?
« 上一篇 2025-11-27
C语言switch中selitems是什么?
下一篇 » 2025-11-27

相关文章

取消
微信二维码
支付宝二维码

目录[+]