Buscar este blog

domingo, 29 de noviembre de 2015

Java Web Start - Found unsigned entry in resource

When you launch a Java Web Start application via JNLP file (check here https://java.com/en/download/faq/java_webstart.xml) there are a lot of security issues you have to keep in mind. Among them is the fact that your jar must be signed in order to Java allow it to be executed.

If your jar is not signed you will get some message like this:
com.sun.deploy.net.JARSigningException: Se ha encontrado una entrada sin firma en el recurso: https://desarr.local/jnlp/XXXXXXX-jar-with-dependencies.jar


I built my jar by using Maven, and the final output is a file called XXXX-0.0.1-SNAPSHOT-jar-with-dependencies.jar. This jar contains all third party classes and resources that my application need to work.
<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-assembly-plugin</artifactId>
 <version>2.6</version>
 <executions>
  <execution>
   <id>make-assembly</id>
   <phase>package</phase>
   <goals>
    <goal>single</goal>
   </goals>
   <configuration>
    <archive>
     <manifest>
      <mainClass>xxxxxxxxxx.Main</mainClass>
     </manifest>
     <manifestEntries>
      <Permissions>all-permissions</Permissions>
      <Codebase>desarr.local</Codebase>
      <Application-Name>xxxxxxxxx</Application-Name>
      <Application-Library-Allowable-Codebase>https://desarr.local</Application-Library-Allowable-Codebase>
      <Entry-Point>xxxxxxxxxx.Main</Entry-Point>
      <Built-By>Sisifo</Built-By>
     </manifestEntries>
    </archive>
    <descriptorRefs>
     <descriptorRef>jar-with-dependencies</descriptorRef>
    </descriptorRefs>
   </configuration>
  </execution>
 </executions>
</plugin>

Note. 
  • I hid the application name on purpose because it is a real application I am working with.
  • I downloaded the application from a Apache server located in "desarr.local", i.e, this is my htdocs basedir.

So I just needed to sign a jar. Fine, no more than five minutes, thats very easy with jarsigner tool (check here https://docs.oracle.com/javase/7/docs/technotes/tools/windows/jarsigner.html).


To sign my jar I created a self-signed certificate acting as a CA, and with it, I signed a second certificate (kind pro eh).
So, I have a PKCS12 certificate called jar-signer signed with my CA:
  • The file is "jar_signer(1234).p12"
  • The file format is PKCS12
  • The password file is "1234"
  • The certificate alias is "jar-signer"
  • The alias key is "1234"



With this I now could sign my jar:
"c:\Program Files\Java\jdk1.7.0_21\bin\jarsigner.exe" -keystore jar_signer(1234).p12 -storetype PKCS12 -storepass 1234 -keypass 1234 XXXX-SNAPSHOT-jar-with-dependencies.jar jar-signer

Ok, I have a signed jar. I launch my JNLP file again and... Fail. I still get the exact same error.
The jar was signed properly. By using the "-verify" option of jarsigner I got success verification (although with some warnings), but the jar was fine.
"c:\Program Files\Java\jdk1.7.0_21\bin\jarsigner.exe" -verify XXXX-0.0.1-SNAPSHOT-jar-with-dependencies.jar
jar verified.

Warning:
This jar contains entries whose certificate chain is not validated.

Re-run with the -verbose and -certs options for more details.

At this point I checked the following issues:
1) My self signed certificates were not suitable.
I passed the jar to our client and they signed it with a "true" certificate,.
Result: Fail.

2) There were problems with the jarsigner tool.
I signed the jar with the jarsigner shipped with JDK 1.6.0_45, 1.7.0_21 and 1.8.0_51.
Result: Fail

3) Set to checked the java option to "keep temporary files on my computer"
In Control Panel > Java > General > Temporary files > Configuration.
Result: Fail

4) Import the sign certificate and the CA as trusted for java.
In Control Panel > Java > Security > Manage Certificates > User
Result: Fail

So I was quite sure that my configuration was fine and that the problem should be in the dam jar.

I opened the jar and I saw that there were a lot of non essential files in it, like licenses, poms, READMEs, etc...



I decided to manually remove all this "crap" and sign again. The result... Success!!!

After a while doing try-error deletes I found that the real problem was related to the file called "\jboss-as-jms-client-bom-7.3.0.Final-redhat-14.pom". Check the previous image and you will note that there is a slash "\" in this name. I think that, for some unknown reason, the java launcher was not able to process properly that file or that this file was not well signed at all.


Well, once I found the root of the problem, the next step was to modify the procedure I was using to create the jar.

