Closed pbeast closed 3 years ago
And no issues when you sign the jar with jarsigner?
Yep, works perfectly. I unpacked both of the archives but failed to identify the difference the "main" class is present at the same location, and the manifest has all of the required entries
Ah, btw, I thought maybe the classes are transferred incorrectly between JARs, so I peeked on jarsigner implementation. I changed your code a bit but that had no effect. Anyway, maybe it worse a PR:
JarEntry newJarEntry = new JarEntry(jarEntry.getName());
newJarEntry.setMethod(jarEntry.getMethod());
newJarEntry.setTime(jarEntry.getTime());
newJarEntry.setComment(jarEntry.getComment());
newJarEntry.setExtra(jarEntry.getExtra());
if (jarEntry.getMethod() == JarEntry.STORED) {
newJarEntry.setSize(jarEntry.getSize());
newJarEntry.setCrc(jarEntry.getCrc());
}
// newJarEntry.setCompressedSize(jarEntry.getCompressedSize());
jos.putNextEntry(newJarEntry);
try (InputStream jis = jar.getInputStream(jarEntry)) {
jis.transferTo(jos);
// byte[] buffer = new byte[2048];
// int read;
// while ((read = jis.read(buffer)) != -1) {
// jos.write(buffer, 0, read);
// }
jos.closeEntry();
}
That's a very interesting bug, but unfortunately it's already quite late here. I'll take a closer look tomorrow.
Thanks a lot!!!
The issue is caused by KSE not transferring the directory entries of the jar. I have a fixed version of JarSigner locally, but need to do more testing before I can commit it to GitHub.
Thanks for reporting!
🙂 I came to the same conclusion but decided to wait for more qualified opinion. Want to share the fixing code and I will do some testing in parallel?
Sure:
/*
* Write out all JAR entries from source JAR to output stream excepting
* manifest and existing signature files for the supplied signature name
*/
private static void writeJarEntries(JarFile jar, JarOutputStream jos, String signatureName) throws IOException {
for (Enumeration<?> jarEntries = jar.entries(); jarEntries.hasMoreElements();) {
JarEntry jarEntry = (JarEntry) jarEntries.nextElement();
if (!jarEntry.isDirectory()) {
String entryName = jarEntry.getName();
// Signature files not to write across
String sigFileLocation = MessageFormat.format(METAINF_FILE_LOCATION, signatureName, SIGNATURE_EXT)
.toUpperCase();
String dsaSigBlockLocation = MessageFormat.format(METAINF_FILE_LOCATION, signatureName,
DSA_SIG_BLOCK_EXT);
String rsaSigBlockLocation = MessageFormat.format(METAINF_FILE_LOCATION, signatureName,
RSA_SIG_BLOCK_EXT);
// Do not write across existing manifest or matching signature files
if ((!entryName.equalsIgnoreCase(MANIFEST_LOCATION)) && (!entryName.equalsIgnoreCase(sigFileLocation))
&& (!entryName.equalsIgnoreCase(dsaSigBlockLocation))
&& (!entryName.equalsIgnoreCase(rsaSigBlockLocation))) {
// New JAR entry based on original
transferJarEntry(jar, jos, jarEntry);
}
} else {
transferJarEntry(jar, jos, jarEntry);
}
}
}
private static void transferJarEntry(JarFile jar, JarOutputStream jos, JarEntry jarEntry) throws IOException {
JarEntry newJarEntry = new JarEntry(jarEntry.getName());
newJarEntry.setMethod(jarEntry.getMethod());
newJarEntry.setCompressedSize(jarEntry.getCompressedSize());
newJarEntry.setCrc(jarEntry.getCrc());
jos.putNextEntry(newJarEntry);
try (InputStream jis = jar.getInputStream(jarEntry)) {
IOUtils.copy(jis, jos);
jos.closeEntry();
}
}
Works like a charm! Thanks again
Also, I combined the fix with my changes (the ones I snitched from the jarsiger) and think it's a bit better approach for handling STORED
entries:
private static void transferJarEntry(JarFile jar, JarOutputStream jos, JarEntry jarEntry) throws IOException {
JarEntry newJarEntry = new JarEntry(jarEntry.getName());
newJarEntry.setMethod(jarEntry.getMethod());
newJarEntry.setTime(jarEntry.getTime());
newJarEntry.setComment(jarEntry.getComment());
newJarEntry.setExtra(jarEntry.getExtra());
if (jarEntry.getMethod() == JarEntry.STORED) {
newJarEntry.setSize(jarEntry.getSize());
newJarEntry.setCrc(jarEntry.getCrc());
}
jos.putNextEntry(newJarEntry);
try (InputStream jis = jar.getInputStream(jarEntry)) {
jis.transferTo(jos);
jos.closeEntry();
}
}
Also, the jis.transferTo(jos);
do exactly the same as IOUtils.copy
but uses bigger buffer - 8192
Ah, I forgot to mention this: Currently KSE has to remain compatible with Java 8. transferTo() was introduced with Java 9. But I'll adopt the "STORE part". Thanks :-)
That makes sense :-) Thanks again for your support
Describe the bug JAVA application that uses spring boot fails to start after signing because unable to find mainClass:
Test project - https://drive.google.com/file/d/1tXtQDw41_SY21nYyRg6IBaB375YTJxUX/view?usp=sharing Unsigned file - test/target/test.jar Signed file - test/target/test-signed.jar
To Reproduce Steps to reproduce the behavior:
mvn package --file pom.xml
java -jar target/test.jar
Hello World!
target/test.jar
jarsigner -verify target/test-signed.jar
jar verified.
(additional options -verbose -certs provide expected info)java -jar target/test-signed.jar
Expected behavior Signed JAR should be runnable
Environment
Version of KSE: 5.4.4 (built from sources)
Version of Java: openjdk version "14.0.2" 2020-07-14 OpenJDK Runtime Environment AdoptOpenJDK (build 14.0.2+12) OpenJDK 64-Bit Server VM AdoptOpenJDK (build 14.0.2+12, mixed mode, sharing)
Platform (OS): macOS Big Sur
P.S. Huge thanks for the wonderful app!