0

I am building a GUI app with JavaFX that is simply a ListView that can be populated with File objects. There are 'Encrypt' and 'Decrypt' buttons which have onAction event handlers which take all the file objects and encrypt them using the AES/CTR/NoPadding transformation from the BouncyCastle security provider. All of this is functioning correctly when I run the app from my IDE (IntelliJ IDEA); however, when I build a JAR artifact I run into problems.

Initially, I was getting hit with a security exception (see below).

Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
at java.base/sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVerifier.java:336)
at java.base/sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier.java:269)
at java.base/java.util.jar.JarVerifier.processEntry(JarVerifier.java:316)
at java.base/java.util.jar.JarVerifier.update(JarVerifier.java:230)
at java.base/java.util.jar.JarFile.initializeVerifier(JarFile.java:749)
at java.base/java.util.jar.JarFile.ensureInitialization(JarFile.java:1016)
at java.base/java.util.jar.JavaUtilJarAccessImpl.ensureInitialization(JavaUtilJarAccessImpl.java:72)
at java.base/jdk.internal.loader.URLClassPath$JarLoader$2.getManifest(URLClassPath.java:873)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:811)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:723)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:646)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:604)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:168)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:468)
at java.base/sun.launcher.LauncherHelper.loadMainClass(LauncherHelper.java:780)
at java.base/sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:675)

I found this similar question:

that informed me on the problem being that combining individually singed JARs into your own JAR can causes a signature mismatch from which my security exception is resultant. The accepted answer on this question didn't solve my problem (I followed the instructions provided but my knowledge of Maven is extremely limited). There was a common theme in a lot of the alternate answers that a solution is to remove delete all files with the .SF, .DSA and .RSA extensions from the META-INF folder.

This answer to the above question:

specifically provided a one-liner to do it. I ran this on my JAR file and I am getting a new security exception.

Although my app can actually start up now, when I try to run any methods using BouncyCastle I am hit with this security exception:

java.lang.SecurityException: JCE cannot authenticate the provider BC
at java.base/javax.crypto.Cipher.getInstance(Cipher.java:722)
at java.base/javax.crypto.Cipher.getInstance(Cipher.java:642)
at crypt.EncFile.EncryptFile(EncFile.java:70)
at uk.anthonybyrne.duban.gui.DubánController.handleEncryptActionEvent(DubánController.java:40)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:273)
at com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1859)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1729)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Node.fireEvent(Node.java:8886)
at javafx.scene.control.Button.fire(Button.java:203)
at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:208)
at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3856)
at javafx.scene.Scene.processMouseEvent(Scene.java:1851)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2584)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:409)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:299)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:447)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:412)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:446)
at com.sun.glass.ui.View.handleMouseEvent(View.java:556)
at com.sun.glass.ui.View.notifyMouse(View.java:942)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)
at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: java.util.jar.JarException: file:/home/john/Downloads/TestJoint/out/artifacts/TestJoint_jar/TestJoint.jar has unsigned entries - uk/anthonybyrne/duban/gui/style.css
at java.base/javax.crypto.JarVerifier.verifySingleJar(JarVerifier.java:459)
at java.base/javax.crypto.JarVerifier.verifyJars(JarVerifier.java:314)
at java.base/javax.crypto.JarVerifier.verify(JarVerifier.java:257)
at java.base/javax.crypto.ProviderVerifier.verify(ProviderVerifier.java:129)
at java.base/javax.crypto.JceSecurity.verifyProvider(JceSecurity.java:192)
at java.base/javax.crypto.JceSecurity.getVerificationResult(JceSecurity.java:220)
at java.base/javax.crypto.Cipher.getInstance(Cipher.java:718)
... 61 more

Now that I have removed the specified files from the META-INF folder, JCE cannot authenticate BouncyCastle. I decided to backtrack and I tried the solution to this similar question:

