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

时间:2012-09-27 04:33:02

标签: java jar code-signing java-web-start jarsigner

快速背景:我们发布了一个webstart应用程序,其中包括我们自己的应用程序罐和众多第三方jar。 Webstart要求jnlp文件引用的所有分布式jar都由单个证书签名。因此,我们使用自签名证书签署所有罐子(我们的罐子和第三方罐子)。一些第三方罐子已经由生产它们的一方签署,但我们只是再次签署它们,这很好。到现在为止。

问题:我们最近从Java 6迁移到Java 7,突然webstart拒绝加载一些jar,抱怨:“无效的SHA1签名文件摘要”。这只发生在一些罐子而不是其他罐子上,并且那些失败的罐子似乎有多个签名。

搜索S.O.从互联网看,Java的jarsigner的默认签名算法在Java 6和Java 7之间已经发生了变化,从SHA1到SHA256,各种人都建议使用“jarsigner -digestalg SHA1”来解决验证问题。我试过了,果然我们的多重签名罐子验证了。所以这似乎是我们问题的解决方法。

从我可以收集到的内容来看,第三方签名似乎是SHA1签名,我们使用默认签名SHA256进行签名,从而导致签名混合。当我使用'-digestalg'开关强制SHA1时,我们有两个相同类型的签名,验证现在可以正常工作。所以似乎这个问题是由多个签名使用不同的算法引起的?或者还有其他一些因素我不知道。

问题:

  1. 为什么无法使用SHA1 + SHA256验证,但使用SHA1 + SHA1进行验证?有技术原因吗?安全政策的原因?为什么不能验证两个签名是否正确?
  2. 使用(继续使用)SHA1代替现在默认的SHA256有什么缺点吗?

3 个答案:

答案 0 :(得分:7)

您可以为每个引用相关jar文件的第三方签名者创建一个单独的JNLP文件,然后使用<extension>让您的主JNLP依赖于这些文件,而不是自己重新签名第三方jar。元件。所有JAR文件必须由同一签名者签名的限制仅适用于一个JNLP,每个扩展可以有不同的签名者。

如果不这样做,您可以在添加自己的第三方签名之前删除第三方签名(通过重新打包而不使用META-INF/*.{SF,DSA,RSA}

答案 1 :(得分:1)

我知道这有点晚了 - 但我们现在要通过这个了。我们的问题是“MD2withRSA”签名问题。我分几步解决了这个问题:

1)与Verisign合作,从我们的证书中删除“旧”算法 - 因此MD2withRSA算法不再用于签署我们的罐子。

2)我们还有一堆第三方罐子,我们只是用我们的证书重新签名。当MANIFEST.MF中列出了SHA1和SHA-256算法时,我们遇到了“并非所有使用相同证书签名的jar”。这只是罐子的一小部分 - 所以对于那些,我们删除了MANIFEST.MF文件的下半部分;具有Name:class和算法规范的那部分。该数据在我们流程的最后部分重新生成。我们解压缩,排除旧的签名信息并重新jar。最后一步是重新签名罐子。我们发现在某些情况下,如果具有SHA1条目的旧Name:条目在MANIFEST.MF中,那么签名不会用SHA-256替换它 - 所以我们手动处理这些jar(现在)。正在努力更新我们的Ant任务来处理这个问题。

抱歉 - 无法说出为什么网站启动无法处理/允许它 - 只是弄清楚如何让它工作!

祝你好运!

答案 2 :(得分:1)

似乎是JRE中的一个错误。我个人认为旧的默认签名算法(带有SHA1摘要的DSA)不如新的(带有SHA256摘要的RSA)安全,所以最好不要使用“-digestalg SHA1”选项。

我通过在构建脚本中使用自定义Ant任务来解决此问题,以便在签名之前“取消签名”我的jar。这样每个罐子只有一个签名。

这是我的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);
    }

}