2016-05-12 74 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