两个会话和直接连接内存 BIO 的 C# 的 OpenSSL 握手失败
OpenSSL Handshake failure with two sessions and directly connected memory BIO's C#
我在使用 C# 中的 OpenSSL
时遇到了一个奇怪的问题。
事实:我已经为 C#
编写了一个包装器,它工作得很好,它可以连接到其他 SSL
服务器,或者接受来自 SSL
客户端的连接。我的包装器仅使用内存 BIO。任何时候我将 MEM
BIO 连接到任何类型套接字的输入和输出 握手成功 ,数据传输 工作 ,所以问题不在包装器中。
我已经用这个包装器制作了两个程序,一个客户端和一个服务器,它们建立了 ssl 会话,当我尝试将数据从服务器发送到客户端(或相反的方式)时,另一端回应由于某种原因它返回发送方数据流量非常慢(即使在本地环回中最大 20kB/s,如果我尝试以更高的速度发送,比如在 SSL_read()
中抽取更多数据,那么在 TCP 上使用 TLS 有巨大的延迟,UDP 上的 DTLS 有时会有 75% 的数据包丢失),所以我决定用包装器对 OpenSSL 进行基准测试,因为某些方法或错误的参数可能会减慢 运行.
我的想法是在一个程序中初始化两个 SSL
上下文,一个客户端和一个服务器,将它们的内存 BIO 相互连接 (RX-TX TX-RX)
,然后开始握手。
就是没办法让他们握手成功!
在 Clienthello 之后使用 DTLS1,服务器开始以 4 个数据包(3x256 字节 + 145)响应 Serverhello 和 Cert 数据,但在第一个数据包之后客户端警告:
After writing to CLIENT (packet: 2): 22016:error:14102044:SSL routines:dtls1_read_bytes:internal error:.\ssl\d1_pkt.c:1370:
After writing to CLIENT (packet: 3): 22016:error:14102044:SSL routines:dtls1_read_bytes:internal error:.\ssl\d1_pkt.c:1370:
After writing to CLIENT (packet: 4): 22016:error:14102044:SSL routines:dtls1_read_bytes:internal error:.\ssl\d1_pkt.c:1370:
After writing to CLIENT (packet: 5): 22016:error:14102044:SSL routines:dtls1_read_bytes:internal error:.\ssl\d1_pkt.c:1370:
两个会话之间的数据包流如下:
Client -> 178 -> Server
Server -> 256 -> Client
Server -> 256 -> Client
Server -> 256 -> Client
Server -> 145 -> Client
Client -> 15 -> Server
我用 Wireshark
分析了这些数据包(在文件转储之后),一切似乎都正常(这个分析不是上面的数据包,而是问题的相同重现):
http://reset.tor.hu/ssl/sima/programom_dtls1.txt
在此之后我得到更多错误:
After writing to SERVER (packet: 5): 22016:error:141020E5:SSL routines:dtls1_read_bytes:ssl handshake failure:.\ssl\d1_pkt.c:819:
Before writing to SERVER (packet: 6): 22016:error:140FD10F:SSL routines:DTLS1_GET_MESSAGE_FRAGMENT:bad length:.\ssl\d1_both.c:892:
After writing to SERVER (packet: 6): 22016:error:14102417:SSL routines:dtls1_read_bytes:sslv3 alert illegal parameter:.\ssl\d1_pkt.c:1200:SSL alert number 47
22016:error:141020E5:SSL routines:dtls1_read_bytes:ssl handshake failure:.\ssl\d1_pkt.c:819:
我唯一可用的想法是调试这个问题,以便在从 BIO 读取数据包后立即将数据包转储到文件中,然后在另一个会话中将数据包写入 BIO 之前,但我没有发现任何问题字节比较后内容不同,包顺序也OK
文件转储在这里:http://reset.tor.hu/ssl/y/direct_bio_error/
我无法想象会出现什么问题,因为他们从来没有在没有失败的情况下完成握手,所以我决定向这个程序添加两个 UDP 套接字,并将套接字连接到 BIO 并相互连接,不出意外第一次握手就成功了。
文件转储在这里:http://reset.tor.hu/ssl/y/socket_ok/
可见,文件内容或数据包序列没有任何区别。
那为什么他们不直接连接 BIO 工作呢?!
您可以在这个视频中看到产生错误和正常工作的程序:https://www.youtube.com/watch?v=4crbSz6JMMY
出于某种原因,当时我唯一的想法是 C# 混合方法调用,但我没有使用任何线程!非托管代码调用回调来读出 BIO,我将 BIO 读出到我的缓冲区,我调用另一个方法将缓冲区写入 BIO 并在非托管代码中调用 SSL_read()
,以使用 BIO 中的数据。而这恰恰是另一种方式。如果我不使用缓冲区,而是使用套接字,它就可以工作。插座可以做什么使其工作?
我什至尝试将 Thread.Sleep(100) 放在 BIO 著作之间,但没有帮助,实际上超过 140ms OpenSSL
崩溃了(但对于套接字,它甚至在 1000 毫秒时也能工作,即使更奇怪)。
直接BIO和流量慢的问题和解决方案是什么?
提前感谢您的回答!
好的,实际上我似乎刚刚解决了 Direct-Memory-BIO-Handshake 问题。
建议在BIO_write()
之后调用SSL_read()
,否则OpenSSL
不会检查READ BIO中的数据,也不会处理它,所以几乎每次都使用它写入BIO后直接。
如果将 OpenSSL
上下文配置为客户端,则 不应在 BIO_write()
之后直接使用 SSL_read()
until握手未完成!
如果 OpenSSL
与套接字一起使用(即使使用内存生物),如果我们在 BIO_write()
之后直接使用 SSL_read()
则它可以正常工作,但两个会话之间的直接 BIO 连接不是。至少作为 C# 中的非托管代码!
有效的BIO写法如下:
public bool BIO_Write(byte[] data)
{
if (Inited & data.Length > 0)
{
Wrapper.BIO_write(BIOR, data, data.Length);
if (isServer(SSL_method))
{
// For SERVER MODE
if (Wrapper.SSL_is_init_finished(SSL))
{
readFromSSL();
}
else
{
Wrapper.SSL_do_handshake(SSL);
}
}
else
{
// For CLIENT MODE
if (Wrapper.SSL_is_init_finished(SSL))
{
readFromSSL();
}
else
{
// It is extremely important to call the SSL_do_handshake()
// only if SSL_want() returns 3, otherwise OpenSSL will
// throw a FATAL ERROR and crash with direct BIO connection
// return value 3 is SSL_ERROR_WANT_WRITE
if (Wrapper.SSL_want(SSL) == 3)
{
Wrapper.SSL_do_handshake(SSL);
}
}
}
return true;
}
else
{
return false;
}
}
我希望这对某人有所帮助!
我在使用 C# 中的 OpenSSL
时遇到了一个奇怪的问题。
事实:我已经为 C#
编写了一个包装器,它工作得很好,它可以连接到其他 SSL
服务器,或者接受来自 SSL
客户端的连接。我的包装器仅使用内存 BIO。任何时候我将 MEM
BIO 连接到任何类型套接字的输入和输出 握手成功 ,数据传输 工作 ,所以问题不在包装器中。
我已经用这个包装器制作了两个程序,一个客户端和一个服务器,它们建立了 ssl 会话,当我尝试将数据从服务器发送到客户端(或相反的方式)时,另一端回应由于某种原因它返回发送方数据流量非常慢(即使在本地环回中最大 20kB/s,如果我尝试以更高的速度发送,比如在 SSL_read()
中抽取更多数据,那么在 TCP 上使用 TLS 有巨大的延迟,UDP 上的 DTLS 有时会有 75% 的数据包丢失),所以我决定用包装器对 OpenSSL 进行基准测试,因为某些方法或错误的参数可能会减慢 运行.
我的想法是在一个程序中初始化两个 SSL
上下文,一个客户端和一个服务器,将它们的内存 BIO 相互连接 (RX-TX TX-RX)
,然后开始握手。
就是没办法让他们握手成功!
在 Clienthello 之后使用 DTLS1,服务器开始以 4 个数据包(3x256 字节 + 145)响应 Serverhello 和 Cert 数据,但在第一个数据包之后客户端警告:
After writing to CLIENT (packet: 2): 22016:error:14102044:SSL routines:dtls1_read_bytes:internal error:.\ssl\d1_pkt.c:1370:
After writing to CLIENT (packet: 3): 22016:error:14102044:SSL routines:dtls1_read_bytes:internal error:.\ssl\d1_pkt.c:1370:
After writing to CLIENT (packet: 4): 22016:error:14102044:SSL routines:dtls1_read_bytes:internal error:.\ssl\d1_pkt.c:1370:
After writing to CLIENT (packet: 5): 22016:error:14102044:SSL routines:dtls1_read_bytes:internal error:.\ssl\d1_pkt.c:1370:
两个会话之间的数据包流如下:
Client -> 178 -> Server
Server -> 256 -> Client
Server -> 256 -> Client
Server -> 256 -> Client
Server -> 145 -> Client
Client -> 15 -> Server
我用 Wireshark
分析了这些数据包(在文件转储之后),一切似乎都正常(这个分析不是上面的数据包,而是问题的相同重现):
http://reset.tor.hu/ssl/sima/programom_dtls1.txt
在此之后我得到更多错误:
After writing to SERVER (packet: 5): 22016:error:141020E5:SSL routines:dtls1_read_bytes:ssl handshake failure:.\ssl\d1_pkt.c:819:
Before writing to SERVER (packet: 6): 22016:error:140FD10F:SSL routines:DTLS1_GET_MESSAGE_FRAGMENT:bad length:.\ssl\d1_both.c:892:
After writing to SERVER (packet: 6): 22016:error:14102417:SSL routines:dtls1_read_bytes:sslv3 alert illegal parameter:.\ssl\d1_pkt.c:1200:SSL alert number 47
22016:error:141020E5:SSL routines:dtls1_read_bytes:ssl handshake failure:.\ssl\d1_pkt.c:819:
我唯一可用的想法是调试这个问题,以便在从 BIO 读取数据包后立即将数据包转储到文件中,然后在另一个会话中将数据包写入 BIO 之前,但我没有发现任何问题字节比较后内容不同,包顺序也OK
文件转储在这里:http://reset.tor.hu/ssl/y/direct_bio_error/
我无法想象会出现什么问题,因为他们从来没有在没有失败的情况下完成握手,所以我决定向这个程序添加两个 UDP 套接字,并将套接字连接到 BIO 并相互连接,不出意外第一次握手就成功了。
文件转储在这里:http://reset.tor.hu/ssl/y/socket_ok/
可见,文件内容或数据包序列没有任何区别。
那为什么他们不直接连接 BIO 工作呢?!
您可以在这个视频中看到产生错误和正常工作的程序:https://www.youtube.com/watch?v=4crbSz6JMMY
出于某种原因,当时我唯一的想法是 C# 混合方法调用,但我没有使用任何线程!非托管代码调用回调来读出 BIO,我将 BIO 读出到我的缓冲区,我调用另一个方法将缓冲区写入 BIO 并在非托管代码中调用 SSL_read()
,以使用 BIO 中的数据。而这恰恰是另一种方式。如果我不使用缓冲区,而是使用套接字,它就可以工作。插座可以做什么使其工作?
我什至尝试将 Thread.Sleep(100) 放在 BIO 著作之间,但没有帮助,实际上超过 140ms OpenSSL
崩溃了(但对于套接字,它甚至在 1000 毫秒时也能工作,即使更奇怪)。
直接BIO和流量慢的问题和解决方案是什么?
提前感谢您的回答!
好的,实际上我似乎刚刚解决了 Direct-Memory-BIO-Handshake 问题。
建议在BIO_write()
之后调用SSL_read()
,否则OpenSSL
不会检查READ BIO中的数据,也不会处理它,所以几乎每次都使用它写入BIO后直接。
如果将 OpenSSL
上下文配置为客户端,则 不应在 BIO_write()
之后直接使用 SSL_read()
until握手未完成!
如果 OpenSSL
与套接字一起使用(即使使用内存生物),如果我们在 BIO_write()
之后直接使用 SSL_read()
则它可以正常工作,但两个会话之间的直接 BIO 连接不是。至少作为 C# 中的非托管代码!
有效的BIO写法如下:
public bool BIO_Write(byte[] data)
{
if (Inited & data.Length > 0)
{
Wrapper.BIO_write(BIOR, data, data.Length);
if (isServer(SSL_method))
{
// For SERVER MODE
if (Wrapper.SSL_is_init_finished(SSL))
{
readFromSSL();
}
else
{
Wrapper.SSL_do_handshake(SSL);
}
}
else
{
// For CLIENT MODE
if (Wrapper.SSL_is_init_finished(SSL))
{
readFromSSL();
}
else
{
// It is extremely important to call the SSL_do_handshake()
// only if SSL_want() returns 3, otherwise OpenSSL will
// throw a FATAL ERROR and crash with direct BIO connection
// return value 3 is SSL_ERROR_WANT_WRITE
if (Wrapper.SSL_want(SSL) == 3)
{
Wrapper.SSL_do_handshake(SSL);
}
}
}
return true;
}
else
{
return false;
}
}
我希望这对某人有所帮助!