如何签署 JavaFX 应用程序并将其部署到单个 .JAR 中?
Posted
技术标签:
【中文标题】如何签署 JavaFX 应用程序并将其部署到单个 .JAR 中?【英文标题】:How can I sign and deploy a JavaFX application into a single .JAR? 【发布时间】:2014-09-03 20:09:19 【问题描述】:我仍在学习 JavaFX 的来龙去脉。一个主要区别是,在 dist 文件夹(除了库)中,除了 .jar 文件之外,我还发现了一个 html 文件和一个 JNLP 文件,这对我来说都没有任何用处(这将是一个桌面应用程序)。
我发现了以下内容(由于敏感/不相关信息而省略了属性):
<delete dir="$store.dir"/>
<mkdir dir="$store.dir"/>
<jar destfile="$store.dir/temp_final.jar" filesetmanifest="skip">
<zipgroupfileset dir="dist" includes="*.jar"/>
<zipgroupfileset dir="dist/lib" includes="*.jar"/>
<manifest>
<attribute name="Main-Class" value="$main.class"/>
</manifest>
</jar>
<zip destfile="$store.jar">
<zipfileset src="$store.dir/temp_final.jar"
excludes="META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
</zip>
<delete file="$store.dir/temp_final.jar"/>
<delete dir = "$build.output.dir"/>
<mkdir dir = "$build.output.dir"/>
<signjar
jar = "$store.jar"
signedjar = "$build.output.dir/$FileName"
alias = "$comodo.key.alias"
storepass = "$comodo.key.storepass"
keystore = "$comodo.key.store"
storetype = "PKCS12"
keypass = "$comodo.key.pass"
tsaurl = "$timestamp.url"/>
这是为了将“可执行”的 MAIN JAR 和所有依赖库构建到一个可以从任何位置运行的单个 .JAR 中,然后对该 JAR 进行签名并将其移动到“签名”目录中。
这适用于任何 JAR 库或 Swing GUI 应用程序 JAR,但是当我用 JavaFX 应用程序尝试同样的事情时,它就失败了:
Error: Could not find or load main class com.javafx.main.Main
发现它不起作用我并不完全惊讶,但这有点问题。我对可能部署“自包含”应用程序进行了一些研究,但这不符合我们的需求。我的雇主花了一些钱购买了 Comodo 证书,我很快就撞到了一堵墙,似乎因为 Apple 的恶作剧(除非我在这个假设中弄错了),你必须以 100 美元的低价加入他们的开发者俱乐部年(未发生)。我并没有专门为苹果平台做开发。我做Java开发。无论如何,如果我是正确的,那将不适合我们,因此 Windows、Linux 和 Mac 的独立部署已经结束(再次,如果我是正确的)。
我希望这很简单。
那么,如何将我创建的用于将包含所有依赖 LIB 的 JAR 编译为代码签名 JAR 的 ANT 脚本应用于 JavaFX 应用程序?
编辑 1:好的,所以肯定说得太早了。不是很接近答案。我试图将我所知道的关于编译和签名常规 JAR 文件的知识告诉 frankenstein,以便包含所有库,并且签名成功地融入了我能够从 manual 中挑选出来的内容。我所拥有的是大杂烩和失败。我得到一个 .JAR 文件,其中包含除主 .JAR 文件之外的所有内容。无论如何,这是 ANT 脚本代码:
属性:
<property name = "name"
value = "APPNAME"/>
<property name = "file"
value = "APPJAR"/>
<property name = "MC"
value = "MAINPACKAGE.MAINCLASS" />
<property name = "released.dir"
value = "released"/>
<property name = "compiled.dir"
value = "$released.dir/compiled"/>
<property name = "stored.dir"
value = "$released.dir/stored"/>
<property name = "signed.dir"
value = "$released.dir/signed"/>
其他:
<delete dir = "$released.dir"/>
<mkdir dir = "$compiled.dir"/>
<fx:jar destfile = "dist/compiled.jar">
<fx:platform javafx = "8.0+" j2se = "8.0"/>
<fx:application name = "$name"
mainClass = "$MC"/>
<fileset dir = "build/classes"/>
<fx:resources>
<fx:fileset dir = "dist" includes = "lib/*.jar"/>
</fx:resources>
</fx:jar>
<fx:signjar keystore = "$comodo.key.store"
alias = "$comodo.key.alias"
storetype = "PKCS12"
keypass = "$comodo.key.pass"
storepass = "$comodo.key.storepass"
jar = "dist/compiled.jar"
destdir = "$compiled.dir"/>
<mkdir dir = "$stored.dir"/>
<jar destFile = "$stored.dir/temp_final.jar"
filesetmanifest = "skip" >
<zipgroupfileset dir = "$compiled.dir"
includes = "compiled.jar"/>
<zipgroupfileset dir = "dist/lib"
includes = "*.jar"/>
<manifest>
<attribute name = "Main-Class"
value = "$main.class"/>
</manifest>
</jar>
<zip destfile = "$stored.dir/$file">
<zipfileset src = "$stored.dir/temp_final.jar"
excludes = "META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
</zip>
<delete file="$stored.dir/temp_final.jar"/>
<mkdir dir = "$signed.dir"/>
<signjar
keystore = "$comodo.key.store"
alias = "$comodo.key.alias"
storetype = "PKCS12"
tsaurl = "$timestamp.url"
keypass = "$comodo.key.pass"
storepass = "$comodo.key.storepass"
jar = "$stored.dir/$file"
destdir = "$signed.dir"/>
这就是我必须在我的时间展示的东西。如果有人能从中收集到一些可能使我走上正轨的东西(如果我什至接近,我觉得我不是),那将是非常棒的。
【问题讨论】:
可能已经找到了查阅文档的答案。不要以为事情会那么容易...... 【参考方案1】:在把我的头骨和里面的东西打成一团难以辨认的连贯思想后,我设法编写了一个脚本来完成我打算完成的任务。其中大部分来自其他人的贡献和咨询docs。另一个主要贡献是 here (该线程的最后一条评论,它链接到另一个 SO 答案,但那个 SO 答案对我没有多大帮助)。感谢我能够从中提取解决方案部分的一切。我希望这对其他需要/想要完成这项任务的人有所帮助。
无论如何:第一件事:属性:
<property name = "JFXProject.name"
value = "PROJECT"/> <!-- Put your own project name here -->
<property name = "JFXMainClass"
value = "MAINPACKAGE.MAINCLASS"/> <!--Put your own main class here -->
<!-- don't edit below this line -->
<property name = "JFX.src.dir"
value = "src"/>
<property name = "JFX.lib.dir"
value = "dist/lib"/>
<property name = "JFX.build.dir"
value = "release/JFXBuild"/>
<property name = "JFX.sign.dir"
value = "release/JFXSign"/>
<property name = "store.dir"
value = "release/store"/>
<property name = "sign.dir"
value = "release/sign"/>
<property name = "comodo.key.store"
value = "PATH-TO-YOUR-CERTIFICATE" /> <!-- You can name this what you like, but you want to make sure the value points to your certificate or JKS file -->
<property name = "comodo.key.storepass"
value = "PASSWORD"/> <!-- Above applies here. Make sure it's the right password -->
<property name = "comodo.key.alias"
value = "ALIAS"/> <!-- Make sure it's your right alias. You can find out how to find that information from [here][3] -->
<property name = "comodo.key.pass"
value = "PASSWORD"/> <!-- In my own experience this was the same as the storepass but it may be different for you -->
<property name = "timestamp.url"
value = "TIMESTAMPURL"/> <!-- This will vary for you depending on your certificate. -->
如果需要,您可以将属性名称更改为对您更有意义的名称,但请确保它们在整个脚本中保持一致。
接下来,我们要清理最后一次构建:
<target name = "JFXClean">
<echo>Cleaning $JFX.build.dir...</echo>
<delete dir = "$JFX.build.dir"/>
<delete dir = "$JFX.sign.dir"/>
<delete dir = "$store.dir"/>
<delete dir = "$sign.dir"/>
</target>
然后我们要为新的干净构建重新创建目录...
<target name = "JFXInit" depends = "JFXClean">
<echo>Creating the build directory...</echo>
<mkdir dir = "$JFX.build.dir"/>
<mkdir dir = "$JFX.sign.dir"/>
<mkdir dir = "$store.dir"/>
<mkdir dir = "$sign.dir"/>
</target>
这部分对于获得正常运行的 JavaFX JAR 文件至关重要:
<target name = "JFXBuild" depends = "jar, JFXInit">
<fx:jar destfile = "$JFX.build.dir/$JFXProject.name.jar">
<fx:application name = "$JFXProject.name"
mainClass = "$JFXMainClass"/>
<fileset dir = "build/classes"/>
</fx:jar>
</target>
这会将所有部分正确存储到 JAR 中的位置(包括您可能拥有的任何 CSS 和 JFXML 文件。如果您不创建 JFXML 应用程序,这可能没有必要。我不知道我没有测试了一下。
然后我们要签署 JavaFX JAR:
<target name = "JFXSign" depends = "JFXBuild">
<fx:signjar keystore = "$comodo.key.store"
alias = "$comodo.key.alias"
storetype = "PKCS12" <!-- This is necessary for me, but may not be for you if you are not using a PFX file -->
keypass = "$comodo.key.pass"
storepass = "$comodo.key.storepass"
jar = "$JFX.build.dir/$JFXProject.name.jar"
destdir = "$JFX.sign.dir"/>
</target>
之后,还有 2 个目标处理采购 zip,然后设置该 zip,还有一个用于唱最后一个 jar 的 final:
<target name = "build" depends = "JFXSign">
<jar destfile = "$store.dir/temp_final.jar"
filesetmanifest = "skip">
<zipgroupfileset dir = "$JFX.build.dir"
includes = "*.jar"/>
<zipgroupfileset dir = "$JFX.lib.dir"
includes = "*.jar"/>
<manifest>
<attribute name = "Main-Class"
value = "$JFXMainClass"/>
</manifest>
</jar>
</target>
<target name = "store" depends = "build">
<zip destfile = "$store.dir/$JFXProject.name.jar">
<zipfileset src = "$store.dir/temp_final.jar"
excludes = "META-INF/*sf, META-INF/*.DSA, META-INF/*RSA"/>
</zip>
<delete file = "$store.dir/temp_final.jar"/>
</target>
<target name = "BuildStoreAndSign" depends = "store">
<signjar
keystore = "$comodo.key.store"
alias = "$comodo.key.alias"
storetype = "PKCS12" <!-- This is necessary for me, but may not be for you if you are not using a PFX file -->
tsaurl = "$timestamp.url"
keypass = "$comodo.key.pass"
storepass = "$comodo.key.storepass"
jar = "$store.dir/$JFXProject.name.jar"
destdir = "$sign.dir"/>
<delete dir = "$JFX.compile.dir"/>
<delete dir = "$JFX.build.dir"/>
<delete dir = "$JFX.sign.dir"/>
<delete dir = "$store.dir"/>
</target>
我无法真正解释这一点,因为我远不是这方面的专家。我基本上能够从示例代码和我找到的源代码中提取我所看到的内容,并将它们放在一起得到这个: 最后一点半有用的信息是this,但请注意这不使用一个罐子(我用一个罐子试过,但没有用。它没有引入 CSS 或应用它)。
还有一点警告:This worked for me。我不能保证它会适合你,但我想修改它会产生类似于我的结果(一个可以在你放置的任何地方运行的单个 .JAR JavaFX 应用程序)。
【讨论】:
期待您的回答@Will。考虑将其更改为您的答案下方的评论,然后在您弄清楚时发布您的完整答案。 很抱歉,但好消息是我有一个可行的解决方案!【参考方案2】:神谕说:http://www.oracle.com/technetwork/articles/javase/single-jar-141905.html
需要修改
<manifest>
<attribute name="Main-Class" value="$main.class"/>
</manifest>
并将其更改为
<manifest>
CHANGE application.MainClass to
<attribute name="JavaFX-Application-Class" value="application.MainClass" />
<attribute name="Main-Class" value="com/javafx/main/Main" />
</manifest>
但是听了上面的 Oracle,我得到了一个带有外部 jar 的单一 JavaFX。
【讨论】:
【参考方案3】:我尝试了您的 build.xml 示例,但未能使其正常工作。
不过,我确实找到了使用 Eclipse 的替代方法。
-
在 Eclipse 中打开您的 JavaFX 项目。
右键单击您的项目并将其导出为可运行的 JAR 文件。
将所需的库提取到生成的 JAR 中。
-
使用标准 jar 签名器为您的可运行 JAR 签名。
为此,您需要先创建一个密钥库:
keytool -genkey -keyalg RSA -alias Aubrey -keystore keystore.jks -storepass YourPassword
然后像这样签名:
jarsigner -keystore c:\path\to\your\keystore.jks -storepass YourPassword c:\path\to\your\file.jar Aubrey
-
最后,创建一个 javafx jnlp 文件。并将您签名的 jar 和 jnlp 上传到您的服务器。
例如:
<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0" xmlns:jfx="http://javafx.com" codebase="http://aubreigo.info/java/" href="FX-People.jnlp">
<information>
<title>FX-People</title>
<vendor>aubrey</vendor>
<description>Google Contacts Viewer</description>
<offline-allowed/>
</information>
<security>
<all-permissions/>
</security>
<resources>
<j2se version="1.6+" href="http://java.sun.com/products/autodl/j2se"/>
<jar href="FX-People.jar" size="109545" download="eager" />
</resources>
<applet-desc main-class="com.javafx.main.NoJavaFXFallback" name="FX-People" >
<param name="requiredFXVersion" value="8.0+"/>
</applet-desc>
<jfx:javafx-desc main-class="fx.contacts.FXContacts" name="FX-People" />
<update check="always"/>
</jnlp>
【讨论】:
以上是关于如何签署 JavaFX 应用程序并将其部署到单个 .JAR 中?的主要内容,如果未能解决你的问题,请参考以下文章
我在哪里可以找到有关 SP 如何使用其私钥签署身份验证请求并将其发送到 IdP 的 URL 的信息?