0

I am calling an external API via RestTemplate that requires SSL Certificate. Already the external API provider has provided me with the certificate file (company.crt) and its key file (company.key). While adding the certificate, key and password in Postman, I am able to successfully call the API. But when I am calling using RestTemplate inside a SpringBoot project, even after adding SSLContext, I am receiving an error.

The steps I have followed:

Created a company.p12 store file from company.crt and company.key file using openssl:

openssl pkcs12 -export -name servercert -in company.crt -inkey company.key -out company.p12

Converted company.p12 to company.jks store file using Keytool:

keytool -importkeystore -destkeystore company.jks -srckeystore company.p12 -srcstoretype pkcs12 -alias companycert

Have created the config inside application.properties file as, after placing company.jks inside resource folder of SpringBoot project:

http.client.ssl.trust-store=classpath:company.jks
http.client.ssl.trust-store-password=Password

Then, I have created a configuration for RestTemplate as:

@Value("${http.client.ssl.trust-store}")
private Resource keyStore;
@Value("${http.client.ssl.trust-store-password}")
private String keyStorePassword;

@Bean
RestTemplate restTemplate() throws Exception {

    SSLContext sslContext = new SSLContextBuilder()
                                 .loadTrustMaterial(
                                     keyStore.getURL(),
                                     keyStorePassword.toCharArray()
                                 ).build();

    SSLConnectionSocketFactory socketFactory = 
              new SSLConnectionSocketFactory(sslContext);

    HttpClient httpClient = HttpClients.custom()
              .setSSLSocketFactory(socketFactory).build();

    HttpComponentsClientHttpRequestFactory factory = 
               new HttpComponentsClientHttpRequestFactory(httpClient);

    return new RestTemplate(factory);

}

Calling an external APIs as:

@Autowired
RestTemplate restTemplate;

@GetMapping("/api")
public Object callAPI() {

    final String ENDPOINT = "https://some-api.domain.com:8533/api/key/";
    Object response = restTemplate.exchange(ENDPOINT, HttpMethod.GET, request, Object.class);
    return response;

}

The error after calling an API via RestTemplate:

sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
ekansh
  • 716
  • 8
  • 16
  • You can check this ... May be a similar issue https://stackoverflow.com/questions/6908948/java-sun-security-provider-certpath-suncertpathbuilderexception-unable-to-find – Sagii Dec 07 '20 at 06:28
  • https://stackoverflow.com/questions/9210514/unable-to-find-valid-certification-path-to-requested-target-error-even-after-c – Sagii Dec 07 '20 at 06:30
  • Imported the certificate to cacerts. Still not working. – ekansh Dec 07 '20 at 07:22
  • Why do you set the certificate on your HTTP client? Do you use mutual authentication ? I mean your server should trust your client or not? – Octavian R. Dec 07 '20 at 08:28
  • The server demands that while sending a request, a valid certificate must be present. Otherwise it gives SSLHandshakeException. I alreday have a valid certificate and a key file. With Postman, after adding certificate and key in settings, the API is providing the response. – ekansh Dec 07 '20 at 09:01
  • Please post here the content of your JKS's used on the client and the server side so that I can help you. `keytool -list -v -keystore keystore.jks` – Octavian R. Dec 07 '20 at 09:36
  • Resolved. Posted the answer below. – ekansh Dec 08 '20 at 10:54

1 Answers1

0

Got it resolved:

application.properties configuration: Add Key store properties

http.client.ssl.trust-store=classpath:company.jks
http.client.ssl.trust-store-password=Password

http.client.ssl.key-store=classpath:company.p12
http.client.ssl.key-store-password=Password

Then, inside RestTemplate configuration:

@Bean
RestTemplate restTemplate() throws Exception {

    TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;

    SSLContext sslContext = new SSLContextBuilder()
        .loadTrustMaterial(
            trustStore.getURL(),
            trustStorePassword.toCharArray(), acceptingTrustStrategy
         )
         .loadKeyMaterial(
             keyStore.getURL(), 
             keyStorePassword.toCharArray(), 
             keyStorePassword.toCharArray())
         .build();

    SSLConnectionSocketFactory socketFactory = 
        new SSLConnectionSocketFactory(sslContext);

    HttpClient httpClient = HttpClients.custom()
        .setSSLSocketFactory(socketFactory).build();

    HttpComponentsClientHttpRequestFactory factory = 
        new HttpComponentsClientHttpRequestFactory(httpClient);

    return new RestTemplate(factory);

}
ekansh
  • 716
  • 8
  • 16