Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Verifying Jar Signature

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);
            }
    }
}

like image 735
Amasuriel Avatar asked Apr 07 '11 21:04

Amasuriel


2 Answers

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.

like image 125
trashgod Avatar answered Oct 19 '22 07:10

trashgod


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.

like image 35
Amasuriel Avatar answered Oct 19 '22 07:10

Amasuriel