У меня возникла эта большая проблема, и я не могу найти ответ на google, поэтому я пытаюсь написать здесь.Apache HttpClient 4.2.3 - SSLException: имя хоста в сертификате не соответствует
Я разрабатываю клиент веб-сервиса. Чтобы подключиться к веб-службе, мне нужно создать ssl-канал с аутентификацией клиента и использовать прокси-сервер (вызов должен выполняться из белого списка). Для этого я использую библиотеку HttpClient 4.2.3 с Java 1.5 (режим совместимости с 1.4) в среде WebSphere 6.1.
Будучи связанным с работой, я удалить все ссылки на фактические вебах-службу, я звоню, чтобы вы найдете некоторые странные имена переменного/хоста/класса здесь и там :)
Я настроил соединение, как этот
// 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 берется из CN сертификата x509 (см. Отладку), и оба сертификата в файле хранилища ключей содержат другие CN. Более того, у меня есть два сертификата в моем хранилище ключей и когда вызывается первый метод AbstractVerifier.verify, в параметрах вызова есть три сертификата, один из которых содержит строку unknown.host.it в CN
- Фактически, t найти строку unknown.host.it в любом сертификате в моем распоряжении
Проблема, вероятно, в том, что я не знаю, как работает SSL, поэтому, если вы видите совершенно неправильную вещь в том, что я сказал, это, вероятно, ключ для решения моей проблемы.
Вы можете мне помочь? Спасибо большое!
#UPDATE 1: на следующий день после :) Так как я хочу, чтобы получить ответ первым (компания должна проверить программное обеспечение), я решил заставить программу принять все имя хоста. Я хочу изменить это позже, но сейчас я просто хочу, чтобы он работал. Так что я добавил это (устаревшее :)) строку в мой код
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keyStore, res.get(KEY_STORE_PASS_CONF), trustStore);
sslSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Сейчас она проходит проверку имени хоста (сюрприз-сюрприз :)), но все же дает мне 403-Запретный ошибку. Владелец сервиса говорит, что я звоню из белого списка, поэтому проблема заключается, я полагаю, в том, что сервер меня не узнает. Говорят, я не вручаю сертификат.
Мой способ добавить сертификат в конструктор SSLSocketFactory, где я передаю хранилище ключей с моим сертификатом внутри. Должен ли я указывать на HttpClient, который я хочу самостоятельно идентифицировать на сервере, когда вы подтверждаете связь?
Спасибо :)
Я нашел временное решение, активировав надежный верификатор имени хоста (например, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER). Я знаю, что это не правильное решение, но у меня будет время решить эту проблему. Проблема в том, что HTTP продолжает возвращать мне 403-запрещенную ошибку. Я вызвал службу поддержки, и они говорят, что я звоню из белого списка (так работает прокси), но по какой-то причине взаимная аутентификация не работает. – CodingMonkey
Связанный: http://stackoverflow.com/q/7256955/435605 –