spring-boot: Signed Jar verification fails from a nested Jar under Oracle Java 17

When running a Spring Boot app as a fat Jar under Java 17, using the Bouncy Castle provider results in an exception SecurityException: JCE cannot authenticate the provider BC with cause IllegalStateException: zip file closed. Any use of the provider seems to trigger the exception, e.g.

Cipher.getInstance("AES/CBC/PKCS5Padding","BC");

I have created a sample Spring Boot app that reproduces the problem.

I stepped through the code and I believe the problem is caused by the Spring Boot JarURLConnection returning an already closed Jar file from getJarFile(). I think this relates to issues #17127 and #25538, but I could be wrong.

This same issue does not occur under Java 11, so I assume something has changed in JarVerifier.verifySingleJar between Java 11 and 17.

The exception stack trace is:

Exception in thread "main" java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Caused by: java.lang.SecurityException: JCE cannot authenticate the provider BC
        at java.base/javax.crypto.Cipher.getInstance(Cipher.java:722)
        at java.base/javax.crypto.Cipher.getInstance(Cipher.java:642)
        at com.example.bctest.BctestApplication.main(BctestApplication.java:14)
        ... 8 more
Caused by: java.lang.IllegalStateException: zip file closed
        at java.base/java.util.zip.ZipFile.ensureOpen(ZipFile.java:831)
        at java.base/java.util.zip.ZipFile.getManifestName(ZipFile.java:1057)
        at java.base/java.util.zip.ZipFile$1.getManifestName(ZipFile.java:1100)
        at java.base/javax.crypto.JarVerifier.verifySingleJar(JarVerifier.java:461)
        at java.base/javax.crypto.JarVerifier.verifyJars(JarVerifier.java:317)
        at java.base/javax.crypto.JarVerifier.verify(JarVerifier.java:260)
        at java.base/javax.crypto.ProviderVerifier.verify(ProviderVerifier.java:130)
        at java.base/javax.crypto.JceSecurity.verifyProvider(JceSecurity.java:190)
        at java.base/javax.crypto.JceSecurity.getVerificationResult(JceSecurity.java:218)
        at java.base/javax.crypto.Cipher.getInstance(Cipher.java:718)
        ... 10 more

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 30
  • Comments: 46 (16 by maintainers)

Commits related to this issue

Most upvoted comments

@philwebb Has this BUG been forgotten?It’s been a long time.

have any temporary solutions?

The bug report was accepted (The Oracle team did a fast and good job) and should be visible as JDK-8313742.

Still no solution?

For anyone watching this issue I just pushed a fix for #29356 which should allow <requiresUnpack> to work.

I could work around this issue by running my Spring Boot app using an exploded directory format. This is the recommended approach by Spring Boot for container images → Container Images

The bug report was accepted (The Oracle team did a fast and good job) and should be visible as JDK-8313742.

Oracle has fixed this issue on 11/06/2023 in version 17.0.11, please upgrade your JDK version.

Here is an alternative solution that worked for me.

The problem is with the combination of a FatJar and Docker. So a solution is, to not use a FarJar but the recommended approach of layering: https://docs.spring.io/spring-boot/docs/current/reference/html/container-images.html

In case you use gradle, this is how to enable it:

tasks.named<BootJar>("bootJar") {
    layered {
        enabled.set(true)
    }
}

building the project gw clean bootJar

next up is building a layered Dockerfile:

FROM eclipse-temurin:17-jre as builder
WORKDIR application
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract

FROM eclipse-temurin:17-jre
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

I hope this helps somebody!

Please take the time to read the history before commenting. See #28837 (comment)

I looked at it, but I couldn’t find how to solve the problem All the methods mentioned above have been tried

I mean that “I have the same issue” is not very helpful and send a notification to the 3K watchers of this repository. If you want to indicate you’re affected by this issue without any extra information, please rather use the reaction (👍) in the original description above.

@zeroleak @evgenyigumnov sorry about that but please use the reaction on the original description rather than this.

Thanks for the sample, @thelateperseus. I’ve reproduced the problem on macOS using Oracle JDK 17.0.1:

$ java -version
java version "17.0.1" 2021-10-19 LTS
Java(TM) SE Runtime Environment (build 17.0.1+12-LTS-39)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.1+12-LTS-39, mixed mode, sharing)
$ java -jar build/libs/spring-boot-bouncy-castle-0.0.1-SNAPSHOT.jar 
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
	at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Caused by: java.lang.SecurityException: JCE cannot authenticate the provider BC
	at java.base/javax.crypto.Cipher.getInstance(Cipher.java:722)
	at java.base/javax.crypto.Cipher.getInstance(Cipher.java:642)
	at com.example.bctest.BctestApplication.main(BctestApplication.java:14)
	... 8 more
Caused by: java.lang.IllegalStateException: zip file closed
	at java.base/java.util.zip.ZipFile.ensureOpen(ZipFile.java:831)
	at java.base/java.util.zip.ZipFile.getManifestName(ZipFile.java:1057)
	at java.base/java.util.zip.ZipFile$1.getManifestName(ZipFile.java:1100)
	at java.base/javax.crypto.JarVerifier.verifySingleJar(JarVerifier.java:461)
	at java.base/javax.crypto.JarVerifier.verifyJars(JarVerifier.java:317)
	at java.base/javax.crypto.JarVerifier.verify(JarVerifier.java:260)
	at java.base/javax.crypto.ProviderVerifier.verify(ProviderVerifier.java:130)
	at java.base/javax.crypto.JceSecurity.verifyProvider(JceSecurity.java:190)
	at java.base/javax.crypto.JceSecurity.getVerificationResult(JceSecurity.java:218)
	at java.base/javax.crypto.Cipher.getInstance(Cipher.java:718)
	... 10 more