Maven JNLP creation with EV Code Signing

2.3k Views Asked by At

I'm using Maven and the webstart-maven-plugin to generate a JNLP file and sign the jar files of my project. We just had to renew our Code Signing certificate and since February 2017, hardware tokens are provided instead of software ones.

According to the GlobalSign support page, the correct way to sign jars with the hardware token is as following (see article):

jarsigner -keystore NONE -storetype PKCS11 -tsa http://timestamp.globalsign.com/scripts/timestamp.dll -providerClass sun.security.pkcs11.SunPKCS11 -providerArg eToken.cfg test.jar "le-d0e453de-66db-414a-8fa8-0a07cfad66b5"

I followed all the steps described in that article and now I'm trying to adjust my pom.xml to apply the EV Code Signing certificate.

Originally I used a keystore (snippet, full pom below):

<!-- SIGNING -->
<sign>
    <keystore>${project.basedir}/src/main/jnlp/my.keystore</keystore>
    <keypass>...</keypass>
    <storepass>...</storepass>
    <alias>...</alias>
    <verify>true</verify>
</sign>

Now I'm trying to update it to get EV Code Signing to work (snippet, full pom below):

<!-- SIGNING -->
<sign>
    <keystore>NONE</keystore>
    <storetype>PKCS11</storetype>
    <storepass>...</storepass>
    <tsa>http://timestamp.globalsign.com/scripts/timestamp.dll</tsa>
    <providerClass>sun.security.pkcs11.SunPKCS11</providerClass>
    <providerArg>${project.basedir}/src/main/resources/token/eToken.config</providerArg>
    <alias>le-d0e453de-66db-414a-8fa8-0a07cfad66b5</alias> <!-- I took the alias from the article as an example -->
    <verify>true</verify>
</sign>

However, it seems that tsa, providerClass and providerArg are not supported unless I missed something. I did not find alot of information about the webstart-maven-plugin or it's not up to date, which is a shame :(

Is there another/better way to sign jars while creating a JNLP? Any help would be much appreciated!

pom.xml code signing (with keystore)

    <profile>
        <id>jnlp</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>webstart-maven-plugin</artifactId>
                    <version>1.0-beta-6</version>
                    <dependencies>
                        <dependency>
                            <groupId>org.codehaus.mojo</groupId>
                            <artifactId>webstart-pack200-impl</artifactId>
                            <version>1.0-beta-6</version>
                        </dependency>
                        <dependency>
                            <groupId>org.codehaus.mojo</groupId>
                            <artifactId>keytool-api-1.7</artifactId>
                            <version>1.5</version>
                        </dependency>
                    </dependencies>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>jnlp</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <!-- The path where the libraries are stored within the jnlp structure. not required. by default the libraries are within the working directory -->
                        <libPath>lib</libPath>
                        <!-- JNLP generation -->
                        <jnlp>
                            <mainClass>myApp.ui.MainApp</mainClass>
                        </jnlp>

                        <!-- SIGNING -->
                        <sign>
                            <keystore>${project.basedir}/src/main/jnlp/my.keystore</keystore>
                            <keypass>...</keypass>
                            <storepass>...</storepass>
                            <alias>...</alias>
                            <verify>true</verify>
                        </sign>
                        <verbose>true</verbose>
                        <updateManifestEntries>
                            <Application-Name>MyApp</Application-Name>
                            <Permissions>all-permissions</Permissions>
                            <Codebase>...</Codebase>
                            <Application-Library-Allowable-Codebase>...</Application-Library-Allowable-Codebase>
                            <Caller-Allowable-Codebase>...</Caller-Allowable-Codebase>
                        </updateManifestEntries>

                        <!-- BUILDING PROCESS -->
                        <pack200>
                            <enabled>false</enabled>
                        </pack200>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-assembly-plugin</artifactId>
                    <version>2.6</version>
                    <configuration>
                        <descriptorRefs>
                            <descriptorRef>jar-with-dependencies</descriptorRef>
                        </descriptorRefs>
                    </configuration>
                    <executions>
                        <execution>
                            <id>assemble-all</id>
                            <phase>package</phase>
                            <goals>
                                <goal>single</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>

