使用TLS PSK加密时,如何正确检测流的结尾? [英] How to detect an end of stream properly, when TLS PSK encryption is used?

查看:114
本文介绍了使用TLS PSK加密时,如何正确检测流的结尾?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经准备好基于Bouncy Castle的 MockPSKTlsClient 的简单TLS PSK客户端测试用例

I have prepared a simple TLS PSK client test case based on MockPSKTlsClient by Bouncy Castle.

在<$我叫c $ c> main 方法:

public static void main(String[] args) throws IOException {
    SecureRandom random      = new SecureRandom();
    TlsPSKIdentity identity  = new BasicTlsPSKIdentity("Client_identity", Hex.decode("1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A"));
    Socket socket            = new Socket(InetAddress.getLocalHost(), 12345);
    TlsClientProtocol proto  = new TlsClientProtocol(socket.getInputStream(), socket.getOutputStream(), random);
    MockPSKTlsClient client  = new MockPSKTlsClient(null, identity);
    proto.connect(client);

    OutputStream clearOs = proto.getOutputStream();
    InputStream clearIs = proto.getInputStream();
    clearOs.write("GET / HTTP/1.1\r\n\r\n".getBytes("UTF-8"));
    Streams.pipeAll(clearIs, System.out);   // why is java.io.EOFException thrown?
}

如您所见,我发送 GET /到openssl服务器的HTTP / 1.1 字符串,其启动方式为:

As you can see, I send a GET / HTTP/1.1 string to the openssl server, which is started as:

# openssl s_server \
        -psk 1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A \
        -psk_hint Client_identity\
        -cipher PSK-AES256-CBC-SHA \
        -debug -state -nocert -accept 12345 -tls1_2 -www

之后,我呼叫 Streams.pipeAll()方法,它只是:

After that I call Streams.pipeAll() method, which is merely:

public static void pipeAll(InputStream inStr, OutputStream outStr)
    throws IOException
{
    byte[] bs = new byte[BUFFER_SIZE];
    int numRead;
    while ((numRead = inStr.read(bs, 0, bs.length)) >= 0) // Why is EOFException thrown?
    {
        outStr.write(bs, 0, numRead);
    }
}

此副本 openssl s_server 到屏幕上的答案,并出人意料地在末尾抛出 EOFException

This copies openssl s_server answer to the screen and also surprisingly throws an EOFException at the end:

TLS-PSK client negotiated TLS 1.2
Established session: 68e647e3276f345e82effdb7cc04649f6872d245ae01489c08ed109c5906dd16
HTTP/1.0 200 ok
Content-type: text/html

<HTML><BODY BGCOLOR="#ffffff">
<pre>

s_server -psk 1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A -psk_hint Client_identity -cipher PSK-AES256-CBC-SHA -debug -state -nocert -accept 12345 -tls1_2 -www 
Secure Renegotiation IS supported
Ciphers supported in s_server binary
TLSv1/SSLv3:PSK-AES256-CBC-SHA       
---
Ciphers common between both SSL end points:
PSK-AES256-CBC-SHA
Signature Algorithms: RSA+SHA1:RSA+SHA224:RSA+SHA256:RSA+SHA384:RSA+SHA512:DSA+SHA1:DSA+SHA224:DSA+SHA256:DSA+SHA384:DSA+SHA512:ECDSA+SHA1:ECDSA+SHA224:ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512
Shared Signature Algorithms: RSA+SHA1:RSA+SHA224:RSA+SHA256:RSA+SHA384:RSA+SHA512:DSA+SHA1:DSA+SHA224:DSA+SHA256:DSA+SHA384:DSA+SHA512:ECDSA+SHA1:ECDSA+SHA224:ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512
---
New, TLSv1/SSLv3, Cipher is PSK-AES256-CBC-SHA
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : PSK-AES256-CBC-SHA
    Session-ID: 68E647E3276F345E82EFFDB7CC04649F6872D245AE01489C08ED109C5906DD16
    Session-ID-ctx: 01000000
    Master-Key: B023F1053230C2938E1D3FD6D73FEB41DEC3FC1068A390FE6DCFD60A6ED666CA2AD0CD1DAD504A087BE322DD2C870C0C
    Key-Arg   : None
    PSK identity: Client_identity
    PSK identity hint: Client_identity
    SRP username: None
    Start Time: 1479312253
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
---
  13 items in the session cache
   0 client connects (SSL_connect())
   0 client renegotiates (SSL_connect())
   0 client connects that finished
  14 server accepts (SSL_accept())
   0 server renegotiates (SSL_accept())
  13 server accepts that finished
   0 session cache hits
   0 session cache misses
   0 session cache timeouts
   0 callback cache hits
   0 cache full overflows (128 allowed)
