18

快速背景: 我们发布了一个 webstart 应用程序,其中包括我们自己的应用程序 jar 和许多第三方 jar。Webstart 要求 jnlp 文件引用的所有分布式 jar 都由单个证书签名。因此,我们使用自签名证书对所有 jar(我们的 jar 和第三方 jar)进行签名。一些第三方 jar 已经由生产方签名,但我们只是再次签名,这样就可以正常工作了。到现在。

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

在 SO 和互联网上搜索后,Java jarsigner 的默认签名算法似乎在 Java 6 和 Java 7 之间发生了变化,从 SHA1 到 SHA256,并且很多人都建议使用“jarsigner -digestalg SHA1”来解决验证问题. 我试过了,果然我们的多重签名 jar 现在验证了。所以这似乎是我们问题的解决方法。

据我所知,第三方签名似乎是 SHA1 签名,而我们使用默认签名 - SHA256 - 导致签名混合。当我使用“-digestalg”开关强制 SHA1 时,我们有两个相同类型的签名,并且验证现在有效。那么问题似乎是由具有不同算法的多个签名引起的?还是我缺少其他一些因素。

问题:

  1. 为什么用 SHA1 + SHA256 验证失败,用 SHA1 + SHA1 验证?有技术原因吗?安全策略原因?为什么它不能验证两个签名都是正确的?
  2. 我们使用(继续使用)SHA1 而不是现在默认的 SHA256 有什么缺点吗?
4

3 回答 3

7

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

如果做不到这一点,您可以在添加自己的签名之前去掉第三方签名(通过重新包装它们而不使用META-INF/*.{SF,DSA,RSA}

于 2012-09-27T07:59:49.903 回答
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);
    }

}
于 2013-10-19T14:36:27.223 回答
1

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

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

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

抱歉 - 无法说明为什么 web start 不能处理/允许它 - 只是想出了如何让它工作!

祝你好运!

于 2013-02-18T17:32:24.873 回答