TheBernman TheBernman - 3 months ago 11
Java Question

First byte drops when writing over Java SSL Socket

I am sending multiple commands in a byte array format and receive responses based on the commands that were sent in a typical client/server communication. My Java app is the client running on Windows 7 and I know what commands to send and what I am expected to receive in the response. However, I do not have any control or have any knowledge of the server source code.

The problem I am having is that on the second command I sent or any commands after that, the first byte of the array is being dropped. When I send the first command, I get the proper response since the first byte is not dropped. When sending the next command or any commands after that, the first byte is dropped which the server does not respond since the command is not in the proper format for the server to recognize.

I am sending these commands over a Java SSLSocket DataOutputStream and of course I am receiving the responses on a DataInputStream. I first perform a handshake with the server and proceed on after the handshake is successful. At this point is when I send the first command and receive the response shown here in hex.:

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


The next command being sent:

Sending: 01 48 65 6c 6c 6f


But this is where I do not receive a response from the server.

When printing out the javax.net.debug output, I can see that the first byte '01' does drop or went missing somehow.

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.


The following is my source code:

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


The questions I have are:


  1. Am I missing a step in writing out the byte array? I have done this over a standard Java Socket with no issues, so is writing over a SSL Socket different from a standard Socket? I looked for this but did not see anything different.

  2. Could this be a certificate issue? If the handshake and the first command are successful, would this mean that the communication has been established at this point and it is beyond the certificate?

  3. Could the server be affecting this? If so, what could be the reason behind this? If writing the byte array to the DataOutputStream is on the client side and the first byte is dropped, how could the server have any affect on the client side?

  4. Could this be a JVM bug?


Answer

It looks like this is actually a function of the JSSE implementation which splits the data between two packets. The first byte goes in one packet and the rest in the next. More detail at this answer

You should be able to override this functionality by including

System.setProperty("jsse.enableCBCProtection", "false");
Comments