当我尝试使用 JavaFX 制作的应用程序对文档进行数字签名时,BouncyCastle 库出现了问题。仅当我使用应用程序的编译映像进行签名时才会出现错误。如果我通过 IDE(Intellij Idea)运行应用程序,它会运行得很好。也许我在编译映像时发生了一些事情,导致没有提取正确的依赖项?
以下是相关代码:
build.gradle 中的依赖项:
dependencies {
implementation('org.controlsfx:controlsfx:11.2.1')
implementation('org.kordamp.bootstrapfx:bootstrapfx-core:0.4.0')
testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
// https://mvnrepository.com/artifact/com.itextpdf/itextpdf
implementation('com.itextpdf:itextpdf:5.5.13.3')
// https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15to18
implementation('org.bouncycastle:bcprov-jdk15to18:1.70')
// https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on
implementation('org.bouncycastle:bcpkix-jdk15on:1.70') {
exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on'
}
}
我必须从 bcpkix-jdk15on 中排除模块 bcprov-jdk15on,否则我会收到模块重复错误。我使用的 bcprov 和 bcpkix 版本是 itextpdf 5.5.13.3 版 pom.xml 中指示的版本。
此外,我检查了 bcpkix-jdk15on 的 jar,有问题的类就在那里。
模块信息:
module a.b.c {
requires javafx.controls;
requires javafx.fxml;
requires org.controlsfx.controls;
requires org.kordamp.bootstrapfx.core;
requires itextpdf;
opens a.b.c to javafx.fxml;
exports a.b.c;
exports a.b.c.controllers;
exports a.b.c.dtos;
opens a.b.c.controllers to javafx.fxml;
}
引发异常的行:
MakeSignature.signDetached(appearance, bouncyCastleDigest, privateKeySignature, chain, null, null, null, 0, MakeSignature.CryptoStandard.CMS);
例外情况:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(Unknown Source)
at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(Unknown Source)
at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(Unknown Source)
at javafx.base/javafx.event.Event.fireEvent(Unknown Source)
at javafx.graphics/javafx.scene.Node.fireEvent(Unknown Source)
at javafx.controls/javafx.scene.control.Button.fire(Unknown Source)
at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(Unknown Source)
at javafx.controls/com.sun.javafx.scene.control.inputmap.InputMap.handle(Unknown Source)
at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(Unknown Source)
at javafx.base/javafx.event.Event.fireEvent(Unknown Source)
at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Unknown Source)
at javafx.graphics/javafx.scene.Scene.processMouseEvent(Unknown Source)
at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(Unknown Source)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(Unknown Source)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source)
at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(Unknown Source)
at javafx.graphics/com.sun.glass.ui.View.notifyMouse(Unknown Source)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at com.sun.javafx.reflect.Trampoline.invoke(Unknown Source)
at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at javafx.base/com.sun.javafx.reflect.MethodUtil.invoke(Unknown Source)
at javafx.fxml/com.sun.javafx.fxml.MethodHelper.invoke(Unknown Source)
... 47 more
Caused by: java.lang.NoClassDefFoundError: org/bouncycastle/cert/X509CertificateHolder
at [email protected]/com.itextpdf.text.pdf.security.MakeSignature.signDetached(Unknown Source)
at [email protected]/com.itextpdf.text.pdf.security.MakeSignature.signDetached(Unknown Source)
at [email protected]/a.b.c.services.Service.sign(Unknown Source)
at [email protected]/a.b.c.services.Service.signSingle(Unknown Source)
at [email protected]/a.b.c.controllers.Controller.onSignButtonClick(Unknown Source)
... 57 more
Caused by: java.lang.ClassNotFoundException: org.bouncycastle.cert.X509CertificateHolder
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
... 62 more
就像我说的,当我在 IDE 上运行应用程序时,签名没有任何问题,但编译后的映像失败了。顺便说一下,要编译映像,我使用以下命令:./gradlew clean jlinkZip,然后使用 bin 文件夹中的 app.bat 运行该程序。
更新 1:我忘了提一下,关于如果我不从 bcpkix 中排除 bcprov 时出现的重复模块错误,只有在使用 gradle 编译时才会出现此错误。如果我通过 IDE 运行应用程序,则不会出现此错误。
更新 2:我比较了具有不同依赖项配置的已编译映像的大小,我注意到,当我仅添加 bcpkix 依赖项时,映像的大小不会增加(更具体地说,位于 lib 文件夹中的“模块”文件)。这是缺少类的依赖项。gradle 是否可能没有使用此依赖项编译应用程序?如果是这样,为什么?依赖项 bcprov-jdk15to18 不会发生这种情况,只有 bcpkix-jdk15on 才会发生这种情况。
我会很感激任何帮助。谢谢。
充气城堡软件是模块化的,您有一个模块。您需要在模块中引入该软件才能使用它。
将此行添加到您的
module-info.java
:这将需要bouncy castle pkix 模块。如果不需要该模块,链接器将不会认为该模块已被使用,也不会将其链接到您的 jlink 映像中。
您还使用了错误版本的 bouncy castle pkix。您应该使用:
将 1.78.1 替换为最新的当前版本。
当您这样做时,您不需要像现在这样排除旧版本的 bouncy castle 提供程序。您也不需要明确依赖 bouncy castle 提供程序 (bcprov-jdk15to18),因为 bcpkix 依赖项将自动传递地选择正确的依赖项(不会是 jdk15to18 版本,它是非模块化的,无法通过 jlink 链接)。
即使你这样做了,我也不知道你尝试做的事情是否会成功。你使用的 itext 版本不是模块化的(没有 module-info.java),因此被用作自动模块。它还具有对旧 jdk8 版本的 bouncy castle 的可选依赖项,这些依赖项可能与新版本的 bouncy castle 不兼容。自动模块对 jlink 来说是个问题,通常 jlink 无法与自动模块一起使用。我认为 gradle jlink 插件试图解决这个问题,但我没有使用它,也不知道它在这方面是否成功。
幸运的是,建议的更改确实起作用了,正如提问者在评论中指出的那样: