简体   繁体   English

是什么阻止Java使用多个签名算法验证签名的jar

[英]What prevents Java from verifying signed jars with multiple signature algorithms

Quick background: We release a webstart application, which includes our own application jars and numerous third-party jars. 快速背景:我们发布了一个webstart应用程序,其中包括我们自己的应用程序罐和众多第三方jar。 Webstart requires that all distributed jars referred to by the jnlp file be signed by a single certificate. Webstart要求jnlp文件引用的所有分布式jar都由单个证书签名。 We therefore sign all jars (our jars and the third-party jars) using a self-signed certificate. 因此,我们使用自签名证书签署所有罐子(我们的罐子和第三方罐子)。 Some third-party jars are already signed by the party which produced them, but we just sign them again, and this works fine. 一些第三方罐子已经由生产它们的一方签署,但我们只是再次签署它们,这很好。 Until now. 到现在。

Problem: We recently moved from Java 6 to Java 7, and suddenly webstart is refusing to load some jars, complaining: "Invalid SHA1 signature file digest". 问题:我们最近从Java 6迁移到Java 7,突然webstart拒绝加载一些jar,抱怨:“无效的SHA1签名文件摘要”。 This only happens for some jars and not others, and the common thread appears among those jars that fail appears to be having multiple signatures. 这只发生在一些罐子而不是其他罐子上,并且那些失败的罐子似乎有多个签名。

After searching around on SO and the internet, it appears that the default signature algorithm for Java's jarsigner has changed between Java 6 and Java 7, from SHA1 to SHA256, and various people are recommending using "jarsigner -digestalg SHA1" to work around verification issues. 在搜索SO和互联网之后,似乎Java的jarsigner的默认签名算法在Java 6和Java 7之间已经发生了变化,从SHA1到SHA256,并且各种人都建议使用“jarsigner -digestalg SHA1”来解决验证问题。 I tried that, and sure enough our multiply-signed jars now verify. 我试过了,果然我们的多重签名罐子验证了。 So this appears to be a workaround for our issue. 所以这似乎是我们问题的解决方法。

From what I can gather, it appears that the third-party signature is a SHA1 signature, and we were signing with the default -- SHA256 -- resulting in a mixing of signatures. 从我可以收集到的内容来看,第三方签名似乎是SHA1签名,我们使用默认签名 - SHA256签名 - 导致签名混合。 When I force SHA1 using the '-digestalg' switch, we have two signatures of the same type, and verification now works. 当我使用'-digestalg'开关强制SHA1时,我们有两个相同类型的签名,验证现在可以正常工作。 So it seems the problem is caused by having multiple signatures with different algorithms? 所以似乎这个问题是由多个签名使用不同的算法引起的? Or is there some other factor I'm missing. 或者还有其他一些我缺失的因素。

Questions: 问题:

  1. Why does it fail to verify with SHA1 + SHA256, but verifies with SHA1 + SHA1? 为什么无法使用SHA1 + SHA256验证,但使用SHA1 + SHA1进行验证? Is there a technical reason? 有技术原因吗? A security policy reason? 安全政策的原因? Why can't it verify that both signatures are correct? 为什么不能验证两个签名是否正确?
  2. Is there any drawback to us using (continuing to use) SHA1 instead of the now-default SHA256? 使用(继续使用)SHA1而不是现在默认的SHA256有什么缺点吗?

Rather than re-signing the third party jars yourself, you can create a separate JNLP file for each third-party signer that refers to the relevant jar files, then have your main JNLP depend on these using the <extension> element. 您可以为每个引用相关jar文件的第三方签名者创建一个单独的JNLP文件,然后让您的主JNLP使用<extension>元素依赖于这些文件,而不是自己重新签名第三方jar。 The restriction that all JAR files must be signed by the same signer only applies within one JNLP, each extension can have a different signer. 所有JAR文件必须由同一签名者签名的限制仅适用于一个JNLP,每个扩展可以具有不同的签名者。

