How to trust ssl certificate programatically while using Spring Rest Client - spring

I am using resttemplate from spring for consuming the Rest Json Webservice
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<Object> entity = new HttpEntity<Object>(glassRequest, headers);
postForObject = restTemplate.postForObject(url, entity, responseClass );
I am using above code for same. It works fine if there is no ssl certification at provider end. However , I get 403 if he uses certificate.
I know this can be handled by below two approches :-
1. Add certificate in trust store of application server.[But i am not running this code from application server but my junit test class]
2. Two add the certificate in java trust store. [However , i want to schedule this junit from enviroment where I don't have access ]
Is this can be done programmatically ?? Like i will include the certificates in my source code itself and then refer them by reading from classpath while making call to service.

Related

Spring Boot WebCLient accept Self Signed Certificate, but not with InsecureTrustManagerFactory

I'm trying to build a REST client using Spring Boot and utilizing WebClient, however I'm conflicted when trying to config HTTPS call to a REST API.
When using RestTemplate, I was able to get self signed certificate working by using TrustSelfSignedStrategy(), thus even when the certificate is self signed, it is still being validated for its hostname, expiry date, etc.
In WebClient, so far I only found the way self signed certificate is by utilizing InsecureTrustManagerFactory, however this will cause the whole validation to be skipped as well, effectively void the purpose of using HTTPS in the first place.
As quoted from Netty documentations:
An insecure TrustManagerFactory that trusts all X.509 certificates without any verification.
NOTE: Never use this TrustManagerFactory in production. It is purely for testing purposes, and thus it is very insecure.
Is there any way I can use self signed certificate in WebClient without having to dismantle all the verification?
Yes, you can use a self signed certificate. What you need to do is add the self signed certificate into a java keystore and load it into your application by getting the keystore and transforming it into a TrustManager. Afterword you can supply the TrustManager to the SslContextBuilder which is needed for configuring the WebClient based on Netty. See below for an example:
Path truststorePath = Paths.get(/path/to/your/truststore)
InputStream truststoreInputStream = Files.newInputStream(truststorePath, StandardOpenOption.READ)
KeyStore truststore = KeyStore.getInstance(KeyStore.getDefaultType());
truststore.load(truststoreInputStream, truststorePassword);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(truststore);
SslContext sslContext = SslContextBuilder.forClient()
.trustManager(trustManagerFactory)
.build()
HttpClient httpClient = HttpClient.create()
.secure(sslSpec -> sslSpec.sslContext(sslContext));
WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build()

Does calling https service by Resttemplate work for all services whose certificate has been imported in the trustore of my client service?

I have a restemplate which can make call to multiple external systems over https.
I configured the resttemplate like so :
SSLContext sslContext = SSLContextBuilder
.create()
.loadTrustMaterial(key, keyPassword)
.build();
HttpClient client = HttpClients
.custom()
.setSSLContext(sslContext)
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(
client);
httpComponentsClientHttpRequestFactory.setConnectTimeout(5000);
httpComponentsClientHttpRequestFactory.setReadTimeout(30000);
return new RestTemplate(httpComponentsClientHttpRequestFactory);
I have setup 2 mock services on 2 separate VMs with ssl enabled and I am testing this restemplate by calling those services over https. And it works.
What I want to confirm is that configuring the restemplate as shown in the above code and importing the certificates of the different services to be called in by the client truststore should work without any additional configurations right?
I am confused because in the code in this example here : https://github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose the author has used 2 different factories i.e. serverTomClientHttpRequestFactory & serverAliceClientHttpRequestFactory, however mine works with a single restemplate. can someone please shed some light on this topic?

Issuer URI of Spring Security OAuth2 Authorization Server

I'm developing OAuth 2.0 authorization server and resource server using Spring Security OAuth 2.0 2.3.4.REELEASE. Little did I know that Spring has deprecated it in favor of Spring Security. However, authorization server is not included in the migration to Spring Security. Only the resource server is included as they are encouraging users to use products instead (one is KeyCloak).
But like many others, I really have to develop my own authorization server so I keep on using Spring Security OAuth 2.0 but only for the authorization server. As for resource server, I'll be using the resource server from Spring Security. I think authorization and resource servers are independent and they are based on the standards of OAuth 2.0 so the implementation could be from different frameworks.
My problem is specifying the authorization server via issuer-uri. I could not determine how Spring OAuth 2.0 authorization server exposes its issuer-uri if there is any. I could not find either any docs how to create one if it does not have one be default.
Please help. Thank you.
Since Spring Security Oauth has ended support you should use the Spring Authorization Server now (https://spring.io/projects/spring-authorization-server). For the isser uri there you can use the base uri of the server. To verify this you can check the well-known-Endpoint under /.well-known/openid-configuration.
The isser uri in the authorization server can be edited with the ProviderSettings:
#Bean
public ProviderSettings providerSettings() {
return ProviderSettings.builder()
.issuer("https://authorization-server:8443")
.build();
}
I believe the solution you are looking for lies within the TokenEnhancer. So, implement a custom token enhancer and add the iss key like so.
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("iss", "issuer uri here");
...
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}

'X-Appengine-Inbound-Appid' header is not set when using Spring RestTemplate

I have two services running on the Google App Engine (MS1 & MS2). MS1 is deployed in the Standard Environment and MS2 is deployed in the Flexible Environment. I am invoking an API from Standard Environment to the Flexible Environment. I want to make sure that MS2 only accepts requests originating from MS1. So, I decided to use this feature from App Engine. I am setting the X-Appengine-Inbound-Appid header and setInstanceFollowRedirects to false in MS1, but looks like App Engine is removing this header. I am not able to find this header in MS2.
HttpHeaders headers = new HttpHeaders();
headers.add("X-Appengine-Inbound-Appid", ApiProxy.getCurrentEnvironment().getAppId());
HttpEntity<MergePdfsResource> entity = new HttpEntity<MergePdfsResource>(mergePdfsResource, headers);
restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory() {
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
super.prepareConnection(connection, httpMethod);
connection.setInstanceFollowRedirects(false);
}
});
ResponseEntity<SomeClass> response = restTemplate.postForEntity(apiUrl, entity, SomeClass.class);
Following is the answer I got from the Google Support:
The X-Appengine-Inbound-Appid header is set only if we are using the URLFetch service. In the java8 runtime environment, the default is native which uses the standard Java network classes. So, I had to set the URL stream handler to urlfetch in appengine-web.xml as below:
<url-stream-handler>urlfetch</url-stream-handler>

