我正在编写一个简单的应用程序来使用 OpenSSL 连接到服务器。最初的想法是编写一个自定义的 BIO 方法来通过 linux 的 SO_TIMESTAMPING 获取有关硬件套接字时间戳的信息,但与此同时,我意识到我在如何构建正确的 BIO 方面遗漏了一些东西,所以我从刮擦。
最基本的想法:让我们使用 BIO 连接到套接字并使用 BIO 来执行加密操作,以便重新创建 SSL_write 和 SSL_read 的基本行为。 实例化 BIO 方法并将其绑定到 SSL 的代码如下:
BIO *default_sck_bio = BIO_new(BIO_s_socket());
BIO *default_ssl_bio = BIO_new(BIO_f_ssl());
BIO *sock_bio = BIO_new(BIO_s_socket());
BIO *ssl_bio = BIO_new(BIO_f_ssl());
BIO_push(default_sck_bio, default_ssl_bio);
SSL_set_bio(ssl, default_sck_bio, default_sck_bio);
通过这种方式,我希望 SSL_read 在底层执行基于 BIO 的 recv(),然后执行 SSL 加密,而对于 SSL_write,执行 SSL 加密,然后执行基于 BIO 的 send()。但是,我的 SSL_connect 不断生成 SSL_ERROR_SSL 并显示错误消息:
error:10000078:BIO routines::uninitialized
。我是不是错过了什么?
解决了这个问题后,我的初衷是编写一个自定义的 BIO 方法,但接下来的问题突然出现在我的脑海中:如何从读/写 BIO 回调内部调用 SSL 加密函数(我通过
BIO_meth_set_read
设置)
)?
经过几天的谷歌搜索,我终于明白了 BIO 是如何工作的。
首先,与加密相关的编码/解码是在调用
SSL_read
和 SSL_write
时在后台管理的。
在问题的基本示例中,不需要
BIO *ssl_bio
。要获得问题中的功能,代码可以更改为:
BIO *default_sck_bio = BIO_new(BIO_s_socket());
BIO *sock_bio = BIO_new(BIO_s_socket());
SSL_set_bio(ssl, default_sck_bio, default_sck_bio);
下图让我们更好地理解流程(感谢 darrenjs 的要点):
+------+ +-----+
|......|--> read(fd) --> BIO_write(rbio) -->|.....|--> SSL_read(ssl) --> IN
|......| |.....|
|.sock.| |.SSL.|
|......| |.....|
|......|<-- write(fd) <-- BIO_read(wbio) <--|.....|<-- SSL_write(ssl) <-- OUT
+------+ +-----+
| | | |
|<-------------------------------->| |<------------------->|
| encrypted bytes | | unencrypted bytes |
总之,要回答“如何从读/写 BIO 回调内部调用 SSL 加密函数(我通过
BIO_meth_set_read
设置)?”的问题:一旦您通过使用 BIO_meth_set_read
设置的读取方法访问数据,数据已经被加密。同样,写入回调中访问的数据也将尚未被解密。
使用自定义BIO方法仍然存在问题,我写的客户端仍然返回错误。看看这个评论,看起来这是一个与openssl 3.0相关的问题。