A note should be made that the patch only makes sign4j to return -1 if the file sizes doesn't match , so one might want to recreate the executable and retry to sign it in the CD-pipeline / build
Last edit: Markus Gothe 2018-08-11
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I've been experiencing this too. Unfortunately it is nondeterministic; sometimes it works and sometimes it doesn't. When it works the file is signed successfully; when it doesn't, running the EXE results in an error message "Invalid or corrupt jarfile" from the launched javaw.exe process. Nothing in my build process changes; I can run the build again, and it might work the next time or it might not. Overall my success rate is only about 50%.
The worst problem is that I can't detect this failure in my build process. The sign4j step completes successfully and returns a 0 exit code. I have to go back at the end of the build and manually run all the EXEs that were generated to see if they are corrupt. Then resign the corrupt files again, manually repeating and testing in a loop, until I get a version that isn't corrupt. It means my CI pipeline can't be trusted, and I risk releasing a corrupt file without constant diligence.
Looking at Markus's patch, it makes me wonder if some signing algorithms add a signature that can vary in size from one run to the next?
I'm using jsign version 7.0 (not the 2.0 version bundled in launch4j) because I needed support for Azure Trusted Signing. If it helps, I can share a copy of the unsigned exe that launch4j produced (which works fine) along with corrupt and successful versions produced by sign4j.
Last edit: David Tuma 2025-06-09
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
After testing, I was able to verify the root cause I suspected earlier: the size of the appended signature can change from one run to the next. Timestamping could be a contributing factor.
Fortunately, the size isn't completely unstable; it usually differs by a few bytes, and toggles between two (or occasionally three) possibilities. This makes it feasible to produce a successful signature by iterating until the sizes match in two consecutive runs.
I took a look at modifying the original sign4j.c program, but C isn't my forte. Since all users of launch4j are Java developers anyway, I wrote a Java version of the sign4j utility that includes a fix for this bug. I took care to make sure I was command-line compatible with the original C version, so it works as a drop-in replacement. I also added an Ant task. I've published it here: https://github.com/dtuma/sign4j-java
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I think I'm also experiencing this
Last edit: Warren Prescott 2018-07-09
Actually I get this BEFORE signing:
Current PE checksum : 00018890
Calculated PE checksum: 011E2EDA MISMATCH!!!!
I found out that the temporary file and the infile might differ in file size and this causing the error. Below is my fix for it.
A note should be made that the patch only makes sign4j to return -1 if the file sizes doesn't match , so one might want to recreate the executable and retry to sign it in the CD-pipeline / build
Last edit: Markus Gothe 2018-08-11
Thanks for the patch, I will have a look.
I've been experiencing this too. Unfortunately it is nondeterministic; sometimes it works and sometimes it doesn't. When it works the file is signed successfully; when it doesn't, running the EXE results in an error message "Invalid or corrupt jarfile" from the launched javaw.exe process. Nothing in my build process changes; I can run the build again, and it might work the next time or it might not. Overall my success rate is only about 50%.
The worst problem is that I can't detect this failure in my build process. The sign4j step completes successfully and returns a 0 exit code. I have to go back at the end of the build and manually run all the EXEs that were generated to see if they are corrupt. Then resign the corrupt files again, manually repeating and testing in a loop, until I get a version that isn't corrupt. It means my CI pipeline can't be trusted, and I risk releasing a corrupt file without constant diligence.
Looking at Markus's patch, it makes me wonder if some signing algorithms add a signature that can vary in size from one run to the next?
I'm using jsign version 7.0 (not the 2.0 version bundled in launch4j) because I needed support for Azure Trusted Signing. If it helps, I can share a copy of the unsigned exe that launch4j produced (which works fine) along with corrupt and successful versions produced by sign4j.
Last edit: David Tuma 2025-06-09
After testing, I was able to verify the root cause I suspected earlier: the size of the appended signature can change from one run to the next. Timestamping could be a contributing factor.
Fortunately, the size isn't completely unstable; it usually differs by a few bytes, and toggles between two (or occasionally three) possibilities. This makes it feasible to produce a successful signature by iterating until the sizes match in two consecutive runs.
I took a look at modifying the original sign4j.c program, but C isn't my forte. Since all users of launch4j are Java developers anyway, I wrote a Java version of the sign4j utility that includes a fix for this bug. I took care to make sure I was command-line compatible with the original C version, so it works as a drop-in replacement. I also added an Ant task. I've published it here: https://github.com/dtuma/sign4j-java