通信安全之数据加密

news2024/12/23 18:55:43

        数据安全的需求如今越来越重要,本篇简单举例给日常的TCP/UDP通信加密,至少能让想干坏事的崽犯罪的成本更高一些(如果会一些BPF的,可能难不住这些崽),能让我们的数据更安全一点。

经典TCP socket编程

下面为大家常见的TCP socket编程示例:

客户端
/* client.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>  
#include <sys/types.h>   
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
//#include "myssl.h"

int main(int argc, char *argv[])
{
	// 定义相关变量
	int sockfd;
	struct sockaddr_in serv_addr;
	char buf[1024] = {0};
	const char *greeting = "Hello from client";
	
	if(argc < 2)
	{
		printf("please input the ip of the server..\n");
		return -1;
	}
	
	//获取并初始化服务端地址
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(8080);
	
	if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr) <= 0)
	{
		fprintf(stderr, "inet_pton failed[%d].\n", errno);
		return -1;
	}
	
	// 创建socket
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		fprintf(stderr, "create socket failed[%d].\n", errno);
		return -1;
	}
	
	// 连接到服务器
	if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
	{
		fprintf(stderr, "connect failed[%d].\n", errno);
		return -1;
	}
	
	// 发送数据
	if(write(sockfd, greeting, strlen(greeting)) < 0)
	{
		fprintf(stderr, "write failed[%d].\n", errno);
		return -1;
	}
	
	// 接收数据
	if(read(sockfd, buf, 1024) < 0)
	{
		fprintf(stderr, "read failed[%d].\n", errno);
		return -1;
	}
	printf("received message:%s\n", buf);
	
	close(sockfd);
}
服务端
/* server.c */
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
#include <sys/types.h>   
#include <sys/socket.h>  
#include <netinet/in.h>
#include <errno.h>
//#include "myssl.h"

#define MAX_CNT 5

