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 are 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 were requested before this one worked 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: