Linxu下c语言实现socket+openssl数据传输加密

news2024/12/23 19:01:40

文章目录

  • 1. Socket连接建立流程
  • 2、Socket+SSL的初始化流程
  • 3、初始化SSL环境,证书和密钥
  • 4、Socket+SSL 的c语言实现
    • 4.1 编写SSL连接函数
    • 4.2 编写加密服务端server.c
    • 4.3 编写加密客户端client.c
  • 5、使用tcpdump检验
    • 源码获取

在进行网络编程的时候,我们通常使用socket进行数据的传输。然而socket作为一个数据传输协议,其本身对数据并不会作加密。所以数据传输的过程可以很轻松地被监听并截获到传输的数据。openssl提供了SSL的加密库,通过ssl+socket的方式可以保证连接安全和数据的加密。

1. Socket连接建立流程

在做socket加密之前,还是先与普通的socket做一个对比。

Client Server connect() accept() send(data) recv(data) send(response) recv(response) close() close() Client Server

上面的是我们通常在做一个socket连接的时候会涉及到的握手过程。服务端会通过accept去接收客户端的请求。客户端通过connect去连接到客户端。使用sendrecv去做数据的传输。那么传输过程中的参数data也就是我们交互的数据了。当我们建立连接开始发送数据的时候。使用一些抓包工具wireshark,或者tcpdump去监听socket端口就能很轻松地获取到传输的明文数据了。

2、Socket+SSL的初始化流程

所以为了避免数据的监听,我们就需要使用SSL去建立一个安全的通道。这里我们先把SSL的建立当作一个子流程

Client Server conn_fd=connect() accept_fd=accept() SSL Sub-Process Begins Initialize SSL Environment(准备证书,私钥) Initialize SSL Environment(准备证书,私钥) Create new SSL session (绑定conn_fd) Create new SSL session (绑定accept_fd) SSL_connect() (传入证书,私钥,和conn_fd进行握手) SSL_accept() (传入证书,私钥,和accept_fd进行握手) SSL Sub-Process Ends SSL_write(data) SSL_read(data) SSL_write(response) SSL_read(response) SSL Shutdown Sub-Process Begins SSL_shutdown() SSL_shutdown() SSL Shutdown Sub-Process Ends close() close() Client Server

SSL子过程主要插入在socket的connect()/accept()和数据交换之间。通过SSL建立完成的所以,一旦SSL握手完成,数据发送和接收的流程与普通的socket通信非常相似,只需要使用SSL_write()和SSL_read()来代替send()和recv()。c

替换 send()SSL_write(ssl, buffer, length)
替换recv()SSL_read(ssl, buffer, length)

3、初始化SSL环境,证书和密钥

  • openssl 生成证书(分别生成私钥和自签名证书),确保环境上已经安装完成了openssl
    • openssl genpkey -algorithm RSA -out key.pem
    • openssl req -new -x509 -key key.pem -out cert.pem -days 365
      在这里插入图片描述
      days为证书的有效期。命令执行完成后在当前目录下就会生成key.pemcert.pem 。记下文件路径,后续会作为参数传入到我们的函数中去。

4、Socket+SSL 的c语言实现

4.1 编写SSL连接函数

在进行SSL初始化的时候需要注意的是,由于连接协商的过程使用的是非对称加密,因此客户端和服务端在初始化的时候是使用的不同的算法。因此,我在函数中加入了一个SSL_MODE参数。用来指明当前是服务端还是客户端的SSL。

