The Source for Java Technology Collaboration
User: Password:



Kirill Grouchnikov

Kirill Grouchnikov's Blog

Signing jars for java.net Web Start applications

Posted by kirillcool on May 20, 2005 at 03:22 AM | Comments (24)

It's common these days to provide a Web Start version of your application that can run on a machine without the need to download the bundle distribution manually. If your application needs special access privileges, you will need to sign your jar file(s) and then ask user to allow installing this application. Here are the basic steps to do the job.

First, you need to create a key store. For this, run the following command:
keytool -genkey -keystore jaxb.keys -alias https://jaxb-workshop.dev.java.net/ -validity 365
Here, in dark green are the parameters. The first one is the name of the key store. This will be a local file that will hold key pairs. The second one is a symbolic name for your key (that will be created in the key store). The last parameter is a number of days that your key will be valid (90 by default). Follow a simple sequence of steps, and don't forget to write down the password to the key store and to the key itself (in case they are different).

Now, you need to make sure that all jar files in your application that need privileges are signed. Here is how you sign a single file:
jarsigner -keystore jaxb.keys -storepass **** jaxb-api.jar https://jaxb-workshop.dev.java.net/
Here, you provide the name of the key store and its password as the first two parameters, the name of the jar file you want to sign as the third parameter, and the key alias as the fourth parameter. Repeat the above steps for all you jar files.

Now it's time to create a JNLP descriptor file for your Web Start application. Here is a simple file:
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="https://jaxb-workshop.dev.java.net/webstart/" href="wizard.jnlp">
   <information>
      <title>XJC Wizard</title>
      <vendor>https://jaxb-workshop.dev.java.net/</vendor>
      <description>Wizard frontend for XJC generator</description>
      <description kind="short">Wizard frontend for XJC generator</description>
      <offline-allowed/>
   </information>
   <offline-allowed/>
   <security>
      <all-permissions/>
   </security>
   <resources>
      <j2se version="1.5+"/>
      <jar href="jaxbw.jar"/>
      <jar href="substance.jar"/>
      <jar href="jaxb-api.jar"/>
      <jar href="jaxb-impl.jar"/>
      <jar href="jaxb-xjc.jar"/>
      <jar href="jsr173_api.jar"/>
      <jar href="activation.jar"/>
   </resources>
   <application-desc main-class="org.jvnet.jaxbw.xjcfe.wizard.WizardMainFrame"/>
</jnlp>
Note that here we asked for all permissions for our application.

All that is left to do - upload all the jar files and the JNLP file to CVS repository under www and put the JNLP URL in your page. Unfortunately, this is not all. When you will run the above JNLP, you will get the following Web Start exception:
JAR resources in JNLP file are not signed by same certificate
The reason for this is simple - one of the jar files that you are using was already signed by another party. Here is the way to find it:
jarsigner -certs -verbose -verify activation.jar
You will see a long list of certificates (one for each file). This means that this specific jar was signed by another party (Sun in our case). The solution for the problem is simple - put this jar in a separate JNLP and reference it in your main JNLP:
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="https://jaxb-workshop.dev.java.net/webstart/" href="activation.jnlp">
   <information>
      <title>Activation</title>
      <vendor>Sun Microsystems, Inc.</vendor>
      <offline-allowed/>
   </information>
   <offline-allowed/>
   <resources>
      <jar href="activation.jar"/>
   </resources>
   <component-desc/>
</jnlp>
As you can see, we don't ask for permissions, as this specific jar doesn't need them. Then, you reference this activation.jnlp in your main JNLP:
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="https://jaxb-workshop.dev.java.net/webstart/" href="wizard.jnlp">
   <information>
      <title>XJC Wizard</title>
      <vendor>https://jaxb-workshop.dev.java.net/</vendor>
      <description>Wizard frontend for XJC generator</description>
      <description kind="short">Wizard frontend for XJC generator</description>
      <offline-allowed/>
   </information>
   <offline-allowed/>
   <security>
      <all-permissions/>
   </security>
   <resources>
      <j2se version="1.5+"/>
      <jar href="jaxbw.jar"/>
      <jar href="substance.jar"/>
      <jar href="jaxb-api.jar"/>
      <jar href="jaxb-impl.jar"/>
      <jar href="jaxb-xjc.jar"/>
      <jar href="jsr173_api.jar"/>
      <extension name="activation" href="activation.jnlp"/>
   </resources>
   <application-desc main-class="org.jvnet.jaxbw.xjcfe.wizard.WizardMainFrame"/>
