無効な SSL 証明書を使用して、Web サーバーでホストされている HTTP サービスを呼び出す必要があります。開発では、 keytoolを使用して証明書をインポートしていますが、クライアントのインストールごとに証明書が異なるため、バンドルすることはできません。
序文: SSL 検証をスキップするのは本当に見苦しいことです。この特定のケースでは、SSL を必要とせず、システム内の他のすべての通信は単純な HTTP を介して行われます。だから私はMITM攻撃などについて本当に気にしません。データ用の SSL がないため、攻撃者は SSL を破る必要はありません。これは、私が制御できないレガシー システムのサポートです。
と を持つを使用HttpURLConnection
しています。これは、私が試したいくつかの自己署名サーバーでは機能しますが、顧客のサイトでは機能しません。私が得ているエラーは次のとおりです。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
有効なホストを制限する方法がありますが、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 コードで無効な証明書を無視させたいと思います。
何か案が ?