SSL* sync_initialize_ssl(const char* cert_path, const char* key_path, SSL_MODE mode, int fd) {
    const SSL_METHOD *method;
    SSL_CTX *ctx;
    SSL *ssl = NULL;
    // 初始化OpenSSL库
    SSL_library_init();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();

    // 根据模式(客户端/服务端)选择合适的方法
    if (mode == SSL_MODE_SERVER) {
        method = SSLv23_server_method();
    } else if (mode == SSL_MODE_CLIENT) {
        method = SSLv23_client_method();
    } else {
        // 未知模式
        printf("Not found method");
        return NULL;
    }

    // 创建SSL上下文
    ctx = SSL_CTX_new(method);
    if (!ctx) {
        printf("Unable to create SSL context");
        return NULL;
    }

    // 配置SSL上下文
    if (SSL_CTX_use_certificate_file(ctx, cert_path, SSL_FILETYPE_PEM) <= 0 || SSL_CTX_use_PrivateKey_file(ctx, key_path, SSL_FILETYPE_PEM) <= 0 ) {
        printf("Not found certificate or private key");
        SSL_CTX_free(ctx);
        return NULL;
    }
    // SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);

    // 创建SSL对象
    ssl = SSL_new(ctx);
    if (!ssl) {
        printf("Failed to create SSL object.");
        return NULL;
    }

    // 设置文件描述符
    if (SSL_set_fd(ssl, fd) == 0) {
        printf("Failed to set fd to SSL object.");
        return NULL;
    }

    // SSL握手
    if ((mode == SSL_MODE_CLIENT && SSL_connect(ssl) <= 0) ||
        (mode == SSL_MODE_SERVER && SSL_accept(ssl) <= 0)) {
        int ssl_result;
        if (mode == SSL_MODE_CLIENT) {
            ssl_result = SSL_connect(ssl);
        } else if (mode == SSL_MODE_SERVER) {
            ssl_result = SSL_accept(ssl);
        }

        int ssl_err = SSL_get_error(ssl, ssl_result);

        const char *err_str;
        switch (ssl_err) {
            case SSL_ERROR_NONE:
                err_str = "No error";
                break;
            case SSL_ERROR_SSL:
                err_str = "Error in the SSL protocol";
                break;
            case SSL_ERROR_WANT_READ:
                err_str = "SSL read operation did not complete";
                break;
            case SSL_ERROR_WANT_WRITE:
                err_str = "SSL write operation did not complete";
                break;
            case SSL_ERROR_WANT_X509_LOOKUP:
                err_str = "SSL X509 lookup operation did not complete";
                break;
            case SSL_ERROR_SYSCALL:
                err_str = "Syscall error";
                break;
            case SSL_ERROR_ZERO_RETURN:
                err_str = "SSL connection was shut down cleanly";
                break;
            case SSL_ERROR_WANT_CONNECT:
                err_str = "SSL connect operation did not complete";
                break;
            case SSL_ERROR_WANT_ACCEPT:
                err_str = "SSL accept operation did not complete";
                break;
            default:
                err_str = "Unknown error";
                break;
        }
        printf("===============SSL handshake failed. Error: %s========!\n", err_str ? err_str : "Unknown");
        return NULL;
    }

    return ssl;
}

4.2 编写加密服务端server.c

#include <stdio.h>  
#include <stdlib.h>  
#include <stddef.h>
#include <string.h>  
#include <unistd.h>  
#include <sys/socket.h>  
#include <arpa/inet.h>
#include <netinet/in.h>  
#include <openssl/ssl.h>  
#include <openssl/err.h>  
  
#define SERVER_PORT 9990  
#define MAXLINE 4096  
  typedef enum {
    SSL_MODE_SERVER,
    SSL_MODE_CLIENT
} SSL_MODE;

int main(int argc, char **argv) {  
    int listenfd, connfd;  
    struct sockaddr_in servaddr, cliaddr;  
    char buf[MAXLINE];  

    // 创建监听套接字  
    listenfd = socket(AF_INET, SOCK_STREAM, 0);  
  
    // 绑定地址和端口  
    memset(&servaddr, 0, sizeof(servaddr));  
    servaddr.sin_family = AF_INET;  
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
    servaddr.sin_port = htons(SERVER_PORT);  
    bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));  
  
    // 监听连接  
    listen(listenfd, 10);  
    printf("Listening on port %d...\n", SERVER_PORT);  
  
    while (1) {  
        // 接受连接请求  
        socklen_t len = sizeof(cliaddr);  
        connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &len);  
        printf("Accepted connection from %s:%d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));  
  
        // 创建 SSL 对象并进行握手  
        SSL *ssl = sync_initialize_ssl("cert.pem", "key.pem", SSL_MODE_SERVER, connfd); 
  
        // 读取客户端发送的数据并回复  
        memset(buf, 0, MAXLINE);  
        SSL_read(ssl, buf, MAXLINE);  
        printf("Received: %s\n", buf);  
        SSL_write(ssl, "Hello, client!", strlen("Hello, client!"));  
  
        // 关闭连接和清理资源  
        close(connfd);  
        SSL_shutdown(ssl);  
        SSL_free(ssl);  
    }  
  

    return 0;  
}

4.3 编写加密客户端client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stddef.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define SERVER_IP "127.0.0.1"  // 请根据需要更改服务器IP
#define SERVER_PORT 9990  
#define MAXLINE 4096  
  typedef enum {
    SSL_MODE_SERVER,
    SSL_MODE_CLIENT
} SSL_MODE;