---
no client certificate available
</BODY></HTML>

TLS-PSK client raised alert: fatal(2), internal_error(80)
> Failed to read record
java.io.EOFException
    at org.bouncycastle.crypto.tls.TlsProtocol.safeReadRecord(Unknown Source)
    at org.bouncycastle.crypto.tls.TlsProtocol.readApplicationData(Unknown Source)
    at org.bouncycastle.crypto.tls.TlsInputStream.read(Unknown Source)
    at de.afarber.tlspskclient2.Main.pipeAll(Main.java:52)
    at de.afarber.tlspskclient2.Main.main(Main.java:44)
Exception in thread "main" java.io.IOException: Internal TLS error, this could be an attack
    at org.bouncycastle.crypto.tls.TlsProtocol.failWithError(Unknown Source)
    at org.bouncycastle.crypto.tls.TlsProtocol.safeReadRecord(Unknown Source)
    at org.bouncycastle.crypto.tls.TlsProtocol.readApplicationData(Unknown Source)
    at org.bouncycastle.crypto.tls.TlsInputStream.read(Unknown Source)
    at de.afarber.tlspskclient2.Main.pipeAll(Main.java:52)
    at de.afarber.tlspskclient2.Main.main(Main.java:44)

我的问题是:为什么抛出 EOFException

My question is: why is EOFException thrown?

通常 InputStream。 read()应该在流末尾返回-1,并且不会引发异常。

Usually InputStream.read() is supposed to return -1 at the end of stream and not throw an exception.

如何正确检测流末尾,当使用TLS PSK加密时?

How to detect an end of stream properly, when TLS PSK encryption is used?

从长远来看,我想将测试用例扩展到嵌入式Jetty前面充当反向PSK TLS代理的程序-并且不希望依靠异常来检测客户端已完成读取或写入操作。

In the long term I would like to extend my test case to a program acting as reverse PSK TLS proxy in front of embedded Jetty - and would prefer not to rely on exceptions to detect that the client is done reading or writing.

推荐答案

EOFException被抛出(自v1.56),因为未收到所需的close_notify警报。这意味着TLS层无法排除应用程序数据被截断的可能性。

EOFException is thrown (as of v1.56) because the required close_notify alert was not received. This means that the TLS layer cannot exclude the possibility that the application data was truncated.

截断意味着您到目前为止收到的数据是正确的(根据活动密码套件)已传输,但可能还有更多未收到的数据。截断可能是偶然的,也可能是恶意的。对于许多应用程序,较新的数据可能会影响较早数据的含义,因此截断可能会任意更改语义。

Truncation means that the data you received so far was correctly (per the active cipher suite) transmitted, but there may have been more data that you didn't receive. Truncation may be accidental or malicious. For many applications, later data may affect the meaning of earlier data, so a truncation may arbitrarily alter semantics.

对于某些应用程序协议,有可能确定不是实际的截断(即只是缺少close_notify)-考虑HTTP Content-Length标头,或者部分或全部截断的数据可能仍然有用地接受-考虑自定义,独立消息流。这不能在TLS层本身中完成;

For some application protocols, it may be possible to determine that there was no actual truncation (i.e. just missing close_notify) - consider the HTTP Content-Length header, or that some or all of the truncated data might still be usefully accepted - consider a stream of self-delimiting, independent messages. This cannot be done in the TLS layer itself; or rather, it is done by requiring close_notify!

因此,将EOFException引发为 [signal]在输入过程中意外到达文件末尾或流末尾 。在这一点上,应用程序应保守地假定数据已被截断,但是特定于应用程序的机制可能仍允许接受部分或全部数据,如上所述。

So, EOFException is raised to "[signal] that an end of file or end of stream has been reached unexpectedly during input". At this point the application should conservatively assume the data was truncated, but application-specific mechanisms may yet allow the acceptance of part or all of the data as explained above.

在(尚未发布的)v1.57版本中,我们添加了TlsNoCloseNotifyException作为EOFException的子类,在这种特定情况下,它将仅/总是抛出该异常,希望允许使用更简单的应用程序代码。

As of (not yet released) v1.57 we have added TlsNoCloseNotifyException as a subclass of EOFException, that will only/always be thrown in this specific case, hopefully allowing for simpler application code.

这篇关于使用TLS PSK加密时,如何正确检测流的结尾?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