我的java程序被打包在一个jar文件中,并使用了一个外部jar库,bouncy castle。我的代码编译良好,但运行jar会导致以下错误:

java.lang.SecurityException: Manifest主属性的签名文件摘要无效

我在谷歌上搜索了一个多小时,想要找到一个解释,但几乎没有什么价值。如果有人看到这个错误之前,可以提供一些帮助,我将不胜感激。


当前回答

我有同样的问题,在gradle创建一个胖罐;更新构建。Gradle文件排除行纠正了这个问题。

jar {
    from {
        configurations.compile.collect {
            it.isDirectory() ? it : zipTree(it)
        }
    }
    exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA'
    manifest {
        attributes 'Main-Class': 'com.test.Main'
    }
}

其他回答

您可以使用Shadow生成一个jar。

Shadow是一个Gradle插件,用于将项目的依赖类和资源组合到一个输出Jar中。组合罐通常被称为fat-jar或super - Jar。

Modify build.gradle plugins { ... // ① Add the shadow plugin id "com.github.johnrengelman.shadow" version "5.2.0" } ... // ② Config the shadow jar, its name is baseName-1.0-classifier.jar shadowJar { archiveBaseName.set('baseName') archiveClassifier.set('classifier') archiveVersion.set('1.0') manifest { attributes 'Main-Class': 'Main' } } // ③ Disable the default jar task jar.enabled = false // ④ Execute the shadowJar task when compiling build.dependsOn(shadowJar) Execute the command gradle build, the jar file will be generated: <Project Directory>/build/libs/baseName-1.0-classifier.jar

I've recently started using IntelliJ on my projects. However, some of my colleagues still use Eclipse on the same projects. Today, I've got the very same error after executing the jar-file created by my IntelliJ. While all the solutions in here talking about almost the same thing, none of them worked for me easily (possibly because I don't use ANT, maven build gave me other errors which referred me to http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException, and also I couldn't figure out what are the signed jars by myself!)

最后,这帮助了我

zip -d demoSampler.jar 'META-INF/*.SF' 'META-INF/*.RSA' 'META-INF/*SF'

猜猜我的jar文件里删除了什么!

deleting: META-INF/ECLIPSE_.SF 
deleting: META-INF/ECLIPSE_.RSA

这个问题似乎与一些与eclipse相关的文件有关。

我也遇到过同样的问题,经过参考,它的工作原理如下:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.1</version>
    <configuration>
        <createDependencyReducedPom>false</createDependencyReducedPom>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <filters>
                    <filter>
                        <artifact>*:*</artifact>
                        <excludes>
                            <exclude>META-INF/*.SF</exclude>
                            <exclude>META-INF/*.DSA</exclude>
                            <exclude>META-INF/*.RSA</exclude>
                        </excludes>
                    </filter>
                </filters>
            </configuration>
        </execution>
    </executions>
</plugin>

安全性已经是一个棘手的话题,但我很失望地看到最流行的解决方案是删除安全性签名。JCE需要这些签名。Maven阴影会爆炸将签名放入META-INF的BouncyCastle jar文件,但BouncyCastle签名对新的超级jar无效(仅对BC jar有效),这就是导致此线程中出现无效签名错误的原因。

是的,排除或删除@ruhsuzbaykus所建议的签名确实可以消除最初的错误,但它也可能导致新的、神秘的错误:

java.security.NoSuchAlgorithmException: PBEWithSHA256And256BitAES-CBC-BC SecretKeyFactory not available

通过显式指定查找算法的位置,如下所示:

SecretKeyFactory.getInstance("PBEWithSHA256And256BitAES-CBC-BC","BC");

我得到了一个不同的错误:

java.security.NoSuchProviderException: JCE cannot authenticate the provider BC

JCE无法对提供者进行身份验证,因为我们已经按照同一线程中其他地方的建议删除了加密签名。

我找到的解决方案是可执行的封装器插件,它使用jar-in-jar的方法将BouncyCastle签名保存在单个可执行的jar中。

更新:

另一种方法(正确的方法?)是使用Maven Jar签名器。这允许您继续使用Maven shade而不会出现安全错误。但是,你必须有一个代码签名证书(Oracle建议搜索“Java代码签名证书”)。POM配置如下所示:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.1.0</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <filters>
                    <filter>
                        <artifact>org.bouncycastle:*</artifact>
                        <excludes>
                            <exclude>META-INF/*.SF</exclude>
                            <exclude>META-INF/*.DSA</exclude>
                            <exclude>META-INF/*.RSA</exclude>
                        </excludes>
                    </filter>
                </filters>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>your.class.here</mainClass>
                    </transformer>
                </transformers>
                <shadedArtifactAttached>true</shadedArtifactAttached>
            </configuration>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jarsigner-plugin</artifactId>
    <version>1.4</version>
    <executions>
        <execution>
            <id>sign</id>
            <goals>
                <goal>sign</goal>
            </goals>
        </execution>
        <execution>
            <id>verify</id>
            <goals>
                <goal>verify</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <keystore>/path/to/myKeystore</keystore>
        <alias>myfirstkey</alias>
        <storepass>111111</storepass>
        <keypass>111111</keypass>
    </configuration>
</plugin>

不,没有办法让JCE承认自签名的证书,所以如果你需要保留BouncyCastle证书,你必须使用jar-in-jar插件或获得JCE证书。

有些依赖项可能是带符号的jarfile。当您将它们全部组合到一个大的jarfile中时,相应的签名文件仍然存在,并且不再与“大的组合”jarfile匹配,因此运行时停止认为jar文件已被篡改(它…可以这么说)。

假设您正在使用ant,可以通过从jarfile依赖项中删除签名文件来解决这个问题。不幸的是,在ant中不可能一步到位。

但是,我能够在Ant中分两步实现这个功能,而不需要具体地命名每个jarfile依赖项,通过使用:

<target name="jar" depends="compile" description="Create one big jarfile.">
    <jar jarfile="${output.dir}/deps.jar">
        <zipgroupfileset dir="jars">
            <include name="**/*.jar" />
        </zipgroupfileset>
    </jar>
    <sleep seconds="1" />
    <jar jarfile="${output.dir}/myjar.jar" basedir="${classes.dir}">
        <zipfileset src="${output.dir}/deps.jar" excludes="META-INF/*.SF" />
        <manifest>
            <attribute name="Main-Class" value="com.mycompany.MyMain" />
        </manifest>
    </jar>
</target>

sleep元素的作用是防止将来出现关于修改日期的文件的错误。

我在链接线程中发现的其他变化并不适合我。