Signing a Java Jar

Blogging this because it took far too long to figure out and because the documentation is spread across too many pages and isn’t at all clear.

Generate some signing keys.

keytool -genkeypair  
    -alias USERNAME  
    -validity 1000  
    -keystore FILENAME.keystore

Answer some questions. The resulting FILENAME.keystore file must be kept private. This generates a key that’s valid for at least 1000 days. The java docs have more information.

Sign the jar

jarsigner  
    -jar JAR.jar  
    -signedjar JAR-signed.jar  
    -keystore FILENAME.keystore  
    -alias USERNAME 
    -tsa http://timestamp.comodoca.com/rfc3161

This will sign the jar file with the private key in the FILENAME.keystore file. The signature and some other metadata will be written to the jar’s manifest. The java docs have more information, but not enough to actually be useful.

The -tsa parameter adds timestamping information to the signature so that, even after the private key has expired in 1000 days, java will be able to verify the signature against the timestamp. It’s all magic really. Here’s an ant task that can sign a jar file.

	
<target name="sign-plugin" depends="build" description="Sign a loadable plugin JAR file">
	<!-- relies on the properties: plugin-jarfile -->
	<signjar 	jar="${build.dir}/${plugin.jar}" 
		signedJar="${plugin.jar}" 
		keystore="${sign-keystore}" 
		keypass="${sign-keypass}" 
		storepass="${sign-keypass}"
		tsaurl="http://timestamp.comodoca.com/rfc3161" 
		alias="${sign-alias}" />
</target>

Run it at the command line like this:

$ ant sign-jar 
    -Dsign-keystore=USERNAME.keystore 
    -Dsign-keypass=SUPERSECRET 
    -Dsign-alias=USERNAME

It should ask for a password.

Export the public key certificate from the keystore

Now that the jar is signed and timestamped with the private key it’s time to move the corresponding public key into a separate file. First export the public key into a certificate file, then import the certificate into a new keystore.

keytool  -export  
    -keystore USERNAME.keystore  
    -alias USERNAME  
    -file USERNAME.cer

This command writes the certificate file USERNAME.cer. You can distribute the .cer file along with the jar file. Java systems can’t use the .cer file directly – it must be imported into a new keystore to verify the jar.

Now Import the public key certificate into a new public keystore.

keytool  -import 
    -alias USERNAME  
    -file USERNAME.cer  
    -keystore USERNAME-public.keystore

This generates a new keystore file which can be used to verify the jar signature and timestamp. The java docs might have more information. The public keystore file can be shared with anyone, and might be more convenient for people to use.

Use the public key to verify the signed jar.

jarsigner  -verify  -keystore USERNAME-public.keystore  JAR-signed.jar

This should give a success message. Woo. Finally. Distribute the JAR-signed.jar and USERNAME-public.keystore files as necessary.

Timestamping

The comodo timetsamping server seems likea good, reliable, free option. Your mileage may vary or something.