我正在设置一个应该与其他可信服务器通信的服务器,并且我正在与apache tomcat在spring boot中使用SSL握手。
我成功地实现了双向SSL,但是,如果我添加了在信任存储中生成证书的Root CA,它会自动信任来自该CA的每个子节点。我希望它检查该特定证书是否在信任存储中,而不仅仅是来自同一父证书。
第二台服务器的应用程序属性是相同的,只是KeyStore和信任库不同。
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=8111main.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();
}
}控制器中的通信:
@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);
}
}我预计这个场景会失败,无论它成功了:
Server1:信任RootCA、Server1、Server2
Server2:信任RootCA,Server2
Server1开始与Server2通信并以某种方式成功
我预计它会失败,因为Server2不信任Server1
发布于 2019-05-31 05:55:40
然而,这并不是最好的解决方案,它可以满足我的需求,如果双方都不信任,就会导致通信失败。我实现了javax.net.ssl.HostnameVerifier并制作了我的CustomNameVerifier,并在制作RestTemplate时将其放入了SSLConnectionSocketFactory中。下面的代码
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;
}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;
}}
如果有人突然发现了这个帖子,并找到了更好的方法,你可以通知我。
https://stackoverflow.com/questions/55777324
复制相似问题