首页 > 其他分享 >20.7 OpenSSL 套接字SSL加密传输

20.7 OpenSSL 套接字SSL加密传输

时间:2023-11-05 14:33:24浏览次数:47  
标签:加密传输 lib SSL ctx ssl new 接字 include

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;
}

至此读者可以分别编译服务端与客户端程序,并首先运行服务端侦听套接字,接着运行客户端,此时即可看到如下图所示的通信流程,至此两者的通信数据包将被加密传输,从而保证了数据的安全性。

标签:加密传输,lib,SSL,ctx,ssl,new,接字,include
From: https://blog.51cto.com/lyshark/8193671

相关文章

  • 20.7 OpenSSL 套接字SSL加密传输
    OpenSSL中的SSL加密是通过SSL/TLS协议来实现的。SSL/TLS是一种安全通信协议,可以保障通信双方之间的通信安全性和数据完整性。在SSL/TLS协议中,加密算法是其中最核心的组成部分之一,SSL可以使用各类加密算法进行密钥协商,一般来说会使用RSA等加密算法,使用TLS加密针对服务端来......
  • 20.6 OpenSSL 套接字分发RSA公钥
    通过上一节的学习读者应该能够更好的理解RSA加密算法在套接字传输中的使用技巧,但上述代码其实并不算完美的,因为我们的公钥和私钥都必须存储在本地文本中且公钥与私钥是固定的无法做到更好的保护效果,而一旦公钥与私钥泄密则整个传输流程都将会变得不安全,最好的保护效果是RSA密钥在每......
  • 20.6 OpenSSL 套接字分发RSA公钥
    通过上一节的学习读者应该能够更好的理解RSA加密算法在套接字传输中的使用技巧,但上述代码其实并不算完美的,因为我们的公钥和私钥都必须存储在本地文本中且公钥与私钥是固定的无法做到更好的保护效果,而一旦公钥与私钥泄密则整个传输流程都将会变得不安全,最好的保护效果是RSA密钥在......
  • shell 升级openssl-1.1.1n openssh-8.9p1
    #!/bin/bash#基于CentOS7.5编写#功能实现升级openssl-1.1.1nopenssh-8.9p1#检测基础环境是否安装yumupdateopenssh-yyuminstall-ygccgcc-c++glibcmakeautoconfopensslopenssl-develpcre-develpam-develyuminstall-ypam*zlib*#configure:error:......
  • TCP的通信流程和socket套接字完成服务端和客户端通信
    一、TCP是一个面向连接的、安全的、流式传输协议,这个协议是传输层协议。面向连接:是一个双向连接,通过三次握手建立连接,通过四次挥手断开连接。安全:tcp通信的过程中,会对发送的每一数据包都会进行校验,如果发现数据丢失,会自动重传。流式传输:发送端和接受端处理数据的速......
  • 20.5 OpenSSL 套接字RSA加密传输
    RSA算法同样可以用于加密传输,但此类加密算法虽然非常安全,但通常不会用于大量的数据传输,这是因为RSA算法加解密过程涉及大量的数学运算,尤其是模幂运算(即计算大数的幂模运算),这些运算对于计算机而言是十分耗时。其次在RSA算法中,加密数据的长度不能超过密钥长度减去一定的填充长度。......
  • SSL证书链及使用
    什么是证书链证书链简单来说是域名钥证书、CA公钥、根证书形成的一个颁发链条,属于公钥的一部分。更白话一点,就是证书链文件包含一系列CA机构公钥的证书。证书链格式一般证书链格式是.chain,证书定义顺序是倒序的,即先权威CA再根CA。以根CA+一个权威CA举例:-----BEGINCERTIFICA......
  • SSL证书检查与报警
    不管是iOSAPP,微信小程序,还是浏览器,随着对安全要求的提高,HTTPS已经几乎是所有网站的标配的。作为运维,监控并保证SSL证书的可用性就成了一个很现实的问题。在早期,域名不是很多的情况下,使用的SSL证书数量有限。可以采用手动录入,定期检查的方式来确保SSL证书在有效期内。但是随着业务......
  • 未能创建 SSL/TLS 安全通道
    事件背景对接ebay的时候,报错:未能创建SSL/TLS安全通道调试发现使用RestSharp并不会,HttpClient不行,猜测是RestSharp底层处理了TLS1.2的支持查阅资料"未能创建SSL/TLS安全通道"错误通常是由于TLS版本或加密协议不匹配引起的。通常情况下,你可以通过更新你的.NET版本来......
  • 20.4 OpenSSL 套接字AES加密传输
    在读者了解了加密算法的具体使用流程后,那么我们就可以使用这些加密算法对网络中的数据包进行加密处理,加密算法此处我们先采用AES算法,在网络通信中,只需要在发送数据之前对特定字符串进行加密处理,而在接收到数据后在使用相同的算法对数据进行恢复即可,读者如果有了套接字编程的基础,那......