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

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?

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:
Further info in my blog post though I suspect you know most of this already, based on your question.

Related

create authorized webclient with cxf jaxrs

I'm a beginner developer. I want to write a rest service call with "apache cxf jaxrs". To do that I create a web client. The sample code is below:
WebClient client = WebClient.create(url);
ClientConfiguration configuration = WebClient.getConfig(client);
MultivaluedHashMap headersMap = new
MultivaluedHashMap();
headersMap.add("key","value");
client.type("application/x-www-form-urlencoded").headers(headersMap);
Form form = new Form();
Response response = client.post(form);
The service which I'm going to call need a one way ssl authorization, so I get the server certificate and convert it to JKS to config the ssl handshake .after creating a socket how can i . combine the web client which i created ssl socket. I don’t want to use spring frame work. Can somebody help me with a simple java sample?
Thanks
The server certificate needs to be added to java CACERTS. That way your java cxf client will 'trust' the server and allow SSL.
You can do this with java keytool as follows:
(assuming your java is at D:\Java and your cert is in a file called server.cer
"D:\Java\bin\keytool.exe" -import -alias my_server -keystore "D:\Java\jre\lib\security\cacerts" -storepass changeit -file server.cer
Trust this certificate? [no]: answer is y
Success is indicated with: "Certificate was added to keystore"

Wildfly does not pass intermediate certificate to HTTPS client

So I have this certificate chain made of 3 certificates:
Root cert (installed in all clients)
Intermediate cert (installed in my browser, but not in other clients/Java apps accessing webservices)
Domain wildcard certificate
Wildfly is configured to use a keystore that holds intermediate and domain certificates in JKS format, along with the private key for the domain cert. When I send a HTTPS request from my browser, the certificate is validated just fine and everything seems OK. But when the Java client application I am working on tries to establish a connection, it cannot build a valid certificate chain:
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
I checked the certificates delivered by Wildfly using openssl (openssl s_client -showcerts -connect <servername>) and confirmed that the intermediate certificate is NOT transmitted even though it is present in the keystore. Now I can work around this in my development environment by adding the intermediate cert to my local trust store, however that is not an acceptable long-term solution for production.
In my Wildfly's standalone.xml I have specified the keystore and the alias of the wildcard certificate.
<server-identities>
<ssl>
<keystore path="/path/to/keytsore.jks" keystore-password="supersecure" alias="*.redacted.com"/>
</ssl>
</server-identities>
Searching through the Wildfly admin docs I saw no mention of intermediate certificates. I tried to add an additional element to the config (same path and password, but alias of the intermediate certificate), but Wildfly would not start up with that.
Any idea how I can get Wildfly to present the intermediate certificate to connecting clients? Does it need to have a specific alias name? Or is there another way to tell Wildfly the alias of any intermediate cert?

Pinpointing which public SSL cert is being used by REST endpoint

I have one webapp that I am running on Tomcat in two environments, Dev and Test. The apps are identical, but the server config and security levels are not. The webapp connects to a an external customer's REST endpoint over https, which they have secured using a public cert. Same endpoint in both environments.
The Dev app works fine, the Test app fails with this:
SSLHandshakeException ... Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
When I curl the endpoint, I see the customer is using a public certificate. I assume that the version of the cert I have in my Dev server is up to date, and my Test server is expired. However, when I examine the two servers' cacerts files in KeyStore Explorer, I don't find the cert at all.
I assume I just do not know how to find the relationship between a specific cert and its parent (parent cert? global cert?). So, either via commands or code, how do I find which entry in my Dev cacerts file represents the cert from the endpoint?

how to enable trust for chain certs in weblogic or java

I have an Oracle service Bus domain running on weblogic 10.3.6 with 2 managed servers in cluster.
We have a proxy service deployed on this domain which goes to an external Business service for validating address. This Business Service is listening on SSL port and SSL cert has EntrustCACert>IntermidateCert1>IntermidateCert2>ServerCert
Issue: Getting below error during connectivity testing -
General runtime error: [Security:090548]The certificate chain received from ws2.site1.com - 197.109.80.xxx contained a V3 CA certificate which was missing the basic constraints.
Solutions i have tried:
1) added JAVA_OPTIONS="${JAVA_OPTIONS} -Dweblogic.security.SSL.enforceConstraints=off " in the startup script and it resolve the issue. But i was told this is not the prefered way and use trust keystore to implement.
2)to implement trust, I copied all the 4 certs in following reverse order EntrustCACert>IntermidateCert1>IntermidateCert2>ServerCert in one file named DSperian.pem and imported in Jrockit cacerts file( /apps/Oracle/jrockit-jdk1.6.0_31-R28.2.3-4.1.0/jre/lib/security/cacerts ) using below command but getting the same above error.
keytool -import -alias DSperian -trustcacerts -file DSperian.pem -keystore cacerts
Question:
Please let me know if im doing the right way to import cert to create trust. so my OSB domain will blindly trust the Business service ( web-service) and ignore
the "basic constraints" error. Do i need to utilize weblogic specific trust keystore file but this OSB weblogic is running on non-ssl port ?
are there any other options available ? Asking Business-service to update their cert to include "basic constraints" is not an option.
There is actually a problem with your certificate.
Security certificates have a set of constraints that allow them to perform certain functions (or restrict them to certain functions).
Have a look at each of the certificates and ensure that they have constraints assigned to them that are suitable for the task.
To get more details on the error enable SSL debugging in your web logic server add the following to your server startup script
-Dssl.debug=true -Dweblogic.StdoutDebugEnabled=true
More information in Configure SSL in WebLogic server
Use the following command to validate your keystone
java utils.ValidateCertChain -jks my key mykeystore
Then, whoever provided the root certificate needs to fix up your issues.

Can't consume a SSL protected webservice with Java/Glassfish

I'm trying to consume a Webservice hosted under https security.
I'm using Java and glassfish and I'm getting the following error:
INFO: HTTP transport error: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching testdomain.com found
The thing is that this specific server is used for testing and it's using the production certificate (the one with CN=domain.com)
I already added the domain.com certificate to my glassfish domain's cacerts keystore using keytool -importcert and it didn't work.
I also tried creating a self signed certificate with the CN=testdomain.com and adding it to the cacerts keystore and it didn't work either...
So how do I configure Java/Glassfish to consume this Web Service?
The CN of the server certificate should match the domain in URL to which the client connects. If still doesn't work, I would check if the IP maps to this hostname too (reverse DNS). It is the client, who verifies it. If you want to bypass this hostname verification, see the example code in my article: http://jakubneubauer.wordpress.com/2011/09/06/java-webservice-over-ssl/
The priciple is that you provide your own HostnameVerifier to the service client proxy.
THe self-signed certificate needs to be installed in the keystore of the Web service, along with its private key, and imported into the truststore of Glassfish.
the self signed certificate needs to be installed in key store of your java client. and testdomain.com should be resolved using dns.

Categories