【问题标题】:Turn a simple socket into an SSL socket将简单的套接字变成 SSL 套接字
【发布时间】:2011-12-03 15:38:17
【问题描述】:

我编写了简单的 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 添加到我的项目中,但我找不到一种简单的方法来实现我想要的。

【问题讨论】:

  • 如果您正在寻找“安全连接”而不是 SSL,您可以查看位于您的应用程序外部的类似 proxychains.sourceforge.net 的内容,并将其设置为通过 SSH 发送流量联系。就应用程序内 SSL 而言,如果您了解 SSL/TLS 应该如何工作,那么 OpenSSL 非常容易。如果您想要替代方案,请尝试 yaSSL 或 gnuTLS 。
  • 定义“简单方法”。 OpenSSl 是 C 程序员的标准。如果你有困难,你应该问一下。
  • 检查这个An Introduction to OpenSSL Programming (Par t I)。第二部分对我来说太高级太难了。不过part2还是值得一看的。
  • 同时检查Secure programming with the OpenSSL API。但我刚刚听到关于 Openssl 有多糟糕以及其他值得一试的替代方案的意见。
  • 另一种选择是使用外部 SSL 包装工具,例如 stunnelstunnel4 软件包位于基于 Debian 的发行版中,并且易于使用。与在您的服务器中添加适当的 SSL 支持相比,存在一些限制,但它对于快速解决方案可能是有益的。我喜欢 stunnel,因为它似乎适合 UNIX 软件工具方法。

标签: c linux sockets unix ssl


【解决方案1】:

对于像我这样的其他人:

曾经在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

【讨论】:

    【解决方案2】:

    使用 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 description

    TLS_method()、TLS_server_method()、TLS_client_method()。 这些是通用的版本灵活 SSL/TLS 方法。实际使用的协议版本将协商为客户端和服务器相互支持的最高版本。支持的协议有 SSLv3、TLSv1、TLSv1.1、TLSv1.2 和 TLSv1.3。

    【讨论】:

    • 不像我想象的那么“简单”,但最后(感谢上帝!)我看到了一些代码。这是跨平台的还是仅适用于类 unix/unix 系统?
    • 我在多个平台上使用过类似的代码:arm、linux和windows。
    • 最后一个if 是错误的。它应该是 if (ssl_err &lt;= 0) { 只有这样它是一个错误。 SSL_accept() 在成功时返回 1,在“受控失败”时返回 0,在“致命失败”时返回 -1。请参阅手册页。
    • 此外,如果不调用 SSL_CTX_set_tmp_dh[_callback](),DH 密码将不起作用。刚刚发现没有它,aNULL 密码将无法工作,产生警报号 40。
    • @DevNull SSLv23_server_method() 声明服务器支持 SSLv2 和 v3,现在已弃用。要支持 TLS 1.1 和 1.2,请将该方法替换为 TLS_server_method()source
    【解决方案3】:

    这是我的示例 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;
    }
    

    【讨论】:

    • 请将解决方案直接包含在您的 SO 帖子中。
    【解决方案4】:

    OpenSSL 相当困难。由于没有完全正确地进行谈判,很容易意外地放弃所有安全性。 (哎呀,我个人被一个错误所困扰,即 curl 没有完全正确地读取 OpenSSL 警报,并且无法与某些网站对话。)

    如果您真的想要快速和简单,请将stud 放在您的程序前面,然后就可以收工了。在不同的进程中使用 SSL 不会减慢您的速度:http://vincent.bernat.im/en/blog/2011-ssl-benchmark.html

    【讨论】:

    • 这是一个实用的答案,但并不能真正回答问题。
    • STUD 于 2016 年被废弃。自述文件推荐:github.com/varnish/hitch
    猜你喜欢
    • 2013-12-25
    • 2023-03-26
    • 1970-01-01
    • 2016-09-03
    • 2013-09-18
    • 1970-01-01
    • 2013-10-14
    • 2016-08-19
    • 2011-06-22
    相关资源
    最近更新 更多