Creating a Java Deployment Ruleset is actually a trivial process – it’s signing it that can be a pain in the backside. In this post we are signing our Java Deployment Rule Set with an Active Directory code signing certificate. I am using jdk1.8.0_181 to create my Java Deployment Rule Set. Do NOT use a version of the JDK that has a major version that is greater than the JRE in your environment (for example if you are running Java 7 in your environment, use the Java 7 JDK and not the Java 8 JDK!).
Create a file called ruleset.xml. It MUST be called ruleset.xml since this is what Java specifically searches for inside the compiles JAR file. The beauty of the ruleset.xml is that you can use wildcards for the URL and for the port. With site.exceptions you can’t do this. Here is a basic example:
<id location="http://*.java.com" />
<action permission="run" />
<id location="https://*.java.com" />
<action permission="run" />
<message>This applet has been blocked by Java. Please contact your administrator for assistance.</message>
You obviously need to add your own URLs to this, and give them permission to “run”. Interestingly if you have multiple versions of JRE installed, in the <action> tag you can provide a specific JRE version for that URL to use. For example:
<action permission="run" version="1.6.0_31" force="true" />
Compile ruleset.xml into a JAR file. This MUST be called DeploymentRuleset.jar because again, this is what Java specifically looks for. When we compile the JAR file, we should NOT specify a full path name to the ruleset.xml because Java cannot locate it within the JAR file. So you should first ‘CD’ to c:\Alkane and then run the following command line:
"C:\Program Files (x86)\Java\jdk1.8.0_181\bin\jar.exe" -cvf "C:\Alkane\DeploymentRuleSet.jar" "ruleset.xml"
Obtain a Code Signing Certificate
Follow this article on obtaining a code signing certificate from Active Directory Certificate Services. Essentially you enable the code signing template in Certificate Services Manager, you then grant permissions to allow a user (yourself!) to request a Code Signing certificate, and you then you request a new certificate.
Export a Code Signing Certificate in PFX format
So now we can assume you’ve followed the steps above and you have a code signing certificate. My code signing certificate forms a chain of:
- Root certificate
- Intermediate certificate
- Code signing certificate
So let’s export them all in PFX format. A PFX will include all certificates in the chain, as well as the private key. And we need all this to create our Java keystore. So…
From the Certificates MMC snap-in, navigate to your Personal certificate store.
Right-click the certificate > All Tasks > Export.
Click ‘Yes, export the private key’
You are only given the option to export it as a PFX certificate. This is fine, but make sure you select ‘Include all certificates in the certification path if possible’ and also ‘Export all extended properties’. Click next.
Specify a password for the private key – lets use alkanecertpass for this example. Click next.
Provide a path to save your certificate in – let’s save it as C:\Alkane\Alkane.pfx. Click next. Click finish.
A .pfx file uses the same format as a .p12 or PKCS12 file. They are used to bundle a private key with its X.509 certificate(s) and to bundle all the members of a chain of trust.
Get the Alias Name of the PFX
Run the following command to get the alias name for your PFX. We need this later on:
"C:\Program Files (x86)\Java\jdk1.8.0_181\bin\keytool.exe" -list -storetype PKCS12 -keystore "C:\Alkane\Alkane.pfx" -storepass "alkanecertpass" -v
My PFX alias name is: alkanealias
Create a JKS Java Keytore
Now we can create our Java keystore from our PFX file. There are a few formats we can create the keystore in (JKS, PKCS12, JCEKS for example) but we will create it in JKS format. Be mindful that:
- If you create your keystore in PKCS12 format, -deststorepass and -destkeypass should be the same
- Change -deststoretype to PKCS12 if you require a keystore in PKCS12 format
- Be careful if you change the -destalias from the -srcalias since when I verified my signed JAR file i got the following message:
This jar contains signed entries which are not signed by the specified alias(es).
I keep the Java keystore alias name the same like so:
"C:\Program Files (x86)\Java\jdk1.8.0_181\bin\keytool.exe" -importkeystore -srckeystore "C:\Alkane\Alkane.pfx" -srcstoretype pkcs12 -srcstorepass "alkanecertpass" -srcalias "alkanealias" -destkeystore "C:\Alkane\trusted.certs" -deststoretype JKS -deststorepass "alkanestorepass" -destkeypass "alkanestorepass" -destalias "alkanealias"
This will create: C:\Alkane\trusted.certs
Add Trusted Root Certificate
By default when we sign the JAR file it looks in the cacerts file (which is a form of keystore called a ‘trust store’) of the signing JDK (in my example, C:\Program Files (x86)\Java\jdk1.8.0_181\jre\lib\security\cacerts) to see if the root certificate is trusted. Because it isn’t in there it deems the root certificate as being untrusted and you will see errors when you verify such as:
- The signer’s certificate chain is not validated.
- CertPath not validated: Path does not chain with any of the trust anchors
So we add the root certificate as a trusted certificate in our own keystore like so:
"C:\Program Files (x86)\Java\jdk1.8.0_181\bin\keytool.exe" -importcert -file "C:\Alkane\Root.cer" -alias "AlkaneRoot" -keystore "c:\Alkane\trusted.certs" -storepass "alkanestorepass" -noprompt
Sign DeploymentRuleset.jar With Java Keystore
Now we can sign the DeploymentRuleset.jar file with our Java keystore:
"C:\Program Files (x86)\Java\jdk1.8.0_181\bin\jarsigner.exe" -verbose -keystore "C:\Alkane\trusted.certs" "C:\Alkane\DeploymentRuleSet.jar" "alkanealias" -storepass "alkanestorepass" -tsa http://sha256timestamp.ws.symantec.com/sha256/timestamp -J-Dhttp.proxyHost=alkanehost -J-Dhttp.proxyPort=alkaneport
Because I am signing behind a proxy, I needed to specify a proxy host and port so I could timestamp my signature. Timestamping is an important part of the process and ensures the signed JAR will remain valid indefinately.
Verify The Signature Of DeploymentRuleset.jar
Now we need to verify the signature of the JAR file against out keystore. Change the storetype parameter to PKCS12 for PKCS12 store types:
"C:\Program Files (x86)\Java\jdk1.8.0_181\bin\jarsigner.exe" -verify -verbose -certs -keystore "C:\Alkane\trusted.certs" -storetype JKS -storepass "alkanestorepass" "C:\Alkane\DeploymentRuleSet.jar" "alkanealias"
Now that everything is signed and verified successfully, we need to test it.
At the moment our Java environment is fairly locked down, and we have deployment.config, deployment.properties and exception.sites located in C:\Windows\SUN\Java\Deployment.
I needed to add our keystore, so I added an entry to deployment.properties like so:
and then I lumped our new trusted.certs file into the same folder.
To test the DeploymentRuleSet.jar file it’s as easy as placing it into this same location (C:\WINDOWS\Sun\Java\Deployment). Then launch the Java control panel applet and head over to the security tab (this may differ for later versions of Java).
There should be a blue link towards the bottom called ‘View the active Deployment Ruleset’. Click it and you should see the contents of your ruleset.xml file and a verification that it is valid and timestamped. Click ‘View Certificate Details’ to verify the certificate.