pom.xml EV Code Signing (with SafeNet token)

    <profile>
        <id>jnlp</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>webstart-maven-plugin</artifactId>
                    <version>1.0-beta-7</version>
                    <dependencies>
                        <dependency>
                            <groupId>org.codehaus.mojo</groupId>
                            <artifactId>webstart-pack200-impl</artifactId>
                            <version>1.0-beta-6</version>
                        </dependency>
                        <dependency>
                            <groupId>org.codehaus.mojo</groupId>
                            <artifactId>keytool-api-1.7</artifactId>
                            <version>1.5</version>
                        </dependency>
                    </dependencies>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>jnlp</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <!-- The path where the libraries are stored within the jnlp structure. not required. by default the libraries are within the working directory -->
                        <libPath>lib</libPath>
                        <!-- JNLP generation -->
                        <jnlp>
                            <mainClass>myApp.ui.MainApp</mainClass>
                        </jnlp>

                        <!-- SIGNING -->
                        <sign>
                            <keystore>NONE</keystore>
                            <storetype>PKCS11</storetype>
                            <storepass>...</storepass>
                            <tsa>http://timestamp.globalsign.com/scripts/timestamp.dll</tsa>
                            <providerClass>sun.security.pkcs11.SunPKCS11</providerClass>
                            <providerArg>${project.basedir}/src/main/resources/token/eToken.config</providerArg>
                            <alias>le-d0e453de-66db-414a-8fa8-0a07cfad66b5</alias> <!-- i took the alias from the article as an example -->
                            <verify>true</verify>
                        </sign>
                        <verbose>true</verbose>
                        <updateManifestEntries>
                            <Application-Name>MyApp</Application-Name>
                            <Permissions>all-permissions</Permissions>
                            <Codebase>...</Codebase>
                            <Application-Library-Allowable-Codebase>...</Application-Library-Allowable-Codebase>
                            <Caller-Allowable-Codebase>...</Caller-Allowable-Codebase>
                        </updateManifestEntries>

                        <!-- BUILDING PROCESS -->
                        <pack200>
                            <enabled>false</enabled>
                        </pack200>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-assembly-plugin</artifactId>
                    <version>2.6</version>
                    <configuration>
                        <descriptorRefs>
                            <descriptorRef>jar-with-dependencies</descriptorRef>
                        </descriptorRefs>
                    </configuration>
                    <executions>
                        <execution>
                            <id>assemble-all</id>
                            <phase>package</phase>
                            <goals>
                                <goal>single</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
3

There are 3 best solutions below

0
On BEST ANSWER

Disclaimer: I'm the maintainer of the javafx-maven-plugin.

This got reported and now being available, for more information see this link: https://github.com/javafx-maven-plugin/javafx-maven-plugin/issues/291

As already mentioned on the javafx-maven-plugin, here is the solution to get this working:

<plugin>
    <groupId>com.zenjava</groupId>
    <artifactId>javafx-maven-plugin</artifactId>
    <version>8.8.4-SNAPSHOT</version>
    <!-- this configuration is share among all executions -->
    <configuration>
        <mainClass>fqdn.to.your.MainClass</mainClass>
        <description>test signing</description>
        <title>launch</title>
        <verbose>true</verbose>
        <j2seVersion>1.8+</j2seVersion>
        <appName>simpleApplicationName</appName>

        <!-- this only sets the field inside jar-file -->
        <allPermissions>true</allPermissions>
    </configuration>
    <executions>
        <execution>
            <!-- required before build-native, creates target/jfx/app -->
            <id>create-jfxjar</id>
            <phase>package</phase>
            <goals>
                <goal>build-jar</goal>
            </goals>
        </execution>
        <execution>
            <!-- creates target/jfx/web -->
            <id>create-jnlp-bundle</id>
            <phase>package</phase>
            <goals>
                <goal>build-native</goal>
            </goals>
            <!-- this configuration is only specific to this execution -->
            <configuration>
                <!-- as we only want to create the JNLP-package, use fixed bundler-ID -->
                <bundler>jnlp<bundler>

                <bundleArguments>
                    <!-- this makes the JNLP-file having permissions being set -->
                    <!-- AND it is the trigger for signing jar-files using jarsigner -->
                    <jnlp.allPermisions>true</jnlp.allPermisions>

                    <!-- the JNLP-bundler is a bit picky about its parametes, it does not use <appName> -->
                    <jnlp.outfile>simpleApplicationName</jnlp.outfile>
                </bundleArguments>

                <!-- this setting is required for the new "jarsigner"-feature -->
                <noBlobSigning>true</noBlobSigning>

                <!-- these are required, please change them for your own requirements -->
                <keyStoreAlias>myalias</keyStoreAlias>
                <keyStorePassword>mypass</keyStorePassword>

                <!-- as this keystore is no file, please disable file-checks -->
                <skipKeyStoreChecking>true</skipKeyStoreChecking>
                <!-- this is new too and required, as PKCS11 does not want some keypass -->
                <skipKeypassWhileSigning>true</skipKeypassWhileSigning>

                <!-- this is used for additional parameters for the jarsigner command -->
                <additionalJarsignerParameters>
                    <additionalJarsignerParameter>-keystore</additionalJarsignerParameter>
                    <additionalJarsignerParameter>NONE</additionalJarsignerParameter>
                    <additionalJarsignerParameter>-storetype</additionalJarsignerParameter>
                    <additionalJarsignerParameter>PKCS11</additionalJarsignerParameter>
                    <additionalJarsignerParameter>-tsa</additionalJarsignerParameter>
                    <additionalJarsignerParameter>http://timestamp.globalsign.com/scripts/timestamp.dll</additionalJarsignerParameter>
                    <additionalJarsignerParameter>-providerClass</additionalJarsignerParameter>
                    <additionalJarsignerParameter>sun.security.pkcs11.SunPKCS11</additionalJarsignerParameter>
                    <additionalJarsignerParameter>-providerArg</additionalJarsignerParameter>
                    <additionalJarsignerParameter>${project.basedir}/src/main/resources/token/eToken.config</additionalJarsignerParameter>
                    <!-- I DO KNOW that this is verbose ... -->
                </additionalJarsignerParameters>

                <!-- the jnlp-bundler gets a bit messy, lots of files, so we want to mimic "jfx:web"-folder-structure -->
                <nativeOutputDir>${project.build.directory}/jfx/web</nativeOutputDir>
            </configuration>
        </execution>
    </executions>
</plugin>

This version is not yet released but available as SNAPSHOT from the sonatype-repository.

4
On

Why not use javafxpackager? It can create both java webstart and executable forms and sign them easily. It's what Oracle recommends. I've been it for years and really like it. I'm using its ant tasks but I believe they have a maven plugin also.

Here are more info:

http://docs.oracle.com/javafx/2/deployment/packager.htm

0
On

I am facing de same problem these days. I succeed in using a "workaround"

Workaround 1 (one fat jar):

  • maven-shade-plugin (it is the easy way to create a "fat jar" with dependencies and then just sign this jar)
  • maven-jarsigner-plugin (to sign the shaded jar from token)
  • webstart-maven-plugin (only for jnlp purpose)

Here is my pom :

<dependencies>

    ...

</dependencies>

