2011-08-31 28 views
35

次のコードを使用して、Googleのサービスに接続しています。このコードは私のローカルマシン上でうまく働いた:Java SSLException:証明書のホスト名が一致しません

HttpClient client=new DefaultHttpClient(); 
HttpPost post = new HttpPost("https://www.google.com/accounts/ClientLogin"); 
post.setEntity(new UrlEncodedFormEntity(myData)); 
HttpResponse response = client.execute(post); 

私はGoogle.comをブロックしていた本番環境でこのコードを置きます。リクエストに応じて、GoogleのIPの1つであるIP:74.125.236.52にアクセスできるようにして、Googleサーバーとの通信を許可しました。私はホストファイルを編集してこのエントリも追加しました。

まだ私はURLにアクセスできませんでした。なぜそれが不思議ですか?だから、私は上記のコードを置き換える:

javax.net.ssl.SSLException:

HttpPost post = new HttpPost("https://74.125.236.52/accounts/ClientLogin"); 

は今、私はこのようなエラーが出る証明書内のホスト名が一致しませんでした: <74.125.236.52>!= <www.google.com>

私は、Googleが複数のIPアドレスを持っているので、これがあると思います。ネットワーク管理者にこれらすべてのIPへのアクセスを許可することはできません。このリスト全体を取得することさえできないかもしれません。

どうすればいいですか? Javaレベルで回避策がありますか?それとも、それはネットワークの人の手に完全に入っていますか?

+0

SSL証明書は通常、名前がそれを適用する* *特定のドメインが付属しており、その名前が一致しない場合名前を要求すると、クライアントは接続が正しく認証されていないことを警告します。クライアントが接続に対して明示的な証明書の上書きを指定できるかどうかを確認できます。 –

+3

URLのホスト名は、証明書のホスト名と一致する必要があります。 hostsファイルを使って動作させてください。そうでない場合は、74.125.236.52のgoogle.comも受け入れるように証明書検証ルーチンを変更することができます(寛大すぎないようにしてください)。 – Thilo

+0

@Thilo:検証ルーチンをオーバーライドする方法は? – WinOrWin

答えて

4

ありがとうVineet Reynolds。あなたが提供したリンクには、多くのユーザーのコメントがありました。そのうちの1つは、私が絶望的に​​試みたもので、助けになりました。私はこの方法を追加しました:

// Do not do this in production!!! 
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){ 
    public boolean verify(String string,SSLSession ssls) { 
     return true; 
    } 
}); 

これは私にとって今のところうまくいくようですが、この解決法は一時的なものです。私はなぜ私のホストファイルが無視されているのかを知るためにネットワークの人々と協力しています。

+12

これは実際には悪い考えです。ちなみに、 '/ etc/nsswitch.conf'のルックアップ順序を確認し、hostsファイルがルックアップで無視されているかどうかを確認したい場合があります。 –

+0

if(url.startsWith( "https:// localhost"))はSSLを無効にします –

+12

これは馬鹿馬鹿しく悪い考え、一時的なない。この文脈であなたにとってはうまくいくかもしれませんが、同様の問題のためにこの質問を見つける他の人には一般化することはできません。 SSLを使用しないこともできます。少なくともその場合は、セキュリティについて誰にも冗談を言っているわけではありません。おそらく決定的に問題を編集し、実際に問題を解決して(ネットワーク担当者などと話した後)、それを受け入れるだけです。 – roguesys

22

証明書の確認プロセスでは、クライアントが使用するURLのサーバーのホスト名を使用して、サーバーによって提示された証明書のDNS名が常に検証されます。

次のコード

HttpPost post = new HttpPost("https://74.125.236.52/accounts/ClientLogin"); 

は、サーバが発行した証明書の共通名、即ちwww.google.com、すなわち74.125.236.52ホスト名と一致するかどうかを検証する証明書検証プロセスをもたらすであろう。明らかに、これは失敗に結びついています(ブラウザでURL https://74.125.236.52/accounts/ClientLoginをブラウズして確認し、結果のエラーを自分自身で確認できます)。

おそらく、安全のために、自分自身を書くのを躊躇しています(安全なものを書く方法を理解していない場合は気にしません)。データセンターにDNSレコードを設定して、 www.google.comへのすべてのルックアップは74.125.236.52に解決されます。これはあなたのローカルDNSサーバまたはあなたのOSのhostsファイルで行われるべきです。他のドメインにもエントリを追加する必要があります。言うまでもなく、これがあなたのISPから返されたレコードと一貫していることを確認する必要があります。

+0

ホストファイルを編集すると問題は解決するはずです...他に何ができるのだろうか?ところで、TrustManagerはこの場合私を助けますか?あなたは私がこれを正しく使う方法を学ぶべき場所を指摘できますか?ありがとう。 – WinOrWin

+0

@WinOrWin [このサイト](http://exampledepot.com/egs/javax.net.ssl/TrustAll.html)には、TrustManagerの例が含まれています。しかし、TrustManagerはサーバーの証明書をまったく検証しないため、本番環境では同じコードを使用することはお勧めしません。あなたがしなければならないことは、 '74.125.236.52 'によって提示されたサーバが' www.google.com'に発行されたことを確認するためのチェックを組み込むことです。これはもちろん、あなたのDNSルックアップを修正するのと同じくらい好まれません。私はWiresharkのダンプを取得して何がうまくいかないのかを見て、 'hosts'ファイルが無視されるようにすることを提案します。 –

+3

これはTrustManagerの問題ではありません。これはHTTPS HostnameVerifierの問題です。 DNSソリューションはそれを解決します。 TrustManagerがそれを解決する可能性はありません。 – EJP

25

hereのようにHostnameVerifierを設定することもできます。これは私がこのエラーを回避するために働いた。

// Do not do this in production!!! 
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER; 

DefaultHttpClient client = new DefaultHttpClient(); 

SchemeRegistry registry = new SchemeRegistry(); 
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory(); 
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier); 
registry.register(new Scheme("https", socketFactory, 443)); 
SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry); 
DefaultHttpClient httpClient = new DefaultHttpClient(mgr, client.getParams()); 

// Set verifier  
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); 

// Example send http request 
final String url = "https://encrypted.google.com/"; 
HttpPost httpPost = new HttpPost(url); 
HttpResponse response = httpClient.execute(httpPost); 
+3

このホスト名ベリファイアを使用することは一般的には悪い考えです([こちらの質問](http://security.stackexchange.com/q/22965/2435))。 – Bruno

+0

Java 1.8の廃止予定リソース – nabsATX

+0

なぜそれを2回設定しますか? – pedr0

2

懸念しているのは、ALLOW_ALL_HOSTNAME_VERIFIERを使用しないことです。

私は自分のホスト名検証プログラムを実装しますか?

class MyHostnameVerifier implements org.apache.http.conn.ssl.X509HostnameVerifier 
{ 
    @Override 
    public boolean verify(String host, SSLSession session) { 
     String sslHost = session.getPeerHost(); 
     System.out.println("Host=" + host); 
     System.out.println("SSL Host=" + sslHost);  
     if (host.equals(sslHost)) { 
      return true; 
     } else { 
      return false; 
     } 
    } 

    @Override 
    public void verify(String host, SSLSocket ssl) throws IOException { 
     String sslHost = ssl.getInetAddress().getHostName(); 
     System.out.println("Host=" + host); 
     System.out.println("SSL Host=" + sslHost);  
     if (host.equals(sslHost)) { 
      return; 
     } else { 
      throw new IOException("hostname in certificate didn't match: " + host + " != " + sslHost); 
     } 
    } 

    @Override 
    public void verify(String host, X509Certificate cert) throws SSLException { 
     throw new SSLException("Hostname verification 1 not implemented"); 
    } 

    @Override 
    public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException { 
     throw new SSLException("Hostname verification 2 not implemented"); 
    } 
} 

共有サーバーでホストされているhttps://www.rideforrainbows.org/に対してテストしましょう。

public static void main (String[] args) throws Exception { 
    //org.apache.http.conn.ssl.SSLSocketFactory sf = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory(); 
    //sf.setHostnameVerifier(new MyHostnameVerifier()); 
    //org.apache.http.conn.scheme.Scheme sch = new Scheme("https", 443, sf); 

    org.apache.http.client.HttpClient client = new DefaultHttpClient(); 
    //client.getConnectionManager().getSchemeRegistry().register(sch); 
    org.apache.http.client.methods.HttpPost post = new HttpPost("https://www.rideforrainbows.org/"); 
    org.apache.http.HttpResponse response = client.execute(post); 
    java.io.InputStream is = response.getEntity().getContent(); 
    java.io.BufferedReader rd = new java.io.BufferedReader(new java.io.InputStreamReader(is)); 
    String line; 
    while ((line = rd.readLine()) != null) { 
     System.out.println(line); 
    } 
} 

SSLException:スレッドで

例外 "メイン" javax.net.ssl.SSLExceptionは:証明書内のホスト名が一致しませんでした!www.rideforrainbows.org = stac.rt.sg OR org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:231)でstac.rt.sg OR www.stac.rt.sg
MyHostnameVerifierで
...

います:

public static void main (String[] args) throws Exception { 
    org.apache.http.conn.ssl.SSLSocketFactory sf = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory(); 
    sf.setHostnameVerifier(new MyHostnameVerifier()); 
    org.apache.http.conn.scheme.Scheme sch = new Scheme("https", 443, sf); 

    org.apache.http.client.HttpClient client = new DefaultHttpClient(); 
    client.getConnectionManager().getSchemeRegistry().register(sch); 
    org.apache.http.client.methods.HttpPost post = new HttpPost("https://www.rideforrainbows.org/"); 
    org.apache.http.HttpResponse response = client.execute(post); 
    java.io.InputStream is = response.getEntity().getContent(); 
    java.io.BufferedReader rd = new java.io.BufferedReader(new java.io.InputStreamReader(is)); 
    String line; 
    while ((line = rd.readLine()) != null) { 
     System.out.println(line); 
    } 
} 

ショー:

ホスト= www.rideforrainbows.org
SSLホスト= www.rideforrainbows.org

少なくとも私は(ホストを比較するロジックを持っている== SSLホスト)を返し、trueを返します。

上記のソースコードは、httpclient-4.2.3.jarおよびhttpclient-4.3.3.jarで動作しています。 HTTPClientの-4.3.3.jarで

+1

あなたの実装は逆DNSルックアップだけを行い、証明書からは何もチェックしていません。したがって、安全ではありません。 – Bruno

+0

これは私の問題を解決しました – Jxadro

4

、別のHttpClientを使用することがあります:。

public static void main (String[] args) throws Exception { 
    // org.apache.http.client.HttpClient client = new DefaultHttpClient(); 
    org.apache.http.client.HttpClient client = HttpClientBuilder.create().build(); 
    System.out.println("HttpClient = " + client.getClass().toString()); 
    org.apache.http.client.methods.HttpPost post = new HttpPost("https://www.rideforrainbows.org/"); 
    org.apache.http.HttpResponse response = client.execute(post); 
    java.io.InputStream is = response.getEntity().getContent(); 
    java.io.BufferedReader rd = new java.io.BufferedReader(new java.io.InputStreamReader(is)); 
    String line; 
    while ((line = rd.readLine()) != null) { 
     System.out.println(line); 
    } 
} 

このHttpClientBuilder.createは、()(ビルド)org.apache.http.implを返します。 client.InternalHttpClient。それはこののホスト名に対応していないので、の問題と一致しません。

+0

この回答は、HTTPライブラリをダウンロードするためのリンクを提供することで改善することができます。 –

+0

これはどのように答えを解決しますか? –

+0

また、これはhttps://issues.apache.org/jira/browse/HTTPCLIENT-1119に関連していますか? –

7

httpcliet4.3.3のよりクリーンなアプローチ(テスト環境のみ)は次のとおりです。

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 

CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); 
+0

これらのクラスは利用できません –

13

私にも同様の問題がありました。私はAndroidのDefaultHttpClientを使用していました。私はHttpsURLConnectionがこの種の例外を処理できることを読んでいます。だから、HttpsURLConnectionからのベリファイアを使用するカスタムHostnameVerifierを作成しました。実装をカスタムHttpClientにラップしました。ここで

public class CustomHttpClient extends DefaultHttpClient { 

public CustomHttpClient() { 
    super(); 
    SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory(); 
    socketFactory.setHostnameVerifier(new CustomHostnameVerifier()); 
    Scheme scheme = (new Scheme("https", socketFactory, 443)); 
    getConnectionManager().getSchemeRegistry().register(scheme); 
} 

はCustomHostnameVerifierクラスです:

public class CustomHostnameVerifier implements org.apache.http.conn.ssl.X509HostnameVerifier { 

@Override 
public boolean verify(String host, SSLSession session) { 
    HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier(); 
    return hv.verify(host, session); 
} 

@Override 
public void verify(String host, SSLSocket ssl) throws IOException { 
} 

@Override 
public void verify(String host, X509Certificate cert) throws SSLException { 

} 

@Override 
public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException { 

} 

}

+0

保存された私の日:)ありがとう!! –

+0

SSL証明書を渡すことで、一種のことを知りたいですか? –

+1

HttpsURLConnectionベリファイアを使用してUR1を検証しているため、SSLをバイパスすることはありません。 – granko87

関連する問題