Apache HttpClient 4.2.3 - SSLException:证书中的主机名不匹配
我遇到这个大问题,我在google上找不到任何答案,所以我试着写在这里。Apache HttpClient 4.2.3 - SSLException:证书中的主机名不匹配
我正在开发一个Web服务客户端。要连接到Web服务,我必须创建一个带有客户端身份验证的ssl通道并使用代理(该呼叫必须从白名单IP完成)。为此,我在WebSphere 6.1环境下使用带Java 1.5的HttpClient 4.2.3库(与1.4兼容的模式)。
作为工作有关,我删除到实际的Web服务我打电话,所以你会发现这里的一些奇怪的变量/主机/类名称相关的内容有:)
我已经设置了这样
// Costruzione del proxy
Resources res = new Resources();
String proxyHost = res.get(PROXY_HOST);
int proxyPort = Integer.parseInt(res.get(PROXY_PORT));
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
// Costruzione del post method
String serviceUrl = "https://" + someHost + meth.getServiceURL();
HttpPost post = new HttpPost(serviceUrl);
post.addHeader("Content-Type", "text/xml");
String soapAction = "https://" + someHost + meth.getAction();
post.addHeader("SOAPAction", soapAction);
AbstractHttpEntity postBody = new StringEntity(soapEnvelope);
post.setEntity(postBody);
// Costruzione dell'ambiente di chiamata HTTPS
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keyStore, res.get(KEY_STORE_PASS_CONF), trustStore);
Scheme httpsScheme = new Scheme("https", HTTPS_PORT, sslSocketFactory);
Scheme httpScheme = new Scheme("http", HTTP_PORT, PlainSocketFactory.getSocketFactory());
final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(httpScheme);
schemeRegistry.register(httpsScheme);
PoolingClientConnectionManager connManager = new PoolingClientConnectionManager(schemeRegistry);
HttpParams params = new BasicHttpParams();
HttpClient client = new DefaultHttpClient(connManager, params);
client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
// CHIAMATA A MEF
HttpResponse resp = client.execute(post);
密钥库和信任的连接已经从一个文件
File ksFile = new File(res.get(KEY_STORE_PATH_CONF));
File tsFile = new File(res.get(TRUST_STORE_PATH_CONF));
InputStream ksInput, tsInput;
try {
ksInput = new FileInputStream(ksFile);
tsInput = new FileInputStream(tsFile);
} catch ... {}
KeyStore keyStore, trustStore;
try {
String ksPassword = res.get(KEY_STORE_PASS_CONF);
String tsPassword = res.get(TRUST_STORE_PASS_CONF);
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(ksInput, ksPassword.toCharArray());
trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(tsInput, tsPassword.toCharArray());
} catch ... {}
当我尝试调用client.execute方法加载,它给了我这个异常
javax.net.ssl.SSLException: hostname in certificate didn't match: <right.host.it> != <unknown.host.it>
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java)
at org.apache.http.conn.ssl.BrowserCompatHostnameVerifier.verify(BrowserCompatHostnameVerifier.java)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java)
at org.apache.http.conn.ssl.SSLSocketFactory.createLayeredSocket(SSLSocketFactory.java:628)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.updateSecureConnection(DefaultClientConnectionOperator.java:232)
at org.apache.http.impl.conn.ManagedClientConnectionImpl.layerProtocol(ManagedClientConnectionImpl.java:401)
at org.apache.http.impl.client.DefaultRequestDirector.establishRoute(DefaultRequestDirector.java:842)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:649)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:480)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java)
at my.class.package.MyClass.callWs(MyWebService.java)
有一些,我注意到奇怪的事情:
- 异常消息未知的主机是正确的的同一个域,所以它是从公司的证书有必要采取
- 我试图调试调用,它看起来像需要其他证书。 unknown.host.it地址取自x509证书的CN(在调试中看到),并且密钥库文件中的两个证书都包含其他CN。此外,我的密钥库中有两个证书,当调用第一个AbstractVerifier.verify方法时,在调用参数中有三个证书,其中一个证书实际上包含“unknown.host.it”字符串,找不到任何证书在unknown.host.it串在我拥有
这个问题可能是,我不知道怎么SSL实际工作,所以如果你看到任何完全错误的东西我说这可能解决我的问题的关键。
你能以某种方式帮助我吗? 非常感谢!
#更新1:后:) 因为我想先收到回复(本公司拥有对软件进行测试)的日子,我已经决定要强制软件接受每一个主机名。我想稍后改变它,但现在我只想让它工作。所以我加入这个(不建议使用:))线,以我的代码
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keyStore, res.get(KEY_STORE_PASS_CONF), trustStore);
sslSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
现在它把主机名验证(意外惊喜:)),但仍然给了我一个403 Forbidden错误。该服务的拥有者说我从一个白名单的IP呼叫,所以问题在于,我想,服务器不认识我。他们说我没有出示任何证书。
我的方法来添加证书是在SSLSocketFactory构造函数,我通过密钥库与我的证书里面。我是否需要指定HttpClient,以便在握手时自我识别服务器?
谢谢:)
这可能是因为您的服务器使用的服务器名称指示(SNI)对同一IP地址(和端口)有两个主机名。
您可以使用openssl
,通过显式指定服务器名称来检查。例如,这两个命令将连接到相同的IP地址,但要求不同的主机名和服务于不同的证书:
openssl s_client -connect www.google.co.uk:443 -servername www.google.co.uk
openssl s_client -connect www.google.co.uk:443 -servername www.google.com
的Java只支持目前客户端SNI,只有Java的,因为7
我使用Java 5 :) 但它实际上曾经工作过一次...它只是停止这样做,我仍然想知道为什么.. – CodingMonkey 2013-04-05 08:53:53
你应该真的升级。也许在服务器上发生了变化? – Bruno 2013-04-05 10:15:32
我同意,但是...我不是那个决定的人。作为所有大公司,我的*有点*不可能升级技术:) – CodingMonkey 2013-04-05 13:48:02
我通过激活可信主机名验证程序(即SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)找到了一个临时解决方案。我知道这不是正确的解决方案,但我有时间解决这个问题。 问题是HTTP一直给我返回一个403-禁止的错误。我打电话给服务支持,他们说我打电话从白名单IP(所以代理工作),但相互身份验证不起作用的原因。 – CodingMonkey 2013-04-05 08:41:07
相关:http://stackoverflow.com/q/7256955/435605 – 2016-07-06 16:32:43