【问题标题】:How to verify a jar signed with jarsigner programmatically如何以编程方式验证使用 jarsigner 签名的 jar
【发布时间】:2010-11-25 08:11:15
【问题描述】:

我想使用 jarsigner 对 jar 进行签名,然后使用 Java 应用程序对其进行验证,该应用程序没有将签名的 jar 作为其类路径的一部分(即仅使用 jar 的文件系统位置)

现在我的问题是从 jar 中取出签名文件,有没有简单的方法可以做到这一点?

我玩过 Inflater 和 Jar InputStreams,但没有运气。

或者这是可以以更好的方式完成的事情?

谢谢

【问题讨论】:

    标签: java code-signing signature jarsigner


    【解决方案1】:

    security Provider implementation guide 概述了验证 JAR 的过程。虽然这些说明是供 JCA 加密服务提供商验证自己的,但它们应该适用于您的问题。

    具体看示例代码"MyJCE.java"中的verify(X509Certificate targetCert)方法。

    【讨论】:

    • 他们也可以提供一个 verifyAllContent() 方法,而不是建议代码;-)
    【解决方案2】:

    您可以使用 jarsigner 应用程序来执行此操作。在 processbuilder(或 Runtime.exec)中,您可以使用这些参数运行命令

     ProcessBulider pb = new ProcessBuilder("/usr/bin/jarsigner", "-verify", "-certs", f.getAbsolutePath());
    

    如果输出 contians 得到验证,则 jar 已签名

    Process p = pb.start();
    p.waitFor();
    InputStream is = p.getInputStream();
    InputStreamReader isr = new InputStreamReader(is);
    BufferedReader br = new BufferedReader(isr);
    String line;
    while ((line = br.readLine()) != null)
    {
    if(line.contains("verified");
    ...
    

    当你有 jarsigner 代码的输出时,你可以做一些更复杂的事情。

    【讨论】:

    • 该示例是特定于平台的,另外 jarsigner 工具通常仅随 JDK 一起提供。
    • jarsigner 不会验证签名者证书(因此任何不受信任的签名都会这样做),它不会检查受信任的时间戳(无法处理来自证书过期的签名者的有效签名)并且它的输出是无用的('即使出现错误也会返回“已验证”,通过解析错误和警告消息可以获得更多信息 - 但这仍然不足以确定证书是否有效)。
    【解决方案3】:

    您可以简单地使用 java.util.jar.JarFile 打开 JAR 并告诉它验证 JAR 文件。如果 JAR 已签名,则 JarFile 可以选择验证它(默认情况下启用)。但是,JarFile 也会愉快地打开未签名的 JAR,因此您还必须检查文件是否已签名。您可以通过检查 JAR 清单中的 *-Digest 属性来做到这一点:具有此类属性属性的元素已签名。

    例子:

    JarFile jar = new JarFile(new File("path/to/your/jar-file"));
    
    // This call will throw a java.lang.SecurityException if someone has tampered
    // with the signature of _any_ element of the JAR file.
    // Alas, it will proceed without a problem if the JAR file is not signed at all
    InputStream is = jar.getInputStream(jar.getEntry("META-INF/MANIFEST.MF"));
    Manifest man = new Manifest(is);
    is.close();
    
    Set<String> signed = new HashSet();
    for(Map.Entry<String, Attributes> entry: man.getEntries().entrySet()) {
        for(Object attrkey: entry.getValue().keySet()) {
            if (attrkey instanceof Attributes.Name && 
               ((Attributes.Name)attrkey).toString().indexOf("-Digest") != -1)
                signed.add(entry.getKey());
        }
    }
    
    Set<String> entries = new HashSet<String>();
    for(Enumeration<JarEntry> entry = jar.entries(); entry.hasMoreElements(); ) {
        JarEntry je = entry.nextElement();
        if (!je.isDirectory())
            entries.add(je.getName());
    }
    
    // contains all entries in the Manifest that are not signed.
    // Ususally, this contains:
    //  * MANIFEST.MF itself
    //  * *.SF files containing the signature of MANIFEST.MF
    //  * *.DSA files containing public keys of the signer
    
    Set<String> unsigned = new HashSet<String>(entries);
    unsigned.removeAll(signed);
    
    // contains all the entries with a signature that are not present in the JAR
    Set<String> missing = new HashSet<String>(signed);
    missing.removeAll(entries);
    

    【讨论】:

    • 通过 JarFile(fileName) 打开 jar 不会验证 JAR 中的类,它只会启用在访问时发生的验证。因此,要验证 Jar 的所有条目,您必须为每个条目调用 jar.getInputStream(..) - 这会触发验证。
    • 请参阅stackoverflow.com/a/5589898/3934807 了解一些实现罗伯特建议的代码
    • 没有提到使用哪个密钥库来验证签名(用于签署 jar 的那个)
    • @madduci - 我和你有同样的问题。您知道如何使用用于签名的密钥库的公钥来完成验证吗?任何帮助表示赞赏。
    • @learner 我已经向 Oracle 提交了一份关于签名 jar 问题的错误报告。这是代码github.com/madduci/ecdsa-bug-jlink-9151223/blob/master/…
    【解决方案4】:

    您可以使用 entry.getCodeSigners() 来获取 JAR 中特定条目的签名者。

    确保使用 verify=true 打开 JarFile 并在调用 entry.getCodeSigners() 之前完全读取 JAR 条目。

    这样的东西可以用来验证不是签名文件的每个条目:

    boolean verify = true;
    JarFile jar = new JarFile(signedFile, verify);
    
    // Need each entry so that future calls to entry.getCodeSigners will return anything
    Enumeration<JarEntry> entries = jar.entries();
    while (entries.hasMoreElements()) {
       JarEntry entry = entries.nextElement();
       IOUtils.copy(jar.getInputStream(entry), new NullOutputStream());
    }
    
    // Now check each entry that is not a signature file
    entries = jar.entries();
    while (entries.hasMoreElements()) {
        JarEntry entry = entries.nextElement();
        String fileName = entry.getName().toUpperCase(Locale.ENGLISH);
        if (!fileName.endsWith(".SF")
           && !fileName.endsWith(".DSA")
           && !fileName.endsWith(".EC")
           && !fileName.endsWith(".RSA")) {
    
           // Now get code signers, inspect certificates etc here...
           // entry.getCodeSigners();
        }
     }
    

    【讨论】:

    猜你喜欢
    • 2016-01-02
    • 2014-06-30
    • 1970-01-01
    • 1970-01-01
    • 2012-05-05
    • 1970-01-01
    • 2013-06-12
    • 2010-10-25
    相关资源
    最近更新 更多