我的java程序被打包在一个jar文件中,并使用了一个外部jar库,bouncy castle。我的代码编译良好,但运行jar会导致以下错误:
java.lang.SecurityException: Manifest主属性的签名文件摘要无效
我在谷歌上搜索了一个多小时,想要找到一个解释,但几乎没有什么价值。如果有人看到这个错误之前,可以提供一些帮助,我将不胜感激。
我的java程序被打包在一个jar文件中,并使用了一个外部jar库,bouncy castle。我的代码编译良好,但运行jar会导致以下错误:
java.lang.SecurityException: Manifest主属性的签名文件摘要无效
我在谷歌上搜索了一个多小时,想要找到一个解释,但几乎没有什么价值。如果有人看到这个错误之前,可以提供一些帮助,我将不胜感激。
当前回答
Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes at sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVerifier.java:314) at sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier.java:268) at java.util.jar.JarVerifier.processEntry(JarVerifier.java:316) at java.util.jar.JarVerifier.update(JarVerifier.java:228) at java.util.jar.JarFile.initializeVerifier(JarFile.java:383) at java.util.jar.JarFile.getInputStream(JarFile.java:450) at sun.misc.URLClassPath$JarLoader$2.getInputStream(URLClassPath.java:977) at sun.misc.Resource.cachedInputStream(Resource.java:77) at sun.misc.Resource.getByteBuffer(Resource.java:160) at java.net.URLClassLoader.defineClass(URLClassLoader.java:454) at java.net.URLClassLoader.access$100(URLClassLoader.java:73) at java.net.URLClassLoader$1.run(URLClassLoader.java:368) at java.net.URLClassLoader$1.run(URLClassLoader.java:362) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:361) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
帮助我的(IntelliJ IDEA 2016.3): 文件->项目结构-> Artifacts ->添加JAR ->选择主类->选择“通过manifest复制到输出目录和链接”->确定->应用->构建->构建Artifacts…- >构建
其他回答
有些依赖项可能是带符号的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元素的作用是防止将来出现关于修改日期的文件的错误。
我在链接线程中发现的其他变化并不适合我。
对于那些在尝试使用maven-shade-plugin创建阴影超级jar时遇到此错误的人,解决方案是通过在插件配置中添加以下行来排除清单签名文件:
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<!-- Additional configuration. -->
</configuration>
这发生在我在Intellij中,当我点击“添加为Maven项目”底线时,Intellij说“发现非托管pom文件”。同时,out文件夹已经生成。所以它最近没有变化。
删除文件夹和运行程序解决了我的问题。然后重新创建Out文件夹。
看看小狐狸的回答吧。我收到的错误和他的非常相似。
安全性已经是一个棘手的话题,但我很失望地看到最流行的解决方案是删除安全性签名。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证书。
一种策略包括使用ANT来简化从每个Jar文件中删除签名。它将采取下列步骤:
复制舱单。MF在临时文件中 从临时文件中删除Name和SHA条目 用临时清单创建一个临时Jar文件 移除临时舱单 将原始Jar文件与临时Jar文件交换
下面是一个ANT宏def做的工作:
<macrodef name="unsignjar" description="To unsign a specific Jar file">
<attribute name="jarfile"
description="The jar file to unsign" />
<sequential>
<!-- Copying to the temporary manifest file -->
<copy toFile="@{jarFile}_MANIFEST.tmp">
<resources>
<zipentry zipfile="@{jarFile}" name="META-INF/MANIFEST.MF"/>
</resources>
</copy>
<!-- Removing the Name and SHA entries from the temporary file -->
<replaceregexp file="@{jarFile}_MANIFEST.tmp" match="\nName:(.+?)\nSH" replace="SH" flags="gis" byline="false"/>
<replaceregexp file="@{jarFile}_MANIFEST.tmp" match="SHA(.*)" replace="" flags="gis" byline="false"/>
<!-- Creating a temporary Jar file with the temporary manifest -->
<jar jarfile="@{jarFile}.tmp"
manifest="@{jarFile}_MANIFEST.tmp">
<zipfileset src="@{jarFile}">
<include name="**"/>
<exclude name="META-INF/*.SF"/>
<exclude name="META-INF/*.DSA"/>
<exclude name="META-INF/*.RSA"/>
</zipfileset>
</jar>
<!-- Removing the temporary manifest -->
<delete file="@{jarFile}_MANIFEST.tmp" />
<!-- Swapping the original Jar file with the temporary one -->
<move file="@{jarFile}.tmp"
tofile="@{jarFile}"
overwrite="true" />
</sequential>
`
在ANT任务中可以这样调用定义:
<target name="unsignJar">
<unsignjar jarFile="org.test.myjartounsign.jar" />
</target>