Java security architecture also provides a facility to digitally sign and verify a Java ARchive (JAR) using the tool jarsigner. This is a very useful tool that enables parties to exchange data as a single package (created using jar utility and consisting of class files, images, sounds and other digital data ) ensuring its integrity and authentication. It is used to
- sign JAR files, and
- verify the signatures and integrity of signed JAR files.
1. Signing JAR
To sign a JAR-bundled file, jarsigner needs a private key, which is used to generate digital signatures. We know that a keystore contains private key(s) each of which can be referred to by an alias. During signing, the jarsigner tool takes this alias to get the private key. If you already have a keystore use it or create one using the following command:
keytool -genkey -alias tom -keyalg RSA -keystore test.jks -storepass 123456 -keypass 123456 -dname ”CN=Tom, OU=IT, O=JU, L=Kolkata, ST=WB, C=IN”
We also assume that there exists a JAR test.jar to be signed. The following is a sample command to create a JAR test.jar:
jar cvf test.jar *.class
A sample output is shown below:
adding: First.class(in = 380) (out= 269)(deflated 29%)
adding: GetProps.class(in = 1624) (out= 857)(deflated 47%)
We can then use the following command to sign it:
jarsigner -keystore test.jks -storepass 123456 test.jar tom
The jarsigner uses private key from the keystore test.jks corresponding to the alias tom to generate digital signatures for the JAR file test.jar. The signed JAR file also contains a copy of the certificate containing the public key corresponding to the private key used to sign the file. This certificate is used by jarsigner to verify the digital signature of the signed JAR file.
Since no output file is specified, signed JAR file is exactly the same as the input JAR file. The – signedjar option may be used to specify the name of the output signed jar file as follows:
jarsigner -keystore test.jks -storepass 123456 -signedjar stest.jar test.jar tom
When a JAR file is signed, a digest is calculated for each file in the JAR. The digest of a file is a hash or encoded representation of the content of the file. The digest changes if and only if the content of the file changes. These digest values are placed in the archive’s manifest file (meta-inf/ manifest.mf). Here is a sample manifest file:
Created-By: 1.7.0_07 (Oracle Corporation)
The first two lines say that the manifest conforms to version 1.0 and was created by the 1.7.0_06 version of the JDK. Each pair of rest lines lists
- the file name,
- the name of the digest algorithm used (SHA), and
- an SHA digest value.
In the signed JAR file, two additional files are placed in the META-INF directory:
- a signature file, with extension .SF and
- a signature block file, with extension .DSA, .RSA, or .EC
This can be verified using the following command:
jar tvf test.jar
This results in a sample output as follows:
This indicates that two additional files tom.sf and tom.rsa have been created and placed under meta-inf directory. By default the base file name of these files is decided from the alias name. However, it may be specified using -sigFile option as follows:
This generates files named mysig.sf and mysig.rsa.
1.1. Signature (.SF) File
The signature file looks similar to the manifest file. However, unlike manifest file where digest values are computed from the file content, the digest values in the signature file are computed from the corresponding entries in the manifest file. Here is a sample signature file:
SHA-256-Digest-Manifest-Main-Attributes: 2lFX/xw8/ju0AeReBcfmD78H9Mij7 ZBd84CDIOniLrQ=
SHA-256-Digest-Manifest: hxfdz/3QnAKpMumlCOTR06uNLJ+iAqoWvozBZ83NTvQ= Created-By: 1.7.0_07 (Oracle Corporation)
In addition, the signature file contains a digest for the entire manifest (sHA-256-Digest-Manifest header in the previous example) file.
1.2. Signature Block File
This is the file which actually contains the signature of .SF file which basically summarizes the contents of all source files of the JAR. It has the extension .DSA, .RSA, or .EC depending on the signature algorithm used. The certificate or certificate chain from the keystore containing the public key to be used for authentication (corresponding to the private key used for signing) is also placed in the signature block file. This file is not human-readable.
2. Verifying JAR
Verifying a JAR file means checking if the signature is valid and if all files remain unchanged as they were when the signature was generated. This involves the following steps:
- Ensure that the .SF file has not been tempered with. This is done by checking if the signature stored in the signature block (.RSA) file was indeed generated using the private key corresponding to the public key whose certificate (or certificate chain) is also stored in the signature block file.
- Ensure that the manifest file has not been tempered with; ensure that no file has not been added/removed from the JAR. This is done by checking the digest listed in each entry in the .SF file with each corresponding section in the manifest.
- Ensure that all source files have not been tempered with. This is done by re-calculating the digest for each file and comparing it with the corresponding digest recorded in the manifest file.
The Java Runtime Environment is responsible for executing those steps. We simply use -verify option do all these steps as follows:
jarsigner -verify test.jar
If everything goes fine, the following message is generated:
If verification fails, the appropriate message is shown. For example, if the manifest file is changed, the following message is displayed:
jarsigner: java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
To get more information, -verbose may be used as follows:
jarsigner –verify –verbose test.jar
This generates a sample output as follows:
Source: Uttam Kumar Roy (2015), Advanced Java programming, Oxford University Press.