2016-11-16 1 views
1

Bouncy Castle이 MockPSKTlsClient을 기준으로 a simple TLS PSK client test case을 준비했습니다. main 방법에서TLS PSK 암호화가 사용되면 스트림 끝을 제대로 감지하는 방법?

내가 전화 : 내가 Streams.pipeAll()를 호출 그 후

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

: 당신이 볼 수 있듯이

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? 
} 

, 나는으로 시작되는 OpenSSL이 서버에 GET/HTTP/1.1 문자열을 보내 방법은 단지 :

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를 던졌습니다 :

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가 발생합니다?

일반적으로 InputStream.read()은 스트림 끝에서 -1을 반환하고 예외를 throw하지 않습니다.

TLS PSK 암호화가 사용될 때 스트림 끝을 올바르게 감지하는 방법은 무엇입니까?

장기적으로 임베디드 Jetty 앞에서 역 PSK TLS 프록시 역할을하는 프로그램으로 테스트 케이스를 확장하고 싶습니다. 클라이언트가 읽기 또는 쓰기를 완료했음을 감지하기 위해 예외를 사용하지 않는 것이 좋습니다.

+1

0이 아닌 스트림의 끝에서 -1을 반환한다고 가정합니다. 이것은 BouncyCastle의 부분에서 분명히 형편없는 디자인입니다. 가능한 절단 공격 때문에'SSLException'을 던져야합니다. – EJP

+0

그리고 *는 'TLS 디코딩 된 스트림의 끝에'* 표시되지 않습니다. 그것은 잘림입니다. 스택 추적을 참조하십시오. – EJP

+0

정확합니다. -1은'InputStream.read()'에 의해 반환되어야합니다. 나는 내 질문을 고마워했다. 여전히 TLS 스트림의 끝을 올바르게 감지하는 방법을 궁금해합니다. –

답변

1

필요한 close_notify 경고를받지 못했기 때문에 EOFException이 throw됩니다 (v1.56 이후). 즉, TLS 계층에서 응용 프로그램 데이터가 잘린 가능성을 배제 할 수 없습니다.

잘림이란 전송 된 데이터가 (전송 된 암호화 집합에 따라) 올바로 전송되었음을 의미하지만 수신하지 못한 데이터가 더 많을 수 있습니다. 절단은 우발적이거나 악의적 인 것일 수 있습니다. 많은 응용 프로그램에서 나중에 데이터가 이전 데이터의 의미에 영향을 줄 수 있으므로 잘림으로 인해 의미가 임의로 변경 될 수 있습니다.

일부 응용 프로그램 프로토콜의 경우 실제 잘림 (즉, close_notify가 누락 됨)이 있는지 확인할 수 있습니다 - HTTP Content-Length 헤더를 고려하거나 잘린 데이터의 일부 또는 전부를 여전히 유용하게 받아 들일 수 있습니다 - 자체 분리되고 독립적 인 메시지의 흐름을 고려하십시오. 이것은 TLS 계층 자체에서 수행 될 수 없습니다. 또는 오히려 close_notify를 요구함으로써 이루어집니다!

따라서 EOFException은 "[신호] 파일 끝이나 스트림 끝이 예기치 않게 입력되는 동안"에 발생합니다. 이 시점에서 애플리케이션은 보수적으로 데이터가 잘린 것으로 가정해야하지만 애플리케이션 별 메커니즘은 위에 설명 된대로 데이터의 일부 또는 전부를 수용 할 수 있습니다.

(아직 릴리스되지 않음) v1.57 우리는 EOFException의 하위 클래스로 TlsNoCloseNotifyException을 추가했습니다.이 예외는이 특별한 경우에만 발생하며 더 간단한 응용 프로그램 코드를 허용합니다.

+0

이것은 반드시 openssl s_server의 버그일까요? – EJP

+0

제 생각에는 버그입니다. 그렇지만 아마도 s_server 유틸리티의 세부 사항 에서뿐만 아니라 "실제"openssl 기반 서버의 기능 일 필요는 없습니다. –

관련 문제