Игнорирование проверки SSL в Java
я должен вызвать службу HTTP, размещенную на веб-сервере с недопустимым сертификатом SSL. В dev я импортирую сертификат с помощью keytool но сертификат будет отличаться при каждой установке клиента,поэтому я не могу просто связать его.
Предисловие: I DO знайте, что пропуск проверки SSL действительно уродлив. В этом конкретном случае мне даже не нужен SSL, а все остальные коммуникации в системе - через простой HTTP. Так что мне действительно все равно. о нападениях MITM или таких. Злоумышленнику не нужно будет заходить так далеко, чтобы нарушить SSL, потому что для данных нет SSL. Это поддержка устаревшей системы, над которой у меня нет контроля.
я использую HttpURLConnection
С SSLSocketFactory
что есть NaiveTrustManager
и a NaiveHostnameVerifier
. Это работает на некоторых самозаверяющих серверах, которые я пробовал, но не на сайте клиента. Ошибка, которую я получаю:
javax.net.ssl.SSLKeyException: [Security:090477]Certificate chain received from xxxxxxxxxx was not trusted causing SSL handshake failure.
at com.certicom.tls.interfaceimpl.TLSConnectionImpl.fireException(Unknown Source)
at com.certicom.tls.interfaceimpl.TLSConnectionImpl.fireAlertSent(Unknown Source)
at com.certicom.tls.record.handshake.HandshakeHandler.fireAlert(Unknown Source)
at com.certicom.tls.record.handshake.HandshakeHandler.fireAlert(Unknown Source)
at com.certicom.tls.record.handshake.ClientStateReceivedServerHello.handle(Unknown Source)
at com.certicom.tls.record.handshake.HandshakeHandler.handleHandshakeMessage(Unknown Source)
at com.certicom.tls.record.handshake.HandshakeHandler.handleHandshakeMessages(Unknown Source)
at com.certicom.tls.record.MessageInterpreter.interpretContent(Unknown Source)
at com.certicom.tls.record.MessageInterpreter.decryptMessage(Unknown Source)
at com.certicom.tls.record.ReadHandler.processRecord(Unknown Source)
at com.certicom.tls.record.ReadHandler.readRecord(Unknown Source)
at com.certicom.tls.record.ReadHandler.readUntilHandshakeComplete(Unknown Source)
at com.certicom.tls.interfaceimpl.TLSConnectionImpl.completeHandshake(Unknown Source)
at com.certicom.tls.record.WriteHandler.write(Unknown Source)
at com.certicom.io.OutputSSLIOStreamWrapper.write(Unknown Source)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
at java.io.FilterOutputStream.flush(FilterOutputStream.java:123)
at weblogic.net.http.HttpURLConnection.writeRequests(HttpURLConnection.java:154)
at weblogic.net.http.HttpURLConnection.getInputStream(HttpURLConnection.java:358)
at weblogic.net.http.SOAPHttpsURLConnection.getInputStream(SOAPHttpsURLConnection.java:37)
at weblogic.net.http.HttpURLConnection.getResponseCode(HttpURLConnection.java:947)
at (my own code)
мой SimpleSocketFactory
выглядит так:
public static final SSLSocketFactory getSocketFactory()
{
if ( sslSocketFactory == null ) {
try {
// get ssl context
SSLContext sc = SSLContext.getInstance("SSL");
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{
new NaiveTrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
log.debug("getAcceptedIssuers");
return new java.security.cert.X509Certificate[0];
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
log.debug("checkClientTrusted");
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
log.debug("checkServerTrusted");
}
}
};
sc.init(null, trustAllCerts, new java.security.SecureRandom());
// EDIT: fixed the following line that was redeclaring SSLSocketFactory sslSocketFactory, returning null every time. Same result though.
sslSocketFactory = sc.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
// EDIT: The following line has no effect
//HttpsURLConnection.setDefaultHostnameVerifier(new NaiveHostNameVerifier());
} catch (KeyManagementException e) {
log.error ("No SSL algorithm support: " + e.getMessage(), e);
} catch (NoSuchAlgorithmException e) {
log.error ("Exception when setting up the Naive key management.", e);
}
}
return sslSocketFactory;
}
на NaiveHostnameVerifier
имеет способ ограничить допустимые хосты, но он оставил null, поэтому в основном принимает что-либо:
public class NaiveHostnameVerifier implements HostnameVerifier {
String[] patterns;
public NaiveHostnameVerifier () {
this.patterns=null;
}
public NaiveHostnameVerifier (String[] patterns) {
this.patterns = patterns;
}
public boolean verify(String urlHostName,SSLSession session) {
if (patterns==null || patterns.length==0) {
return true;
} else {
for (String pattern : patterns) {
if (urlHostName.matches(pattern)) {
return true;
}
}
return false;
}
}
}
синтаксис такой:
try {
conn = (HttpURLConnection)url.openConnection();
if (conn instanceof HttpsURLConnection) {
((HttpsURLConnection)conn).setSSLSocketFactory(SimpleSSLSocketFactory.getSocketFactory());
// EDIT: added this line, the HV has to be set on connection, not on the factory.
((HttpsURLConnection)conn).setHostnameVerifier(new NaiveHostnameVerifier());
}
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-type","application/x-www-form-urlencoded");
conn.connect();
StringBuffer sbContent = new StringBuffer();
// (snip)
DataOutputStream stream = new DataOutputStream(conn.getOutputStream ());
stream.writeBytes(sbContent.toString());
stream.flush();
stream.close();
} catch (ClassCastException e) {
log.error("The URL does not seem to point to a HTTP connection");
return null;
} catch (IOException e) {
log.error("Error accessing the requested URL", e);
return null;
}
когда я ищу сообщение об ошибке, большинство людей просто импортируют сертификат в свой магазин, но опять же, я не могу этого сделать, потому что я не знаю, какой сертификат это будет. Моя единственная альтернатива, если это не сработает, - сделать инструмент, который может загрузить сертификат и добавить его более простым способом, чем зашифрованные командные строки, но я бы предпочел мой Java-код просто игнорирует недопустимый сертификат.
есть идеи ?
2 ответов
на самом деле нет ничего плохого в коде выше. Проблема, похоже, связана с Weblogic и этим модулем TLS Certicom. Когда я смотрю на сервер параметры протокол SSL и дополнительно Я вижу, что могу указать пользовательский HostnameVerifier (SSLMBean.HostnameVerifier), но единственный элемент, предполагающий возможность вмешательства в проверку сертификата, является устаревшим.
Я попробовал вышеуказанный код вне Weblogic, и он работал красиво (исправлен HostnameVerifier в сообщении, хотя).
затем я попытался добавить "- DUseSunHttpHandler=true " к веблогическим параметрам, как это было предложено ipolevoy в это другой вопрос. Это начало работать.
при этом переключение обработчика HTTP на сервер служебной шины Oracle кажется немного рискованным. Вполне могут быть побочные эффекты, которые вернутся, чтобы укусить меня через несколько недель...
Я также попытался определить свой собственный trustStore и указать его jssecacert, который содержал необходимый ключ. Он также был проигнорирован Weblogic, потому что у него есть своя настройка trustStore для каждого сервера. Поэтому я прибегаю к тому, чтобы попросить администратора вручную импортировать необходимые ключи или указать Weblogic в мой собственный магазин.
на самом деле, это ошибка know в версиях Weblogic ниже 10.3.5, для которой есть патч, доступный от Oracle. Подробности см. В документе 1474989.1 в моей поддержке Oracle.
исправление выше не рекомендуется (но поддерживается) обходной путь Oracle, который будет работать, но не является предпочтительным решением.
предпочтительным решением является загрузка исправления, упомянутого в статье Oracle, и замена верификатора имени хоста SSL на новый, который также часть Weblogic 10.3.5 и выше. Если вы хотите оставаться совместимым с Oracle с точки зрения поддержки, это путь.