Read byte[] from server - java

I am trying to read byte[] that is being send from a client to a server.
This is my client code...
din = new DataInputStream(socket.getInputStream());
dout = new DataOutputStream(socket.getOutputStream());
Cipher cipher = Cipher.getInstance("RSA");
// encrypt the aeskey using the public key
cipher.init(Cipher.ENCRYPT_MODE, pk);
byte[] cipherText = cipher.doFinal(aesKey.getEncoded());
dout.write(cipherText);
And this is my server code...
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
String chiper = dis.readUTF();
System.out.println(chiper);
However, the dis.readUTF(); line fails with an exception...
java.io.EOFException at java.io.DataInputStream.readFully(DataInputStream.java:197)
at java.io.DataInputStream.readUTF(DataInputStream.java:609)
at java.io.DataInputStream.readUTF(DataInputStream.java:564)
at gameserver.ClientHandler.run(GameServer.java:65)
Could someone please help me understand why this doesn't work.

For starters, if you write a sequence of (encrypted!) bytes at one end, and trying to read a UTF-formatted string at the other end...you're going to have a bad time.
I'd suggest that on the client side you should do something like
dout.writeInt(cipherText.length);
dout.write(cipherText);
and then on the server side you should do something like
int byteLength = dis.readInt(); // now I know how many bytes to read
byte[] theBytes = new byte[byteLength];
dis.readFully(theBytes);