int main(int argc, char **argv) {
    int sockfd;
    struct sockaddr_in servaddr;
    char buf[MAXLINE];
    // 创建 socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    // 设置服务器地址和端口
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(SERVER_IP);
    servaddr.sin_port = htons(SERVER_PORT);

    // 连接到服务器
    connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

    // 创建 SSL 对象并进行握手
    SSL *ssl = sync_initialize_ssl("cert.pem", "key.pem", SSL_MODE_CLIENT, sockfd); 
    // 发送数据给服务器
    SSL_write(ssl, "Hello, server!", strlen("Hello, server!"));

    // 读取服务器的回复
    memset(buf, 0, MAXLINE);
    SSL_read(ssl, buf, MAXLINE);
    printf("Received: %s\n", buf);

    // 关闭连接和清理资源
    close(sockfd);
    SSL_shutdown(ssl);
    SSL_free(ssl);

    return 0;
}


5、使用tcpdump检验

服务端和客户端编写完成后,分别运行起来,这里我运行的端口是9990。使用tcpdump抓取9990端口的传输数据
tcpdump -i any port 9990 -A

在这里插入图片描述
此时分别运行后,服务端客户端数据完成交互后,抓包所看到的数据已经是密文传输了。

源码获取

需要获取源码的可以关注公众号"一颗程序树",点击菜单栏的免费源码输入关键词socket即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1040408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分布式算法相关,使用Redis落地解决1-2亿条数据缓存

面试题&#xff1a;1~2亿数据需要缓存&#xff0c;请问如何设计个存储案例 回答&#xff1a;单机单台100%不可能&#xff0c;肯定是分布式存储&#xff0c;用redis如何落地&#xff1f; 一般业界有三种解决方案&#xff1a; 哈希取余分区 2亿条记录就是2亿个k&#xff0c;v&…

Linux学习-HIS部署(3)

Jenkins插件资源下载 Jenkins部署 Jenkins部署 #Jenkins主机安装OpenJDK环境 [rootJenkins ~]# yum clean all; yum repolist -v ... Total packages: 8,265 [rootJenkins ~]# yum -y install java-11-openjdk-devel.x86_64 #安装OpenJDK11 [rootJenkins ~]# ln -s /usr/l…

Css 美化滚动条

