Here’s a quick tutorial about how you can put your Clojure + Seesaw applications on the Web to be started using Java Web Start. I will be using Leiningen build tool.
Sample Clojure + Seesaw application using Leiningen
Let’s make a sample app first.
$ lein new com.icyrock.clojure.csjws Created new project in: /home/icyrock.com/clojure/com.icyrock.clojure.csjws Look over project.clj and start coding in com/icyrock/clojure/csjws/core.clj
Edit your project.clj to this:
(defproject com.icyrock.clojure.csjws "1.0.0-SNAPSHOT" :description "com.icyrock.clojure.csjws" :dependencies [[org.clojure/clojure "1.3.0"] [seesaw "1.4.0"]] :main com.icyrock.clojure.csjws.core)
lein deps to fetch the dependencies.
Edit the only file in src subtree:
to the following:
(ns com.icyrock.clojure.csjws.core (:gen-class) (:use [seesaw core mig])) (defn make-button [row col] (button :text (format "(%d, %d)" row col) :listen [:action (fn [e] (alert (format "Hi, you clicked on (%d, %d)!" row col)))])) (defn make-content  (mig-panel :constraints ["fill"] :items (let [rows 4 cols 4] (for [row (range rows) col (range cols)] [(make-button row col) (if (and (< row (dec rows)) (= col (dec cols))) "grow, wrap" "grow")])))) (defn -main [& args] (native!) (-> (frame :title "com.icyrock.clojure.csjws.core" :content (make-content) :width 400 :height 400 :on-close :exit) show!))
Run it with
lein run – this will give you a 4×4 button grid such as this:
Seesaw is using reflection, for which you need security permissions to run. To be able to grant these, jar needs to be signed. Here are the steps needed for a self-signed jar:
- Generate public / primary key pair
- Sign jar given that certificate
The first step is needed only if you don’t have the certificate (or want to use a new one). All you need is contained in the JDK itself.
Generate public / primary key pair
$ keytool -genkeypair -alias icyrock.com \ -keystore icyrock.com.jks -validity 365 \ -storepass password -keypass password \ -dname "CN=icyrock.com,O=icyrock.om,C=US"
This will give you a file called icyrock.com.jks, protected by password “password”, which contains a key pair with alias “icyrock.com” valid for 365 days and protected by password “password”. You can list this:
$ keytool -list -keystore icyrock.com.jks -storepass password -keypass password -v Keystore type: JKS Keystore provider: SUN Your keystore contains 1 entry Alias name: icyrock.com Creation date: Apr 11, 2012 Entry type: PrivateKeyEntry Certificate chain length: 1 Certificate: Owner: CN=icyrock.com, O=icyrock.om, C=US Issuer: CN=icyrock.com, O=icyrock.om, C=US Serial number: 4f864b76 Valid from: Wed Apr 11 23:26:46 EDT 2012 until: Thu Apr 11 23:26:46 EDT 2013 Certificate fingerprints: MD5: 34:8E:22:BF:D8:1A:C8:6F:40:ED:8B:B5:97:5A:65:75 SHA1: 2E:49:87:1D:14:11:C6:C3:F1:AE:FC:6A:60:82:10:38:8E:B4:67:FA Signature algorithm name: SHA1withDSA Version: 3
Note that the above is actually a X.509 certificate (see keytool docs for more info). This certificate can be used to sign jars with jarsigner, which is all we need.
The file that keytool generated (icyrock.com.jks) is called JKS – Java Key Store – for a very good reason. It stores key information. This line from the above is important:
Entry type: PrivateKeyEntry
That should be sufficient for anyone to consider this file very important from security standpoint. It contains information that allows anyone in its possesion to create signed files. Those files will bear that certificate details, but the content might be vastly different. In real life, this is called identity theft. In the same way you would not give your house key to a random person, you should not give your .jks files to random people.
On the other hand, you can give out your certificate – this can be used by other people to verify your jar. To export the certificate, you do this:
$ keytool -export -keystore icyrock.com.jks -storepass password -keypass password -file icyrock.com.crt -alias icyrock.com
You can now give this cert file, which others can import into their .jks keystore like this:
$ keytool -importcert -file icyrock.com.crt -keystore icyrock.com-cert.jks -storepass password -keypass password -alias icyrock.com-cert
The certificate itself is different than the key, here’s the output:
$ keytool -list -keystore icyrock.com-cert.jks -storepass password -keypass password -v Keystore type: JKS Keystore provider: SUN Your keystore contains 1 entry Alias name: icyrock.com-cert Creation date: Jul 14, 2012 Entry type: trustedCertEntry Owner: CN=icyrock.com, O=icyrock.om, C=US Issuer: CN=icyrock.com, O=icyrock.om, C=US Serial number: 4f864b76 Valid from: Wed Apr 11 23:26:46 EDT 2012 until: Thu Apr 11 23:26:46 EDT 2013 Certificate fingerprints: MD5: 34:8E:22:BF:D8:1A:C8:6F:40:ED:8B:B5:97:5A:65:75 SHA1: 2E:49:87:1D:14:11:C6:C3:F1:AE:FC:6A:60:82:10:38:8E:B4:67:FA SHA256: 2D:63:93:C0:26:8D:8C:5C:16:AD:22:D9:31:42:05:F2:9D:B8:9C:75:BC:68:F1:40:D6:8B:95:7C:61:D2:79:2B Signature algorithm name: SHA1withDSA Version: 3
The important line here is:
Entry type: trustedCertEntry
which says it contains the certificate only. To compare to the house analogy, this would be as if you gave someone your address. They could then come to your house and confirm that’s really your house, but without the key that only you have they could not come in. In a similar way, this certificate can be used to verify .jar files, but not sign them.
Creating uberjar and signing it
uberjar command which creates a jar that contains your code plus all dependencies packed into one jar. Run that and you should get a jar called com.icyrock.clojure.csjws-1.0.0-standalone.jar in the root folder of the project.
Let’s sign that jar with the certificate we created above:
$ jarsigner -keystore icyrock.com.jks \ -storepass password -keypass password \ -signedjar com.icyrock.clojure.csjws-1.0.0-SNAPSHOT-standalone-signed.jar \ com.icyrock.clojure.csjws-1.0.0-SNAPSHOT-standalone.jar icyrock.com
This will generate a new file:
that is now signed. You can verify this file:
$ jarsigner -verify \ com.icyrock.clojure.csjws-1.0.0-SNAPSHOT-standalone-signed.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.
Due to our certifiacte being self-signed, the above warning is printed. Now the certificate file we generated above comes into play:
$ jarsigner -keystore icyrock.com-cert.jks -verify \ com.icyrock.clojure.csjws-1.0.0-SNAPSHOT-standalone-signed.jar jar verified.
In this case, jarsigner used the provided certificate to confirm that all contents of the .jar file actually were signed with the key corresponding to that certificate.
You can start this using regular
java -jar command:
$ java -jar \ com.icyrock.clojure.csjws-1.0.0-SNAPSHOT-standalone-signed.jar
Java Web Start
Now that you have a signed file, you can put it on some Web server to be picked up by javaws. For this, you first need to make a .jnlp file. Say you have a local Web server accessible via http://localhost and you want to put this under http://localhost/clojure/. Here’s a JNLP file that would work for that arrangement:
<?xml version="1.0" encoding="utf-8"?> <jnlp spec="1.0+" codebase="http://localhost/clojure/" href="csjws.jnlp"> <information> <title>com.icyrock.clojure.csjws.core</title> <vendor>icyrock.com</vendor> <icon href="com.icyrock.clojure.csjws.png"/> <offline-allowed/> </information> <security> <all-permissions/> </security> <resources> <j2se version="1.6.0+"/> <jar href="com.icyrock.clojure.csjws-1.0.0-SNAPSHOT-standalone-signed.jar"/> </resources> <application-desc main-class="com.icyrock.clojure.clsjws.core"/> </jnlp>
Now if you put this JNLP and the signed jar file (com.icyrock.clojure.csjws-1.0.0-SNAPSHOT-standalone-signed.jar) so they are accessible via http://localhost/clojure/, you should be able to then do:
$ javaws http://localhost/clojure/com.icyrock.clojure.csjws.jnlp
Of course, you can do that with any browser that has Java plug-in enabled by going to the same location:
This should launch Java Web Start. It will download the file and ask you to acknowledge the certificate (as it is self-signed, not by a trusted CA authority). To confirm this is the right certificate, you can compare the fingerprints presented by javaws and the certificate itself. In general, only if these match and only if you trust the provider of the certificate should you proceed – otherwise, you can potentially allow malicious code to execute without security restrictions, which is not a desirable result.
This may look like this:
When you click on “More information…”, you get this:
When you click on “Certificate details…”, you get this:
Here you can see the SHA1 fingerprint. Take this and compare to the certificate. Usually, for self-signed certificates, this can be given e.g. on the site for users to compare. If these match, you can be pretty sure the code was signed by that certificate and all is good. The best way is to give the certificate (icyrock.com-cert.jks or icyrock.com.crt) to your users, which can then import it and the browser will automatically do the verification for them. You can close these two and click Run on the original dialog – the app will launch.