6

ホスト名が間違っている例外が発生しました。プログラムでこのコードを使用しました (いくつかのリンクから取得)。プログラムは正常に動作しています。私の質問は十分に安全ですか?? (証明書チェーンを検証していないため)

public class Host {

    public String subscribe() throws Exception {   
        String resp = "";
        String urlString="https://xxx.xxx.xx.xx:8443/WebApplication3/NewServlet";
        URL url;
        URLConnection urlConn;
        DataOutputStream printout;
        DataInputStream input;
        String str = "";
        int flag=1;

        try {
            HostnameVerifier hv = new HostnameVerifier() {
                public boolean verify(String urlHostName, SSLSession session) {
                    System.out.println("Warning: URL Host: " + urlHostName + " vs. "
                      + session.getPeerHost());
                    return true;
                }
            };

            trustAllHttpsCertificates();
            HttpsURLConnection.setDefaultHostnameVerifier(hv);

            url = new URL(urlString);
            urlConn = url.openConnection();
            urlConn.setDoInput(true);
            Object object;
            urlConn.setUseCaches(false);

            urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            input = new DataInputStream(urlConn.getInputStream());

            while (null != ((str = input.readLine()))) {
                if (str.length() >0) {
                    str = str.trim();
                    if(!str.equals("")) {
                        //System.out.println(str);
                        resp += str;
                    }
                }
            }
            input.close();
        } catch ( MalformedURLException mue) { 
            mue.printStackTrace();
        } catch(IOException ioe) {
            ioe.printStackTrace();
        }
        return resp;
    }

    public static class miTM implements javax.net.ssl.TrustManager,
        javax.net.ssl.X509TrustManager {

        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) {
            return true;
        }

        public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) {
            return true;
        }

        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
            return;
        }

        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
            return;
        }
    }

    private static void trustAllHttpsCertificates() throws Exception {

        //  Create a trust manager that does not validate certificate chains:
        javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];

        javax.net.ssl.TrustManager tm = new miTM();

        trustAllCerts[0] = tm;

        javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("SSL");

        sc.init(null, trustAllCerts, null);

        javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

    }

}
4

4 に答える 4

4

のコードはmiTM実際には SSL セキュリティ チェックを無効にするため、セキュリティ レベルはかなり低くなります (SSL 証明書が壊れている場合にのみエラーが発生しますが、証明書がドメインと一致しない場合はエラーは発生しません)。

基本的に、セキュリティなしで接続しようとします。それが必要な場合、解決策は「十分に安全」かもしれませんが、ほとんどの場合、答えは「いいえ」です。

この種の問題の正しい解決策は、このドメインに一致する証明書を作成することです。

残念ながら、HTTP サーバーが「仮想ホスティング」を使用している場合 (= 多くのドメイン名が同じ IP アドレスにマップされている場合)、これは不可能です。この問題の正しい解決策は、独自の IP アドレスを取得することです。

それでも Java のみのソリューションを試してみたい場合は、https ://stackoverflow.com/a/3293720/34088 の回答をご覧ください。

于 2012-04-10T09:10:04.217 に答える
1

コードをクリーンアップし、安全を維持する方法を次に示します。コードは既知のサービス (信頼できる) に接続すると思います。ホスト名が一致しない場合でも Java SSL スタックが接続を受け入れるようにするには、サーバー証明書を JVM トラスト ストアに追加するのが最善の方法です。

まず、ブラウザからサーバー証明書をエクスポートして、ディスクに保存できます。Linux からはopenssl s_client -connect xxx.xxx.xx.xx:8443、サーバー証明書を ascii-armor 形式で使用し、テキスト ファイルにコピー/貼り付けできます。

jre/lib/security/cacerts次に、keytool を使用してサーバー証明書を JKS ファイルにインポートします。

keytool -import -alias myservice -file servercertificate.cer

私が好むもう 1 つのオプションは、Java が更新されたときのリグレッションを回避するためcacertsに、自分の場所にコピーして、javax.net.ssl.trustStoreシステム プロパティのおかげでそれを宣言することです。

サーバー証明書は信頼ストアにあるため、有効期限が切れるまで信頼されます。これは、自己署名サーバー証明書によく使用されます。

于 2012-04-14T19:45:56.637 に答える
1

このような種類の例外を取得するために使用される Java で何度も

問題は、ipconflict/ip-domain の不一致/無効な証明書である可能性があります

適切なIPアドレスを使用して証明書をインストールすることで解決しました。

于 2012-04-20T04:01:34.333 に答える
1

接続を安全にするには、 (少なくとも)次のことを行う必要があります。

  • 証明書を信頼していることを確認し、
  • ホスト名を確認します (これがおそらく信頼できる唯一の証明書であることが確実にわかっている場合を除きます)。

コードは次の 2 つの点で失敗します。

  • 使用TrustManagerしている は証明書をまったくチェックしません (例外をスローすることはありませんが、API はCertificateException証明書が信頼されていない場合にフォームをスローすることを期待しています)。
  • ホスト名ベリファイアは常に を返しますtrue

コードを修正するには:

  • デフォルトのトラスト マネージャーを保持するか、独自のトラスト ストアとデフォルトで初期化しますTrustManagerFactory
  • デフォルトのホスト名ベリファイアを保持します。

質問のタイトル (「ホスト名が正しくない例外」) と URL の例https://xxx.xxx.xx.xx:8443は、IP アドレスに接続していることを示唆しているようです。

一部のブラウザーとは異なり、Java は仕様 ( RFC 2818 ) に厳密に従っています。

タイプ dNSName の subjectAltName 拡張が存在する場合、それを ID として使用する必要があります。それ以外の場合は、証明書の Subject フィールドの (最も具体的な) Common Name フィールドを使用する必要があります。Common Name の使用は既存の慣行ですが、推奨されておらず、証明機関は代わりに dNSName を使用することをお勧めします。

[...]

場合によっては、URI がホスト名ではなく IP アドレスとして指定されます。この場合、iPAddress subjectAltName が証明書に存在し、URI の IP と正確に一致する必要があります。

これは、サーバー証明書のサブジェクト DN の共通名 (CN) に IP アドレスを入れるだけでは済まないことを意味します。IP アドレスを使用している場合は、サブジェクトの別名エントリにある必要があります。(Java 7 以降でkeytoolは、そのような証明書を生成するオプションがあります。)

使用するコマンドの詳細については、この回答を参照してください。

とはいえ、IP アドレスの使用は、せいぜいテスト環境でしか機能しません。商用の CA が IP アドレス ベースの証明書を提供するとは思いません。DNS エントリを設定することをお勧めします (hostsテスト環境のファイルだけであっても)。

IP アドレスを使用していない場合でも、サーバーに接続しようとしているホスト名に対してその証明書が有効であることを確認する必要があります。サブジェクトの別名エントリがある場合は、そのうちの 1 つがホスト名と一致する必要があります。 ; それ以外の場合、ホスト名は、この証明書のサブジェクト DN の CN RDN に含まれている必要があります。

于 2012-04-20T09:34:04.443 に答える