gburgalum01
gburgalum01

Reputation: 301

Spring Boot + OAuth2 + REST API - Certification Path Not Found

I'm implementing a RESTful API in an existing Spring Boot v2.1.4 application. The application contains a Spring MVC layer that is secured using Spring Security. There are views built using JSP. Some of the JSPs are embedded with client-side scripts that invoke AJAX calls to retrieve information. These calls will execute against the API rather than the existing MVC endpoints.

The new service endpoints are protected using OAuth2, and an authorization server has been set up within the application and activated using the @EnableAuthorizationServer annotation. To enable SSL on these endpoints, I've generated a private/public key pair using keytool.

keytool -genkeypair -alias apitest -keyalg RSA -validity 365 -keysize 2048 -keystore apitest.jks

I then execute the following command to export the self-signed certificate.

keytool -export -alias apitest -keystore apitest.jks -rfc -file apitest.cer

I've added the certificate to a truststore file for which the path to the file and the password are loaded into system properties when the application starts.

System.setProperty("javax.net.ssl.trustStore", truststorePath);
System.setProperty("javax.net.ssl.trustStorePassword", truststorePassword);

Since the MVC layer is secured by Spring Security, I attempt to retrieve the OAuth2 token from the token endpoint when the user has successfully logged into the application. The token is returned in the response as a cookie (not shown here).

ResourceOwnerPasswordAccessTokenProvider provider = new ResourceOwnerPasswordAccessTokenProvider();
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setClientAuthenticationScheme(AuthenticationScheme.none);
UriComponents uriComponents = ServletUriComponentsBuilder.fromServletMapping(request).path("/oauth/token").build();
resource.setAccessTokenUri(uriComponents.toUriString());
resource.setScope(Arrays.asList("read", "write"));
resource.setClientId(oAuth2ClientDetails.getClientId());
resource.setClientSecret(oAuth2ClientDetails.getClientSecret());
resource.setGrantType("password");
resource.setUsername(userName);
resource.setPassword(userCredentials);
OAuth2AccessToken accessToken = provider.obtainAccessToken(resource, new DefaultAccessTokenRequest());
return accessToken.getValue();

When this logic fires off the call to the token endpoint, the following error is observed.

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://localhost:8443/testapp/oauth/token": sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:744) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:670) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:579) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]

Since both the API endpoint and the authorization server are both running on localhost, I thought that a self-signed cert in the truststore file would be sufficient. What am I missing to finish this setup?

Upvotes: 0

Views: 2002

Answers (1)

Gary Archer
Gary Archer

Reputation: 29291

I suspect your problem may be related to the certs not having a parent root authority. I usually recommend the approach of creating real world self signed certificates like this, with a trust chain:

  • First create the Root Certification Authority
  • Then use that to create Server SSL Certs

ROOT AUTHORITY

For local development with OAuth I create a root authority called mycompany.ca.crt, and then add Java trust like this, plus also distribute this cert to browsers:

  • keytool -keystore cacerts -storepass changeit -importcert -alias mycompanyca -file ~/Desktop/mycompany.ca.crt

CREATING SSL CERTS

I then use the root certificate to create SSL certs, and no extra trust configuration is needed.

SCRIPTS

If interested, take a look at these scripts of mine, which use the openssl tool:

  • makeCerts.ps1 script is for Windows
  • makeCerts.sh script is for MacOS or Linux
  • Other files are created when scripts are run
  • You would need to edit domain names to match the ones you are using

My examples use a wildcard domain that I use for local PC development, and I also add domains to my host file. In a browser client I then see this:

enter image description here

Further info in my blog post though I suspect you know most of this already, based on your question.

Upvotes: 0

Related Questions