java 验证 Jar 签名

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5587656/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 11:48:02  来源:igfitidea点击:

Verifying Jar Signature

javajar-signing

提问by Amasuriel

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

我正在尝试以编程方式验证 jar 文件没有被明显篡改。我有 2 个要防止的用例。1) 现有类的修改 2) 在 jar 中添加新类

I signed the jar using jarsigner. When I verify either of the above cases with jarsigner it works like I would expect.

我使用 jarsigner 对 jar 进行了签名。当我使用 jarsigner 验证上述任何一种情况时,它的工作方式与我预期的一样。

When I try to do it programmatically using the samples in How to verify a jar signed with jarsigner programmaticallyor How to verify signature on self signed jar?however, I don't get any SecurityExceptions...or any exceptions at all for that matter.

当我尝试使用如何以编程方式验证使用 jarsigner签名的 jar如何验证自签名 jar 上的签名中的示例以编程方式执行此操作时 但是,我没有收到任何 SecurityExceptions ......或者根本没有任何异常。

Not sure what I am doing wrong since those snippets seemed to work for other people. Any ideas? This is JDK 1.6 BTW.

不确定我做错了什么,因为这些片段似乎对其他人有用。有任何想法吗?这是 JDK 1.6 顺便说一句。

Edit: As requested below, a sample of the code...supply your own modified jar :)

编辑:根据下面的要求,代码示例...提供您自己修改过的 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);
            }
    }
}
    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);
            }
    }
}

采纳答案by Amasuriel

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.

我有我的篡改签名 jar,但我也编译了所有相同的类,因为这是我的开发环境。因此,类加载器通过 jar 类获取已编译的类。编译的类没有清单,因此没有生成安全错误。

Once I deleted my compiled classes I got the expected security exceptions.

一旦我删除了我编译的类,我就会得到预期的安全异常。

回答by trashgod

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.

使用下面的示例,我获得了正确签名的 JAR ( true) 和更改的 JAR ( false)的预期结果。触发测试效果的一种简单方法是更改​​ 中列出的摘要之一META-INF/MANIFEST.MF

Note that this approach ignores entries that are notlisted in the manifest. Using jarsigner -verifyreports, "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.

请注意,此方法会忽略清单中列出的条目。使用jarsigner -verify报告,“此 jar 包含未经完整性检查的未签名条目。” 完全读取流后,entry.getCodeSigners()可用于确定条目是否有任何签名者。

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 signersourcehas been added after getting the input stream.

注意:对于 JDK 8,仅仅获取输入流是不够的。在 中jarsigner,流也必须被读取。在上面的代码中,在获取输入流后添加了一个改编自jar signer的循环。