JerseyClient using DefaultHttpClient

I need to access a JAX-WS webservice protected by SSL using Jersey Client. I have successfully got this working with limited configuration and just letting the Client use the default HTTPURLConnection API.
This approach wont work however for my overall solution because it does not allow me the flexibility to change the credentials used when making a request to the WS. I'm trying to use DefaultHTTPClient instead and then passing it to the Client object on intialization.
NTCredentials credentials = new NTCredentials("username", "password",
computerName, domainName);
DefaultHttpClient httpClienttemp = new DefaultHttpClient();
DefaultHttpClient httpClient = wrapClient(httpClienttemp);
httpClient.getCredentialsProvider().setCredentials(AuthScope.ANY, credentials );
ClientConfig config = new DefaultClientConfig();
The wrapClient method creates an X509TrustManager and overrides the necessary methods so that all certificates are accepted. It also creates a SchemeRegistry entry for https access on port 443. This configuration results in a Connection refused exception.
The strange thing is, if i add an additional entry in the SchemeRegistry for http and give it a port of 443 then the request does get sent however a Connection Reset exception then gets thrown.
The Url i use to create the WebResource object is https however the SOAPAction i declare in the header uses http. Any ideas where im going wrong?
This is a limitation of the default HTTP Client (com.sun.jersey.api.client.Client) documented in the Jersey docs. You will have to use Apache HTTP Client to achieve this functionality.
Looks like someone already recommended doing this in the answer to your previous question: Jersey Client API - authentication.
EDIT: Corrected reference to the default Jersey HTTP Client to avoid confusion.

Resources