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

(图片来源网络,侵删)
OpenSSL 是目前最流行的开源 SSL/TLS 工具库,它提供了丰富的 API 来实现加密通信。
我们将分两部分介绍:
- SSL/TLS 服务器端
- 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 服务器端
服务器端需要加载自己的证书和私钥,用于向客户端证明自己的身份。

(图片来源网络,侵删)
准备证书和私钥
服务器必须有一个证书和一个与之匹配的私钥,你可以使用 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.key 和 server.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.crt 和 server.key 在同一目录下。
# 编译 # -lssl 和 -lcrypto 是必须的,链接 OpenSSL 的 SSL 和加密库 # -g 用于调试,可以打印出 OpenSSL 的错误信息 gcc -g ssl_server.c -o ssl_server -lssl -lcrypto # 运行 ./ssl_server
现在服务器正在监听 4433 端口,等待客户端连接。

(图片来源网络,侵删)
第二部分: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("
