AES加密更改Java中的文件大小

omvjsjqw  于 2023-06-28  发布在  Java
关注(0)|答案(3)|浏览(199)

我试图加密所有.class文件内的jar文件与AES。在加密过程之后,我将其写入一个新的.jar文件。但输出文件大小会加倍。AES加密对文件大小的改变这么大是正常的吗?
输入文件:8.914.293字节-输出文件:19.023.321字节

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

zf2sa74q1#

问题是你读的是压缩数据,一旦读到它就会被解压缩。之后,您加密“明文”数据,这将使数据随机化。当您放回文件时,压缩例程将无法压缩此随机数据。所以,是的,你会期望. jar文件的大小增长。如果你加密. jar文件本身,那么你不会得到一个大的大小增加。
请注意,您似乎没有使用认证加密,也没有存储随机IV。这意味着您的实现并不像它可能的那样安全。

xghobddn

xghobddn2#

不,仅加密就显著增加文件大小是不正常的。
正如@mahdi hashemi所提到的那样,有轻微的开销,但这通常在每个加密“会话”16-32字节之间。我没有详细研究过代码,但通常你应该生成一个随机初始化向量,它的大小与块大小相同,16字节,并与加密数据一起存储,然后用PKCS 5填充CBC,最多额外16字节。严格地说,你不能用AES填充PKCS 5,我假设它实际上将是称为PKCS 7的逻辑扩展。因此,每个加密会话或段的开销高达32字节是正常的。(您应该指定PCKS 7作为AES的填充)。
它的大小增加到两倍是不正常的,你在代码中做错了什么。
而且,不,它对输出大小没有任何影响,你用什么块大小读取输入-一个字节或所有。AES CBC的工作原理是一样的,当阅读大量数据时,内存使用的轻微增加对输出大小没有影响--当然,前提是代码中没有与分块相关的错误,但导致扩展的是错误,而不是分块。
扩展背后的实际原因可能是.jar文件实际上是一种压缩zip存档的形式,并且您将其作为解压缩流读取,然后加密解压缩流,因此您的加密文件可能错过了jar文件中“内置”的压缩。

vs91vp4v

vs91vp4v3#

是的,AES加密增加文件大小是正常的。其原因是加密算法(包括AES)向输出中添加了一些额外的字节,这是出于安全原因所需的。此外,您使用的填充方案(PKCS5Padding)可能会向明文添加额外的字节,以确保加密的数据是密码块大小的倍数。这可能会导致输出大小增加,特别是对于小文件。
在您的代码中,您还使用ByteArrayOutputStream在加密之前将每个JarEntry的整个内容读取到内存中。这可能会导致大文件的内存问题,也可能导致文件大小增加。相反,您可以按块读取和加密数据,并将每个块写入输出流。这将有助于保持较低的内存使用率并减少输出文件的总体大小。
下面是代码的更新版本,它以块的形式读取和加密数据: java

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();

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

        String name = entry.getName();
        if(name.endsWith(".class")){
            JarEntry ne = new JarEntry(name);
            dst_jar.putNextEntry(ne);
            
            int len;
            while ((len = is.read(buf, 0, buf.length)) != -1) {
                byte[] encryptedData = cipher.update(buf, 0, len);
                dst_jar.write(encryptedData);
            }
            byte[] finalEncryptedData = cipher.doFinal();
            dst_jar.write(finalEncryptedData);
        } else {
            JarEntry ne = new JarEntry(name);
            dst_jar.putNextEntry(ne);
            int len;
            while ((len = is.read(buf, 0, buf.length)) != -1) {
                dst_jar.write(buf, 0, len);
            }
        }
        is.close();
        dst_jar.closeEntry();
    }
    src_jar.close();

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

在这个更新的代码中,输入流以1024字节的块读取,并且每个块在读取时被加密并写入输出流。最后一个块使用doFinal()方法加密,这确保所有剩余的字节都被正确加密并添加到输出中。这种方法应该有助于减少内存使用并最大限度地减少文件大小的增加。

相关问题