我遇到了这个大问题而我在谷歌上找不到任何答案,所以我试着写在这里。
我正在开发一个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)
我注意到有些奇怪的事情:
问题可能是我不知道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?
谢谢:)
答案 0 :(得分:3)
您的服务器可能使用服务器名称指示(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。