Spring Boot SSL Client

2019-01-13 23:35发布

问题:

I am new to Spring Boot. So far I am enjoying it. I have developed a demo SSL rest web server that correctly handles mutual X.509 certificate authentication. Using an IE browser with self signed client & server certificates, I have tested that the demo rest web server is working correctly -- both the server and browser are successfully exchanging and validating each others certificates.

I am having trouble finding an SSL client example that shows how to include the client certificate and issue the https. Anybody have a simple rest client example that shows how to consume my ssl server?

Best Regards, Steve Mansfield

回答1:

Given that you're using Spring, here's an example that shows how to use Spring's RestTemplate and Apache's HttpClient configured with a client certificate and to trust a self-signed certificate from the server:

KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(new File("keystore.jks")),
        "secret".toCharArray());
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
        new SSLContextBuilder()
                .loadTrustMaterial(null, new TrustSelfSignedStrategy())
                .loadKeyMaterial(keyStore, "password".toCharArray()).build());
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
        httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
ResponseEntity<String> response = restTemplate.getForEntity(
        "https://localhost:8443", String.class);


回答2:

I could not get the above client submitted by Andy to work. I kept getting errors saying that "localhost != clientname". Anyways, I got this to work correctly.

 import java.io.IOException;

 import org.apache.commons.httpclient.HttpClient;
 import org.apache.commons.httpclient.HttpException;
 import org.apache.commons.httpclient.URI;
 import org.apache.commons.httpclient.methods.GetMethod;

 public class SSLClient {

      static
        {
          System.setProperty("javax.net.ssl.trustStore","c:/apachekeys/client1.jks");
          System.setProperty("javax.net.ssl.trustStorePassword", "password");
          System.setProperty("javax.net.ssl.keyStore", "c:/apachekeys/client1.jks");
          System.setProperty("javax.net.ssl.keyStorePassword", "password");
       }

     public static void main(String[] args) throws HttpException, IOException {

         HttpClient client = new HttpClient();
         GetMethod method = new GetMethod();
         method.setURI(new URI("https://localhost:8443/restserver", false));
         client.executeMethod(method);

         System.out.println(method.getResponseBodyAsString());

     }

 }


回答3:

The example of user1707141 didn´t work for me and skmansfield seems rather depending on specific files, that aren´t convention with Spring Boot / Maven. Also Andy Wilkinson´s answer uses the constructor SSLConnectionSocketFactory, which was deprecated in Apache httpclient 4.4+ and also seems quite complex.

So I created a example project that should show everything 100% comprehensible here: https://github.com/jonashackt/spring-boot-rest-clientcertificate

Besides the normal usage of the RestTemplate with @Autowired in your Testclass, be sure to configure your RestTemplate like this:

package de.jonashackt.restexamples;

import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.ResourceUtils;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;

@Configuration
public class RestClientCertTestConfiguration {

    private String allPassword = "allpassword";

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception {

        SSLContext sslContext = SSLContextBuilder
                .create()
                .loadKeyMaterial(ResourceUtils.getFile("classpath:keystore.jks"), allPassword.toCharArray(), allPassword.toCharArray())
                .loadTrustMaterial(ResourceUtils.getFile("classpath:truststore.jks"), allPassword.toCharArray())
                .build();

        HttpClient client = HttpClients.custom()
                .setSSLContext(sslContext)
                .build();

        return builder
                .requestFactory(new HttpComponentsClientHttpRequestFactory(client))
                .build();
    }
}


回答4:

I know its too late, but here is the code that works for me.

@SpringBootApplication
public class Application {

	private static final Logger log = LoggerFactory.getLogger(Application.class);

	public static void main(String args[]) {

			makeWebServiceCall();

	}
	
	public static void makeWebServiceCall() {
		TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;

		SSLContext sslContext;
		ResponseEntity<String> response = null;
		try {
			sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy)
					.build();

			SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);

			CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();

			HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();

			requestFactory.setHttpClient(httpClient);

			RestTemplate restTemplate = new RestTemplate(requestFactory);
			
			StringBuffer plainCreds = new StringBuffer();
			plainCreds.append("username");
			plainCreds.append(":");
			plainCreds.append("password");
			byte[] plainCredsBytes = plainCreds.toString().getBytes();
			byte[] base64CredsBytes = Base64.getEncoder().encode(plainCredsBytes);
			String userBase64Credentials = new String(base64CredsBytes);
			

			HttpHeaders headers = new HttpHeaders();
			headers.add("Authorization", "Basic " + userBase64Credentials);
			headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
			headers.setContentType(MediaType.APPLICATION_JSON);

			HttpEntity entity = new HttpEntity<>(headers);

			String url = "https:restUrl";
			
			response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
			
			if(response.getStatusCodeValue() == 200) {
				log.info("Success! Further processing based on the need");
			} else {
				log.info("****************Status code received: " + response.getStatusCodeValue() + ".************************");
			}

		} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
			log.error("Exception occured. Here are the exception details: ", e);
		} catch(HttpClientErrorException e) {
			if(e.getRawStatusCode() == 403) {
				log.info("****************Status code received: " + e.getRawStatusCode() + ". You do not have access to the requested resource.************************");
				
			} else if(e.getRawStatusCode() == 404) {
				log.info("****************Status code received: " + e.getRawStatusCode() + ". Resource does not exist(or) the service is not up.************************");

			} else if(e.getRawStatusCode() == 400) {
				log.info("****************Status code received: " + e.getRawStatusCode() + ". Bad Request.************************");

			} else {
				log.info("****************Status code received: " + e.getRawStatusCode() + ".************************");
				
			}

           log.info("****************Response body: " + e.getResponseBodyAsString() + "************************");
		}
	}
 }

Here is the maven filed

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.springframework</groupId>
<artifactId>gs-consuming-rest</artifactId>
<version>0.1.0</version>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.2.RELEASE</version>
</parent>

<properties>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>

<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.3</version>
</dependency>


        <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
    <version>4.4.6</version>
</dependency>


</dependencies>


<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>



回答5:

This worked for me :

TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
javax.net.ssl.SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
        .loadTrustMaterial(null, acceptingTrustStrategy).build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);