I\'m trying to programmatically verify that a jar file has not been obviously tampered with. I have 2 use cases I want to prevent. 1) Modifications of existing classes 2) ad
I figured out why this was happening to me...it was a stupid mistake.
I had my tampered signed jar, but I also had all the same classes compiled since this was my dev env. So the classloader picked up the compiled classes over the jar classes. There is no manifest for the compiled classes, so no security errors were generated.
Once I deleted my compiled classes I got the expected security exceptions.
Using the example below, I obtained the expected result for a correctly signed JAR (true
) and an altered JAR (false
). One simple way to trigger the effect for testing is to change one of the digests listed in META-INF/MANIFEST.MF
.
Note that this approach ignores entries that are not listed in the manifest. Using jarsigner -verify
reports, "This jar contains unsigned entries which have not been integrity-checked." After reading the stream completely, entry.getCodeSigners()
may be used to determine if an entry has any signers.
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/** @see http://stackoverflow.com/questions/5587656 */
public class Verify {
public static void main(String[] args) throws IOException {
System.out.println(verify(new JarFile(args[0])));
}
private static boolean verify(JarFile jar) throws IOException {
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
try {
byte[] buffer = new byte[8192];
InputStream is = jar.getInputStream(entry);
while ((is.read(buffer, 0, buffer.length)) != -1) {
// We just read. This will throw a SecurityException
// if a signature/digest check fails.
}
} catch (SecurityException se) {
return false;
}
}
return true;
}
}
Note: For JDK 8, its not enough to merely get the input stream. As in jarsigner, the stream must be read from, too. In the code above, a loop adapted from the jar signer
source has been added after getting the input stream.