2016-05-12 21 views
1

我以字節數組格式發送多個命令,並根據在典型的客戶機/服務器通信中發送的命令接收響應。我的Java應用程序是在Windows 7上運行的客戶端,我知道要發送什麼命令以及我希望在響應中收到哪些命令。但是,我沒有任何控制權或對服務器源代碼有任何瞭解。通過Java寫入時首字節丟失SSL Socket

我遇到的問題是在發送的第二個命令或之後的任何命令之後,數組的第一個字節被丟棄。當我發送第一個命令時,我得到了正確的響應,因爲第一個字節沒有被丟棄。當發送下一個命令或之後的任何命令時,第一個字節被丟棄,服務器不響應,因爲該命令的格式不正確,服務器無法識別。

我通過Java SSLSocket DataOutputStream發送這些命令,當然我正在接收DataInputStream上的響應。我首先與服務器握手並在握手成功後繼續。在這一點上是當我發送的第一個命令和接收十六進制:

Sending: 01 03 03 
Receive: 01 0e fd 85 02 09 01 01 04 01 06 

正在發送的下一個命令這裏顯示的響應:

Sending: 01 48 65 6c 6c 6f 

但是,這是我沒有收到迴應從服務器。

打印出javax.net.debug輸出時,我可以看到第一個字節'01'確實丟失或丟失了。