/*设置滚动条宽度为 6px*/ ::-webkit-scrollbar {width: 6px; } /*设置背景颜色&#xff0c;并设置边框倒角&#xff0c;设置滚动动画&#xff0c;0.2 */ ::-webkit-scrollbar-thumb {background-color: #0003;border-radius: 10px;transition: all .2s ease-in-out; } /*设置滚…

探索创意的新辅助,AI与作家的完美合作

在现代社会&#xff0c;文学创作一直是人类精神活动中的重要一环。从古典文学到现代小说&#xff0c;从诗歌到戏剧&#xff0c;作家们以他们的独特视角和文学天赋为我们展示了丰富多彩的人生世界。而近年来&#xff0c;人工智能技术的快速发展已经渗透到各行各业&#xff0c;文…

Leetcode191. 位1的个数

力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 1 的个数&#xff08;也被称为汉明重量&#xff09;。 思路&…

人工智能AI 全栈体系(六)

第一章 神经网络是如何实现的 这些年神经网络的发展越来越复杂&#xff0c;应用领域越来越广&#xff0c;性能也越来越好&#xff0c;但是训练方法还是依靠 BP 算法。也有一些对 BP 算法的改进算法&#xff0c;但是大体思路基本是一样的&#xff0c;只是对 BP 算法个别地方的一…

mapper文件添加@Mapper注解爆红

如图所示 报错原因&#xff1a;缺少相关的依赖 <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version> </dependency> 添加之后并刷新依赖…

C++: stack 与 queue

目录 1.stack与queue stack queue 2.priority_queue 2.1相关介绍 2.2模拟实现priority_queue --仿函数: --push --pop --top --size --empty --迭代器区间构造 2.3仿函数 3.容器适配器 stack模拟实现 queue模拟实现 学习目标: 1.stack和queue介绍与使用 2.pri…

Toaster - Android 吐司框架,专治 Toast 各种疑难杂症

官网 https://github.com/getActivity/Toaster 这可能是性能优、使用简单&#xff0c;支持自定义&#xff0c;不需要通知栏权限的吐司 想了解实现原理的可以点击此链接查看&#xff1a;Toaster 源码 集成步骤 如果你的项目 Gradle 配置是在 7.0 以下&#xff0c;需要在 bui…

如何使用Docker安装最新版本的Redis并设置远程访问(含免费可视化工具)

文章目录 安装Docker安装Redisredis.conf文件远程访问Redis免费可视化工具相关链接Docker是一种开源的应用容器引擎,使用Docker可以让我们快速部署应用环境,本文介绍如何使用Docker安装最新版本的Redis。 安装Docker 首先需要安装Docker,具体的安装方法可以参考Docker官方文…

用友U8 crm客户关系管理存在任意文件上传漏洞2 附POC

文章目录 用友U8 crm客户关系管理存在任意文件上传漏洞2 附POC1. 用友U8 crm客户关系管理简介2.漏洞描述3.影响版本4.fofa查询语句5.漏洞复现6.POC&EXP7.整改意见8.往期回顾 用友U8 crm客户关系管理存在任意文件上传漏洞2 附POC 免责声明&#xff1a;请勿利用文章内的相关…

知识图谱(5)知识表示

基于Node2Vec补全KG 知识图谱属于异质图&#xff0c;图谱包含三个元素&#xff1a;实体&#xff08;图中的节点&#xff09;&#xff0c;类型&#xff08;节点的标识&#xff09;&#xff0c;关系&#xff08;边的标识&#xff09;。KG就是把所有不同种类的信息连接在一起而得…

第1篇 目标检测概述 —(2)目标检测算法介绍

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。目标检测算法是一种计算机视觉算法&#xff0c;用于在图像或视频中识别和定位特定的目标物体。常见的目标检测算法包括传统的基于特征的方法&#xff08;如Haar特征和HOG特征&#xff09;以及基于深度学习的方法&#xff0…

C语言指针笔试题讲解

大家好&#xff0c;我们来学习一些C语言的指针笔试题。对于C语言指针的模块想必大家都非常的头疼吧&#xff0c;那么我们就来就来看看一些关于C语言指针的笔试题。 首先让我们看到我们今天的第一题。 int main() { int a[5] { 1, 2, 3, 4, 5 }; int *ptr (int *)(&a 1)…

AUTOSAR RTE介绍(更新版230925)

RTE是什么 AUTOSAR RTE(Run Time Environment)实现了AUTOSAR系统中的虚拟功能总线(VFB),提供了SWC(Software Component)之间的访问接口和SWC对于BSW资源的访问接口。RTE为SWC中的Runnable提供与其他SWC或者BSW模块通信的接口,RTE将Runnable映射到OS Task中,并且管理Runna…

二进制中1的个数 C++实现

题目&#xff1a; 代码&#xff1a; #include<iostream> using namespace std; const int N100010; int a[N]; int n;int lowbit(int x){return x & -x; }int main(){scanf("%d",&n);for(int i0;i<n;i) scanf("%d",&a[i]);for(int i…

抽检监测实施

声明 本文是学习GB-T 42893-2023 电子商务交易产品质量监测实施指南. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件提供了开展电子商务交易的有形产品质量监测的总则&#xff0c;监测准备、监测实施、监测效果评价 与反馈等过程指导…

this.$nextTick()的使用场景

事件循环机制&#xff1a; 同步代码执行->查找异步队列&#xff0c;推入执行栈&#xff0c;执行Vue.nextTick[事件循环1]->查找异步队列&#xff0c;推入执行栈&#xff0c;执行Vue.nextTick[事件循环2]->查找异步队列&#xff0c;推入执行栈&#xff0c;执行Vue.nex…

Win10专业版系统一键重装怎么操作?

Win10专业版系统一键重装怎么操作&#xff1f;与传统的系统重装相比&#xff0c;一键重装不仅省去了繁琐的安装步骤&#xff0c;这一简单操作使得系统维护和恢复变得更加便捷&#xff0c;让用户不再为系统问题而烦恼。下面小编给大家详细介绍关于一键重装Win10专业版系统的操作…

使用bash脚本编译Qt工程

最近在搭建Qt工程的编译服务器&#xff0c;需要通过shell脚本执行工程的编译过程&#xff0c;写在这里&#xff0c;总结最近的工作。 1. 构建过程 以Windows为例&#xff0c;在QtCreator中左侧选择“项目”便可以看到编译的指令和参数&#xff0c;这些操作也会在我们点击“构建…