0

I have a zip file that is AES encrypted. After decrypting I'm left with a byte[] containing the zip content. But when I try to unzip it, using a ByteArrayInputStream, ZipInputStream.getNextEntry() returns null right away. Debugging, I see that my byte[] doesn't have a required local file header signature which is

static long LOCSIG = 0x04034b50L; // "PK\003\004"

so ZipInputStream.getNextEntry() returns null.

If, however, I write those decrypted bytes out to a file and then use a FileInputStream() that I pass to ZipInputStream(), everything works as expected. Below is my current code. Can anyone suggest a way to unzip without first writing out to a temporary file?

    byte[] data = AESUtil.decryptInputStream(...);
    ByteArrayInputStream bis = new ByteArrayInputStream(data);
    ZipInputStream stream = new ZipInputStream(bis);
    ZipEntry entry;
    while ((entry = stream.getNextEntry()) != null) {
        ...
    }
wolfman
  • 123
  • 9
  • 1
    Use a [CipherInputStream](https://docs.oracle.com/en/java/javase/16/docs/api/java.base/javax/crypto/CipherInputStream.html). – VGR Jul 08 '21 at 17:45
  • The same thing happens when I use a CipherInputStream. The ZipInputStream.readLOC() returns null on this comparison: ** if (get32(tmpbuf, 0) != LOCSIG) { return null; } ** – wolfman Jul 08 '21 at 18:57
  • I need to correct my initial post - and apologize: I incorrectly wrote that if I write the decrypted bytes out to a file and then use FileInputStream with the ZipInputStream everything works. I didn't actually try that - I simply ran the UNIX command "unzip" on the decrypted file and that worked. I just assumed - incorrectly - that FileInputStream/ZipInputStream would work if that were the case. So, in summary, it looks like I'm writing out a decrypted zip file with bytes that the "zip" program can handle, but ZipInputStream cannot. – wolfman Jul 08 '21 at 19:43
  • I should clarify: use CipherInputStream instead of a ByteArrayOutputStream. I suspect the problem may lie in your AWSUtil.decryptInputStream method. So just use `new ZipInputStream(new CipherInputStream(originalInputStream, cipher))`. – VGR Jul 08 '21 at 20:40
  • @VGR I understood your original post. I was doing exactly what you specified in your subsequent clarification. But I still got the same result - the ZipInputStream.getnextEntry() returns null because the first 32 bytes of the byte stream don't match some expected header value of 0x04034b50L. But the standalone "unzip" program unzips a file with the same exact bytes just fine. I think I might need to try another library (isn't there an apache commons zip/unzip lib?) – wolfman Jul 08 '21 at 21:49
  • `unzip` is known to be highly tolerant of extra bytes at the start and/or end of the file. I suspect the java.util.zip API isn’t as tolerant. – VGR Jul 08 '21 at 22:01

1 Answers1

0

I've come to the conclusion that ZipInputStream just isn't flexible enough. When I substituted the above code with Apache Commons' compress classes, everything works. Below is a working implementation using that library and the same byte array:

byte[] data = AESUtil.decryptInputStream(...);
SeekableInMemoryByteChannel inMemoryByteChannel = new SeekableInMemoryByteChannel(data);
ZipFile zipFile = new ZipFile(inMemoryByteChannel);
Iterator<ZipArchiveEntry> iterator = = zipFile.getEntriesInPhysicalOrder().asIterator();
while (iterator.hasNext()) {
    ...
}
wolfman
  • 123
  • 9