The maven-assembly-plugin (check here http://maven.apache.org/plugins/maven-assembly-plugin/) has a config option called "descriptors" to reference an external descriptor file. Based on this file, the plugin know how to build the jar.
There are four predefined descriptors, being jar-with-dependencies just one of them.

I changed the plugin configuration and created a custom descriptor.
<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-assembly-plugin</artifactId>
 <version>2.6</version>
 <executions>
  <execution>
   <id>make-assembly</id>
   <phase>package</phase>
   <goals>
    <goal>single</goal>
   </goals>
   <configuration>
    <archive>
     <manifest>
      <mainClass>xxxxxxxxxx.Main</mainClass>
     </manifest>
     <manifestEntries>
      <Permissions>all-permissions</Permissions>
      <Codebase>desarr.local</Codebase>
      <Application-Name>xxxxxxxxxx</Application-Name>
      <Application-Library-Allowable-Codebase>https://desarr.local</Application-Library-Allowable-Codebase>
      <Entry-Point>xxxxxxxxxx.Main</Entry-Point>
      <Built-By>Sisifo</Built-By>
     </manifestEntries>
    </archive>
    <descriptors>
     <descriptor>src/dev/assembly/distribution.xml</descriptor>
    </descriptors>
   </configuration>
  </execution>
 </executions>
</plugin>

And this is my distribution.xml file:
<assembly
 xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
 <id>jar-with-dependencies</id>
 <formats>
  <format>jar</format>
 </formats>
 <includeBaseDirectory>false</includeBaseDirectory>
 <dependencySets>
  <dependencySet>
   <outputDirectory>/</outputDirectory>
   <useProjectArtifact>true</useProjectArtifact>
   <unpack>true</unpack>
   <scope>runtime</scope>
   <unpackOptions>
    <excludes>
     <exclude>**/README</exclude>
     <exclude>**/LICENSE</exclude>
     <exclude>**/NOTICE</exclude>
     <exclude>**/ASL2.0</exclude>
     <exclude>**/INDEX.LIST</exclude>
     <exclude>**/*.txt</exclude>
     <exclude>**/*.html</exclude>
     <exclude>**/.project</exclude>
     <exclude>**/.classpath</exclude>
     <exclude>jboss-as-checkstyle/*</exclude>
     <!-- Eclsuiones relacionadas con maven -->     
     <exclude>META-INF/maven/**</exclude>
     <exclude>**/pom.xml</exclude>
     <exclude>**/pom.properties</exclude>
     <exclude>*.pom</exclude>
     <exclude>**/*.pom</exclude>
     <!-- Licencias -->
     <exclude>META-INF/licenses/*</exclude>
     <exclude>licenses/*</exclude>
     
          
     <exclude>%regex[jboss-as-jms-client-bom]</exclude>
     <exclude>\jboss-as-jms-client-bom-7.3.0.Final-redhat-14.pom</exclude>
    </excludes>
   </unpackOptions>
  </dependencySet>
 </dependencySets>

With this configuration what I was trying to do was to remove all non-essential files and, specifically, the jboss-as-jms-client-bom-7.3.0.Final-redhat-14.pom.
Result: Fail
Even doing so, this file was still there. I tried all kind of expressions, excludes, fieldsets, etc... but no luck.


So, the plan B was to use a ant task to open the jar file, remove the file, and repack it.
For this I used maven-antrun-plugin.
<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-antrun-plugin</artifactId>
 <version>1.7</version>
 <executions>
  <execution>
   <id>elimina-bom</id>
   <phase>package</phase>
   <goals>
    <goal>run</goal>
   </goals>
   <configuration>
    <tasks>        
     <zip file="${project.build.directory}/${project.build.finalName}-limpio.jar">
      <zipfileset src="${project.build.directory}/${project.build.finalName}-jar-with-dependencies.jar">
       <exclude name="**/*.pom" />
      </zipfileset>          
     </zip>
    </tasks>
   </configuration>
  </execution>
 </executions>
</plugin>

Finally, after long hours of failures, my JNLP app was working...

2 comentarios:

  1. Hi.

    Thanks for this great tutorial,
    I have a question.

    How did you create a self-signed certificate acting as a CA??

    Please help me. Thanks a lot

    ResponderEliminar
  2. Hi

    With keyStoreExplorer, when you generate a new key pair, there is an option to "Add Extensions".

    There are a lot of extensions types, but I jus marked two:
    - Basic Constraints: Subject is a CA
    - Key Usage: Certificate signing

    This would be your CA.

    Once the CA was created, you can use the contextual menu option "Sign New Key Pair" and create a second certificate signed by the first one.

    Regards

    ResponderEliminar