将简单套接字转换为 SSL 套接字

我编写了简单的 C 程序,它们使用套接字(“客户端”和“服务器”)。 (UNIX/Linux 使用)

服务器端只是创建一个套接字:

sockfd = socket(AF_INET, SOCK_STREAM, 0);

然后绑定到 sockaddr:

bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));

倾听(接受并阅读) :

listen(sockfd,5);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
read(newsockfd,buffer,255);

客户端创建套接字,然后写入该套接字。

现在,我想以最简单、最田园诗般的方式将这个简单的连接转换为 SSL 连接。

我试图将 OpenSSL添加到我的项目中,但是我找不到一种简单的方法来实现我想要的东西。

171962 次浏览

OpenSSL 是相当困难的。因为没有完全正确地进行谈判,所以很容易意外地放弃所有的安全保障。(见鬼,我个人曾经被一个 bug 咬过,curl 读取 OpenSSL 警报的方式不完全正确,并且无法与某些网站交流。)

如果你真的想快速简单,把 种马在你的程序前面一天叫它。在不同的进程中使用 SSL 不会减慢您的速度: http://vincent.bernat.im/en/blog/2011-ssl-benchmark.html

使用 OpenSSL 时有几个步骤。您必须制作一个 SSL 证书,该证书可以包含带有私钥的证书,确保指定证书的确切位置(本例将其放在根目录中)。有很多很好的教程。

其中包括:

#include <openssl/applink.c>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

您需要初始化 OpenSSL:

void InitializeSSL()
{
SSL_load_error_strings();
SSL_library_init();
OpenSSL_add_all_algorithms();
}


void DestroySSL()
{
ERR_free_strings();
EVP_cleanup();
}


void ShutdownSSL()
{
SSL_shutdown(cSSL);
SSL_free(cSSL);
}

接下来是大部分的功能。您可能需要在连接上添加 while 循环。

int sockfd, newsockfd;
SSL_CTX *sslctx;
SSL *cSSL;


InitializeSSL();
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd< 0)
{
//Log and Error
return;
}
struct sockaddr_in saiServerAddress;
bzero((char *) &saiServerAddress, sizeof(saiServerAddress));
saiServerAddress.sin_family = AF_INET;
saiServerAddress.sin_addr.s_addr = serv_addr;
saiServerAddress.sin_port = htons(aPortNumber);


bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));


listen(sockfd,5);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);


sslctx = SSL_CTX_new( SSLv23_server_method());
SSL_CTX_set_options(sslctx, SSL_OP_SINGLE_DH_USE);
int use_cert = SSL_CTX_use_certificate_file(sslctx, "/serverCertificate.pem" , SSL_FILETYPE_PEM);


int use_prv = SSL_CTX_use_PrivateKey_file(sslctx, "/serverCertificate.pem", SSL_FILETYPE_PEM);


cSSL = SSL_new(sslctx);
SSL_set_fd(cSSL, newsockfd );
//Here is the SSL Accept portion.  Now all reads and writes must use SSL
ssl_err = SSL_accept(cSSL);
if(ssl_err <= 0)
{
//Error occurred, log and close down ssl
ShutdownSSL();
}

然后你就可以用以下方法读或写:

SSL_read(cSSL, (char *)charBuffer, nBytesToRead);
SSL_write(cSSL, "Hi :3\n", 6);

更新 应该使用最适合您需要的 TLS 方法调用 SSL_CTX_new,以支持更新版本的安全性,而不是 SSLv23_server_method()。参见: OpenSSL SSL _ CTX _ new 描述

TLS _ method () ,TLS _ server _ method () ,TLS _ client _ method (). 这些是通用的 版本-灵活性 SSL/TLS 方法。实际使用的协议版本将协商到客户端和服务器相互支持的最高版本。支持的协议是 SSLv3、 TLSv1、 TLSv1.1、 TLSv1.2和 TLSv1.3。

对于像我这样的人:

在目录 demos/ssl/的 SSL 源中曾经有一个示例,其示例代码是 C + + 。现在它只能通过历史记录获得: Https://github.com/openssl/openssl/tree/691064c47fd6a7d11189df00a0d1b94d8051cbe0/demos/ssl

你可能需要找到一个工作版本,我最初在2015年11月6日发布了这个答案。我还得编辑源代码,不多。

证书: . pem in demos/certs/apps/: https://github.com/openssl/openssl/tree/master/demos/certs/apps

这里我的示例 ssl 套接字服务器线程(多连接) https://github.com/breakermind/CppLinux/blob/master/QtSslServerThreads/breakermindsslserver.cpp

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <unistd.h>
#include <iostream>


#include <breakermindsslserver.h>


using namespace std;


int main(int argc, char *argv[])
{
BreakermindSslServer boom;
boom.Start(123,"/home/user/c++/qt/BreakermindServer/certificate.crt", "/home/user/c++/qt/BreakermindServer/private.key");
return 0;
}

就像你见过的那样,毛茸茸的。但是“简单”的部分是,在使用 OpenSSL 设置了 SSL/TLS 会话之后,在 HTTPS 套接字上读/写所遵循的模式与在 HTTP 上遵循的模式相同。

区别在于您使用的是 SSL_read/SSL_write函数,而不是 read/write函数。SSL_read/SSL_write函数的第一个参数是 SSL指针,而不是文件描述符。

无论如何,下面是一个完整的 Unix 环境 OpenSSL 示例,包含编译/运行指令、 APT 依赖项和引用。这只是一些工作代码,你可以用它们来开始你的新生活。

在成功编译时,您将看到一个关于已废弃的 OpenSSL 函数的警告。

运行成功后,您将看到 example.com 的 TLS 证书主题被打印到标准输出中,然后是 example.com 的 HTML 内容。

Https://github.com/angstyloop/c-web/blob/main/openssl-fetch-example.c

在 Ubuntu 22.04上测试。