2015-10-27 64 views
0

我正在嘗試創建一個從Java到Vala服務器的SSL TCP連接。 一切工作正常,直到我發送第二個包到服務器。 (也是第一個包發送正常)。 服務器只接收第二個包的第一個字節(在本例中爲「1」),沒有其他的, 但如果我連接到服務器沒有SSL一切正常。 我認爲服務器不是問題,因爲來自另一個Vala客戶端的其他連接工作得很好。TLS包後的神祕字節

我正在使用不受信任的證書,因此我創建了一個自定義TrustManager,並使用OpenJDK 7(基本操作系統 - Linux)。 這裏是我的代碼:

//Main: 
SSLHandler handler = new SSLHandler(); 
handler.createSecureSocket("localhost", 7431); 

byte[] data = {1,4,1,1,1,1}; 
handler.getOutputStream().write(data); 
handler.getOutputStream().write(data); 

// SSLHandler 
public class SSLHandler { 

    // SSL Socket erstellen 
    SSLSocket sslSocket; 

    public void createSecureSocket(String ip, int port) throws UnknownHostException, IOException, KeyManagementException, NoSuchAlgorithmException { 

     SSLSocketFactory factory = (SSLSocketFactory) new DefaultTrustManager().createSSLFactory("TLS"); 

     sslSocket = (SSLSocket) factory.createSocket(ip, port); 
    } 

    public OutputStream getOutputStream() throws IOException { 
     return sslSocket.getOutputStream(); 
    } 

    public InputStream getInputStream() throws IOException { 
     return sslSocket.getInputStream(); 
    } 

} 

//Custom Trust Manager 
public class DefaultTrustManager implements X509TrustManager { 

    @Override 
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {} 

    @Override 
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {} 

    @Override 
    public X509Certificate[] getAcceptedIssuers() { 
     return null; 
    } 

    public SSLSocketFactory createSSLFactory(String protocol) throws NoSuchAlgorithmException, KeyManagementException { 
     SSLContext sslContext = SSLContext.getInstance(protocol); 

     TrustManager[] byPassTrustManager = new TrustManager[] {this}; 

     sslContext.init(null, byPassTrustManager, new SecureRandom()); 

     return sslContext.getSocketFactory(); 
    } 
} 

是否有人知道這個問題的解決方案?

+0

'getAcceptedIssuers()'不能返回null:請參閱Javadoc。不要使用這個根本不安全的代碼。你的證據證明數據沒有被正確接收?接收代碼? – EJP

+0

@EJP我不認爲API需要它,但實際上getAcceptedIssuers不在客戶端使用,所以錯誤從未咬人 –

回答

3

TLDR:做多次接收。

像TCP這樣的TLS/SSL被定義爲流服務並且不保證保留記錄邊界,只保證按順序傳遞字節 - 或者如果這是不可能的,則表明錯誤。例如,在TCP或TLS/SSL中,如果一方發送每個1000字節的操作,並且接收方執行一系列接收操作,則可能會收到500,700,1200和600字節。一般情況下,接收器必須在必要時做多次接收以接收多於一個字節的數據結構。這可以通過使用分隔符來完成,比如SMTP:繼續閱讀,直到獲得由CRLF終止的一系列行(名義上,Postelian接收者可能接受其他行)。或者只需要一個計數:繼續閱讀,直到總共得到N個字節。例如,HTTP和HTTPS同時使用這些(在某些情況下更多)。

在實踐中,TCP實現經常會拆分大於約1000字節的數據,這是由於它們將數據分段用於傳輸和重新組合的方式。因此,大約在1982年後,TCP程序員還沒有教過這個問題,他們從經驗中學習到「繼續閱讀直到完成」。

但是歷史上SSL/TLS的實現主要是確實保留記錄邊界高達大約16k字節,這也是由於協議在內部工作的方式。因此,許多沒有閱讀規範的程序員都認爲SSL/TLS是一項記錄服務。 2011年由於the BEAST attack而改變,這在一些(相當有限的)情況下會破壞使用SSLv3或TLSv1.0以CBC模式密碼發送的加密數據,因爲這些協議通過鏈接IV來實現CBC模式一個數據記錄到下一個。針對BEAST的一種防禦(由包括Java JSSE在內的許多堆棧實現)是在兩個(或更多)部分中傳輸每個數據記錄,使得敏感部分的IV不會提前顯示;非正式共識是使用1個字節,然後(最多)使用剩餘的n-1個字節。見https://security.stackexchange.com/questions/63215/why-does-firefox-split-https-request

請注意,此防禦措施僅在SSL連接中的第二次和隨後寫入時完成,因爲第一次使用基於KDF而不是鏈接的IV,並且僅用於CBC模式密碼套件,因爲只有它們鏈接到IV這種時尚。對於TLSv1.1及更高版本,這並不是必需的,但目前我無法驗證JSSE是否真的在此情況下忽略它。