DataIputStream.readUTF() is for data that you have written with DataOutputStream.writeUTF()`. You haven't written UTF so you can't read it.
This is binary data so you shouldn't be thinking about UTF or strings at all. Write the length of the array with writeInt(), then the array with write(). At the other end, read the length with readInt(), allocate a byte[] buffer that big and then read the ciphertext into it with readFully().

Yo have to get the message with the read method and get the number of characters of the real messages and then convert this to a string
int bytesRead = 0;
byte[] messageByte = new byte[1000];
bytesRead = dis.read(messageByte);
String chiper = new String(messageByte, 0, bytesRead);
System.out.println(chiper);

on client side, you should convert the byte[] array to String and use
dout.writeUTF() to send the converted String.

Related

Lost bytes in input stream?

I'm writing a program where I send bytes of a key from a keypair that I created over an output socket and use them to recreate the key on the other side.
Server:
KeyPairGenerator dsaKeyPairGenerator =
KeyPairGenerator.getInstance("DSA");
dsaKeyPairGenerator.initialize(1024);
KeyPair dsakeyPair = dsaKeyPairGenerator.generateKeyPair();
PrivateKey dsaPrivate = dsakeyPair.getPrivate();
PublicKey dsaPublic = dsakeyPair.getPublic();
byte[] dsaPublicbytes = dsaPublic.getEncoded();
clientSocket.getOutputStream().write(dsaPublicbytes.length);
clientSocket.getOutputStream().write(dsaPublicbytes);
Client:
int dsalength = clientSocket.getInputStream().read();
byte[] dsaPublicbytes = new byte[dsalength];
clientSocket.getInputStream().read(dsaPublicbytes);
X509EncodedKeySpec dsaspec = new X509EncodedKeySpec(dsaPublicbytes);
KeyFactory dsakeyFactory = KeyFactory.getInstance("DSA");
PublicKey dsaKey = dsakeyFactory.generatePublic(dsaspec);
However, on this line I get an error:
PublicKey dsaKey = dsakeyFactory.generatePublic(dsaspec);
The trace for the error itself:
Exception in thread "main" java.security.spec.InvalidKeySpecException: Inappropriate key specification: IOException: Detect premature EOF
at sun.security.provider.DSAKeyFactory.engineGeneratePublic(DSAKeyFactory.java:119)
at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
at Client.main(Client.java:36)
I have researched and I've seen that the EOF occurs because there aren't enough bytes to create the key, which leads me to believe that it is a problem with how I am sending the bytes. Am I sending the bytes incorrectly?
Lost bytes in input stream
Unread bytes in input stream. You assumed that read() filled the buffer. It isn't obliged to do that. Use DataInputStream.readFully().
You're also limiting yourself to 128 key bytes by using write(int), and read() with no parameters, for sending/receiving the length word. Use DataOutputStream.writeInt() and DataInputStream.readInt() for that.
Assuming that your first byte is not sending the size of key byte array or you are using the key that has size bigger than 256-bit, then the array will be incomplete.
Try using DataOutputStream methods of writeLong() or writeInt() to send byte size to initiate the right size array. Secondly try using buffers to read when its being send.
Here is little bit of my code from my file socket sender:
This is sending part:
OutputStream os = sock.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeInt(mybytearray.length);
dos.write(mybytearray, 0, mybytearray.length);
This is recieving part:
InputStream in = sock.getInputStream();
DataInputStream dis = new DataInputStream(in);
int byteSize = clientData.readInt();
byte[] byteData = new Byte[byteSize];
dis.read(byteData);
You may want to buffer receiving part by telling how many bytes to read using method of DIS read(byte[],int,int) until all of the bytes were read. I tested my code on same machine with very small size data so the connection stability was not a factor.

Using ObjectOutputStream to send a byte array of a public key, encrypt something, and send the encrypted version back

So I have a server side public key and private key, my aim is to send the client the public key, the client will encrypt a string with the key, then send the bytes through a stream, and the server will decrypt the byte array.
Exception:
javax.crypto.BadPaddingException: Decryption error
Code:
Sending the encoded key.
handler.getOos().writeObject(publicKey.getEncoded());
handler.getOos().flush();
Receiving the byte array (of the encoded key):
Object o = ois.readObject();
if (o instanceof byte[]) {
JChat.get().setServerPublicKey(KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec((byte[]) o)));
JChat.get().go();
}
The go() method (here I use a DataOutputStream to send the byte array):
public void go() {
String text = "hello darkness my old friend";
byte[] encrypted = encrypt(text, serverPublicKey);
try {
handler.getDos().write(encrypted);
handler.getDos().flush();
} catch (IOException e) {
e.printStackTrace();
}
}
Reading the byte array, on the server side:
int count = dis.available();
byte[] in = new byte[count];
dis.readFully(in);
System.out.println(Server.decrypt(in, Server.get().getPrivateKey()));
The decryption method throws this exception:
javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:380)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:291)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:363)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at com.archiepking.Server.decrypt(Server.java:97)
at com.archiepking.net.ClientHandler$1.run(ClientHandler.java:44)
at java.lang.Thread.run(Thread.java:745)
Any suggestions as to what I am doing wrong? Please note:
Dos = DataOutputStream Dis = DataInputStream Oos = ObjectOutputStream
Ois = ObjectInputStream
I am using two different sockets, one for sending objects and one for datatypes (as my chat application will need both).
What can I do to fix this error?
FURTHER INFORMATION:
Generation of keys:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
FileOutputStream fosPublic = new FileOutputStream("public");
fosPublic.write(publicKeyBytes);
fosPublic.close();
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
FileOutputStream fosPrivate = new FileOutputStream("private");
fosPrivate.write(privateKeyBytes);
fosPrivate.close();
publicKey = keyPair.getPublic();
privateKey = keyPair.getPrivate();
The problem is that you are using DataInputStream.available() to determine how many bytes to read. That method does not do what you apparently think that it does.
From the Javadoc of this method:
Returns an estimate of the number of bytes that can be read (or
skipped over) from this input stream without blocking by the next
caller of a method for this input stream. The next caller might be the
same thread or another thread. A single read or skip of this many
bytes will not block, but may read or skip fewer bytes.
It just returns the number of bytes that can be read without blocking, which can be far less than the actual number of bytes that you sent, especially if you are using network Sockets to send/receive that data.
The solution:
before writing the bytes, write an int with the writeInt method that contains the number of bytes that you're writing
before reading the bytes, call readInt to read the number of bytes that will follow, and construct a byte array of the right length from that number.
If you are using an ObjectOutputStream why bother converting the public key to a byte array using getEncoded? You can serialize the object directly. e.g.
handler.getOos().writeObject(publicKey);
Or if you have to use the encoded version, then remove the ObjectOutputStream and use ByteArrayOutputStream instead.

How to write byte[] to an IoSession

As shown below I can read the byte[] sent from an IoSession without having a protocol decoder.
IoBuffer in = (IoBuffer) message;
byte[] inBytes = in.array();
int length = inBytes[0];
inBytes = Arrays.copyOfRange(inBytes, 1, length + 1);
ByteString incomingMessage = ByteString.copyFrom(inBytes);
But when I try to do:
someIoSession.write(incomingMessage.toByteArray());
I get the following error.
Don't know how to handle message of type XXXX. Are you missing a protocol encoder?
How can I just write the bytes into an IoSession?
someIoSession.write(IoBuffer.wrap(incomingMessage.toByteArray()));

Java client server chat pads strings with squares when converting to byte[]

I'm building a chat client and server as part of a class project and running into one problem I can't seem to fix. Text has to be passed in the form of fixed size byte[] (either 32 or 64 bytes) depending on the particular case.
When I change the strings to byte[] with the .getBytes() method it pads out the length of the string with empty squares. This is fine during transit and receipt but at some point I need to change the string to it's original format (currently done with new String(byte[]) and delete the empty squares.
I can't seem to find a good way to do this. Any suggestions?
Relevant code bits client side:
byte[] bigDataByte = new byte[64];
sendData[2] = (bigDataByte = message.getBytes())
for (int i = 0; i < sendData.length; i++){
if (sendData[i] != null){
DatagramPacket sendPacket = new DatagramPacket(sendData[i], sendData[i].length, IPAddress, clientPort);
clientSocket.send(sendPacket);
}
}
Relevant code bits server side:
String name = new String(getBytes(32));
private static byte[] getBytes(int size) throws IOException {
byte[] dataByte = new byte[size];
DatagramPacket dataPacket = new DatagramPacket(dataByte, dataByte.length);
servSocket.receive(dataPacket);
return dataPacket.getData();
}
Not sure, but the issue might be that you are not specifying the charset.
Try using the
constructor: String(byte[] bytes, String charsetName)
and the method: getBytes(String charsetName).
e.g.
byte[] bytes = str.getBytes("UTF-8");
and
String str = new String(bytes, "UTF-8");
The default ones use the platform's default charset, which could lead to a mismatch.

basic encryption for game data

I think i'm looking for some sort of basic file encryption but don't know where to start.
I'm looking for someone to tell me where to start looking or, even better, offer some code.
I've written a game that currently saves data to a general text file. This of course could be changed by anyone who wished to do so.
What i need is to create a file that can store integers and strings that is difficult if not impossible to be edited outside of the game.
In my searching i came across .dat files but they seemed more complicated that what i'm looking for.
All help is appreciated, Alex.
You can write your data to a ByteBuffer and then you can distort your data by a simple algorithm. For example, assume that the data you want to save is a String array, you can do this:
String[] data; // the data you want to save
int byteLength = 0;
byte[][] bytes = new byte[data.length][];
// Calculate the length of the content.
for(int i=0; i<data.length; i++) {
bytes[i] = data[i].getBytes();
byteLength += bytes[i].length;
byteLength += 4; // this is for an integer, which is for the length of the String
}
// Transfer the content to a ByteBuffer object
ByteBuffer buffer = ByteBuffer.allocate(byteLength);
for(int i=0; i<bytes.length; i++) {
// Put the length of the current byte array
buffer.putInt(bytes[i].length);
for(int j=0; j<bytes[i].length; j++) {
// Reverse the byte so that it can't be understood
buffer.put((byte)(~bytes[i][j]));
}
}
After writing all of your content to the ByteBuffer object, you can take the resulting byte array and write it down to a file.
FileOutputStream fos = new FileOutputStream("YourFileName.anyExtension");
fos.write(buffer.array());
fos.close();
While reading the file back, you should first read an integer, which is the length of the data you should read as byte array, then you should read this byte array.
FileInputStream fis = new FileInputStream("YourFileName.anyExtension");
DataInputStream dis = new DataInputStream(fis);
ArrayList<String> list = new ArrayList<String>();
byte[] bytes;
while(dis.available()) {
int length = dis.readInt();
bytes = new byte[length];
for(int i=0; i<length; i++) {
// Those bytes were reversed, right?
bytes[i] = (byte)(~dis.readByte());
}
// Convert byte array to String
String str = new String(bytes);
list.add(str);
}
Now you have an ArrayList of your String data.
Of course this is not the best, the safest, and the fastest algorithm. You can always find or create faster. But I think this is a good example of doing those kind of things.
If you are using Java you can just try and create a class that implements Serializable This way you can just create an object with all your meta info stored inside, serialize it, and when you wanna load it just deserialize it again.
Its not very safe though since you only need to know have the class it was made with, to deserialize it. But it is something to begin with.
Look into digital signatures, specifically HMACs. Those are pretty much exactly what you need, and the Java Crypto framework should make things fairly straightforward. Here's a potentially relevant SO entry: How to generate an HMAC in Java equivalent to a Python example?
You could pass your file writing stream thru a CipherOutputStream
Generate a random string, or number or anything. get its byte array, produce a key, and use it to encrypt your file.
byte password[] = (WHAT YOUR WANT. STRING, NUMBER, etc.).getBytes();
DESKeySpec desKeySpec;
try {
desKeySpec = new DESKeySpec(password);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(desKeySpec);
Cipher desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
desCipher.init(Cipher.ENCRYPT_MODE, key);
// Create stream
FileOutputStream fos = new FileOutputStream("Your file here");
BufferedOutputStream bos = new BufferedOutputStream(fos);
CipherOutputStream cos = new CipherOutputStream(bos, desCipher);
}
Now you can write to the file using cos
Reading the file is done the same way using the SecretKey object
SecretKey key = loadKey(); // Deserialize your SecretKey object
try {
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
FileInputStream fis = new FileInputStream("Your file here");
BufferedInputStream bis = new BufferedInputStream(fis);
CipherInputStream cis = new CipherInputStream(bis, cipher);
now you can read using cis
The downside is you need to keep the SecretKey object (Serialize it or something) it wouldn't be a problem for any low level hacker to get the data (since the key is stored on the device) but it wouldn't allow just changing your data using a text editor.

Categories