【问题标题】:Directly Read/Write Handshake data with Memory BIO使用 Memory BIO 直接读/写握手数据
【发布时间】:2014-05-10 07:10:35
【问题描述】:

我需要创建一个可以直接读取/写入握手数据的 OpenSSL 连接。原因是握手数据将在 UDP 连接中传输(DTLS 不是一个选项,因为数据不是直接在数据报中,而是在另一个协议数据包中,EAP 如果你好奇的话)。到目前为止,我已经创建了一个 OpenSSL 连接,但我什至无法读取客户端的握手以发送到服务器。

在我的研究中,我发现我需要一个 Memory BIO 来读取/写入连接,但无法弄清楚如何提取握手数据。以下是我初始化 client 连接的方法:

SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();

ctx = SSL_CTX_new(SSLv3_client_method());
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);

ssl = SSL_new(ctx);
rbio = BIO_new(BIO_s_mem());
wbio = BIO_new(BIO_s_mem());

SSL_set_bio(ssl, rbio, wbio);
SSL_set_connect_state(ssl);

我尝试过 doint SSL_connect 来启动握手:

int ret = SSL_connect(ssl);

但返回-1,然后执行SSL_get_error(ssl, res),我得到一个错误代码2,然后我用该代码执行ERR_error_string并得到:

error:00000002:lib(0):func(0):system lib

另外,如果我使用 SSL_do_handshake 而不是 SSL_connect 我会得到完全相同的错误。

我已经能够通过 TCP 设置 OpenSSL 连接,但从未使用内存 BIO 进行过此操作,因此非常感谢您提供任何帮助。谢谢!

【问题讨论】:

标签: c ssl openssl


【解决方案1】:

我终于让它工作了,我的方法是正确的:

需要函数SSL_set_connect_state(ssl) 来告诉连接为握手初始化做准备。然后,我们调用SSL_do_handshake(ssl) 开始握手。此函数将返回-1,因为握手尚未完成,但我们实际上可以从客户端 ssl 连接 BIO 写入器读取数据并使用我们想要的协议发送数据(在我的例子中,通过 UDP 的 EAP RADIUS 数据包)。

客户

ctx = SSL_CTX_new(SSLv3_client_method());
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);

ssl = SSL_new(ctx);
rbio = BIO_new(BIO_s_mem());
wbio = BIO_new(BIO_s_mem());

SSL_set_bio(ssl, rbio, wbio);
SSL_set_connect_state(ssl); 

SSL_do_handshake(ssl); // This will return -1 (error) as the handshake is not finished, we can ignore it.

char buf[4096];
BIO_read(wbio, buf, 4096); // Read from BIO, put data in buffer

// Then use data in buffer to send to the server

另一方面,服务器应该使用凭证和私钥进行配置。此外,我们应该使用SSL_set_accept_state() 而不是SSL_set_connect_state(),因为服务器将等待客户端的握手问候。然后,我们只需将客户端握手问候数据写入服务器 BIO 读取器:

服务器

ctx = SSL_CTX_new(SSLv3_server_method()); // This is the server!
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);

ssl = SSL_new(ctx);
rbio = BIO_new(BIO_s_mem());
wbio = BIO_new(BIO_s_mem());

SSL_set_bio(ssl, rbio, wbio);
SSL_set_accept_state(ssl); // The server uses SSL_set_accept_state

// Here we get the data from the client suppose it's in the variable buf
// and write it to the connection reader BIO.
BIO_write(rbio, buf, strlen(buf));

if (!SSL_is_init_finished(ssl)) {
    SSL_do_handshake(ssl);
}

我们可以使用SSL_is_init_finished(ssl)函数来检查握手是否完成,当握手没有完成时我们调用SSL_do_handshake(ssl),然后再次从BIO_writer读取数据发送给客户端。

客户端和服务器之间的这个过程应该在连接完成之前完成(即SSL_is_init_finished(ssl)返回true)。

然后,握手完成后,您可以使用SSL_readSSL_write 函数在客户端/服务器之间发送安全 数据。希望这个简短的解释对某人有用!

【讨论】:

【解决方案2】:

尝试这样做

// Connect the TCP socket (this is client side)
int sock = tcp_connect(host, port);

// Create BIO around the connected TCP socket 
BIO *sbio = BIO_new_socket(sock, BIO_NOCLOSE);
SSL_set_bio(ssl,sbio,sbio); 

int ret = SSL_connect(ssl);

将 BIO 视为套接字的环绕。对于客户端,您必须为其提供一个使用 connect(...) 调用连接到所需主机和端口的套接字。对于服务器端,只需使用从 accept(...) 调用返回的套接字。

【讨论】:

  • 感谢您的回答,但正如我在问题中指出的那样,我使用的不是 TCP 连接,而是通过 RADIUS (udp) 的 EAP 数据包,需要使用内存 BIO。
猜你喜欢
  • 1970-01-01
  • 2016-01-27
  • 2014-04-12
  • 2016-08-20
  • 1970-01-01
  • 1970-01-01
  • 2018-04-20
  • 2015-12-18
  • 1970-01-01
相关资源
最近更新 更多