Spring Boot and OpenJDK: ECDSA KeyFactory not available

Hi there!

I have the following setup:

  • Spring Boot Backend (2.1.8.RELEASE) with REST endpoints
  • AdoptOpenJDK11.0.3.7
  • Keycloak 9.0.0/8.0.2 (same issue with both versions)

My REST endpoints are secured with Spring Security and Bearer tokens coming from Keycloak.

When calling any endpoint with a valid token I get the following error: Caused by: java.security.NoSuchAlgorithmException: ECDSA KeyFactory not available

See full stack trace

java.lang.RuntimeException: java.security.NoSuchAlgorithmException: ECDSA KeyFactory not available at org.keycloak.jose.jwk.JWKParser.createECPublicKey(JWKParser.java:110) ~[keycloak-core-9.0.0.jar:9.0.0] at org.keycloak.jose.jwk.JWKParser.toPublicKey(JWKParser.java:74) ~[keycloak-core-9.0.0.jar:9.0.0] at org.keycloak.util.JWKSUtils.getKeysForUse(JWKSUtils.java:41) ~[keycloak-core-9.0.0.jar:9.0.0] at org.keycloak.adapters.rotation.JWKPublicKeyLocator.sendRequest(JWKPublicKeyLocator.java:101) ~[keycloak-adapter-core-9.0.0.jar:9.0.0] at org.keycloak.adapters.rotation.JWKPublicKeyLocator.getPublicKey(JWKPublicKeyLocator.java:63) ~[keycloak-adapter-core-9.0.0.jar:9.0.0] at org.keycloak.adapters.rotation.AdapterTokenVerifier.getPublicKey(AdapterTokenVerifier.java:121) ~[keycloak-adapter-core-9.0.0.jar:9.0.0] at org.keycloak.adapters.rotation.AdapterTokenVerifier.createVerifier(AdapterTokenVerifier.java:111) ~[keycloak-adapter-core-9.0.0.jar:9.0.0] at org.keycloak.adapters.rotation.AdapterTokenVerifier.verifyToken(AdapterTokenVerifier.java:47) ~[keycloak-adapter-core-9.0.0.jar:9.0.0] at org.keycloak.adapters.BearerTokenRequestAuthenticator.authenticateToken(BearerTokenRequestAuthenticator.java:103) ~[keycloak-adapter-core-9.0.0.jar:9.0.0] at org.keycloak.adapters.BearerTokenRequestAuthenticator.authenticate(BearerTokenRequestAuthenticator.java:88) ~[keycloak-adapter-core-9.0.0.jar:9.0.0] at org.keycloak.adapters.RequestAuthenticator.authenticate(RequestAuthenticator.java:67) ~[keycloak-adapter-core-9.0.0.jar:9.0.0] at org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter.attemptAuthentication(KeycloakAuthenticationProcessingFilter.java:154) ~[keycloak-spring-security-adapter-9.0.0.jar:9.0.0] at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.1.6.RELEASE.jar:5.1.6.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.6.RELEASE.jar:5.1.6.RELEASE] at org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter.doFilter(KeycloakPreAuthActionsFilter.java:96) ~[keycloak-spring-security-adapter-9.0.0.jar:9.0.0] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.6.RELEASE.jar:5.1.6.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.6.RELEASE.jar:5.1.6.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.6.RELEASE.jar:5.1.6.RELEASE] at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.1.6.RELEASE.jar:5.1.6.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.6.RELEASE.jar:5.1.6.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE] at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.6.RELEASE.jar:5.1.6.RELEASE] at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.1.6.RELEASE.jar:5.1.6.RELEASE] at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.1.6.RELEASE.jar:5.1.6.RELEASE] at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE] at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:712) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:461) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:384) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:394) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:253) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:348) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:173) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.24.jar:9.0.24] at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na] Caused by: java.security.NoSuchAlgorithmException: ECDSA KeyFactory not available at java.base/java.security.KeyFactory.<init>(KeyFactory.java:138) ~[na:na] at java.base/java.security.KeyFactory.getInstance(KeyFactory.java:183) ~[na:na] at org.keycloak.jose.jwk.JWKParser.createECPublicKey(JWKParser.java:107) ~[keycloak-core-9.0.0.jar:9.0.0] ... 49 common frames omitted

Thanks a lot! Any help is appreciated :slight_smile:

Can you try the latest Spring Boot and Keycloak version first? Or try to post some config example?

Security.addProvider(new BouncyCastleProvider());

at the very top of the main(String[] args) method solved the issue.

I’m curious why you need to manually add the provider yourself. I seem to have the same setup as you do with the exception that I am using a more up to date java version -> 11.0.6.hs-adpt
A closer look at https://adoptopenjdk.net/release_notes.html seems to suggest a couple of fixes around that same topic in versions > 11.0.3

It could be that this was a bug in your specific version or a difference in how the project is setup.
I am using a generic approach with spring-security-oauth2-jose and spring-security-oauth2-resource-server with as only config property spring.security.oauth2.resourceserver.jwt.jwk-set-uri
Which method of configuration are you using?

I’m sorry for reviving this, but I’m also facing the same issue, and the One-Liner Serena provided works for me too. I’ve made an account just to make this comment.

Here is my pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.3.3.RELEASE</version>
	<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>rixon.event.sourcing</groupId>
<artifactId>EventSorcerer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>EventSorcerer</name>
<description>Event Sourcing project</description>

<properties>
	<java.version>11</java.version>
</properties>

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-mongodb</artifactId>
	</dependency>
	<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <!--<scope>provided</scope>-->
	</dependency>
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
	</dependency>

	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-data-rest</artifactId>
	</dependency>
	
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
		<exclusions>
			<exclusion>
				<groupId>org.junit.vintage</groupId>
				<artifactId>junit-vintage-engine</artifactId>
			</exclusion>
		</exclusions>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-oauth2-client</artifactId>
	</dependency>
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
	</dependency>
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-data-mongodb</artifactId>
	</dependency>
	<dependency>
	    <groupId>org.mongodb</groupId>
	    <artifactId>mongo-java-driver</artifactId>
	    <version>3.12.7</version>
	</dependency>
	<dependency>
	    <groupId>org.mongodb</groupId>
	    <artifactId>mongodb-driver-sync</artifactId>
	    </dependency>
	<dependency>
	    <groupId>org.springframework.data</groupId>
	    <artifactId>spring-data-mongodb</artifactId>
	    </dependency>
	<dependency>
	    <groupId>org.springframework.data</groupId>
	    <artifactId>spring-data-releasetrain</artifactId>
	    <version>Lovelace-SR9</version>
	    <type>pom</type>
	    <scope>import</scope>
	</dependency>
	<dependency>
	    <groupId>org.keycloak</groupId>
	    <artifactId>keycloak-spring-boot-starter</artifactId>
	</dependency>
</dependencies>

<build>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
		</plugin>
	</plugins>
</build>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.keycloak.bom</groupId>
            <artifactId>keycloak-adapter-bom</artifactId>
            <version>11.0.2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

I’m using OpenJDK 11. I’ve read somewhere that this version doesn’t come with elliptic cryptography (Whatever this is), and the one-liner is resolving the issue. But with this one-liner, we aren’t allowed to use the application commercially.

Where does it say that you can’t use your application commercially?

There was another thread that stated this. I’ll try to find it.