2

I have a java project that is built with maven. During the package phase, the code is built into a jar (maven-jar-plugin 3.1.1) and is wrapped into an .exe (launch4j-maven-plugin 1.7.25)

Part of my code opens the .exe as a file system to fetch resources. Specifically, using java.nio.file.FileSystems.newFileSystem(URL url), passing in the exe

This file system properly properly identifies that my file is indeed a wrapped jar, and reads it as such.

This all breaks down when I try to digitally sign my .exe file. After digitally signing it, I can no longer open the exe as a filesystem in Java. The specific error I have narrowed it down to is at ZipFileSystem.java:991

            // Now scan the block backwards for END header signature
            for (int i = buf.length - ENDHDR; i >= 0; i--) {
                if (buf[i+0] == (byte)'P'    &&
                    buf[i+1] == (byte)'K'    &&
                    buf[i+2] == (byte)'\005' &&
                    buf[i+3] == (byte)'\006' &&
                    (pos + i + ENDHDR + ENDCOM(buf, i) == ziplen)) { -- this check fails. We find the header name, but the size check fails.

So, digitally signing the .exe file added some amount of bytes to my file size, and is causing the jar file's integrity check to always fail. I have tried signing the jar before wrapping (jarsigner), signing the exe after with both Advanced Installer Program and Jsign, and have not been able to remedy this issue.

Having the jar wrapped in an exe that is then signed, causes the jar to become unreadable by java.nio.file.FileSystems. What can I do to fix this? Is there a signer that will know to update the jar's integrity check value? Any help is appreciated

Zephias
  • 21
  • 2
  • https://stackoverflow.com/q/47646135/1424875 seems to be related - it could be the case that without rewriting ZipFileSystem.java, you might have an issue with both the ZIP and the signature needing the end of the file. With that said, I'm not familiar enough with the rules for the ZIP format to be sure. – nanofarad Jun 25 '21 at 17:24
  • The code you have posted is the standard way to find the ZIP end header and then find the central directory. And the way Windows executable files are signed and verified can not be changed. The only option would be to pack your JAR inside another JAR files plus some classloader that can load the classes from the inner JAR. This is the common way to pack signed JAR files into a FAT Jar file. See e.g. https://stackoverflow.com/a/33420518/150978 – Robert Jun 25 '21 at 17:49
  • But as it is your program you could change the integrity check for the Windows EXE version and check instead the PE signature of the whole EXE file. Unfortunately Jsign can only sign but not verify the created signature so this would require a bit of work. – Robert Jun 25 '21 at 17:55

1 Answers1

0

That's quite amazing that ZipFileSystem manages to read a jar embedded in an executable file, but I don't think it was intended to be used that way.

You could work around this issue by loading the resources from the jar using the classloader instead of using a NIO filesystem:

InputStream resource = Thread.currentThread().getContextClassLoader().getResourceAsStream("path/to/your/resource");
Emmanuel Bourg
  • 9,601
  • 3
  • 48
  • 76