Failing that, you could strip out the third party signatures before adding your own (by repacking them without META-INF/*.{SF,DSA,RSA} ) 如果做不到这一点,您可以在添加自己的签名之前删除第三方签名(通过重新打包而不使用META-INF/*.{SF,DSA,RSA}

I know this is a bit late - but we are going thru this now. 我知道这有点晚了 - 但我们现在正在通过这个。 Our problem was the "MD2withRSA" signing issue. 我们的问题是“MD2withRSA”签名问题。 I resolved the problem in a couple steps: 我分几步解决了这个问题:

1) Worked with Verisign to remove the 'old' algorithm from our certificate - so the MD2withRSA algorithm was no longer used to sign our jars. 1)与Verisign合作从我们的证书中删除“旧”算法 - 因此MD2withRSA算法不再用于签署我们的罐子。

2) We also have a pile of 3rd party jars and we just re-sign them with out our certificate. 2)我们还有一堆第三方罐子,我们只是用我们的证书重新签名。 We encountered the 'not all jars signed with the same certificate' when both the SHA1 and SHA-256 algorithms were listed in the MANIFEST.MF. 当MANIFEST.MF中列出了SHA1和SHA-256算法时,我们遇到了“并非所有使用相同证书签名的jar”。 This was just a small subset of the jars - so for those, we removed the bottom half of the MANIFEST.MF file; 这只是罐子的一小部分 - 所以对于那些,我们删除了MANIFEST.MF文件的下半部分; that part with the Name: class and the algorithm spec. 具有Name:class和算法规范的那部分。 That data is re-generated in the last part of our process. 该数据在我们流程的最后部分重新生成。 We unzip, exclude the old signing info and re-jar. 我们解压缩,排除旧的签名信息并重新jar。 Last step is to re-sign the jars. 最后一步是重新签名罐子。 We found that in some cases, if the old Name: entry with the SHA1 entry was in the MANIFEST.MF, that the signing did not replace it with the SHA-256 - so we manually handle those jars (for now). 我们发现在某些情况下,如果具有SHA1条目的旧Name:条目在MANIFEST.MF中,那么签名不会用SHA-256替换它 - 所以我们手动处理这些jar(现在)。 Working on updating our Ant tasks to handle this. 致力于更新我们的Ant任务来处理这个问题。

Sorry - can't speak to why web start doesn't handle/allow it - just figured out how to make it work! 对不起 - 无法说出为什么网站启动无法处理/允许它 - 只是弄清楚如何让它工作!

Good Luck! 祝好运!

Seems like a bug in the JRE. 好像是JRE中的一个错误。 Personally I'm assuming the old default signing algorithm (DSA with SHA1 digest) is less secure than the new one (RSA with SHA256 digest), so it's best not to use the "-digestalg SHA1" option. 就个人而言,我假设旧的默认签名算法(带有SHA1摘要的DSA)不如新的(带有SHA256摘要的RSA)安全,所以最好不要使用“-digestalg SHA1”选项。

I solved this problem by using a custom Ant task in my build script to 'unsign' my jars before signing them. 我通过在构建脚本中使用自定义Ant任务来解决此问题,以便在签名之前“取消签名”我的jar。 That way there is only one signature for each jar. 这样每个罐子只有一个签名。

Here's my Ant task: 这是我的Ant任务:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.resources.FileProvider;
import org.apache.tools.ant.types.resources.FileResource;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.ResourceUtils;

public class UnsignJar extends Task {

    protected List<FileSet> filesets = new ArrayList<FileSet>();

    protected File todir;

    public void addFileset(final FileSet set) {
        filesets.add(set);
    }

    public void setTodir(File todir) {
        this.todir = todir;
    }

    @Override
    public void execute() throws BuildException {
        if (todir == null) {
            throw new BuildException("todir attribute not specified");
        }
        if (filesets.isEmpty()) {
            throw new BuildException("no fileset specified");
        }

        Path path = new Path(getProject());
        for (FileSet fset : filesets) {
            path.addFileset(fset);
        }

        for (Resource r : path) {
            FileResource from = ResourceUtils.asFileResource(r
                    .as(FileProvider.class));

            File destFile = new File(todir, from.getName());
            File fromFile = from.getFile();

            if (!isUpToDate(destFile, fromFile)) {
                unsign(destFile, fromFile);
            }
        }


    }

    private void unsign(File destFile, File fromFile) {
        log("Unsigning " + fromFile);
        try {
            ZipInputStream zin = new ZipInputStream(
                    new FileInputStream(fromFile));
            ZipOutputStream zout = new ZipOutputStream(
                    new FileOutputStream(destFile));

            ZipEntry entry = zin.getNextEntry();
            while (entry != null) {
                if (!entry.getName().startsWith("META-INF")) {
                    copyEntry(zin, zout, entry);
                }
                zin.closeEntry();

                entry = zin.getNextEntry();
            }

            zin.close();
            zout.close();

        } catch (IOException e) {
            throw new BuildException(e);
        }
    }

    private void copyEntry(ZipInputStream zin, ZipOutputStream zout,
            ZipEntry entry) throws IOException {
        zout.putNextEntry(entry);
        byte[] buffer = new byte[1024 * 16];
        int byteCount = zin.read(buffer);
        while (byteCount != -1) {
            zout.write(buffer, 0, byteCount);
            byteCount = zin.read(buffer);
        }
        zout.closeEntry();
    }

    private boolean isUpToDate(File destFile, File fromFile) {
        return FileUtils.getFileUtils().isUpToDate(fromFile, destFile);
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM