通信安全之数据加密

        数据安全的需求如今越来越重要,本篇简单举例给日常的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.mfbz.cn/a/538604.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

In Memoriam Fabrizio Flacco

一、背景 最近在看人机协作相关的论文&#xff0c;其中有一篇是Arash Ajoudani于2018发表在Autonomous Robots题为Progress and prospects of the human–robot collaboration的综述。当看到最后Acknowledgements部分&#xff0c;有一句话是The authors would like to thank a…

小样本计数网络FamNet(Learning To Count Everything)

小样本计数网络FamNet(Learning To Count Everything) 大多数计数方法都仅仅针对一类特定的物体&#xff0c;如人群计数、汽车计数、动物计数等。一些方法可以进行多类物体的计数&#xff0c;但是training set中的类别和test set中的类别必须是相同的。 为了增加计数方法的可拓…

基于tcmalloc的高并发内存池

内存池 池化技术&#xff1a; 池是在计算机技术中经常使用的一种设计模式&#xff0c;其内涵在于&#xff1a;将程序中需要经常使用的核心资源 先申请出来&#xff0c;放到一个池内&#xff0c;由程序自己管理&#xff0c;这样可以提高资源的使用效率&#xff0c;也可以保证本程…

【新版HI3559AV100开发注意事项(四)】

新版HI3559AV100开发注意事项&#xff08;四&#xff09; 三十、HI3559A参数中对输入分辨率限制的原因是&#xff1f; 答&#xff1a;分辨率限制有两个来源&#xff1a; 一个是时钟频率最高为600M&#xff0c;开启一拍两像素之后相当于1200M。你这个数据量太大了&#xff0c;6…

【IR-SDE】Image Restoration SDE项目演示运行app.py

背景&#xff1a; code:GitHub - Algolzw/image-restoration-sde: Image Restoration with Mean-Reverting Stochastic Differential Equations, ICML 2023. Winning solution of the NTIRE 2023 Image Shadow Removal Challenge. paper: Official PyTorch Implementations o…

Terraform 语法配置

配置语法 Terraform 的配置文件都是以 .tf 为后缀Terraform 支持两种模式 HCL、JSON Provider 插件 providers 地址&#xff1a;Terraform Registry Terraform 通过 provider 管理基础设施&#xff0c;使用 provider 与云供应商 API 进行交互&#xff0c;每个 Provider 都包含…

2024年DeFi的四大主导趋势:Restaking、Layer3、AI和DePin

DeFi&#xff08;去中心化金融&#xff09;行业在2024年将继续呈现快速增长的势头&#xff0c;驱动这一增长的主要因素将是四大主导趋势&#xff1a;Restaking、Layer3、AI和DePin。这些趋势将推动DeFi生态系统的发展&#xff0c;为用户提供更多的机会和创新。 趋势1&#xff…

JavaScript函数式编程

函数式编程 课程介绍 为什么要学习函数编程以及什么是函数式编程函数式编程的特性(纯函数、柯里化、函数组合等)函数式编程的应用场景函数式编程库Lodash 为什么要学习函数式编程 函数式编程是非常古老的一个概念&#xff0c;早于第一台计算机的诞生&#xff0c; 函数式编程…

有图片转成PDF文件格式的方法吗?分享图片转成PDF文件的方法

将图片转换为PDF文件是一个相对简单的过程&#xff0c;但也需要一定的步骤和注意事项。下面&#xff0c;我将详细介绍如何将图片转换为PDF文件&#xff0c;包括所需的工具、步骤以及可能遇到的问题和解决方案。 首先&#xff0c;我们需要一个能够将图片转换为PDF文件的工具。市…

SV-704XT 100W网络有源音柱 校园广播音柱

SV-704XT 100W网络有源音柱 一、描述 SV-704XT是深圳锐科达电子有限公司的一款壁挂式网络有源音柱&#xff0c;具有10/100M以太网接口&#xff0c;可将网络音源通过自带的功放和喇叭输出播放&#xff0c;其采用防水设计&#xff0c;功率100W。SV-704XT作为网络广播播放系统的终…

分布式强化学习

标题 易混淆概念联邦学习与强化学习1&#xff09;联邦学习应用于强化学习2&#xff09;强化学习应用于联邦学习 时空图卷积网络&#xff08;ST-GCN&#xff09;基本概念结合训练 易混淆概念 DistributionalRL是分布RL&#xff0c;不是分布式RL。分布RL是把Q值从一个期望构建成…

程序员搞副业你可以这样做

程序员搞副业你可以这样做 文章目录 程序员搞副业你可以这样做01/开发外包项目02/开源项目赢取打赏盈利模式之一&#xff1a;多种产品线盈利模式之二&#xff1a;技术服务型盈利模式之三&#xff1a;应用服务托管&#xff08;ASP&#xff09;盈利模式之四&#xff1a;软、硬件一…

边缘计算网关究竟是什么呢?它又有什么作用呢?-天拓四方

在数字化时代&#xff0c;信息的传输与处理变得愈发重要&#xff0c;而其中的关键节点之一便是边缘计算网关。这一先进的网络设备&#xff0c;不仅扩展了云端功能至本地边缘设备&#xff0c;还使得边缘设备能够自主、快速地响应本地事件&#xff0c;提供了低延时、低成本、隐私…

20240412,引用,函数高级

老子什么时候能找到一个很爱我还和我一样喜欢看日出日落的对象 一&#xff0c;引用 给变量起别名&#xff0c;数据类型 & 别名原名&#xff1b;引用一定要初始化&#xff0c;初始化之后不能更改 #include <iostream> using namespace std; int main() {int a 10;i…

PostgreSQL入门到实战-第二十一弹

PostgreSQL入门到实战 PostgreSQL中表连接操作(五)官网地址PostgreSQL概述PostgreSQL中RIGHT JOIN命令理论PostgreSQL中RIGHT JOIN命令实战更新计划 PostgreSQL中表连接操作(五) 使用PostgreSQL RIGHT JOIN连接两个表&#xff0c;并从右表返回行 官网地址 声明: 由于操作系统…

【前沿模型解析】潜在扩散模型 2-3 | 手撕感知图像压缩 基础块 自注意力块

1 注意力机制回顾 同ResNet一样&#xff0c;注意力机制应该也是神经网络最重要的一部分了。 想象一下你在观看一场电影&#xff0c;但你的朋友在给你发短信。虽然你正在专心观看电影&#xff0c;但当你听到手机响起时&#xff0c;你会停下来查看短信&#xff0c;然后这时候电…

CSS特效---纯CSS实现点击切换按钮

1、演示 2、一切尽在代码中 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><meta name"viewport" content"w…

第11版《中国网络安全行业全景图》发布,谁霸榜了软件供应链安全领域?

近日&#xff0c;知名网络安全行业媒体安全牛正式发布了第11版《中国网络安全行业全景图》&#xff08;以下简称”全景图“&#xff09;&#xff0c;共收录了国内网络安全企业454家&#xff0c;细分领域共收录2413项&#xff0c;旨在优先展现当前热门网络安全领域中具有较强市场…

mysql题目1

tj11: ​ select * from t_student where grade 大一 and major 软件工程 ​ tj12: SELECTt_student.name, count(t_choice.cid)FROMt_choiceINNER JOINt_courseON t_choice.cid t_course.idINNER JOINt_studentON t_choice.sid t_student.id GROUP BYt_choice.sid HAVIN…

如何免费搭建幻兽帕鲁服务器?

雨云是一家国内的云计算服务提供商&#xff0c;为了吸引用户推出了积分兑换云产品活动&#xff0c;只需要完成简单积分任务即可获得积分&#xff0c;积分可以兑换免费游戏云、对象存储或者虚拟主机。本文将给大家分享雨云免费游戏云领取及幻兽帕鲁开服教程。 第一步&#xff1a…
最新文章