int main(int argc, char *argv[])
{
	// 定义相关变量
	int server_fd, new_socket;
    int opt = 1; 
	struct sockaddr_in serv_addr, cli_addr;
	socklen_t cli_addr_len;
	char buf[1024] = {0};
	const char *greeting = "Hello from server";
	
	// 创建socket文件描述符
	if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		fprintf(stderr, "create socket failed[%d].\n", errno);
		return -1;
	}

    // 设置socket选项
	if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) 
	{  
        fprintf(stderr, "set socket failed[%d].\n", errno);
		return -1;  
    }
	
	// 绑定socket到指定端口和地址
	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(8080);
	
	if(bind(server_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
	{
		fprintf(stderr, "create bind failed[%d].\n", errno);
		return -1;
	}
	
	// 监听
	if(listen(server_fd, MAX_CNT) < 0)
	{
		fprintf(stderr, "listen failed[%d].\n", errno);
		return -1;
	}
	
	// 接受客户端连接请求
	memset(&cli_addr, 0, sizeof(cli_addr));
	cli_addr_len = sizeof(cli_addr);
	if((new_socket = accept(server_fd, (struct sockaddr *)&cli_addr, (socklen_t *)&cli_addr_len)) < 0 )
	{
		fprintf(stderr, "accept failed[%d].\n", errno);
		return -1;
	}

	printf("Client [%s-%d] connected.\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
	
	// 接收数据
	if(read(new_socket, buf, 1024) < 0)
	{
		fprintf(stderr, "read failed[%d].\n", errno);
		return -1;
	}
	printf("received message:%s\n", buf);
	
	// 发送数据
	if(write(new_socket, greeting, strlen(greeting)) < 0)
	{
		fprintf(stderr, "write failed[%d].\n", errno);
		return -1;
	}
	
	// 关闭socket
	close(new_socket);
	close(server_fd);
	
	printf("server closed.\n");
	return 0;
}

开启两个终端,运行结果如下:

数据加密方式TCP socket编程

封装加密初始化程序

        我们创建两个文件,一个为myssl.h,一个为对应的实现文件myssl.c,如下:

/* myssl.h */

#ifndef __MYSSL_H_
#define __MYSSL_H_

#include <openssl/ssl.h>

typedef enum{
	SSL_MODE_SERVER,
	SSL_MODE_CLIENT
}SSL_MODE;//对象方法枚举

SSL *init_ssl(char *cert_path, char *key_path, SSL_MODE mode, int fd);//初始化函数

#endif
/* myssl.c */
#include <openssl/ssl.h>
#include <stdio.h>
#include <stdlib.h>
#include "myssl.h"

SSL *init_ssl(char * cert_path, char * key_path, SSL_MODE mode, int fd)
{
    // 声明套件变量
	const SSL_METHOD *method;
	SSL_CTX *ctx;
	SSL *ssl = NULL;

    // 添加支持的算法,加载错误信息
	OpenSSL_add_all_algorithms();
	SSL_load_error_strings();

    // 获取对象方法
    // 不同版本的openssl对应的API也是有区别的,需要注意
	if(mode == SSL_MODE_SERVER)
	{
		method = SSLv23_server_method();
	}
	else if(mode == SSL_MODE_CLIENT)
	{
		method = SSLv23_client_method();
	}
	else
	{
		printf("unkown method\n");
		return NULL;
	}

    // 创建CTX
	ctx = SSL_CTX_new(method);
	if(!ctx)
	{
		printf("create SSL CTX failed.\n");
		return NULL;
	}

    // 读取证书(公钥)和密钥(私钥)
	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 file\n");
		return NULL;
	}

    // 创建SSL
	ssl = SSL_new(ctx);
	if(!ssl)
	{
		printf("failed create SSL.\n");
		return NULL;
	}

    // 绑定套接字的文件描述符到SSL中
	if(SSL_set_fd(ssl, fd) == 0)
	{
		printf("SSL set fd failed.\n");
		return NULL;
	}

    // 连接套接字
	if((mode == SSL_MODE_SERVER && SSL_connect(ssl) <= 0)||
		(mode == SSL_MODE_CLIENT && SSL_connect(ssl) <= 0))
	{
		printf("failed handshake with ssl.\n");
		return NULL;
	}

	return ssl;
}

 修改经典socket编程代码

        我们来对上面的客户端和服务端程序做些修改,以期使得变成加密的套接字通信。

客户端
/* client.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>  
#include <sys/types.h>   
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include "myssl.h"

int main(int argc, char *argv[])
{
	// 定义相关变量
	int sockfd;
	struct sockaddr_in serv_addr;
	char buf[1024] = {0};
	const char *greeting = "Hello from client";
	
	if(argc < 2)
	{
		printf("please input the ip of the server..\n");
		return -1;
	}
	
	//获取并初始化服务端地址
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(8080);
	
	if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr) <= 0)
	{
		fprintf(stderr, "inet_pton failed[%d].\n", errno);
		return -1;
	}
	
	// 创建socket
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		fprintf(stderr, "create socket failed[%d].\n", errno);
		return -1;
	}
	
	// 连接到服务器
	if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
	{
		fprintf(stderr, "connect failed[%d].\n", errno);
		return -1;
	}
	
	// 初始化加密函数
	SSL *ssl = init_ssl("./cert.pem", "./key.pem", SSL_MODE_CLIENT, sockfd);
	
	// 发送数据
	if(SSL_write(ssl, greeting, strlen(greeting)) < 0)	//if(write(sockfd, greeting, strlen(greeting)) < 0)
	{
		fprintf(stderr, "write failed[%d].\n", errno);
		return -1;
	}
	
	// 接收数据
	if(SSL_read(ssl, buf, 1024) < 0) //if(read(sockfd, buf, 1024) < 0)
	{
		fprintf(stderr, "read failed[%d].\n", errno);
		return -1;
	}
	printf("received message:%s\n", buf);
	
	close(sockfd);
	
	SSL_shutdown(ssl);
	SSL_free(ssl);
}

服务端
/* server.c */

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
#include <sys/types.h>   
#include <sys/socket.h>  
#include <netinet/in.h>
#include <errno.h>
//#include "myssl.h"

#define MAX_CNT 5

int main(int argc, char *argv[])
{
	// 定义相关变量
	int server_fd, new_socket;
	int opt = 1;
	struct sockaddr_in serv_addr, cli_addr;
	socklen_t cli_addr_len;
	char buf[1024] = {0};
	const char *greeting = "Hello from server";
	
	// 创建socket文件描述符
	if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		fprintf(stderr, "create socket failed[%d].\n", errno);
		return -1;
	}
	
	// 设置socket选项
	if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) 
	{  
        fprintf(stderr, "set socket failed[%d].\n", errno);
		return -1;  
    } 
	
	// 绑定socket到指定端口和地址
	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(8080);
	
	if(bind(server_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
	{
		fprintf(stderr, "create bind failed[%d].\n", errno);
		return -1;
	}
	
	// 监听
	if(listen(server_fd, MAX_CNT) < 0)
	{
		fprintf(stderr, "listen failed[%d].\n", errno);
		return -1;
	}
	
	// 接受客户端连接请求
	memset(&cli_addr, 0, sizeof(cli_addr));
	cli_addr_len = sizeof(cli_addr);
	if((new_socket = accept(server_fd, (struct sockaddr *)&cli_addr, (socklen_t *)&cli_addr_len)) < 0 )
	{
		fprintf(stderr, "accept failed[%d].\n", errno);
		return -1;
	}		

	printf("Client [%s-%d] connected.\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
	
	// 初始化加密函数
	SSL *ssl = init_ssl("./cert.pem", "./key.pem", SSL_MODE_SERVER, new_socket);
	
	// 接收数据
	if(SSL_read(ssl, buf, 1024) < 0) //if(read(new_socket, buf, 1024) < 0)
	{
		fprintf(stderr, "read failed[%d].\n", errno);
		return -1;
	}
	printf("received message:%s\n", buf);
	
	// 发送数据
	if(SSL_write(ssl, greeting, strlen(greeting)) < 0) //if(write(new_socket, greeting, strlen(greeting)) < 0)
	{
		fprintf(stderr, "write failed[%d].\n", errno);
		return -1;
	}
	
	// 关闭socket
	close(new_socket);
	close(server_fd);
	
	SSL_shutdown(ssl);
	SSL_free(ssl);
	
	printf("server closed.\n");
	return 0;
}
Makefile

        为了方便总体编译,我们创建一个Makefile,内容如下:

LIBS=-lssl -lcrypto
all:
	gcc -o server server.c myssl.c $(LIBS)
	gcc -o client client.c myssl.c $(LIBS)
clean:
	rm -rf server client
证书、密钥

        为方便测试,我们利用openssl生成密钥key.pem及公钥文件cert.pem,命令如下:

密钥

$openssl genpkey -algorithm RSA -out key.pem

公钥(利用密钥生成,生成之前需要根据提示输入一些地址公司等信息)

$sudo openssl req -new -x509 -key key.pem -out cert.pem -days 3650

注意openssl的版本程序中用到的和命令行的这个要保持一致,结果如下:

编译问题

        在终端执行make命令,如果出现找不到<openssl/ssl.h>,那么你的系统环境可能没有安装openssl开发库,我们可以通过下面命令安装:

sudo apt-get update  
sudo apt-get install libssl-dev

安装完毕后,<openssl/ssl.h>应该可以在/usr/include/openssl/目录中找到。

如果安装成功之后还是报找不到头文件的错误,通过下面命令检查定位头文件所在位置:

dpkg -L libssl-dev | grep ssl.h

如果你确定环境中有这个头文件而其他方法都不行的话,可以试试手动添加路径指定,如下:

gcc -o myprogram myprogram.c -I/usr/include/openssl -lssl -lcrypto

如果方法都试遍了,我们只能尝试卸掉重新安装。

sudo apt-get remove --purge libssl-dev  
sudo apt-get update  
sudo apt-get install libssl-dev

如果报找不到openssl的某些API 或相关问题,此时则需参考下面的流程对源码做出修改。

补充

        因为openssl每个跨级的版本(0.9/1.0/3.0)API的变动较大(有些遗弃有些修改),兼容性不好,我们编写的应用源代码可能会需要做些修改(如果使用的openssl变动了版本)。如果没有openssl的源码,系统中只有库文件,我们可以通过objdump命令来检索其用的哪些API,方便我们替换修改,举例:

$objdump -Tt libxxx.so | grep SSL*

类似返回如下结果:

参考

B站爱吃甜食的老猫的视频

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

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

相关文章

佑雅的小布谷数据平台获取token如何实现

小博股数据开放平台是面向全部用户的股票数据开放平台&#xff0c;通过调用接口可以获取股票的历史数据。在调用之前需要进行下面的准备工作&#xff0c;第一步注册&#xff1a; 用户在注册之后&#xff0c;登录点击头像进入个人中心&#xff0c;在功能模块的最下方有一个创建应…

IntelliJ IDEA 2024.1安装与激活[破解]

一&#xff1a;IDEA官方下载 ①如题&#xff0c;先到IDEA官方下载&#xff0c;简简单单 ②IDEA官方&#xff1a;IntelliJ IDEA – the Leading Java and Kotlin IDE 二&#xff1a;获取脚本 &#x1f31f;网盘下载&#xff1a;jetbra (密码&#xff1a;lzh7) &#x1f31f;获取…

51单片机入门_江协科技_25~26_OB记录的笔记_蜂鸣器教程

25. 蜂鸣器 25.1. 蜂鸣器介绍 •蜂鸣器是一种将电信号转换为声音信号的器件&#xff0c;常用来产生设备的按键音、报警音等提示信号 •蜂鸣器按驱动方式可分为有源蜂鸣器和无源蜂鸣器&#xff08;开发板上用的无源蜂鸣器&#xff09; •有源蜂鸣器&#xff1a;内部自带振荡源&a…

报文 消息

报文消息域 MsgField name 域名称 length 长度 fillChar 填充字符 fillSide 填充位置 报文消息片 MsgPiece 由多个消息域按一定的顺序组成 private List<MsgField> itemList new LinkedList<~>();组装消息 报文消息包 MsgPackage 由多个消息片组成 String[]…

FreeRTOS启动任务调度器

FreeRTOS启动任务调度器 这部分内容就要去深入了解源码以及熟悉汇编语言的操作。依旧正点原子的视频。下面首先看开启任务调度器这部分源码&#xff1a; 1开启任务调度器 任务调度器用于启动任务调度器&#xff0c;任务调度器启动后&#xff0c; FreeRTOS 便会开始进行任务调…

MyBatis 应用的组成

王有志&#xff0c;一个分享硬核Java技术的互金摸鱼侠 加入Java人的提桶跑路群&#xff1a;共同富裕的Java人 大家好&#xff0c;我是王有志。在上一篇文章的最后&#xff0c;我们写了一个简单的例子&#xff0c;今天我们就通过这个例子来看一看一个标准的 MyBatis 应用程序由哪…

PS入门|如何使用“主体”功能进行抠图?

前言 前段时间讲到给各种图标和LOGO抠图的办法&#xff0c;分别使用的是 钢笔工具蒙版 PS入门&#xff5c;规规矩矩的图形怎么抠出来&#xff1f; 魔棒工具蒙版 PS入门&#xff5c;黑白色的图标怎么抠成透明背景 色阶蒙版 PS入门&#xff5c;目标比较复杂&#xff0c;但背景…

HTML+CSS+JS复习回顾

环境搭建 下载VScode&#xff0c;依次下载插件&#xff1a;HTML CSS support、Live Server、Auto Rename Tag 一、HTML篇 HTML通过一系列的标签&#xff08;元素&#xff09;来定义文本、图像、链接等。HTML标签是由尖括号包围的关键字。标签通常成对出现&#xff0c;包括开…

在Spring中使用Redis

端口怎么设置&#xff0c;看我前一篇文章 前面使用jedis&#xff0c;通过Jedis对象中各种方法来操作redis的。 此处Spring中则是通过StringRedisTemplate来操作redis。 最原始提供的类是RedisTemplate StringRedisTemplate是RedisTemplate的子类&#xff0c;专门处理文本数据的…

2014最新AIGC创作系统ChatGPT网站源码+AI绘画网站源码+GPT4-All联网搜索模型

一、文章前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧。已支持…

无重复的最长字串

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 问题 给定一个字符串&#xff0c;我们需要找到该字符串中的最长无重复子串的长度。 示例 让我们以一个具体的示例来说明这个问题&#…

四、书城开发--3、书城图书部分的开发

书城图书部分 首先我们做书城首页搜索栏下面的图片展示 我们在书城首页组件中通过home请求方法中获取回来的数据中&#xff0c;打印出来可以看到那个banner就是我们现在要的图片 我们在data中定义一个变量banner用来存放获取回来的数据中的banner 然后把它展示出来就可以了&a…

B. Burning Midnight Oil Codeforces Round 112 (Div. 2)

题目链接&#xff1a; Problem - 165B - Codeforceshttps://codeforces.com/problemset/problem/165/B 题目大意&#xff1a; 最后写了至少n个&#xff0c;每次衰减k倍&#xff08;/k&#xff09;&#xff0c;问最初的v最小为多少。 思路&方法&#xff1a; 二分答案。 AC代…

想要品牌传播有效,先清楚这三个本质问题

在互联网时代&#xff0c;企业想要提高市场竞争力就需要做好品牌传播。然而有许多企业在做品牌传播时都会踩坑&#xff0c;原因是因为忽视了这三点&#xff0c;接下来就让媒介盒子和大家分享&#xff1a; 一、 文案本质是“购买理由” 在文案技巧中经常会出现一些词&#xff…

重学Java,JDK安装,Java环境配置,Could not find Java SE Runtime Environment问题解决

文章目录 前言JDK下载什么是JDK下载JDK官网下载历史版本下载 JDK安装生成JRE配置环境变量进入环境变量配置界面新建系统变量JAVA_HOME编辑系统变量PATHPath编辑界面1Path编辑界面2 配置CLASSPATH 验证安装情况问题反馈Error: opening registry key Software\JavaSoft\Java Runt…

WordPress网站备份和迁移教程

我们之前遇到购买了hostease的客户需要进行wordpress的网站备份的迁移操作。 以下是一份完整的指南&#xff0c;介绍了备份和迁移WordPress网站的步骤&#xff1a; 步骤一&#xff1a;备份WordPress网站 使用插件进行备份&#xff1a; 安装并激活备份插件&#xff0c;例如Up…

SSH远程登陆系统(RedHat9)

ssh的基本用法 ssh hostname/IP # 如果没有指定用什么用户进行连接&#xff0c;默认使用当前用户登录 ssh –l username hostname/IP ssh usernamehostname ssh usernameIP在第一次连接到服务器时&#xff0c;会自动记录服务器的公钥指纹信息 如果出现密钥变更导致错误可以…

Spring Cloud 集成 Redis 发布订阅

目录 前言步骤引入相关maven依赖添加相关配置 使用方法发布订阅发布一个消息 注意总结 前言 在当今的软件开发领域&#xff0c;分布式系统已经成为一种主流的架构模式&#xff0c;尤其是在处理大规模、高并发、高可用的业务场景时。然而&#xff0c;随着系统复杂性的增加&…

Training - Kubeflow 的 PyTorchJob 配置 DDP 分布式训练 (ncclInternalError)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/137569332 Kubeflow 的 PyTorchJob 是 Kubernetes 自定义资源&#xff0c;用于在 Kubernetes 上运行 PyTorch 训练任务&#xff0c;是 K…

【学习】软件测试中为什么要进行接口测试?

接口测试是软件开发过程中不可或缺的一环&#xff0c;它主要是对软件系统中各个模块之间的接口进行测试&#xff0c;以验证它们是否能够正确地交互和协作。接口测试的目的是确保软件系统的各个部分能够无缝地协同工作&#xff0c;从而提高整个系统的质量和稳定性。 一、什么是接…