站长之家0,免费推广平台有哪些软件,手游源码,长春网站建长春做网站1 概述
tcp server和tcp client同时使用openssl库#xff0c;可对通信双方流通的字节序列进行加解密#xff0c;保障通信的安全。本文以c编写的tcp server和tcp client为例子#xff0c;openssl的版本为v3。
2 安装openssl v3
2.1 安装 perl-IPC-Cmd
openssl项目中的co…1 概述
tcp server和tcp client同时使用openssl库可对通信双方流通的字节序列进行加解密保障通信的安全。本文以c编写的tcp server和tcp client为例子openssl的版本为v3。
2 安装openssl v3
2.1 安装 perl-IPC-Cmd
openssl项目中的config脚本需要用到perl-IPC-Cmd工具。
yum -y install perl-IPC-Cmd2.2 安装openssl v3
安装的结果放在目录/opt/openssl中。
mkdir -p /opt
cd /opt
wget --no-check-certificate https://www.openssl.org/source/openssl-3.1.0.tar.gz
tar xf openssl-3.1.0.tar.gz
cd openssl-3.1.0
./config --prefix/opt/openssl
make -j8
make install安装完毕后结果如下图所示 3 生成SSL私钥和证书
3.1 创建自签CA
以下将生成一个名称为ca.key的key文件和一个名称为ca.crt的证书文件CA的common name叫EXAMPLE Cert Authority。
openssl req -x509 -sha256 -newkey rsa:4096 -keyout ca.key -out ca.crt \
-days 3650 \
-nodes -subj /CNEXAMPLE Cert Authority3.2 生成key文件和csr文件
key文件名称为server.keycsr文件名称为server.csrcommon name为apigateway.example.com。
openssl req -new -newkey rsa:4096 -keyout server.key -out server.csr -nodes \
-subj /CNapigateway.example.com3.2 编写csr扩展文件extfile
SAN就在subjectAltName字段中指定可以写多个域名和IP例如域名为apigatewayIP为192.168.16.128。
cat extfile.cnf EOF
[ v3_req ]
# Extensions to add to a certificate request
extendedKeyUsage serverAuth,clientAuth
subjectAltName DNS:apigateway.example.com,DNS:localhost,IP:192.168.16.128,IP:127.0.0.1
basicConstraints CA:FALSE
keyUsage nonRepudiation, digitalSignature, keyEncipherment
EOF3.3 生成证书文件
依据key文件、csr文件和csr扩展文件生成证书文件名称为server.crt
openssl x509 -req -sha256 -days 3650 \
-extfile extfile.cnf \
-extensions v3_req \
-in server.csr \
-CA ca.crt \
-CAkey ca.key \
-out server.crt \
-CAcreateserial3.4 检验证书文件是否由指定CA签署
openssl verify -CAfile ca.crt -verbose server.crt4 编写服务端代码
代码来自博客https://blog.csdn.net/lf940841989/article/details/16881331
#include stdio.h
#include stdlib.h
#include errno.h
#include string.h
#include sys/types.h
#include netinet/in.h
#include sys/socket.h
#include sys/wait.h
#include unistd.h
#include arpa/inet.h
#include openssl/ssl.h
#include openssl/err.h#define MAXBUF 1024int main(int argc, char **argv)
{int sockfd, new_fd;socklen_t len;struct sockaddr_in my_addr, their_addr;unsigned int myport, lisnum;char buf[MAXBUF 1];SSL_CTX *ctx;printf(Usage: %s port listenno(1) ip publickey privaterkey\n, argv[0]);if(argc ! 6) {printf(parameter error\n);exit(0);}if (argv[1])myport atoi(argv[1]);elsemyport 7838;if (argv[2])lisnum atoi(argv[2]);elselisnum 2;/* SSL 库初始化*/SSL_library_init();/* 载入所有SSL 算法*/OpenSSL_add_all_algorithms();/* 载入所有SSL 错误消息*/SSL_load_error_strings();/* 以SSL V2 和V3 标准兼容方式产生一个SSL_CTX 即SSL Content Text */ctx SSL_CTX_new(SSLv23_server_method());//ctx SSL_CTX_new(SSLv3_server_method());/* 也可以用SSLv2_server_method() 或SSLv3_server_method() 单独表示V2 或V3* 标准*/if (ctx NULL) {ERR_print_errors_fp(stdout);exit(1);}/* 载入用户的数字证书 此证书用来发送给客户端。证书里包含有公钥*/if (SSL_CTX_use_certificate_file(ctx, argv[4], SSL_FILETYPE_PEM) 0) {ERR_print_errors_fp(stdout);exit(1);}/* 载入用户私钥*/if (SSL_CTX_use_PrivateKey_file(ctx, argv[5], SSL_FILETYPE_PEM) 0) {ERR_print_errors_fp(stdout);exit(1);}/* 检查用户私钥是否正确*/if (!SSL_CTX_check_private_key(ctx)) {ERR_print_errors_fp(stdout);exit(1);}/* 开启一个socket 监听*/if ((sockfd socket(PF_INET, SOCK_STREAM, 0)) -1) {perror(socket);exit(1);} elseprintf(socket created\n);bzero(my_addr, sizeof(my_addr));my_addr.sin_family PF_INET;my_addr.sin_port htons(myport);if (argv[3])my_addr.sin_addr.s_addr inet_addr(argv[3]);elsemy_addr.sin_addr.s_addr INADDR_ANY;if (bind(sockfd, (struct sockaddr *) my_addr, sizeof(struct sockaddr)) -1) {perror(bind);exit(1);} elseprintf(binded\n);if (listen(sockfd, lisnum) -1) {perror(listen);exit(1);} elseprintf(begin listen\n);while (1) {SSL *ssl;len sizeof(struct sockaddr);/* 等待客户端连上来*/if ((new_fd accept(sockfd, (struct sockaddr *) their_addr,len)) -1) {perror(accept);exit(errno);} elseprintf(server: got connection from %s, port %d, socket %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) {perror(accept);close(new_fd);break;}/* 开始处理每个新连接上的数据收发*/bzero(buf, MAXBUF 1);strcpy(buf, server-client);/* 发消息给客户端*/len SSL_write(ssl, buf, strlen(buf));if (len 0) {printf(消息%s发送失败错误代码是%d错误信息是%s\n,buf, errno, strerror(errno));goto finish;} elseprintf(消息%s发送成功共发送了%d 个字节\n,buf, len);bzero(buf, MAXBUF 1);/* 接收客户端的消息*/len SSL_read(ssl, buf, MAXBUF);if (len 0)printf(接收消息成功:%s共%d 个字节的数据\n,buf, len);elseprintf(消息接收失败错误代码是%d错误信息是%s\n,errno, strerror(errno));/* 处理每个新连接上的数据收发结束*/finish:/* 关闭SSL 连接*/SSL_shutdown(ssl);/* 释放SSL */SSL_free(ssl);/* 关闭socket */close(new_fd);}/* 关闭监听的socket */close(sockfd);/* 释放CTX */SSL_CTX_free(ctx);return 0;
}5 编写客户端代码
代码来自博客https://blog.csdn.net/lf940841989/article/details/16881331
#include stdio.h
#include string.h
#include errno.h
#include sys/socket.h
#include resolv.h
#include stdlib.h
#include netinet/in.h
#include arpa/inet.h
#include unistd.h
#include openssl/ssl.h
#include openssl/err.h#define MAXBUF 1024void ShowCerts(SSL * ssl)
{X509 *cert;char *line;cert SSL_get_peer_certificate(ssl);if (cert ! NULL) {printf(数字证书信息:\n);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);} elseprintf(无证书信息\n);
}int main(int argc, char **argv)
{int sockfd, len;struct sockaddr_in dest;char buffer[MAXBUF 1];SSL_CTX *ctx;SSL *ssl;printf(Usage: %s server_ip server_port\n, argv[0]);if (argc ! 3) {printf(参数格式错误正确用法,比如: %s 127.0.0.1 80\n此程序用来从某个IP 地址的服务器某个端口接收最多MAXBUF 个字节的消息\n,argv[0]);exit(0);}/* SSL 库初始化参看ssl-server.c 代码*/SSL_library_init();OpenSSL_add_all_algorithms();SSL_load_error_strings();ctx SSL_CTX_new(SSLv23_client_method());// ctx SSL_CTX_new(SSLv3_client_method());if (ctx NULL) {ERR_print_errors_fp(stdout);exit(1);}/* 创建一个socket 用于tcp 通信*/if ((sockfd socket(AF_INET, SOCK_STREAM, 0)) 0) {perror(Socket);exit(errno);}printf(socket created\n);/* 初始化服务器端对方的地址和端口信息*/bzero(dest, sizeof(dest));dest.sin_family AF_INET;dest.sin_port htons(atoi(argv[2]));if (inet_aton(argv[1], (struct in_addr *) dest.sin_addr.s_addr) 0) {perror(argv[1]);exit(errno);}printf(address created\n);/* 连接服务器*/if (connect(sockfd, (struct sockaddr *) dest, sizeof(dest)) ! 0) {perror(Connect );exit(errno);}printf(server connected\n);/* 基于ctx 产生一个新的SSL */ssl SSL_new(ctx);SSL_set_fd(ssl, sockfd);/* 建立SSL 连接*/if (SSL_connect(ssl) -1)ERR_print_errors_fp(stderr);else {printf(Connected with %s encryption\n, SSL_get_cipher(ssl));ShowCerts(ssl);}/* 接收对方发过来的消息最多接收MAXBUF 个字节*/bzero(buffer, MAXBUF 1);/* 接收服务器来的消息*/len SSL_read(ssl, buffer, MAXBUF);if (len 0)printf(接收消息成功:%s共%d 个字节的数据\n,buffer, len);else {printf(消息接收失败错误代码是%d错误信息是%s\n,errno, strerror(errno));goto finish;}bzero(buffer, MAXBUF 1);strcpy(buffer, from client-server);/* 发消息给服务器*/len SSL_write(ssl, buffer, strlen(buffer));if (len 0)printf(消息%s发送失败错误代码是%d错误信息是%s\n,buffer, errno, strerror(errno));elseprintf(消息%s发送成功共发送了%d 个字节\n,buffer, len);finish:/* 关闭连接*/SSL_shutdown(ssl);SSL_free(ssl);close(sockfd);SSL_CTX_free(ctx);return 0;
} 6 编译服务端代码并运行
g -o server server_ssl.c -Wl,-rpath,/opt/openssl/lib64 -L/opt/openssl/lib64 -lssl -lcrypto -I/opt/openssl/include./server 8888 1 127.0.0.1 server.crt server.key7 编译客户端代码
g -o client client_ssl.c -L/opt/openssl/lib64 -lssl -lcrypto -I/opt/openssl/include -Wl,-rpath,/opt/openssl/lib648 运行客户端
./client 127.0.0.1 8888客户端的输出如下图
客户端会打印服务端传输过来的ssl证书CA和CN正是本文在创建证书阶段所填写的信息说明在TCP握手之后进行SSL握手也是成功的。握手成功后往服务端发送信息from client-server。
此时服务端的输出如下图 服务端在SSL握手成功后通过SSL_read()方法接收来自客户端发送过来的加密信息并自动解密明文信息为from client-server。 9 小结
tcp握手成功后服务端和客户端代码使用openssl库能加解密tcp中传输的字节序列保障通信的安全。在代码编写套路上服务端和客户端代码在拿到fd之后将fd塞进ssl上下文对象中使用格式如SSL_XXX的方法来进行SSL握手、读写数据。