【发布时间】:2017-01-12 16:18:57
【问题描述】:
我想使用以下代码发送此消息“Hello world!\n Bye world!”。此代码在 2 个 SSL 记录中发送消息,但我需要在 1 个 SSL 记录中发送它。
我读过一些关于 base64 here 和 here 的内容,但我不知道如何在我的代码中使用它们!谁能帮我解决我的问题。
问题:我想用“\n”分隔的部分而不是2部分发送消息!
更多说明: 我使用 OpenSSL s_client 连接它,我强制它使用 TLSv1 并使用wireshark 嗅探网络。实际上,此代码是作为数据库代理的更大项目的简化。在主项目中,我们需要向不受我们控制的服务器发送消息,它只能处理 1 个 SSL 记录。
我的 OpenSSL 命令:
s_client -connect 127.0.0.1:9999 -tls1
Wireshark 消息结果:
17 03 01 00 24 db f3 59 37 98 78 3b b6 06 b0 c1 66 0c 78 04 4d 50 60 54 19 37 fe
77 65 27 7f 4e e8 4e 9a d7 94 66 3f 0d 03 17 03 01 00 34 83 a8 39 b3 3e 9a 35 7b
a2 64 07 35 9b c5 d7 d0 c9 03 3c 43 ac d8 1c ad d5 0f 55 34 10 6c 99 3e 57 b1 51
d4 a5 36 6a 8e 23 08 e0 2d 33 c1 53 63 4e d2 bd bd
我的模拟代码:
#include <cstdlib>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <unistd.h>
using namespace std;
static void ssl_set_sys_error(int ssl_error)
{
int error = 0;
switch (ssl_error) {
case SSL_ERROR_ZERO_RETURN:
error = 0;
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
#ifdef SSL_ERROR_WANT_CONNECT
case SSL_ERROR_WANT_CONNECT:
#endif
#ifdef SSL_ERROR_WANT_ACCEPT
case SSL_ERROR_WANT_ACCEPT:
#endif
error = 1;
break;
case SSL_ERROR_SSL:
/* Protocol error. */
#ifdef EPROTO
error = EPROTO;
#else
error = SOCKET_ECONNRESET;
#endif
break;
case SSL_ERROR_SYSCALL:
case SSL_ERROR_NONE:
default:
break;
};
}
int main()
{
/*
------------------ START Initialize Server ------------------
*/
int serverfd, clientfd;
struct sockaddr_in vir_serv_addr, cli_addr;
serverfd = socket(AF_INET, SOCK_STREAM, 0);
bzero((char *) &vir_serv_addr, sizeof (vir_serv_addr));
vir_serv_addr.sin_family = AF_INET;
vir_serv_addr.sin_addr.s_addr = INADDR_ANY;
vir_serv_addr.sin_port = htons(9999);
bind(serverfd, (struct sockaddr *) &vir_serv_addr, sizeof (vir_serv_addr));
listen(serverfd, 5);
socklen_t client = sizeof (cli_addr);
clientfd = accept(serverfd, (struct sockaddr*) &cli_addr, (socklen_t*) & client);
/*
------------------- END Initialize Server -------------------
*/
/*
------------------ START SSL ------------------
*/
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
SSL_CTX* context = SSL_CTX_new(TLS_server_method());
SSL_CTX_set_ecdh_auto(context, 1);
SSL_CTX_use_certificate_file(context, "server-cert.pem", SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(context, "server-key.pem", SSL_FILETYPE_PEM);
SSL_CTX_load_verify_locations(context, "ca-cert.pem", NULL);
SSL_CTX_check_private_key(context);
SSL* ssl = SSL_new(context);
BIO *rbio = BIO_new(BIO_s_mem());
BIO *wbio = BIO_new(BIO_s_mem());
SSL_set_bio(ssl, rbio, wbio);
char* buffer[8192];
int n;
SSL_set_accept_state(ssl);
while (!SSL_is_init_finished(ssl)) {
n = recv(clientfd, buffer, 8192, 0);
n = BIO_write(rbio, buffer, n);
int r = SSL_do_handshake(ssl);
n = BIO_read(wbio, buffer, 8192);
n = send(clientfd, buffer, n, 0);
bzero(buffer, 8192);
BIO_flush(rbio);
BIO_flush(wbio);
if (r != 1) {
ERR_print_errors_fp(stderr);
int err_SSL_get_error = SSL_get_error(ssl, r);
switch (err_SSL_get_error) {
case SSL_ERROR_NONE:
case SSL_ERROR_SSL:
return 0;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
continue;
default:
return 0;
}
}
}
string message = "Hello world! \n Bye world!";
n = SSL_write(ssl, message.c_str(), message.size());
n = BIO_read(wbio, buffer, 8192);
n = send(clientfd, buffer, n, 0);
/*
------------------- END SSL -------------------
*/
}
【问题讨论】:
-
您如何检查消息是否在 2 条记录中发送?因为我怀疑这一点。也许您尝试逐行阅读?
-
为什么发送数据时使用多少 SSL 记录很重要?数据是任意的。 TCP 将确保它以一种或另一种方式可靠地发送,并且接收器的 SSL 引擎将以连贯的方式将其重新组合在一起。 SSL 可以随意使用它想要的许多记录来发送应用程序数据。此外,即使 SSL 不拆分数据,TCP 仍然可以。您的要求不现实。
-
@SteffenUllrich:我使用了wireshark。在 TLSv1.1 和 TLSv1.2 中它只是一条 SSL 记录,但在 TLSv1 中它是 2 条记录,我必须使用这个版本的 TLS。
-
@RemyLebeau:实际上这段代码是一个更大的数据库代理项目的简化。在主项目中,我们需要向不受我们控制的服务器发送消息,它只能处理 1 个 SSL 记录。
-
@Heil_Me 但是为什么呢?这是服务器方面的错误实现。您的代理不应该迎合这样的错误。 AFAIK,您不能强制 OpenSSL 按照您的要求行事。改为修复服务器。或者停止一次发送如此多的数据,以至于 OpenSSL 不得不将其拆分为多条记录。
标签: c++ sockets ssl tcp openssl