2

我有一个用 C++ 编写的 Windows 客户端/服务器程序,我几乎将 OpenSSL 集成到其中。

我确实想声明,我已经查看了与我自己类似的所有其他帖子,但我还没有找到我的问题的正确答案。大多数其他示例都非常接近答案,但我似乎无法产生实际结果。

对于我的测试用例,我只是每 5 秒使用 OpenSSL 安全套接字将 7 个字符的字符串从客户端传递到服务器。我没有发送任何其他信息。最初的问题是我没有正确使用 SSL_read 并且我没有读取所需的明显额外数据,并且 SSL_read 将停止响应并且客户端将失去连接。

在阅读了所有其他示例之后,我进一步完成了这一步,但我永远无法得到想要的结果。我尝试了超过 10 种类型的 SSL_read 实现,但在读取数据时我无法让代码成为非阻塞的。我将发布我最新的实现(不是难以置信的优化):

int received = -1;
int tE = 0;

    if( tE != SSL_ERROR_WANT_READ && tE != SSL_ERROR_WANT_WRITE )
    {
        received = SSL_read(sSecureSocket.ssl, buffer, sizeof(buffer)); 
        if( received == -1 )
        {
            tE = SSL_get_error( sSecureSocket.ssl, received );
            if( tE == SSL_ERROR_WANT_READ )
            {
                int tReadWaiting = 1;
                while( tReadWaiting )
                {
                    tReadWaiting = 0;
                    fd_set rfd;
                    FD_ZERO(&rfd);
                    FD_SET(receiveSocket, &rfd);
                    timeval tv = { 0 };
                    select(receiveSocket+1, &rfd, 0, 0, &tv);
                    if (!FD_ISSET(receiveSocket, &rfd))
                    {
                        tReadWaiting = 1;
                    }
                }
                received = SSL_read(sSecureSocket.ssl, buffer, sizeof(buffer)); 
            }
        }
    }

这段代码在每次发送数据包时都会成功提取数据包,但是如您所见,一旦触发 SSL_ERROR_WANT_READ/WRITE,我必须使用 select() 语句。这种方法取自本论坛的另一个主题,假设您应该阅读直到调用 SSL_ERROR_WANT_READ/WRITER。另外,我确实使用了 SSL_pending() 但在我拥有的每种情况下它总是返回 0,它绝不是非零数。

该程序是用非阻塞套接字编写的,带有对 Windows 消息的 WSAASyncSelect 调用。所有这些工作正常。

但发生的事情与我的预期相反。代码似乎总是等待不存在的数据。我意识到 SSL 套接字也可以在随机时间进行握手。我只是想弄清楚为什么一旦我收到来自服务器的数据,为什么 SSL_read 似乎有数据,或者 select() 检测到需要在我发送的 5 秒间隔之间读取数据我的数据包,以便 select() 循环一直阻塞。即使数据包被完全读取,下一条消息也会被回调到 read 函数中,并且程序本身永远不能离开这个函数来执行任何其他过程。

非常感谢任何帮助,我以前从来没有在我的智慧结束编程,这让我非常接近它。

更新 1:

这是套接字监听代码:

ctx = sSecureSocket.InitServerCTX();        // initialize SSL 
    sSecureSocket.LoadCertificates(ctx, VGlobal::CURRWORKDIR + "\\openssl2\\" + "mycert.pem", VGlobal::CURRWORKDIR + "\\openssl2\\" + "mycert.pem"); // load certs 
    servSecureSocket = sSecureSocket.OpenListener(39245);    // create server socket 

当服务器上接受套接字时调用它:

//Always connect as a secure socket first
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
SSL *ssl;

receiveSocket = accept(servSecureSocket, (struct sockaddr*)&addr, &len);  // accept connection as usual 
printf("Connection: %s:%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
sSecureSocket.ssl = SSL_new(ctx);              // get new SSL state with context 
SSL_set_fd(sSecureSocket.ssl, receiveSocket);      // set connection socket to SSL state 
sSecureSocket.Servlet(sSecureSocket.ssl);         // service connection 

这些是功能:

//-----------------------------------------------------
int SecureSocket::OpenListener(int port)
{   int sd;
struct sockaddr_in addr;

WSADATA wsadata;
int error = WSAStartup( 0x0202, &wsadata );

if( error )
    return false;

sd = socket(PF_INET, SOCK_STREAM, 0);
//bzero(&addr, sizeof(addr));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
    perror("can't bind port");
   // abort();
}
if ( listen(sd, 10) != 0 )
{
    perror("Can't configure listening port");
   // abort();
}
return sd;
}

//-----------------------------------------------------
SSL_CTX* SecureSocket::InitServerCTX(void)
{   
SSL_METHOD *method;
SSL_CTX *ctx;

OpenSSL_add_all_algorithms();  /* load & register all cryptos, etc. */
SSL_load_error_strings();   /* load all error messages */
ctx = SSL_CTX_new(SSLv23_server_method());
if ( ctx == NULL )
{
    ERR_print_errors_fp(stderr);
}
return ctx;
}

//-----------------------------------------------------
void SecureSocket::LoadCertificates(SSL_CTX* ctx, CString CertFile, CString KeyFile)
{
/* set the local certificate from CertFile */
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
{
    ERR_print_errors_fp(stderr);
}
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
{
    ERR_print_errors_fp(stderr);
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
    fprintf(stderr, "Private key does not match the public certificate\n");
}
}

//-----------------------------------------------------
void SecureSocket::ShowCerts(SSL* ssl)
{   X509 *cert;
char *line;

cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
if ( cert != NULL )
{
    printf("Server certificates:\n");
    line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
    printf("Subject: %s\n", line);
    free(line);
    line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
    printf("Issuer: %s\n", line);
    free(line);
    X509_free(cert);
}
else
    printf("No certificates.\n");
}

//-----------------------------------------------------
void SecureSocket::Servlet(SSL* ssl) /* Serve the connection -- threadable */
{
int acceptDone = SSL_accept(ssl);

//If this blocking this will be done immediately, if it is not we must loop until it is complete
while( acceptDone < 1 )
{
    acceptDone = SSL_accept(ssl);
}

ShowCerts(ssl);        /* get any certificates */
}
4

0 回答 0