Independent Independent - 1 month ago 11
Java Question

Read different inputs from InputStream JAVA

I'm writing a code that send encrypted file from client to server
but first the client send the encrypted message digest of the file to the server and then send the name of the file and at the end it will send the bytes of encrypted file,
but in the server side it read all these variables as one variable which is the digest ,

and when the server trying to decrypt the digest it throws

Illegal Block Size Exception

My question here is how can the server read and save them in different variables ??

Client

// set mode to encrypt
AesCipher.init(Cipher.ENCRYPT_MODE, key);

DataOutputStream toServer = new DataOutputStream(socket.getOutputStream());

// get the digest of the file
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] hash = md.digest(bytes);

// encrypt digest and write it to file
byte [] encryptedHash = AesCipher.doFinal(hash);
toServer.write(encryptedHash);


// write file name to server
toServer.writeUTF(fileName);

//encrypt file
byte[] encryptedByte = AesCipher.doFinal(bytes);


// write file to server
toServer.write(encryptedByte);
toServer.flush();
socket.close();


Server

// read digest of the file
byte [] digest =IOUtils.toByteArray(fromClient);

// decrypt it
AesCipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedDigest = AesCipher.doFinal(digest);

// read file name to be received
String fileName = fromClient.readUTF();

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName);
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);

// read file bytes from client
byte[] fileBytes = IOUtils.toByteArray(fromClient);

AesCipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedByte = AesCipher.doFinal(fileBytes);
bos.write(decryptedByte, 0, decryptedByte.length);
bos.close();


also I tried this code but it didn't works too

// read digest of the file
ByteArrayOutputStream buffer = new ByteArrayOutputStream();

int nRead;
byte[] data = new byte[1024];

while ((nRead = fromClient.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}

buffer.flush();
byte[] digest = buffer.toByteArray();

// decrypt it
AesCipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedDigest = AesCipher.doFinal(digest);

// read file name to be received
String fileName = fromClient.readUTF();

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName);
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);

// read file bytes from client
byte[] fileBytes = IOUtils.toByteArray(fromClient);

AesCipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedByte = AesCipher.doFinal(fileBytes);
bos.write(decryptedByte, 0, decryptedByte.length);
bos.close();

EJP EJP
Answer Source

IOUtils.toByteArray(InputStream) reads the entire stream. So instead of just getting the hash bytes, you got the whole stream, and there was nothing left for the filename or the ciphertext, and the hash didn't check.

You don't need external libraries for this. You can do it all with DataInputStream and DataOutputStream. But you do need to send the length of the hash ahead of the hash.

Client:

        // set mode to encrypt
        AesCipher.init(Cipher.ENCRYPT_MODE, key);

        DataOutputStream toServer = new DataOutputStream(socket.getOutputStream());

        // get the digest of the file
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] hash = md.digest(bytes);

        // encrypt digest and write it to file
        byte [] encryptedHash = AesCipher.doFinal(hash);
        toServer.writeInt(encryptedHash.length);
        toServer.write(encryptedHash);

        // write file name to server
        toServer.writeUTF(fileName);

        //encrypt file
        byte[] encryptedByte = AesCipher.doFinal(bytes);

        // write file to server
        toServer.writeInt(encryptedByte.length);
        toServer.write(encryptedByte);
        socket.close();

Server:

    // read digest of the file
    int digestLength = fromClient.readInt();
    byte[] digest = new byte[digestLength];
    fromClient.readFully(digest);

    // decrypt it
    AesCipher.init(Cipher.DECRYPT_MODE, key);
    byte[] decryptedDigest = AesCipher.doFinal(digest);

    // read file name to be received
    String fileName = fromClient.readUTF();

    File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName);
    FileOutputStream fos = new FileOutputStream(file);
    BufferedOutputStream bos = new BufferedOutputStream(fos);

    // read file bytes from client
    int fileLength = fromClient.readInt();
    byte[] fileBytes = new byte[fileLength];
    fromClient.readFully(fileBytes);

    AesCipher.init(Cipher.DECRYPT_MODE, key);
    byte[] decryptedByte = AesCipher.doFinal(fileBytes);
    bos.write(decryptedByte, 0, decryptedByte.length);
    bos.close();

However the encryption and decryption parts of this would be much better done with CipherInputStream and CipherOutputStream. You shouldn't load entire files into memory.

Note that the file.createNewFile() call was redundant before new FileOutputStream(...).

Why you're encrypting a message digest is another mystery. You should be using it as a final step to compare with a locally-generated digest after decryption.