简体   繁体   English

如何在Spring Boot中手动配置SSL握手

[英]How to manually configurate SSL Handshake in Spring boot

I am setting up a server that should communicate with other trusted servers, and I am using SSL Handshake in spring boot with apache tomcat. 我正在设置一个应与其他受信任服务器通信的服务器,并且在春季启动时使用apache tomcat使用SSL握手。

I managed to succeed in making 2 way SSL however if I add Root CA that did generate the certificates in the trust store it automatically trusts every child from that CA. 我设法成功制作了2种方式的SSL,但是,如果我添加了确实在信任存储区中生成证书的根CA,它将自动信任该CA中的每个子级。 I want it to check if that specific certificate is in the trust store not just from the same parent. 我希望它检查特定证书是否在信任库中,而不仅仅是来自同一父证书。

Application properties are the same for the second server just with different KeyStore and Truststore. 第二台服务器的应用程序属性是相同的,只是具有不同的KeyStore和Truststore。

Application.properties Application.properties

server.ssl.key-store=classpath:booker.p12
server.ssl.key-store-password=pass
server.ssl.key-password=pass
server.ssl.key-alies=booker
server.ssl.trust-store=classpath:bookerTrust.p12
server.ssl.trust-store-password=pass
server.ssl.trust-store-type = PKCS12
server.ssl.client-auth=need
server.port=8111

RestTemplate setup in main.java main.java中的RestTemplate设置

    @Bean
    public RestTemplate restTemplate() throws Exception {
        RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
        restTemplate.setErrorHandler(
                new DefaultResponseErrorHandler() {
                    @Override
                    protected boolean hasError(HttpStatus statusCode) {
                        return false;
                    }
                });

        return restTemplate;
    }

    private ClientHttpRequestFactory clientHttpRequestFactory() throws Exception {
        return new HttpComponentsClientHttpRequestFactory(httpClient());
    }

    private HttpClient httpClient() throws Exception {
        // Load our keystore and truststore containing certificates that we trust.
        SSLContext sslcontext =
                SSLContexts.custom().loadTrustMaterial(trustResource.getFile(), trustStorePassword.toCharArray())
                        .loadKeyMaterial(keyStore.getFile(), keyStorePassword.toCharArray(),
                                keyPassword.toCharArray()).build();
        SSLConnectionSocketFactory sslConnectionSocketFactory =
                new SSLConnectionSocketFactory(sslcontext, new HostCustomVerifer(trustResource,trustStorePassword,trustStoreType));
        return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
    }
}

The communication in the controller: 控制器中的通讯:

@RestController
@RequestMapping(value = "/server1")
public class ClientController {

    @Autowired
    RestTemplate restTemplate;

    @RequestMapping(value = "/data", method = RequestMethod.GET)
    ResponseEntity<?> getMessage(ServletRequest request) {
    return ResponseEntity.ok("Server1 successfully called!");
    }


