無効なSSL証明書を使用してWebサーバー上でホストされているHTTPサービスを呼び出す必要があります。デベロッパーでは、キーツールで証明書をインポートしていますが、クライアントのインストールごとに証明書が異なるため、単にそれをバンドルすることはできません。JavaでSSL検証を無視する
序文:I DO SSL検証をスキップするのは本当に醜いことがわかります。この特定のケースでは、私はSSLを必要とせず、システム内の他のすべての通信は単純なHTTP経由です。だから、私は本当にMITMの攻撃などについては気にしません。攻撃者は、データのSSLが存在しないため、SSLを破るために遠ざかる必要はありません。これは私が制御できないレガシーシステムのサポートです。
SSLSocketFactory
にNaiveTrustManager
と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
が有効なホストを制限する方法がありますが、それはとても基本的に何でも受け入れて、ヌル残っている:
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コードでは無効な証明書を無視するようにしたいと考えています。
証明書(およびホスト名)の検証を無視すると潜在的なMITM攻撃への接続が開かれることに気付きますか?証明書のエラーを無視する必要がある場合、(管理)セキュリティ手順に欠陥があるようです。また、デフォルトのJSSEプロバイダを使用していないため、Certicom TLSオプションを参照する必要があります。 – Bruno
私はMITM攻撃をよく承知しています。実際、誰かがそのネットワーク上でMITM攻撃を行うことができれば、我々は彼がコミュニケーションを傍受するよりもずっと大きな問題を抱えています。最初は、私が証明書を取得したソフトウェアのステップを検討していましたが、ユーザーが検証した場合は、それを保管しておいてください。しかし、これは私が取り組もうとしている小さな問題よりも大きな課題です。私はCerticom TLSオプションを見ていきます。 –
WebLogicはSSLで恐ろしい、恐ろしいことをしてしまいました。彼らはJava標準に従っていません。 – erickson