当前位置: 首页 > article >正文

使用 OpenSSL 实现 SSL/TLS 握手的流程和 Demo 示例

使用 OpenSSL 实现 SSL/TLS 握手的流程和 Demo 示例

      • 1. 什么是 SSL/TLS 握手?
      • 2. SSL/TLS 握手流程解析
        • 2.1 客户端 Hello
        • 2.2 服务端 Hello
        • 2.3 服务端证书
        • 2.4 客户端密钥交换
        • 2.5 会话密钥生成
        • 2.6 握手完成
      • 3. 使用 OpenSSL 实现 SSL/TLS Demo
        • 3.1 环境准备
        • 3.2 服务端代码
        • 3.3 客户端代码
        • 3.4 编译和运行

1. 什么是 SSL/TLS 握手?

SSL/TLS 是保护网络通信的基础协议,主要用于确保数据传输的安全性、完整性和机密性。在 SSL/TLS 握手过程中,客户端和服务端协商会话密钥和加密参数,以确保后续通信的安全性。


2. SSL/TLS 握手流程解析

以下是一个典型的 SSL/TLS 握手流程:

2.1 客户端 Hello
  • 步骤:客户端发送 ClientHello 消息。
  • 内容
    • 支持的协议版本(如 TLS 1.2/1.3)。
    • 支持的加密套件(如 AES、RSA 等)。
    • 一个随机数(用于生成会话密钥)。
    • 会话 ID(可选)。
2.2 服务端 Hello
  • 步骤:服务端响应 ServerHello 消息。
  • 内容
    • 协商的协议版本。
    • 选择的加密套件。
    • 服务端生成的随机数。
    • 服务端的数字证书。
2.3 服务端证书
  • 步骤:服务端发送数字证书。
  • 内容:证书包含服务端的公钥、服务端域名、证书颁发机构签名等。
2.4 客户端密钥交换
  • 步骤
    • 客户端生成一个随机密钥(称为 Pre-Master Secret)。
    • 用服务端的公钥加密 Pre-Master Secret,并发送给服务端。
2.5 会话密钥生成
  • 步骤:客户端和服务端分别基于双方的随机数和 Pre-Master Secret,通过密钥派生函数生成对称加密密钥。
2.6 握手完成
  • 双方使用对称密钥验证握手消息的完整性,确保协商过程未被篡改。
  • 此后,双方可以基于对称密钥进行安全通信。

3. 使用 OpenSSL 实现 SSL/TLS Demo

下面的示例展示了如何使用 OpenSSL 编写简单的 SSL/TLS 服务端和客户端。

3.1 环境准备
  • 安装 OpenSSL:

    sudo apt-get install openssl libssl-dev
    
  • 生成服务端证书和私钥:

    openssl req -x509 -newkey rsa:2048 -nodes -keyout server.key -out server.crt -days 365
    
3.2 服务端代码

用 C 语言编写一个简单的 OpenSSL 服务端。

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

#define PORT 4443

void initialize_openssl() {
    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();
}

void cleanup_openssl() {
    EVP_cleanup();
}

SSL_CTX *create_context() {
    const SSL_METHOD *method = SSLv23_server_method();
    SSL_CTX *ctx = SSL_CTX_new(method);

    if (!ctx) {
        perror("Unable to create SSL context");
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }

    return ctx;
}

void configure_context(SSL_CTX *ctx) {
    if (SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM) <= 0 ||
        SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }
}

int main() {
    int sock;
    struct sockaddr_in addr;

    initialize_openssl();
    SSL_CTX *ctx = create_context();
    configure_context(ctx);

    sock = socket(AF_INET, SOCK_STREAM, 0);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = INADDR_ANY;

    bind(sock, (struct sockaddr *)&addr, sizeof(addr));
    listen(sock, 1);

    printf("Server is listening on port %d...\n", PORT);

    while (1) {
        struct sockaddr_in client_addr;
        socklen_t client_len = sizeof(client_addr);
        int client = accept(sock, (struct sockaddr *)&client_addr, &client_len);

        SSL *ssl = SSL_new(ctx);
        SSL_set_fd(ssl, client);

        if (SSL_accept(ssl) <= 0) {
            ERR_print_errors_fp(stderr);
        } else {
            const char reply[] = "Hello, SSL Client!\n";
            SSL_write(ssl, reply, strlen(reply));
        }

        SSL_free(ssl);
        close(client);
    }

    close(sock);
    SSL_CTX_free(ctx);
    cleanup_openssl();
    return 0;
}
3.3 客户端代码

编写一个对应的 SSL 客户端。

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

#define PORT 4443
#define SERVER "127.0.0.1"

void initialize_openssl() {
    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();
}

void cleanup_openssl() {
    EVP_cleanup();
}

int main() {
    int sock;
    struct sockaddr_in addr;

    initialize_openssl();

    SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method());
    if (!ctx) {
        perror("Unable to create SSL context");
        ERR_print_errors_fp(stderr);
        exit(EXIT_FAILURE);
    }

    sock = socket(AF_INET, SOCK_STREAM, 0);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    inet_pton(AF_INET, SERVER, &addr.sin_addr);

    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
        perror("Connection failed");
        exit(EXIT_FAILURE);
    }

    SSL *ssl = SSL_new(ctx);
    SSL_set_fd(ssl, sock);

    if (SSL_connect(ssl) <= 0) {
        ERR_print_errors_fp(stderr);
    } else {
        char buf[1024];
        SSL_read(ssl, buf, sizeof(buf));
        printf("Received: %s", buf);
    }

    SSL_free(ssl);
    close(sock);
    SSL_CTX_free(ctx);
    cleanup_openssl();
    return 0;
}
3.4 编译和运行

编译服务端和客户端代码:

gcc -o server server.c -lssl -lcrypto
gcc -o client client.c -lssl -lcrypto

运行服务端:

./server

运行客户端:

./client

输出示例:

  • 服务端日志:Server is listening on port 4443...
  • 客户端输出:Received: Hello, SSL Client!

http://www.kler.cn/a/504162.html

相关文章:

  • day07_Spark SQL
  • [微服务]redis数据结构
  • RV1126+FFMPEG推流项目(3)VI模块视频编码流程
  • 【update 更新数据语法合集】.NET开源ORM框架 SqlSugar 系列
  • rtthread学习笔记系列(4/5/6/7/15/16)
  • Android 对接口的封装使用
  • 从玩具到工业控制--51单片机的跨界传奇【2】
  • 运维练习题2
  • STORM:从多时间点2D图像中快速重建动态3D场景的技术突破
  • WordPress如何配置AJAX以支持点击加载更多?
  • GPT(General Purpose Timer)定时器
  • 【STM32-学习笔记-2-】外部中断
  • 元素隐式具有 “any“ 类型,因为类型为 “string“ 的表达式不能用于索引类型
  • sympy常用函数与错误笔记
  • 高级java每日一道面试题-2025年01月14日-框架篇[Spring篇]-Spring框架的事务管理有哪些优点?
  • ETL 数据抽取
  • ElectronSharp,.Net跨平台的多一种选择
  • 如何查看gitlab mr labels 添加和删除的历史
  • Photoshop快速无痕去除照片海报中的文字logo
  • 软件测试之单元测试总结
  • Java 面试中的高频算法题详解
  • WordPress开发进群V2主题,多种引流方法,引私域二次变现
  • TinyML中的去中心化资源共享:无线双层Gossip并行随机梯度下降用于协作学习
  • React:构建用户界面的JavaScript库
  • IvorySQL 4.0 之 Invisible Column 功能解析
  • 性能测试工具Jmeter分布式运行