<build>
    <plugins>

        ...

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.0.0</version>
            <executions>
                <execution>
                    <id>shade</id>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer
                                implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <manifestEntries>
                                    <Permissions>all-permissions</Permissions>
                                </manifestEntries>
                            </transformer>
                        </transformers>
                    </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>
                    <phase>package</phase>
                    <goals>
                        <goal>sign</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <keystore>NONE</keystore>
                <storepass>******</storepass>
                <storetype>PKCS11</storetype>
                <tsa>http://rxxxxx.globalsign.com/advanced</tsa>
                <providerClass>sun.security.pkcs11.SunPKCS11</providerClass>
                <providerArg>${project.basedir}/src/main/eToken.cfg</providerArg>
                <alias>xxxxxxxxxxxxx</alias>
                <archive>${project.build.directory}/${project.build.FinalName}.${project.packaging}</archive>
                <arguments>
                    <argument>-J-Dhttp.proxyHost=my.proxy.com</argument>
                    <argument>-J-Dhttp.proxyPort=8080</argument>
                </arguments>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.codehaus.mojo.webstart</groupId>
            <artifactId>webstart-maven-plugin</artifactId>
            <version>1.0-beta-7</version>
            <executions>
                <execution>
                    <id>build-jnlp</id>
                    <phase>package</phase>
                    <goals>
                        <goal>jnlp</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <makeArchive>false</makeArchive>
                <jnlp>
                    <inputTemplateResourcePath>${project.basedir}/src/main/jnlp</inputTemplateResourcePath>
                    <inputTemplate>template.vm</inputTemplate>
                    <mainClass>test</mainClass>
                </jnlp>
            </configuration>
        </plugin>
    </plugins>

</build>

And template.vm :

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" codebase="http://www.mycompany.com/poc" href="launch.jnlp">
    <information>
        <title>xxxx</title>
        <vendor>$project.Organization.Name</vendor>
        <homepage href="http://www.mycompany.com" />
        <description>$project.Description</description>
        <offline-allowed />
    </information>
    <security>
        <all-permissions />
    </security>
    <resources>
        <j2se version="1.7+" />
        $dependencies
    </resources>
    <application-desc main-class="$mainClass" />
</jnlp>

Workaround 2 (several jars):

  • maven-jar-plugin (to set all permissions in main jar manifest)
  • webstart-maven-plugin (only for jnlp purpose)
  • maven-jarsigner-plugin (to sign all jars in /jnlp from token)

Here is my pom :

<dependencies>

    ...

</dependencies>

<build>
    <plugins>

        ...
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <executions>
                <execution>
                    <id>update-manifest-permissions-entry</id>
                    <phase>prepare-package</phase>
                    <goals>
                        <goal>jar</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <archive>
                    <addMavenDescriptor>false</addMavenDescriptor>
                    <manifestEntries>
                        <Permissions>all-permissions</Permissions>
                    </manifestEntries>
                </archive>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>webstart-maven-plugin</artifactId>
            <version>1.0-beta-7</version>
            <executions>
                <execution>
                    <id>build-jnlp</id>
                    <phase>package</phase>
                    <goals>
                        <goal>jnlp</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <makeArchive>false</makeArchive>
                <jnlp>
                    <inputTemplateResourcePath>${project.basedir}/src/main/jnlp</inputTemplateResourcePath>
                    <inputTemplate>template.vm</inputTemplate>
                    <mainClass>test</mainClass>
                </jnlp>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jarsigner-plugin</artifactId>
            <version>1.4</version>
            <executions>
                <execution>
                    <id>sign</id>
                    <phase>install</phase>
                    <goals>
                        <goal>sign</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <keystore>NONE</keystore>
                <storepass>xxxxx</storepass>
                <storetype>PKCS11</storetype>
                <tsa>http://xxx.globalsign.com/xxx</tsa>
                <providerClass>sun.security.pkcs11.SunPKCS11</providerClass>
                <providerArg>${project.basedir}/src/main/eToken.cfg</providerArg>
                <alias>xxxxxxx</alias>
                <processMainArtifact>false</processMainArtifact>
                <archiveDirectory>${project.build.directory}/jnlp</archiveDirectory>
                <arguments>
                    <argument>-J-Dhttp.proxyHost=myproxy.company.com</argument>
                    <argument>-J-Dhttp.proxyPort=8080</argument>
                </arguments>
            </configuration>
        </plugin>
    </plugins>

</build>

And template.vm :

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" codebase="http://www.mycompany.com/poc" href="launch.jnlp">
    <information>
        <title>xxxx</title>
        <vendor>$project.Organization.Name</vendor>
        <homepage href="http://www.mycompany.com" />
        <description>$project.Description</description>
        <offline-allowed />
    </information>
    <security>
        <all-permissions />
    </security>
    <resources>
        <j2se version="1.7+" />
        $dependencies
    </resources>
    <application-desc main-class="$mainClass" />
</jnlp>