</jnlp>

Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Thanks to your tutorial, my open source project tcpmon can now be run from the Web using Java Web Start. I had tried doing this many months back, but was quickly frustrated for the lack of a good tutorial.
    You may want to add the following ant commands (works great in Netbeans) as well to automate this process:

    <property name="key.alias" value="https://tcpmon.dev.java.net/"/>
    <property name="keystore.location" value="dist/project.keys"/>
    <property name="keystore.password" value="yourpwd"/>
    <genkey alias="${key-alias}" keystore="${keystore.location}" storepass="${keystore.password}" validity="3650" dname="CN=Your-CN-Name, OU=Your-OU-Name, O=Your-Org, C=US"/>
    <signjar jar="dist/tcpmon.jar" signedjar="dist/tcpmon-signed.jar" alias="${key-alias}" keystore="${keystore.location}" storepass="${keystore.password}"/>
    </target>

    Inderjeet Singh

    Posted by: inder on May 20, 2005 at 11:42 AM

  • It will be great if you can also point to a comprehensive list of different permissions that are possible (rather than just using AllPermissions).
    Inderjeet Singh

    Posted by: inder on May 20, 2005 at 11:45 AM

  • Inderjeet,
    The JNLP specification PDF lists only two available permissions:

    all-permissions all-permissions environment
    j2ee-application-client-permissions environment that meets the security specifications of the J2EE Application Client environment

    Both these permissions provide unrestricted access to the network and local disk.
    I suppose that you were referring to the java.lang.Permission hierarchy (complete list here). But these are permissions for locally started JVM as far as i know.

    Posted by: kirillcool on May 20, 2005 at 12:20 PM

  • This is very good information, but it would be VERY nice if the java.net developers could take advantage of the pack200 compression supported by Java 5.0. This would make the jar files EXTREMELY small (80% smaller in some cases). We have attempted this by following some instructions found at http://joust.kano.net/weblog/archive/2004/10/16/pack200-on-apache-web-server/

    Unfortunately, it requires the developer to supply local .htaccess files for the directory that the JNLP files lives in, but we don't seem to have access to do that on java.net. Is there a reason why we cannot take advantage of the HUGE benefits of pack200 compression on java.net? It seems that it would be in everyone's best interests if we could.

    Thanks
    Erik

    Posted by: evickroy on May 21, 2005 at 04:38 PM

  • Requiring all-permissions is a bad practice !

    I realy advocate that this type of all permission is deprecated ASAP.

    Instead we should have an "on demand" declarative premission mode :

    - you choose "on-demand" type and give the list of the permissions you wants (with all the parameters)
    - the JNLP implementation analyse this list and give an advice to the user (for instance of it is only single network connect to the internet to get a resource threat is low for the user, but if this is unlimited access to local drive, threat is high). The consequence is that user is presented a threat-o-meter and an analysis of the requirements of the applications and can decide to give the rights or not.
    - If the user gives the rights by clicking "Grant" then, the application will only be given the rights listed here ! not any other right will be given at any time ;-)

    Doing so, you can have a list of the exact rights and you will constraint your developper to explain the permission they requires and give details of the reasons

    Any thoughts ?

    Regards,
    BJB (long time webstart supporter)

    Posted by: bjb on May 23, 2005 at 10:32 AM

  • bjb,
    have you filed an RFE on bugparade or proposed this on Mustang forum? It seems that a lot of effort will be put to Web Start in Mustang, so your proposal can be (partially) accpeted and implemented. Then, of course, what will happen with all the existing applications?

    Posted by: kirillcool on May 23, 2005 at 01:11 PM

  • Hi,

    Need help on implementing the jarsigner in Netbeans,I'm getting the following error


    Created dir: C:\CVS\OperatorInterface\project\OperatorInterface\dist
    Building jar: C:\CVS\OperatorInterface\project\OperatorInterface\dist\OperatorInterface.jar
    Generating Key for ${key-alias}
    keytool error: java.lang.Exception: Key pair not generated, alias <${key-alias}> already exists


    for the build settting as ;


    <target name="-post-jar">
    <property name="key.alias" value="https://shazam.net/"/>
    <property name="keystore.location" value="uiKeyStore"/>
    <property name="keystore.password" value="*******"/>
    <genkey alias="${key-alias}" keystore="${keystore.location}" storepass="${keystore.password}" validity="3650" dname="CN=shazam, OU=MeenDarly, O=net, C=US"/>
    <signjar jar="dist/OperatorInterface.jar" signedjar="dist/OperatorInterface.jar" alias="${key-alias}" keystore="${keystore.location}" storepass="${keystore.password}"/>
    </target>


    Thanks ,
    Meena

    Posted by: mdarly on May 24, 2005 at 03:26 PM

  • meena,
    you need to call genkey only once, not every time you run your build.

    Posted by: kirillcool on May 24, 2005 at 08:53 PM

  • Kirill thanks ,but by making sure that the key is generated once by deleting the uikeystore I got rid of the problem alias problem ,but now I'm getting an errror at sign jar as ;


    Created dir: C:\CVS\OperatorInterface\project\OperatorInterface\dist
    Building jar: C:\CVS\OperatorInterface\project\OperatorInterface\dist\OperatorInterface.jar
    Generating Key for ${key-alias}
    Signing JAR: C:\CVS\OperatorInterface\project\OperatorInterface\dist\OperatorInterface.jar
    java.lang.InternalError: jzentry == 0,
    jzfile = 10222936,
    total = 1328,
    name = C:\CVS\OperatorInterface\project\OperatorInterface\dist\OperatorInterface.jar,
    i = 1,
    message = couldn't read LOC header
    at java.util.zip.ZipFile$2.nextElement(ZipFile.java:323)
    at sun.security.tools.JarSigner.getManifestFile(JarSigner.java:986)
    at sun.security.tools.JarSigner.signJar(JarSigner.java:524)
    at sun.security.tools.JarSigner.run(JarSigner.java:139)
    at sun.security.tools.JarSigner.main(JarSigner.java:53)
    Exception in thread "main"

    greatly appreciate your help.

    thanks

    Posted by: mdarly on May 25, 2005 at 08:16 AM

  • meena,
    From the exception stack trace it appears that your jar file lacks a manifest. Check if you have a meta-inf directory in your jar and a manifest.mf file in it. A simple manifest looks like this:

    Manifest-Version: 1.0

    Here is a simple ant task for adding an existing manifest.mf file in your jar:

    <target name="jar-bin" description="create runtime jar">
    <jar destfile="***.jar"
    manifest="*****/META-INF/MANIFEST.MF">
    <fileset dir="*****" includes="****/*.class"/>
    <fileset dir="*****" excludes="****"/>
    </jar>
    </target>

    Posted by: kirillcool on May 25, 2005 at 08:44 AM

  • Kirill thanks ,I got around 'LOC header error' its was because the signed jar file name is also the same as the jar file name ,after renaming different names it worked. However thanks for the manifest code piece.The following the correct worked code ,where the dist folder gets deleted each time of the build so the key does'nt get replicated and errror.

    <target name="-post-jar">
    <property name="key.alias" value="https://shazam.net/"/>
    <property name="keystore.location" value="dist/uiKeyStore"/>
    <property name="keystore.password" value="***********"/>
    <genkey alias="${key-alias}" keystore="${keystore.location}" storepass="${keystore.password}" validity="3650" dname="CN=shazam, OU=MeenDarly, O=net, C=US"/>
    <signjar jar="dist/OperatorInterface.jar" signedjar="dist/OperatorInterface_signed.jar" alias="${key-alias}" keystore="${keystore.location}" storepass="${keystore.password}"/>
    </target>

    Posted by: mdarly on May 25, 2005 at 08:49 AM

  • meena,
    No problem. Next time you put a comment with XML content, you'll have to wrap it in pre tags and replace all < by &lt; and all > by &gt; in order to show it correctly. Another thing - don't put out your passwords when you copy-paste (i replaced it by ****** in your last comment).

    Posted by: kirillcool on May 25, 2005 at 10:29 AM

  • Thanks for hiding the password ,I thought I hid like my first message and I was wondering why the text content did'nt show up right .

    Posted by: mdarly on May 25, 2005 at 11:15 AM

  • Kirill,
    do you have thoughts on unsigning the jar ?thanks

    Posted by: mdarly on May 25, 2005 at 12:12 PM

  • Meena,
    As far as i know, there is only one way to unsign jar file:

    Create temp directory and copy the signed jar there
    Open it with jar xvf
    Delete the original signed jar
    Create new unsigned jar with jar cvf

    This would be, of course, easy to do with ant. However,
    note that unsigning the jar will remove (most probably) valid
    and possibly better (authenticated by trusted party) signing.
    As you can mix jars with different signatures in the same Web Start
    application, and run signed jars in untrusted environment
    (no permissions), why would you want to do this?

    Posted by: kirillcool on May 25, 2005 at 12:57 PM

  • Thanks ,I did'nt wanted to unjar and resign , I thought if there was any key word that I could tag like jarsigner.
    Well ,anyway I got around this , as I had to reference a third party jar already signed . I implemented as mentioned in the link.
    http://java.sun.com/products/javawebstart/faq.html#72.


    Posted by: mdarly on May 25, 2005 at 01:54 PM

  • Kirill, nice tutorial - thanks a lot.

    Posted by: aidano on June 02, 2005 at 05:35 PM

  • Otlichno, molodets Kirill. Can you also point how to get a free CA signed key. I saw some article about getting free mail certificate, but can't recall.

    Posted by: mockba on June 09, 2005 at 05:52 PM

  • Kohsuke suggested your blog. Using xjc to gen some of the OAGIS schemas we get hundreds of classes. In order to write beans to get or set values in the content tree from a database using these classes it's a lot of work to go thru all of the source code to find the getter and setter methods and the parents of elements especially when that parent is extended making the getter and setter of the parent a totally different class.

    Wouldn't it be great if there were a tool to run on a content tree to show the hierarchy. Something for each element like:
    (property type) Class.method

    For example to retrieve an element called "Product Name" the listing or GUI might show:
    (String) Item.getProductName,
    (List) Items.getItem,
    (Items) PurchaseOrder.getItems

    Posted by: icoman on April 21, 2006 at 08:50 AM

  • icoman,
    I'm not sure how this question is related to the specific blog entry. There's a forum for JAXB related questions right here. In addition, there's an excellent mailing list at jaxb.dev.java.net (users).

    Posted by: kirillcool on April 21, 2006 at 11:14 AM

  • Great blog, Kirill :) i wish i had found it before i wrote a blog on JNLP! I put an "update" at the end of my article, but it's only on the link from my blog and not on the link from the frontpage, which is a pity - the permalink seems to change when you edit the entry, and the frontpage link stays out of date, and new comments (as i posted) aren't reflected on it either :(

    Posted by: evanx on July 21, 2006 at 03:53 AM

  • Hi Kirill, thank you very much for your precious help. Now our application is on line in web start style. I've a little question about a problem that start when we use a Soap call.

    Access denied (java.util.PropertyPermission com.sun.xml.rpc.client.ContentNegotionation read)

    Where is the mistake?

    Thanks
    Michelangelo T.

    Posted by: eurocv on November 23, 2006 at 04:19 PM

  • Спасибо Кирил, отлично написанно!!!

    Posted by: vavk on December 09, 2007 at 12:36 PM

  • Hi, I have about 15 jars in my project which are distributed in about 5 folders. I am signing them all with the same certificate. After signing I verified that all have only 1 cert (only 1 DSA file per jar). even then I am getting this error: -
    JAR resources in JNLP file are not signed by same certificate


    Please help me.

    Posted by: pranavverma on December 13, 2007 at 01:07 AM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds