Several Java updates back, Oracle introduced a feature to Java called Deployment Rulesets, which allowed enterprise deployment managers to whitelist specific sites to be able to run Java applets without providing warnings or errors to the end users.
There’s lots of good documentation about the general process, so I won’t cover it here. Check these out if this is new to you:
I got a request from a user to add a certain site to the Deployment Ruleset, so I did the usual thing:
<rule> <id location="*.domain.com" /> <action permission="run" /> </rule>
Except it didn’t work.
This site, rather than running the Java applet via the web, instead downloads a JNLP file. This JNLP file is essentially a bookmark that then downloads other .jar files into the Oracle cache, and then runs them locally, with the same Deployment rules.
The Deployment rules for JNLP are a bit harder and more stringent than normal Java web apps. The simple URL isn’t sufficient to make it work.
After scouring around for some details on this, I did find a helpful post in Oracle’s community detailing how to use the certificate hash to approve all jar files from that domain instead. That way, as long as the same cert was used (which is generally the case), users would have permission to launch jar files that were downloaded and signed with that cert.
So the next obvious question is: how do we find the cert? Luckily, Oracle documents that too:
Get the Certificate Hash
Problem is, I didn’t know what jar file it was talking about. I only had a .jnlp file to work with.
Thanks to a hat tip from Michael Lynn on this, otherwise I’d have been flabbergasted. When the .jnlp file is loaded by Java Web Start, it downloads all the jar files it needs into the Oracle cache.
Thanks to Oracle’s documentation, that’s located here:
Unfortunately, the cache isn’t very helpful. Inside the cache was a directory named
6.0, and inside there was a bunch of directories numbered 1-50. Inside each of those directories were pairs of files, named with random numbers, one with no extension, one with an .idx extension.
The hat tip from Michael Lynn is that those files without extensions actually are the .jar files, just unlabelled. If you’re lucky, you may be able to sort them by modification or creation time, to see which ones you actually want to work with. If you’re unlucky, there’s a way to figure out more precisely what file to look for:
- Open this file in a text editor:
- Add/change the following settings:
deployment.trace.level=all deployment.javapi.lifecycle.exception=true deployment.trace=true deployment.log=true
- Run the .jnlp file, which will proceed to download the .jar files it needs (or validate them inside the cache folder).
- When you encounter the Deployment Rule Set violation exception, look in the logs folder:
- The last modified log will contain a ton of data, but somewhere in there will be the security message indicating a violation. It will look something like this (despite being a .log file, it’s actually XML):
<record> <date>2016-01-14T20:42:44</date> <millis>1452832964163</millis> <sequence>1036</sequence> <logger>com.sun.deploy</logger> <level>FINE</level> <class>com.sun.deploy.trace.LoggerTraceListener</class> <method>print</method> <thread>11</thread> <message>security: JUT Record: javaws application denied [Java applets for this domain have been blocked. Contact Help Desk for questions.] http://domain.com/JavaClient/: app_model=*a whole lot of garbage* </message> </record>
This message is the “final” failure message indicating that the URL (in this example,
"http://domain.com/JavaClient/:") failed, and produced the message specified by your Ruleset.xml default response (in this example, “Java applets for this domain have been blocked…”).
From here, you need to scroll farther back in the records to see the exact file that triggered this reaction:
<record> <date>2016-01-14T20:42:43</date> <millis>1452832963045</millis> <sequence>1029</sequence> <logger>com.sun.deploy</logger> <level>FINE</level> <class>com.sun.deploy.trace.LoggerTraceListener</class> <method>print</method> <thread>11</thread> <message>security: Validating cached jar url=http://domain.com/JavaClient/pcclient.jar ffile=/Users/nmcspadden/Library/Application Support/Oracle/Java/Deployment/cache/6.0/58/586c64fa-40bd2f37 com.sun.deploy.cache.CachedJarFile@660395a5 </message> </record>
Finally, we got the path of the exact file that, in our example above, is actually “pcclient.jar”, downloaded into the cache.
Once you’ve identified one of the cached jar files to work with, you can actually extract the certificate hash:
keytool -printcert -jarfile filename | more
This will get you output that looks like this:
keytool -printcert -jarfile ~/Library/Application\ Support/Oracle/Java/Deployment/cache/6.0/2/1b2c3982-2e369813 Signer #1: Signature: Owner: CN=..., O=..., L=..., ST=..., C=... Issuer: CN=CA, OU=ou, O=Symantec Corporation, C=US Serial number: <serial> Valid from: Tue Feb 10 16:00:00 PST 2015 until: Wed Apr 11 16:59:59 PDT 2018 Certificate fingerprints: MD5: <md5hash> SHA1: <sha1hash> SHA256: 89:14:B8:4A:F8:B3:2A:0D:3B:A1:49:28:D9:B1:6F:D6:CE:E4:2A:42:62:EB:C4:71:A1:E8:22:AE:84:8C:38:F1 Signature algorithm name: SHA256withRSA Version: 3
That SHA256 hash is what you’re looking for, just without colons.
You can now add that hash directly to your Java ruleset with the
<rule> <id> <certificate hash="8914B84AF8B32A0D3BA14928D9B16FD6CEE42A4262EBC471A1E822AE848C38F1" /> </id> <action permission="run" /> </rule>
Test your JNLP and see if that works!