Search code examples
javaaesfilesize

AES encryption changes file size in Java


I am trying to encrypt all .class files inside a jar file with AES. After the encrypt process, I write it to a new .jar file. But the output file size doubles. Is it normal for AES encryption to change the file size this much?

in file: 8.914.293 bytes - out file: 19.023.321 bytes

public static void main(String[] args) throws Exception {
    SecretKey aesKey = new SecretKeySpec(aesKeyBytes, 0, aesKeyBytes.length, "AES");
    IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, aesKey, ivSpec);
            
    File sourceFile = new File("C:\\test\\test.jar");
    File encryptFile = new File("C:\\test\\testOUT.jar");
    encrypt(cipher, sourceFile, encryptFile);
}

private static void encrypt(Cipher cipher, File sourceFile, File targetFile) throws Exception {
    targetFile.delete();

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buf = new byte[1024];
    
    FileOutputStream dst_fos = new FileOutputStream(targetFile);
    JarOutputStream dst_jar = new JarOutputStream(dst_fos);
    
    JarFile src_jar = new JarFile(sourceFile);
    for (Enumeration<JarEntry> enumeration = src_jar.entries(); enumeration.hasMoreElements();) {
        JarEntry entry = enumeration.nextElement();

        InputStream is = src_jar.getInputStream(entry);
        int len;
        while ((len = is.read(buf, 0, buf.length)) != -1) {
            baos.write(buf, 0, len);
        }
        byte[] bytes = baos.toByteArray();

        String name = entry.getName();
        if(name.endsWith(".class")){
            try {
                bytes = encryptBytes(cipher, bytes);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        JarEntry ne = new JarEntry(name);
        dst_jar.putNextEntry(ne);
        dst_jar.write(bytes);
        baos.reset();
    }
    src_jar.close();

    dst_jar.close();
    dst_fos.close();
}

private static byte[] encryptBytes(Cipher cipher, byte[] data) throws Exception {
    byte[] encryptedData = cipher.doFinal(data);
    return encryptedData;
}   

Solution

  • No, it is not normal for just encryption to increase the file size significantly.

    There is a slight overhead as mentioned by @mahdi hashemi, but this is typically between 16-32 bytes per encryption "session". I haven't studied the code in detail, but normally you should generate a random initialization vector which is the same size as the block size, 16 bytes, and store with the encrypted data and then for CBC with PKCS5 padding up to an additional 16 bytes. Strictly speaking you can't have PKCS5 padding with AES, I'm assuming it in fact will be the logical extension called PKCS7. So, up to 32 bytes of overhead per encryption session, or segment is normal. (You should be specifying PCKS7 as padding for AES).

    It is not normal for it to increase to twice the size, you're doing something wrong in the code.

    And, no, it has no effect whatsoever on the output size what chunk size you read the input with - one byte or everything. AES CBC works the same, and the slight increase memory use when reading a lot of data has no effect on the output size - provided of course there are no bugs in the code related to the chunking, but then it's the bug that's causing the expansion, not the chunking or not.

    The actual reason behind the expansion is likely that .jar files are in fact a form of compressed zip-archives, and you're reading it as a decompressed stream and then encrypting the decompressed stream, so your encrypted file is probably missing out on the compression "built in" in jar-files.