    @RequestMapping(value = "/sdata", method = RequestMethod.GET)
    public ResponseEntity<String> getMsData() {
        try {
            String msEndpoint = https://localhost:8111/api/server2/data";
            return new ResponseEntity<String>( restTemplate.getForObject(new URI(msEndpoint), String.class), HttpStatus.OK) ;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return new ResponseEntity<String>("Exception occurred.. so, returning default data", HttpStatus.BAD_GATEWAY);
    }


}

I expect for this scenario to fail however it succeeds: 我希望这种情况会失败,但是会成功:

Server1: Trusts RootCA,Server1,Server2 Server1:信任RootCA,Server1,Server2

Server2: Trusts RootCA,Server2 Server2:信任RootCA,Server2

Server1 starts communication with Server2 and succeeds somehow Server1开始与Server2通信并以某种方式成功

I expect it to fail because Server2 does not trust Server1 我预计它会失败,因为Server2不信任Server1

Not the best solution, however, it worked for my needs to fail communication if not trusted both ways. 但是,这不是最好的解决方案,它满足了我的双向通信失败的需求。 I implemented javax.net.ssl.HostnameVerifier and made my CustomNameVerifier and did put that in SSLConnectionSocketFactory when making RestTemplate. 我实现了javax.net.ssl.HostnameVerifier并制作了CustomNameVerifier,并在制作RestTemplate时将其放在SSLConnectionSocketFactory中。 Code that follows 后面的代码

Bean code should go in main (With @SpringBootApplication) Bean代码应该放在main中(使用@SpringBootApplication)

@Bean
public RestTemplate template() throws Exception{

    RestTemplate restTemplate = new RestTemplate();

    KeyStore keyStore;
    KeyStore trustStore;
    HttpComponentsClientHttpRequestFactory requestFactory = null;
    try {
        keyStore = KeyStore.getInstance(keyStoreType);
        ClassPathResource classPathResource = new ClassPathResource(keyStoreLocation.getFilename());
        InputStream inputStream = classPathResource.getInputStream();
        keyStore.load(inputStream, keyStorePassword.toCharArray());

        trustStore = KeyStore.getInstance(trustStoreType);
        ClassPathResource classPathResourceTrust = new ClassPathResource(trustStoreLocation.getFilename());
        InputStream trustInput = classPathResourceTrust.getInputStream();
        trustStore.load(trustInput, trustStorePassword.toCharArray());

        javax.net.ssl.SSLContext sslContext = SSLContextBuilder.create()
                .loadKeyMaterial(keyStore, keyStorePassword.toCharArray())
                .loadTrustMaterial(trustStore, null).setProtocol("TLS")
                .build();
        SSLConnectionSocketFactory sslConnectionSocketFactory =
                new SSLConnectionSocketFactory(sslContext,
                new HostCustomVerifer(trustStoreLocation,trustStorePassword,trustStoreType));

        HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory)
                .setMaxConnTotal(Integer.valueOf(200))
                .setMaxConnPerRoute(Integer.valueOf(200))
                .build();

        requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        requestFactory.setReadTimeout(Integer.valueOf(10000));
        requestFactory.setConnectTimeout(Integer.valueOf(10000));

        restTemplate.setRequestFactory(requestFactory);
    } catch (Exception exception) {
        System.out.println("Exception Occured while creating restTemplate "+exception);
        exception.printStackTrace();
    }
    return restTemplate;
}

The CustomNameVerifier is as follows: (Even though you get a different error message ( it should be untrusted certificate you get for name validation failure. CustomNameVerifier如下所示:(即使您收到其他错误消息(由于名称验证失败,它也应该是不受信任的证书)。

public class HostCustomVerifer implements HostnameVerifier {

private String trustStorePassword;
private String trustResource;
private String trustType;

@Autowired
public HostCustomVerifer(@Value("${server.ssl.trust-store}") Resource trustResource,@Value("${server.ssl.trust-store-password}") String trustStorePassword,@Value("${server.ssl.trust-store-type}") String trustType) {
    this.trustStorePassword = trustStorePassword;
    this.trustResource = trustResource.getFilename();
    this.trustType = trustType;
}

@Override
public boolean verify(String hostName, SSLSession sslSession) {
    try {
        X509Certificate list[] =  (X509Certificate[]) sslSession.getPeerCertificates();
        int size = list.length;
        KeyStore trustStore = KeyStore.getInstance(trustType);
        ClassPathResource classPathResourceTrust = new ClassPathResource(trustResource);
        InputStream trustInput = classPathResourceTrust.getInputStream();
        trustStore.load(trustInput, trustStorePassword.toCharArray());  
        for (X509Certificate x509Certificate : list) {
            ArrayList<String> content = Collections.list(trustStore.aliases());
            for (String alias : content) {
                X509Certificate cert = (X509Certificate) trustStore.getCertificate(alias);
                boolean isSelfSigned = cert.getIssuerDN().equals(cert.getSubjectDN());
                if(cert.equals(x509Certificate) && (!isSelfSigned || size==1))
                    return true;
            }
        }
    } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException e) {
        e.printStackTrace();
    }
    return false;
}

} }

If someone suddenly stumbles upon this thread and finds a better way you can inform me. 如果有人突然发现该线程并找到更好的方法,您可以通知我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM