20.7 OpenSSL 套接字SSL加密传输
OpenSSL 中的 SSL 加密是通过 SSL/TLS 协议来实现的。SSL/TLS 是一种安全通信协议,可以保障通信双方之间的通信安全性和数据完整性。在 SSL/TLS 协议中,加密算法是其中最核心的组成部分之一,SSL可以使用各类加密算法进行密钥协商,一般来说会使用RSA等加密算法,使用TLS加密针对服务端来说则需要同时载入公钥与私钥文件,当传输被建立后客户端会自行下载公钥并与服务端完成握手,读者可将这个流程理解为上一章中RSA的分发密钥环节,只是SSL将这个过程简化了,当使用时无需关注传输密钥对的问题。
与RSA实现加密传输一致,使用SSL实现加密传输读者同样需要自行生成对应的密钥对,密钥对的生成可以使用如下命令实现;
- 生成私钥: openssl genrsa -out privkey.pem 2048
- 生成公钥: openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095
执行如上两条命令,读者可得到两个文件首先生成2048位的privkey.pem也就是私钥,接着利用私钥文件生成cacert.pem证书文件,该文件的有效期为1095天也就是三年,当然此处由于是测试可以使用自定义生成,如果在实际环境中还是需要购买正规签名来使用的。

服务端实现代码与原生套接字通信保持高度一致,在连接方式上同样采用了标准API实现,唯一的不同在于当accept函数接收到用于请求时,我们需要通过SSL_new产生一个SSL对象,当需要发送数据时使用SSL_write,而当需要接收数据时则使用SSL_read函数,通过使用这两个函数即可保证中间的传输流程是安全的,其他流程与标准套接字编程保持一致,如下是服务端完整代码实现。
#include <WinSock2.h>
#include <iostream>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/pem.h>
#include <openssl/crypto.h>
extern "C"
{
#include <openssl/applink.c>
}
#pragma comment(lib, "WS2_32.lib")
#pragma comment(lib,"libssl.lib")
#pragma comment(lib,"libcrypto.lib")
#define MAXBUF 1024
int main(int argc, char** argv)
{
SOCKET sockfd, new_fd;
struct sockaddr_in socket_ptr, their_addr;
char buf[MAXBUF + 1] = {0};
SSL_CTX* ctx;
// SSL库初始化
SSL_library_init();
// 载入所有SSL算法
OpenSSL_add_all_algorithms();
// 载入所有SSL错误消息
SSL_load_error_strings();
// 以SSLV2和V3标准兼容方式产生一个SSL_CTX即SSLContentText
ctx = SSL_CTX_new(SSLv23_server_method());
if (ctx == NULL)
{
std::cout << "[-] 产生CTX上下文对象错误" << std::endl;
return 0;
}
else
{
std::cout << "[+] 产生CTX上下文对象" << std::endl;
}
// 载入用户的数字证书,此证书用来发送给客户端,证书里包含有公钥
if (SSL_CTX_use_certificate_file(ctx, "d://cacert.pem", SSL_FILETYPE_PEM) <= 0)
{
std::cout << "[-] 载入公钥失败" << std::endl;
return 0;
}
else
{
std::cout << "[+] 已载入公钥" << std::endl;
}
// 载入用户私钥
if (SSL_CTX_use_PrivateKey_file(ctx, "d://privkey.pem", SSL_FILETYPE_PEM) <= 0)
{
std::cout << "[-] 载入私钥失败" << std::endl;
return 0;
}
else
{
std::cout << "[+] 已载入私钥" << std::endl;
}
// 检查用户私钥是否正确
if (!SSL_CTX_check_private_key(ctx))
{
std::cout << "[-] 用户私钥错误" << std::endl;
return 0;
}
// 开启Socket监听
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
WSACleanup();
return 0;
}
// 创建套接字
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
return 0;
}
socket_ptr.sin_family = AF_INET;
socket_ptr.sin_addr.s_addr = htonl(INADDR_ANY);
socket_ptr.sin_port = htons(9999);
// 绑定套接字
if (bind(sockfd, (struct sockaddr*)&socket_ptr, sizeof(struct sockaddr)) == -1)
{
return 0;
}
if (listen(sockfd, 10) == -1)
{
return 0;
}
while (1)
{
SSL* ssl;
int len = sizeof(struct sockaddr);
// 等待客户端连接
if ((new_fd = accept(sockfd, (struct sockaddr*)&their_addr, &len)) != -1)
{
printf("客户端地址: %s --> 端口: %d --> 套接字: %d \n", inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);
}
// 基于ctx产生一个新的SSL
ssl = SSL_new(ctx);
// 将连接用户的socket加入到SSL
SSL_set_fd(ssl, new_fd);
// 建立SSL连接
if (SSL_accept(ssl) == -1)
{
closesocket(new_fd);
break;
}
// 开始处理每个新连接上的数据收发
memset(buf, 0, MAXBUF);
strcpy(buf, "[服务端消息] hello lyshark");
// 发消息给客户端
len = SSL_write(ssl, buf, strlen(buf));
if (len <= 0)
{
goto finish;
return 0;
}
memset(buf, 0, MAXBUF);
// 接收客户端的消息
len = SSL_read(ssl, buf, MAXBUF);
if (len > 0)
{
printf("[接收到客户端消息] => %s \n", buf);
}
// 关闭套接字连接
finish:
SSL_shutdown(ssl);
SSL_free(ssl);
closesocket(new_fd);
}
closesocket(sockfd);
WSACleanup();
SSL_CTX_free(ctx);
system("pause");
return 0;
}
客户端实现代码同样与原生套接字编程保持一致,如下是完整代码,读者可以发现当使用connect连接到服务端后,依然调用了SSL_connect函数,此处的函数功能是在服务端下载证书信息,并完成证书通信验证,当验证实现后,则读者就可以向原生套接字那样去操作数据包的流向了。
#include <WinSock2.h>
#include <iostream>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/pem.h>
#include <openssl/crypto.h>
extern "C"
{
#include <openssl/applink.c>
}
#pragma comment(lib, "WS2_32.lib")
#pragma comment(lib,"libssl.lib")
#pragma comment(lib,"libcrypto.lib")
#define MAXBUF 1024
void ShowCerts(SSL* ssl)
{
X509* cert;
char* line;
cert = SSL_get_peer_certificate(ssl);
if (cert != NULL)
{
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("[+] 证书: %s \n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("[+] 颁发者: %s \n", line);
free(line);
X509_free(cert);
}
else
{
printf("[-] 无证书信息 \n");
}
}
int main(int argc, char** argv)
{
int sockfd, len;
struct sockaddr_in dest;
char buffer[MAXBUF + 1] = { 0 };
SSL_CTX* ctx;
SSL* ssl;
// SSL库初始化
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
// 建立CTX上下文
ctx = SSL_CTX_new(SSLv23_client_method());
if (ctx == NULL)
{
WSACleanup();
return 0;
}
// 创建Socket
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
WSACleanup();
return 0;
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
WSACleanup();
return 0;
}
// 初始化服务器端(对方)的地址和端口信息
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("127.0.0.1");
dest.sin_port = htons(9999);
// 连接服务器
if (connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0)
{
WSACleanup();
return 0;
}
// 基于ctx产生一个新的SSL
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sockfd);
// 建立 SSL 连接
if (SSL_connect(ssl) != -1)
{
printf("[+] SSL连接类型: %s \n", SSL_get_cipher(ssl));
ShowCerts(ssl);
}
//接收服务器来的消息 最多接收MAXBUF字节
len = SSL_read(ssl, buffer, MAXBUF);
if (len > 0)
{
printf("接收消息: %s --> 共 %d 字节 \n", buffer, len);
}
else
{
goto finish;
}
memset(buffer, 0, MAXBUF);
strcpy(buffer, "[客户端消息] hello Shark");
// 发消息给服务器
len = SSL_write(ssl, buffer, strlen(buffer));
if (len > 0)
{
printf("[+] 发送成功 \n");
}
finish:
// 关闭连接
SSL_shutdown(ssl);
SSL_free(ssl);
closesocket(sockfd);
SSL_CTX_free(ctx);
system("pause");
return 0;
}
至此读者可以分别编译服务端与客户端程序,并首先运行服务端侦听套接字,接着运行客户端,此时即可看到如下图所示的通信流程,至此两者的通信数据包将被加密传输,从而保证了数据的安全性。