Padded plaintext before ENCRYPTION: len = 32 
0000: 48 65 6C 6C 6F FE 57 F9 4A 29 13 8F 2B AB 71 A3 Hello.W.J)..+.q. 
0010: 16 12 29 FF D5 DE 12 48 8B 06 06 06 06 06 06 06 ..)....H........ 
main, WRITE: TLSv1 Application Data, length = 32 
[Raw write]: length = 37 
0000: 17 03 01 00 20 34 42 ED 88 FC 41 2D 13 1A FD BA .... 4B...A-.... 
0010: 64 0E 9D C7 FE 11 76 96 48 09 A6 BC B2 BC 0E FA d.....v.H....... 
0020: C8 5B 79 4B 82          .[yK. 

下面是我的源代碼:

import java.io.DataInputStream; 
import java.io.DataOutputStream; 
import java.io.File; 
import java.io.FileInputStream; 
import java.io.InputStream; 
import javax.net.ssl.SSLSocketFactory; 
import javax.net.ssl.TrustManagerFactory; 
import javax.net.ssl.HandshakeCompletedEvent; 
import javax.net.ssl.HandshakeCompletedListener; 
import javax.net.ssl.SSLContext; 
import javax.net.ssl.SSLSocket; 
import javax.net.ssl.X509TrustManager; 
import javax.net.ssl.TrustManager; 
import java.security.KeyStore; 

public class SSLSocketTest 
{ 
    private SSLSocket sslSocket = null; 
    private SSLSocketFactory sslSocketFactory = null; 
    private String ipAddress = "192.168.100.99"; 
    private int port = 9999; 

    DataOutputStream dataOS = null; 
    DataInputStream dataIS = null; 

    private boolean handshakeSuccessful = false; 

    public static void main(String[] args) 
    { 
     SSLSocketTest sslSocketTest = new SSLSocketTest(); 
     sslSocketTest.sslSocketConnect(); 
    } 

    SSLSocketTest() 
    { 
     System.setProperty("javax.net.debug", "all"); 

     try{ 
      File certFile = new File("cacerts"); 

      KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
      char[] certPassword = "changeit".toCharArray(); 
      InputStream fileIS = new FileInputStream(certFile); 
      keyStore.load(fileIS, certPassword); 
      fileIS.close(); 

      SSLContext sslContext = SSLContext.getInstance("TLSv1"); 

      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
      trustManagerFactory.init(keyStore); 
      X509TrustManager defaultTrustManager = (X509TrustManager)trustManagerFactory.getTrustManagers()[0]; 

      sslContext.init(null, new TrustManager[] {defaultTrustManager}, null); 
      sslSocketFactory = sslContext.getSocketFactory(); 
     }catch(Exception e){ 
      e.printStackTrace(); 
     } 
    } 

    public void sslSocketConnect() 
    { 
     try{ 
      sslSocket = (SSLSocket) sslSocketFactory.createSocket(ipAddress, port); 

      dataOS = new DataOutputStream(sslSocket.getOutputStream()); 
      dataIS = new DataInputStream(sslSocket.getInputStream()); 

      sslSocket.setSoTimeout(15000); 

      //Handshake 
      sslSocket.addHandshakeCompletedListener(new MyHandshakeListener()); 
      sslSocket.startHandshake(); 
      while(!handshakeSuccessful) 
      { 
       Thread.sleep(100); 
      } 

      //Sending commands 
      byte[] firstCommand = new byte[]{(byte)0x01, (byte)0x03, (byte)0x03}; 

      String[] firstCommandResponse = processCommand(firstCommand); 

      byte[] secondCommand = new byte[]{(byte)0x01, (byte)0x48, (byte)0x65, (byte)0x6C, (byte)0x6C, (byte)0x6F}; 

      String[] secondCommandResponse = processCommand(secondCommand); 

      disconnect(); 
     }catch(Exception e){ 
      e.printStackTrace(); 
     } 
    } 

    public void disconnect() 
    { 
     try{ 
      byte[] endConnection = new byte[]{(byte)0x01, (byte)0x01, (byte)0x02, (byte)0x03}; 

      processCommand(endConnection); 

      dataOS.close(); 
      dataIS.close(); 
      sslSocket.close(); 
     }catch (Exception e){ 
      e.printStackTrace(); 
     } 
    } 

    public String[] processCommand(byte[] command) 
    { 
     String[] returnResponse = null; 

     byte[] commandResponse = new byte[120]; 
     byte[] trimCommandResponse; 

     try{ 
      int commandResponseLength = -1; 
      int errorCount = 0; 

      while(commandResponseLength == -1) 
      { 
       StringBuilder cmdStr = new StringBuilder(); 
       cmdStr.append("Sending: "); 
       for(int i=0; i<command.length; i++) 
       { 
        cmdStr.append(fixHexStringData(Integer.toHexString(command[i])) + " "); 
       } 
       System.out.println(cmdStr.toString()); 

       dataOS.write(command, 0, command.length); 
       dataOS.flush(); 

       commandResponseLength = dataIS.read(commandResponse); 

       errorCount++; 
       if(errorCount == 3) 
       { 
        throw new Exception(); 
       } 
      } 

      returnResponse = new String[commandResponseLength]; 
      trimCommandResponse = new byte[commandResponseLength]; 

      //Getting Reponse Data 
      for(int i=0; i<commandResponseLength; i++) 
      { 
       returnResponse[i] = fixHexStringData(Integer.toHexString(commandResponse[i])); 
       trimCommandResponse[i] = commandResponse[i]; 
      } 

      StringBuilder rcvStr = new StringBuilder();    
      rcvStr.append("Receive: "); 
      for(int i=0; i<returnResponse.length; i++) 
      { 
       rcvStr.append(returnResponse[i] + " "); 
      } 
      System.out.println(rcvStr.toString()); 

     }catch(Exception e){ 
      e.printStackTrace(); 
     } 

     return returnResponse; 
    } 

    private String fixHexStringData(String dataByte) 
    { 
     if(dataByte.length() < 2) 
     {  
      dataByte = "0" + dataByte; 
     } 
     else if(dataByte.length() > 2) 
     { 
      dataByte = dataByte.substring(dataByte.length()-2); 
     } 
     return dataByte; 
    } 

    class MyHandshakeListener implements HandshakeCompletedListener 
    { 
      public void handshakeCompleted(HandshakeCompletedEvent e) 
      { 
      System.out.println("Handshake succesful!"); 

      handshakeSuccessful = true; 
      } 
    } 
} 

我已經是問題:

  1. 我缺少在寫出字節數組的一個步驟?我已經通過一個標準的Java Socket完成了這個任務,沒有問題,所以通過一個不同於標準Socket的SSL Socket來寫?我尋找這個,但沒有看到任何不同。
  2. 這可能是一個證書問題?如果握手和第一個命令成功,這是否意味着此時的通信已經建立並且超出了證書?
  3. 服務器能否影響到這一點?如果是這樣,這背後的原因是什麼?如果將字節數組寫入DataOutputStream位於客戶端,並且第一個字節被丟棄,那麼服務器如何在客戶端產生影響?
  4. 這可能是一個JVM的錯誤?
+0

你不需要任何的是握手代碼重寫此功能。握手在第一個I/O上自動啓動,如果您第一次同步啓動它,則不需要等待循環。除非你在監聽器中做了一些比設置布爾值更有趣的事情,否則全部刪除它。 – EJP

+0

謝謝。這只是一個示例代碼來找出字節丟失的原因。這種情況下的握手暫時用於在發送任何命令之前確認通信已建立。在此示例之外,意圖是永遠不會使用握手。 – TheBernman

回答

0

看起來這實際上是JSSE實現的一個功能,它在兩個數據包之間分割數據。第一個字節在一個數據包中,其餘的在下一個數據包中。在this answer

更多細節您應該能夠通過包括

System.setProperty("jsse.enableCBCProtection", "false"); 
+0

非常感謝。通過提供這個細節,這導致使用以下來解決分組分組的問題:System.setProperty(「jsse.enableCBCProtection」,「false」);你能否把這個添加到你的答案中。謝謝。 – TheBernman

+0

出於性能方面的原因,建議使用BufferedOutputStream緩衝出站網絡連接 - 這樣可以避免每發送一個寫入呼叫都發送一個數據包。 – Robert