我想询问我在Java 1.7上使用Spring Web Services 2.2.2.RELEASE(在Spring Boot 1.3.0.M4下)观察到的主机验证行为差异的根源。
配置WebServiceTemplate以使用HttpComponentsMessageSender时,我最终得到的错误如下:
I/O error: Certificate for <uswebservice.uat.hroffice.com> doesn't match any of the subject alternative names: [*.sbcsystems.com, sbcsystems.com]; nested exception is javax.net.ssl.SSLException: Certificate for <uswebservice.uat.hroffice.com> doesn't match any of the subject alternative names: [*.sbcsystems.com, sbcsystems.com]
当我重新配置WebServiceTemplate以使用 HttpsUrlConnectionMessageSender 时,上述错误消失,并且验证主机名(根据RFC 2818)的调用似乎不会发生在上面的例子中(或者至少我无法追踪它)虽然我可以看到Spring的 HttpUrlConnectionMessageSender 的连接( sun.net.www.protocol.https.DelegateHttpsURLConnection:在 createConnection()方法中为 https:// uswebservice.uat.hroffice.com/IWSUAT/customS ... 分配了相应的主机名验证程序 (javax.net.ssl.HttpsURLConnection中$ DefaultHostnameVerifier)。
在成功案例中是否发生了主机名验证(我倾向于认为它仍然存在,但我很感激能够发现如何调用)我正在质疑的是 HttpComponentsMessageSender (错误案例)获取证书失败的证书。我检查了Java安装附带的 cacerts 文件( \ jdk1.7.0_76 \ jre \ lib \ security ) - 它不在那里。安全URL(上方)中出现的证书包含正确的主题Alt名称:
Not Critical
DNS Name: *.uat.hroffice.com
DNS Name: uat.hroffice.com
而在产生错误的情况下,证书似乎具有以下Subject Alt Name字段的值,这使得它无法按照规范尝试取消引用上面的URI:
[8]: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
DNSName: *.sbcsystems.com
DNSName: sbcsystems.com
]
有问题的神秘证书在SoapUI中也是可见的(这是我能够获得的)然而在该工具中运行测试不会产生错误 - 我假设主机名验证正在某种程度上绕过那里(也许有人知道细节)。
证书是两个不同的证书,因为它们有不同的到期日期。
我还尝试通过提供 HostnameVerifier 的noop实现来禁用主机名验证,但这似乎是一种有问题的安全方法,所以在放弃它的过程中我被所描述的问题弄得一团糟以上。
如果有人能够对我正在经历的症状有所了解,我会提前感激。
以下是相关的Spring配置:
@Bean
public KeyStore keyStore() throws Throwable {
KeyStoreFactoryBean keyStoreFactory = new KeyStoreFactoryBean();
keyStoreFactory.setPassword(keyStorePassword);
keyStoreFactory.setLocation(new ClassPathResource(keyStoreName));
keyStoreFactory.setType("jks");
keyStoreFactory.afterPropertiesSet();
return keyStoreFactory.getObject();
}
@Bean
public KeyManager[] keyManagers() throws Throwable{
KeyManagersFactoryBean keyManagerFactory = new KeyManagersFactoryBean();
keyManagerFactory.setKeyStore(keyStore());
keyManagerFactory.setPassword(keyStorePassword);
keyManagerFactory.afterPropertiesSet();
return keyManagerFactory.getObject();
}
@Bean
public HttpsUrlConnectionMessageSender httpsUrlSender() throws Throwable {
HttpsUrlConnectionMessageSender sender = new HttpsUrlConnectionMessageSender();
sender.setSslProtocol("TLS");
sender.setKeyManagers(keyManagers());
/*sender.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}});*/
return sender;
}
@Bean
public WebServiceTemplate webServiceTemplate() throws Throwable {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMarshaller(marshaller());
webServiceTemplate.setUnmarshaller(marshaller());
webServiceTemplate.setDefaultUri(defaultUri);
webServiceTemplate.setMessageFactory(messageFactory());
webServiceTemplate.setMessageSender(/*new HttpComponentsMessageSender()*/httpsUrlSender());
return webServiceTemplate;
}
在ssllabs.com上分析网站时,据报道只能在支持SNI的浏览器中工作,也许从Java中使用它的事实也会对上述行为产生一些影响(但Java 1.7似乎增加了正确的SNI行为)默认情况下)?