0

I am trying to encrypt a zip file with a password using AES-256 encryption. Below is the code that I am running.

import java.io.*;
import java.security.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;


public class Main {
    public static void encryptAndClose(FileInputStream fis, FileOutputStream fos) 
            throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {

        // Length is 16 byte
        SecretKeySpec sks = new SecretKeySpec("1234567890123456".getBytes(), "AES");
        // Create cipher
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, sks);      

        // Wrap the output stream for encoding
        CipherOutputStream cos = new CipherOutputStream(fos, cipher);       

        //wrap output with buffer stream
        BufferedOutputStream bos = new BufferedOutputStream(cos);     

        //wrap input with buffer stream
        BufferedInputStream bis = new BufferedInputStream(fis); 

        // Write bytes
        int b;
        byte[] d = new byte[8];
        while((b = bis.read(d)) != -1) {
            bos.write(d, 0, b);
        }
        // Flush and close streams.
        bos.flush();
        bos.close();
        bis.close();
    }


    public static void decryptAndClose(FileInputStream fis, FileOutputStream fos) 
            throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {

        SecretKeySpec sks = new SecretKeySpec("1234567890123456".getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, sks);

        CipherInputStream cis = new CipherInputStream(fis, cipher);

        //wrap input with buffer stream
        BufferedInputStream bis = new BufferedInputStream(cis); 

        //wrap output with buffer stream
        BufferedOutputStream bos = new BufferedOutputStream(fos);       

        int b;
        byte[] d = new byte[8];
        while((b = bis.read(d)) != -1) {
            bos.write(d, 0, b);
        }
        bos.flush();
        bos.close();
        bis.close();
    }
    
    
    static void zipFile(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException {
        if (fileToZip.isHidden()) {
            return;
        }
        if (fileToZip.isDirectory()) {
            if (fileName.endsWith("/")) {
                zipOut.putNextEntry(new ZipEntry(fileName));
                zipOut.closeEntry();
            } else {
                zipOut.putNextEntry(new ZipEntry(fileName + "/"));
                zipOut.closeEntry();
            }
            File[] children = fileToZip.listFiles();
            for (File childFile : children) {
                zipFile(childFile, fileName + "/" + childFile.getName(), zipOut);
            }
            return;
        }
        FileInputStream fis = new FileInputStream(fileToZip);
        ZipEntry zipEntry = new ZipEntry(fileName);
        zipOut.putNextEntry(zipEntry);
        byte[] bytes = new byte[1024];
        int length;
        while ((length = fis.read(bytes)) >= 0) {
            zipOut.write(bytes, 0, length);
        }
        fis.close();
    }
    
    static void unZipFile(ZipEntry zipEntry,File destDir,ZipInputStream zis, byte[] buffer) throws IOException{
        while (zipEntry != null) {
            File newFile = newFile(destDir, zipEntry);
            if (zipEntry.isDirectory()) {
                if (!newFile.isDirectory() && !newFile.mkdirs()) {
                    throw new IOException("Failed to create directory " + newFile);
                }
            } else {
                // fix for Windows-created archives
                File parent = newFile.getParentFile();
                if (!parent.isDirectory() && !parent.mkdirs()) {
                    throw new IOException("Failed to create directory " + parent);
                }
                
                // write file content
                FileOutputStream fos = new FileOutputStream(newFile);
                int len;
                while ((len = zis.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                }
                fos.close();
            }
        zipEntry = zis.getNextEntry();
       }
    }
    public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
        File destFile = new File(destinationDir, zipEntry.getName());

        String destDirPath = destinationDir.getCanonicalPath();
        String destFilePath = destFile.getCanonicalPath();

        if (!destFilePath.startsWith(destDirPath + File.separator)) {
            throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
        }

        return destFile;
    }
    
    public static void main(String[]args) {
        
        //compress to zip.
        String sourceFile = "C:\\test";
        FileOutputStream fos;
        try {
            fos = new FileOutputStream("C:\\test\\test.zip");
            ZipOutputStream zipOut = new ZipOutputStream(fos);
            File fileToZip = new File(sourceFile);

            zipFile(fileToZip, fileToZip.getName(), zipOut);
            zipOut.close();
            fos.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        //encrypt the zip.
        String outDir = "C:/test";
        String outFilename = "test-encrypt.zip";
        
        String inDir = "C:/test";
        String inFilename = "test.zip";
        
        File output= new File(outDir, outFilename);

        File input= new File(inDir, inFilename);

        if (input.exists()) {
            System.out.println("test");
            FileInputStream inStream;
            try {
                inStream = new FileInputStream(input);
                FileOutputStream outStream = new FileOutputStream(output);
    
                encryptAndClose(inStream, outStream);   
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

The intent of the code is to create a zip file then encrypt the generated zip file with AES. The code can successfully generate a zip file that can be zipped and unzipped with WinZip or 7zip.

However, when it tries to encrypt the generated zip file, the code instead causes the file (e.g.test-encrypt.zip) to be corrupted and cannot be opened by programs like WinZip or 7zip.

I wish to do this without using zip4j if possible. Is there any problem with my code or understanding of the code?

This is my first time working with encryption and any help would be much appreciated!

xyiong
  • 363
  • 1
  • 10
  • You need to follow the [Winzip standard for zip encryption with AES](https://www.winzip.com/en/support/aes-encryption/). – President James K. Polk Sep 07 '22 at 15:33
  • Apart from encrypting the data, you also need to write the aes related headers and appropriate zip encryption headers to the zip file. You can either use [zip4j](https://github.com/srikanth-lingala/zip4j) to do it for you, or if you want to do it yourself, have a look at zip4j's code to know how its done. Another resource to look at apart from the AES link in the comment above would be the [zip spec](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT) – Srikanth Reddy Lingala Sep 08 '22 at 09:11

2 Answers2

2

If you create a ZIP first, and then encrypt the ZIP file using AES algorithm or whatever algorithm, the output file won't remain a ZIP. It won't be following the ZIP standard format. So, it won't be possible to unzip.

You need to first Decrypt the file and get back the original ZIP, and then only that original zip can be opened/unzipped using any wellknown zip handling software.

Encrypting a ZIP file will result in the bytes inside the file changed, which will result in the software not recognizing the file header, necessary ZipEntry record structure, etc.

The best option is to provide a password at the time of creating the zip, so that it cannot be unzipped without a password. OR Encrypt the original files, and then put the encrypted files inside the Zip. But, don't encrypt the ZIP file.

Ishan
  • 400
  • 2
  • 8
  • Hi, thank you for the reply! I see... The decryption method I am required to use would rely on using wellknown zip handling software so I think I'll try providing a password at the time of creating the zip. As far as I know, zip4j allows me to do that quite easily. However, is there any way or advice that you might know to do so without using external libraries like zip4j? – xyiong Sep 07 '22 at 15:03
  • I don't think JDK's own zip related classes support password to deflate or inflate. So, you have to use 3rd party libraries if you want to create a common password-protected zip that can be opened using any well-known software. OR You can just use the same routines that you have created to encrypt the ZIP file that won't be unzipped using well-known software, but you provide your own inflater that decrypts the zip and then unzips. – Ishan Sep 08 '22 at 06:59
0

Java does not support zip file encryption. Anyway, it supports very limited activities with zip files. You have to try to make you work with zip file much more easy. E.g. you can check zip4jvm

Create (or open existed) zip archive and add some regular files and/or directories with AES 256bit encryption.

Path zip = Paths.get("filename.zip");
Collection<Path> paths = Arrays.asList(
        Paths.get("/bikes/ducati-panigale-1199.jpg"),
        Paths.get("/bikes/honda-cbr600rr.jpg"),
        Paths.get("/cars"),
        Paths.get("/saint-petersburg.jpg"));
ZipEntrySettings settings = ZipEntrySettings.builder()
                                            .encryption(Encryption.AES_256, "password".toCharArray())
                                            .build();
ZipIt.zip(zip).entrySettings(settings).add(paths);

To get more details about AES encryptions in zip you can check AesEncoder.java

Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35