20.7 OpenSSL 套接字SSL加密传输的更多相关文章
- linux下使用vsftp搭建FTP服务器:匿名登录,账号登录,SSL加密传输
目录 一.关于FTP和VSFTP 二.ftp.sftp.vsftp.vsftpd的区别 三.项目一:搭建一台所有人都可以访问的通用FTP服务器 3.1 项目要求 3.2 项目思路分析 3.3 使用vs ...
- .net mvc 站点自带简易SSL加密传输 Word报告自动生成(例如 导出数据库结构) 微信小程序:动画(Animation) SignalR 设计理念(一) ASP.NET -- WebForm -- ViewState ASP.NET -- 一般处理程序ashx 常用到的一些js方法,记录一下 CryptoJS与C#AES加解密互转
.net mvc 站点自带简易SSL加密传输 因项目需要,传输数据需要加密,因此有了一些经验,现简易抽出来分享! 请求:前端cryptojs用rsa/aes 或 rsa/des加密,后端.net ...
- Vsftpd支持SSL加密传输
ftp传输数据是明文,弄个抓包软件就可以通过数据包来分析到账号和密码,为了搭建一个安全性比较高ftp,可以结合SSL来解决问题 SSL(Secure Socket Layer)工作于传输层和应用程 ...
- 搭建支持SSL加密传输的vftpd
让vsftpd支持SSL 必须让OPENSSL≥0.9.6版本还有就是本身vsftpd版本是否支持 查询vsftpd软件是否支持SSL [root@localhost vsftpd]# ...
- linux网络编程-(socket套接字编程UDP传输)
今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...
- socket套接字TCP协议传输-案例测试
术语: 套接字接口:socket,是一个IP地址和一个端口号的组合,套接字可以唯一标识整个Internet中的一个网络进程. TCP连接:一对套接字接口(一个用于接收,一个用于发送)可定义面向连接的协 ...
- 使用JavaMail发送邮件,465端口开启ssl加密传输
package com.wangxin.test; import java.security.Security; import java.util.Date; import java.util.Pro ...
- SpringMvc使用JavaMail发送邮件,并使用465端口开启ssl加密传输,保证邮件安全
代码在本地发邮件没任何问题.但部署到服务器,发送邮件显示连接异常.一开始非常懵逼,之后telnet smtp.163.com 25不通,发现是云服务器出于安全考虑,关闭了服务器的25端口,而25端口是 ...
- .net mvc 站点自带简易SSL加密传输
因项目需要,传输数据需要加密,因此有了一些经验,现简易抽出来分享! 请求:前端cryptojs用rsa/aes 或 rsa/des加密,后端.net 解密返回后端.net用rsa/aes 或 rsa/ ...
- thrift之TTransport层的堵塞的套接字I/O传输类TSocket
本节将介绍第一个实现具体传输功能的类TSocket,这个类是基于TCP socket实现TTransport的接口.下面具体介绍这个类的相关函数功能实现. 1.构造函数 分析一个类的功能首先看它的定义 ...
随机推荐
- 正确处理 CSV 文件的引号和逗号
CSV(Comma-Separated Values,逗号分割值),就是用纯文本的形式存储表格数据,最大的特点就是方便. 作为开发,我们经常面临导数据的问题,特别是后台系统,产品或者运营的同事常常会提 ...
- 图技术在 LLM 下的应用:知识图谱驱动的大语言模型 Llama Index
LLM 如火如荼地发展了大半年,各类大模型和相关框架也逐步成型,可被大家应用到业务实际中.在这个过程中,我们可能会遇到一类问题是:现有的哪些数据,如何更好地与 LLM 对接上.像是大家都在用的知识图谱 ...
- DevOps实战:使用GitLab+Jenkins+Kubernetes(k8s)建立CI/CD解决方案
目录 一.系统环境 二.前言 三.DevOps简介 四.CI/CD简介 五.安装并配置docker参数 六.使用registry搭建镜像仓库 七.安装部署gitlab代码仓库 7.1 创建gitlab ...
- 快速解决 const 与 typedef 类型组合时 ,const修饰谁的问题
C++使用typedef 给复合类型定义别名时,与const结合会产生看似"令人困惑"的类型推定,例如 typedef char* pstring; const pstring c ...
- CAJViewer卡的解决办法
在做毕业设计时,使用了CAJViewer7.2版本的阅读器,使用起来非常卡,翻页总是不流畅,体验感极差. 最后实在受不了了,去百度了一下,在贴吧中看到了疑似解决方案,尝试了一下,真的不卡了.所以特此写 ...
- 1Nginx基础及编译安装
1Nginx基础 1.Nginx概述 Nginx 功能介绍 Nginx(发音为"engine-x")是一个开源的高性能 HTTP和反向代理服务器.它具有以下功能: 1.静态文件服务 ...
- 《Kali渗透基础》15. WEB 渗透
@ 目录 1:WEB 技术 1.1:WEB 攻击面 1.2:HTTP 协议基础 1.3:AJAX 1.4:WEB Service 2:扫描工具 2.1:HTTrack 2.2:Nikto 2.3:Sk ...
- c++中的宏#define用途
宏的一些作用,包括但不限于这些 定义一个变量.字符串.类型 定义一个函数.条件表达式 条件编译.调试信息,异常类 定义结构体.命名空间 定义模版.枚举.函数对象 #define宏定义在C++中用于定义 ...
- Gradle安装配置教程
一.安装前检查 检查电脑上是否安装JDK,如果没有安装,请查看JDK安装教程:点击查看 如果电脑上已经安装JDK,按Win + R键,输入cmd,然后点击确定 输入java -version,点击回车 ...
- 用OLED屏幕播放视频(3): 使用cuda编程加速视频处理
下面的系列文章记录了如何使用一块linux开发扳和一块OLED屏幕实现视频的播放: 项目介绍 为OLED屏幕开发I2C驱动 使用cuda编程加速视频处理 这是此系列文章的第3篇, 主要总结和记录了如何 ...