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) additions of new classes in the jar
I signed the jar using jarsigner. When I verify either of the above cases with jarsigner it works like I would expect.
When I try to do it programmatically using the samples in How to verify a jar signed with jarsigner programmatically or How to verify signature on self signed jar? however, I don't get any SecurityExceptions...or any exceptions at all for that matter.
Not sure what I am doing wrong since those snippets seemed to work for other people. Any ideas? This is JDK 1.6 BTW.
Edit:
As requested below, a sample of the code...supply your own modified jar :)
JarFile myJar;
try
{
//Insert the full path to the jar here
String libPath = ""
stature = new JarFile(libPath,true);
//Don't really need this right now but was using it to inspect the SHA1 hashes
InputStream is = myJar.getInputStream(myJar.getEntry("META-INF/MANIFEST.MF"));
Manifest man = myJar.getManifest();
is.close();
verifyJar(myJar);
}
catch (IOException ioe)
{
throw new Exception("Cannot load jar file", ioe);
}
private void verifyJar(JarFile jar) throws Exception
{
Enumeration<java.util.jar.JarEntry> entries = jar.entries();
while (entries.hasMoreElements())
{
java.util.jar.JarEntry entry = entries.nextElement();
try
{
jar.getInputStream(entry);
//Also tried actually creating a variable from the stream in case it was discarding it before verification
//InputStream is = jar.getInputStream(entry);
//is.close();
}
catch (SecurityException se)
{
/* Incorrect signature */
throw new Exception("Signature verification failed", se);
}
catch (IOException ioe)
{
throw new Exception("Cannot load jar file entry", ioe);
}
}
}
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.