我有一个用C ++编写的用于Windows的客户端/服务器程序,我几乎将OpenSSL集成到了。
我确实想说明我查看了与我自己类似的所有其他帖子,我还没有找到问题的正确答案。大多数其他exmaples非常接近答案,但我似乎无法产生实际结果。
对于我的测试用例,我只是每隔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()语句。这种方法取自本论坛的另一个主题,比如你应该读取UNTIL,调用SSL_ERROR_WANT_READ / WRITER。另外,我确实使用SSL_pending()但它总是在我拥有的每种情况下返回0,从来不是非零数字。
该程序使用非阻塞套接字编写,并对Windows消息进行WSAASyncSelect调用。所有这一切都很好。
但是,所发生的事情与我的预期相反。代码似乎总是等待不存在的数据。我意识到SSL套接字也意味着在随机时间进行握手。我只想弄清楚为什么一旦我收到来自服务器的数据,为什么看起来SSL_read似乎有数据,或者select()检测数据需要在我发送的5秒间隔之间读取我的数据包,以便select()循环在整个时间内阻塞。即使数据包被完全读取,下一条消息也会被回调到读取函数中,程序本身永远不会让此函数执行任何其他过程。非常感谢任何帮助,我以前从未接受过我的智慧结束编程,这让我非常接近它。
更新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 */
}