that suggested to sign the BouncyCastle library a second time with my own certificate. I followed the Oracle docs on creating a keystore with keytool. I used the following command to create my keystore:

keytool -keystore dubankeystore.jks -genkey -alias Duban -keyalg RSA

I then used this keystore and jarsigner to sign the BouncyCastle library that I included in my project as per the instructions in the accepted answer to "BouncyCastle Cryptography provider library used with applet on Java 7u40".

The only modification I made was to use SHA-256 instead of SHA1 as the digest algorithm because java would not run the program when using SHA1 and told me to use SHA-256.

jarsigner -keystore ./dubankeystore.jks -storepass XfWkJ2voZZaebWPXtxnyYCshyv6TX4BD3o6zBLvPkmr3at2GKh -digestalg SHA-256 ./bcprov-jdk15on-169.jar Duban

The outcome of this was a nice message, 'jar signed.', after which my project could run as normally; however, once again when I build a JAR artifact using my signed copy of the BouncyCastle library I still get the following security exception:

Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
at java.base/sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVerifier.java:336)
at java.base/sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier.java:269)
at java.base/java.util.jar.JarVerifier.processEntry(JarVerifier.java:316)
at java.base/java.util.jar.JarVerifier.update(JarVerifier.java:230)
at java.base/java.util.jar.JarFile.initializeVerifier(JarFile.java:749)
at java.base/java.util.jar.JarFile.ensureInitialization(JarFile.java:1016)
at java.base/java.util.jar.JavaUtilJarAccessImpl.ensureInitialization(JavaUtilJarAccessImpl.java:72)
at java.base/jdk.internal.loader.URLClassPath$JarLoader$2.getManifest(URLClassPath.java:873)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:811)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:723)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:646)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:604)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:168)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:468)
at java.base/sun.launcher.LauncherHelper.loadMainClass(LauncherHelper.java:780)
at java.base/sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:675)

I tried signing my JAR artifact after this again and i'm getting the same result. I'm really stumped now. I'm still a Java-noob so it could be something simple going over my head or I could be signing BouncyCastle incorrectly. I'm not sure how to debug any further than i've explained so far. Any help is appreciated.

jewelsea
  • 150,031
  • 14
  • 366
  • 406
Anthony
  • 23
  • 1
  • 5
  • Running JavaFX applications as a single jar isn't really a supported or recommended configuration (even though you can currently usually do it). JavaFX is built to be a modular system where the JavaFX system is on the module path, not the class path. So a better approach might be to package and distribute your application like that. Unfortunately it is not straight-forward and full discussion to do this is outside of scope of what I can provide here. Perhaps this [reference project](https://github.com/dlemmermann/JPackageScriptFX) could assist. – jewelsea Sep 15 '21 at 17:55
  • I think looking at resources for Java 7 to help solve your issue is probably not recommended. Any modern JavaFX runtime requires a modern java runtime version to run on, so info regrading Java 7 may not be applicable. – jewelsea Sep 15 '21 at 18:08
  • I also don't think you should be signing the bouncy castle jar. If the jar is signed, it should be by the provider (bouncy castle). You shouldn't have to sign it, nor do I think you should mess with it by deleting existing signature information as another answer you referenced does. – jewelsea Sep 15 '21 at 18:10
  • @jewelsea Thanks for your feedback. I will look into that project. I just thought JavaFX was like AWT/Swing and I could use it the same way. This is my first real attempt at a proper app so i'm still learning how to works properly. – Anthony Sep 15 '21 at 19:21
  • The applet-based stuff and solutions are things from years ago that are long dead. Even Swing and AWT solutions are no longer (and can no longer be) distributed in that way due to lack of support both in the browser technology and the recent Java runtimes. To achieve working applets there were always many compromises and difficulties required. When you search for old answers that reference them, they often rely on these compromises in their solutions. Today's distribution technology uses different toolsets and processes which face a different set of challenges and compromises. – jewelsea Sep 15 '21 at 19:31

0 Answers0