/ JVM, SECURITY, JAR, SPRING BOOT, POLICY

Signing and verifying a standalone JAR

Last week, I wrote about the JVM policy file that explicitly lists allowed sensitive API calls when running the JVM in sandboxed mode. This week, I’d like to improve the security by signing the JAR.

This is the 3rd post in the JVM Security focus series.Other posts include:

  1. The Java Security Manager: why and how?
  2. Proposal for a Java policy files crafting process
  3. Signing and verifying a standalone JAR (this post)
  4. Crafting Java policy files, a practical guide
  5. Beware the Attach API

The nominal way

This way doesn’t work. Readers more interested in the solution than the process should skip it.

Create a keystore

The initial step is to create a keystore if none is already available. There are plenty of online tutorials showing how to do that.

keytool -genkey -keyalg RSA -alias selfsigned -keystore /path/to/keystore.jks -storepass password -validity 360

Fill in information accordingly.

Sign the application JAR

Signing the application JAR must be part of the build process. With Maven, the JAR signer plugin is dedicated to that. Its usage is quite straightforward:

<plugin>
  <artifactId>maven-jarsigner-plugin</artifactId>
  <version>1.4</version>
  <executions>
  <execution>
    <id>sign</id>
    <goals>
    <goal>sign</goal>
    </goals>
  </execution>
  </executions>
  <configuration>
  <keystore>/path/to/keystore.jks</keystore>
  <alias>selfsigned</alias>
  <storepass>${store.password}</storepass>
  <keypass>${key.password}</keypass>
  </configuration>
</plugin>

To create the JAR, invoke the usual command-line and pass both passwords as system properties:

mvn package -Dstore.password=password -Dkey.password=password

Alternatively, Maven’s encryption capabilities can be used to store passwords in a dedicated settings-security.xml to further improve security.

Configure the policy file

Once the JAR is signed, the policy file can be updated to make use of it. This requires only the following configuration steps:

  1. Point to the keystore
  2. Configure the allowed alias
keystore "keystore.jks";

grant signedBy "selfsigned" codeBase "file:target/spring-petclinic-1.4.2.jar" {
  ...
}

Notice the signedBy keyword followed by the alias name - the same one as in the keystore above.

Launching the JAR with the policy file

The same launch command can be used without any change:

java -Djava.security.manager -Djava.security.policy=jvm.policy -jar target/spring-petclinic-1.4.2.jar

Unfortunately, it doesn’t work though this particular permission had already been configured!

Caused by: java.security.AccessControlException: access denied ("java.lang.reflect.ReflectPermission" "suppressAccessChecks")
  at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
  at java.security.AccessController.checkPermission(AccessController.java:884)
  at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
  at java.lang.reflect.AccessibleObject.setAccessible(AccessibleObject.java:128)
  at org.springframework.util.ReflectionUtils.makeAccessible(ReflectionUtils.java:475)
  at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:141)
  at org.springframework.boot.SpringApplication.createSpringFactoriesInstances(SpringApplication.java:420)

The strangest part is that permissions requested before this one work all right. The reason is to be found in the particular structure of the JAR created by the Spring Boot plugin: JAR dependencies are packaged untouched in a BOOT-INF/lib folder in the executable JAR. Then Spring Boot code uses custom class-loading magic to load required classes from there.

JAR signing works by creating a specific hash for each class, and by writing them into the JAR manifest file. During the verification phase, the hash of a class is computed and compared to the hash of the manifest. Hence, permissions related to classes located in the BOOT-INF/classes folder work as expected.

However, the org.springframework.boot.SpringApplication class mentioned in the stack trace above is part of the spring-boot.jar located under BOOT-INF/lib: verification fails as there’s no hash available for the class in the manifest.

Thus, usage of the Spring Boot plugin for JAR creation/launch is not compatible with JAR signing.

The workaround

Aside from Spring Boot, there’s a legacy way to create standalone JARs: the Maven Shade plugin. This will extract every class of every dependency in the final JAR. This is possible with Spring Boot apps, but it requires some slight changes to the POM:

  1. In the POM, remove the Spring Boot Maven plugin
  2. Configure the main class in the Maven JAR plugin:
    <plugin>
      <artifactId>maven-jar-plugin</artifactId>
      <version>3.0.2</version>
      <configuration>
        <archive>
          <manifest>
            <mainClass>org.springframework.samples.petclinic.PetClinicApplication</mainClass>
          </manifest>
        </archive>
      </configuration>
    </plugin>
  3. Finally, add the Maven Shade plugin to work its magic:
    <plugin>
      <artifactId>maven-shade-plugin</artifactId>
      <version>2.4.3</version>
      <configuration>
        <minimizeJar>true</minimizeJar>
      </configuration>
      <executions>
        <execution>
          <phase>package</phase>
          <goals>
            <goal>shade</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
The command-line to launch the JAR doesn’t change but permissions depend on the executed code, coupled to the JAR structure. Hence, the policy file should be slightly modified.

Lessons learned

While it requires to be a little creative, it’s entirely possible to sign Spring Boot JARs by using the same techniques as for any other JARs.

To go further:

Nicolas Fränkel

Nicolas Fränkel

Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with focused interests like Rich Internet Applications, Testing, CI/CD and DevOps. Also double as a trainer and triples as a book author.

Read More
Signing and verifying a standalone JAR
Share this