1

I have an endpoint which requires SSL authentication. I'm able to successfully post a request on that endpoint with:

curl --location --request POST 'https://someurl.click' --header 'some headers' --cert my_cert.pem

I need to create a Spring Boot application which POSTs a request to that endpoint using that certificate with RestTemplate. I've read that PEM certificates are not valid and I need to use p12 or JKS.

So I converted my certificate to p12 with:

openssl pkcs12 -export -in my_cert.pem -out my_cert.p12

And used it as a Trust Store in my Bean as suggested by several references (where trustStore points to the converted p12 certificate):

    @Value("${trust-store}")
    private Resource trustStore;

    @Value("${trust-store-password}")
    private String trustStorePassword;

    @Bean
    public RestTemplate getRestTemplate(RestTemplateBuilder builder) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        SSLContext sslContext = new SSLContextBuilder()
        .loadTrustMaterial(trustStore.getURL(), trustStorePassword.toCharArray())
        .build();
    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);

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

    return builder
        .requestFactory(() -> new HttpComponentsClientHttpRequestFactory(httpClient))
        .build();
    }

When using this RestTemplate I'm having SSL handshake errors.

Code:

String response = restTemplate.postForObject(myEndpoint, request, String.class);

Output:

2021-08-04 12:06:41.926 ERROR 129727 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://myurl.click": readHandshakeRecord; nested exception is javax.net.ssl.SSLException: readHandshakeRecord] with root cause

java.net.SocketException: Broken pipe (Write failed)
        at java.base/java.net.SocketOutputStream.socketWrite0(Native Method) ~[na:na]
        at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110) ~[na:na]
        at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150) ~[na:na]
        at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeChangeCipherSpec(SSLSocketOutputRecord.java:221) ~[na:na]
        at java.base/sun.security.ssl.OutputRecord.changeWriteCiphers(OutputRecord.java:162) ~[na:na]
        at java.base/sun.security.ssl.ChangeCipherSpec$T10ChangeCipherSpecProducer.produce(ChangeCipherSpec.java:118) ~[na:na]
        at java.base/sun.security.ssl.Finished$T12FinishedProducer.onProduceFinished(Finished.java:395) ~[na:na]
        at java.base/sun.security.ssl.Finished$T12FinishedProducer.produce(Finished.java:379) ~[na:na]...

Any advice is greately appreciated.

1 Answer 1

3

I was able to make it work by using a JKS keystore instead of a p12 certificate.

First, I used the private key and both private and public keys as an input to generate a P12 certificate:

openssl pkcs12 -export -inkey <private_key>.pem -in <all_keys>.pem -name new_certificate -out certificate.p12

Finally, I converted the P12 certificate into a JKS keystore using keytool:

keytool -importkeystore -srckeystore certificate.p12 -srcstoretype pkcs12 -destkeystore certificate.jks

Then, I could finally load it in a RestTemplate instance using SSLContext:

@Value("${trust-store}")
private String trustStore;
@Value("${trust-store-password}")
private String trustStorePassword;

@Bean
public RestTemplate getRestTemplate(RestTemplateBuilder builder) throws IOException, CertificateException,
                                                                        NoSuchAlgorithmException, KeyStoreException,
                                                                        KeyManagementException, UnrecoverableKeyException {
    Resource trustStoreCertificate = new ClassPathResource(trustStore);
    File certificate = new File("combined.jks");
    FileUtils.copyInputStreamToFile(trustStoreCertificate.getInputStream(), certificate);
    SSLContext sslContext = new SSLContextBuilder()
        .loadKeyMaterial(certificate , trustStorePassword.toCharArray(), trustStorePassword.toCharArray()).build();
    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
    
    HttpClient httpClient = HttpClients.custom()
        .setSSLSocketFactory(socketFactory)
        .build();
    
    certificate.delete();
    
    return builder
        .requestFactory(() -> new HttpComponentsClientHttpRequestFactory(httpClient))
        .build();
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.