2012-03-05 10 views
22

"javax.net.ssl.SSLPeerUnverifiedException: No peer certificate error"は、Android 2.3を実行しているが4ではなくエミュレータで取得しています。私はhttps経由でライブサーバーに接続しようとしています。有効なThawte証明書を使用し、すべてのブラウザとAndroid 3および4で正常に動作します。Android 2.3では「ピア証明書がありません」エラーですが、4ではありません

誰かがコードヘルプをお持ちの場合は、ありがとう。また、誰かが安全な回避策について提案している場合、私はそれを感謝します。私はまだ学んでいる、私はこの問題に1週間いた。それは終わらなければならないので、私は仕事と学習を続けることができます。 Urgh。ここで

はHttpClientをコード、礼儀アントワーヌ・ハウク(http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/)です:

DefaultHttpClient client = new MyHttpClient(getApplicationContext()); 

      HttpPost post = new HttpPost(server_login_url); 
      List <NameValuePair> parameters = new ArrayList <NameValuePair>(); 
      parameters.add(new BasicNameValuePair("username", user)); 
      parameters.add(new BasicNameValuePair("password", pass)); 

      try { 
       post.setEntity(new UrlEncodedFormEntity(parameters, HTTP.UTF_8)); 
      } catch (UnsupportedEncodingException e2) { 
       // TODO Auto-generated catch block 
       Log.d(DEBUG_TAG, "in UnsupportedEncodingException - " + e2.getMessage()); 
       e2.printStackTrace(); 
      } 
       // Execute the GET call and obtain the response 
      HttpResponse getResponse = null; 

      try { 
       getResponse = client.execute(post); 
      } catch (ClientProtocolException e) { 
       // TODO Auto-generated catch block 
       // Toast.makeText(getBaseContext(),message,Toast.LENGTH_LONG).show(); 
       Log.d(DEBUG_TAG, "in ClientProtocolException - " + e.getMessage()); 
      } catch (IOException e) { 
       // TODO Auto-generated catch block 
       // Toast.makeText(getBaseContext(),message,Toast.LENGTH_LONG).show(); 
       Log.d(DEBUG_TAG, "in client.execute IOException - " + e.getMessage()); 
       e.printStackTrace(); 
      } 

エラーがIOExceptionをブロックでキャッチされています

import java.io.InputStream; 
    import java.security.KeyStore; 
    import java.security.cert.CertificateException; 

    import javax.net.ssl.SSLContext; 
    import javax.net.ssl.TrustManager; 
    import javax.net.ssl.X509TrustManager; 
    import javax.security.cert.X509Certificate; 

    import org.apache.http.conn.ClientConnectionManager; 
    import org.apache.http.conn.scheme.PlainSocketFactory; 
    import org.apache.http.conn.scheme.Scheme; 
    import org.apache.http.conn.scheme.SchemeRegistry; 
    import org.apache.http.conn.ssl.SSLSocketFactory; 
    import org.apache.http.impl.client.DefaultHttpClient; 
    import org.apache.http.impl.conn.SingleClientConnManager; 

    import android.content.Context; 

    public class MyHttpClient extends DefaultHttpClient { 

    final Context context; 

    public MyHttpClient(Context context) { 
     this.context = context; 
    } 

    @Override 
    protected ClientConnectionManager createClientConnectionManager() { 
     SchemeRegistry registry = new SchemeRegistry(); 
     registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 
     // Register for port 443 our SSLSocketFactory with our keystore 
     // to the ConnectionManager 
     registry.register(new Scheme("https", newSslSocketFactory(), 443)); 
     return new SingleClientConnManager(getParams(), registry); 
    } 

    private SSLSocketFactory newSslSocketFactory() { 
     try { 
      // Get an instance of the Bouncy Castle KeyStore format 
      KeyStore trusted = KeyStore.getInstance("BKS"); 
      // Get the raw resource, which contains the keystore with 
      // your trusted certificates (root and any intermediate certs) 
      InputStream in = context.getResources().openRawResource(R.raw.my_cert); 
      try { 
       // Initialize the keystore with the provided trusted certificates 
       // Also provide the password of the keystore 
       trusted.load(in, "my_pass".toCharArray()); 
      } finally { 
       in.close(); 
      } 

      // Pass the keystore to the SSLSocketFactory. The factory is responsible 
      // for the verification of the server certificate. 
      SSLSocketFactory sf = new SSLSocketFactory(trusted); 
      // Hostname verification from certificate 
      // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506 
      sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); 
      return sf; 
     } catch (Exception e) { 
      throw new AssertionError(e); 
     } 
    } 
} 

そして、ここではそれをインスタンス化するコードです。ここではスタックは次のとおりです。

javax.net.ssl.SSLPeerUnverifiedException: No peer certificate 
org.apache.harmony.xnet.provider.jsse.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:258) 
org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:93) 
org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:381) 
org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:164) 
org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164) 
org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119) 
org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:359) 
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555) 
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487) 
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465) 
org.ffb.tools.SplashActivity$LoginTask.makeConnection(SplashActivity.java:506) 
org.ffb.tools.SplashActivity$LoginTask.doLogin(SplashActivity.java:451) 
org.ffb.tools.SplashActivity$LoginTask.doInBackground(SplashActivity.java:439) 
org.ffb.tools.SplashActivity$LoginTask.doInBackground(SplashActivity.java:1) 
android.os.AsyncTask$2.call(AsyncTask.java:185) 
java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306) 
java.util.concurrent.FutureTask.run(FutureTask.java:138) 
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088) 
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581) 
java.lang.Thread.run(Thread.java:1019) 

ここで(opensslコマンドから)チェーンの順序は次のとおりです。

チェーンは、私が思うよさそうです。

i:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA 
    1 s:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA 
    i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized  use only/CN=thawte Primary Root CA 
    2 s:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For  authorized use only/CN=thawte Primary Root CA 
    i:/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services  Division/CN=Thawte Premium Server CA/[email protected] 

答えて

28

このthreadは、同様の問題をデバッグしたときに本当に役に立ちました。

概要アンドロイド2.3 HTTPS/SSLチェックリスト:

  • あなたのCAは、Androidの2.3 list of trusted CA'sにある場合

    - やThawteある - アプリ内の証明書を含める必要はありません。
  • Android 2.3ではServer Name Indicationがサポートされていないため、サーバがSSLハンドシェイクに頼っている場合、Androidは期待している証明書を取得していない可能性があります。
  • サーバーに証明書チェーンがインストールされていて、正しく注文されていますか?ほとんどのブラウザでは、順不同の証明書チェーンが処理されますが、Android 2.3では処理されません。上記のスレッドのbdcの答えは、 "openssl s_client -connect yourserver.com:443"でSSL証明書とチェーンの有効性をチェックする方法を説明しています。
  • ボトムドロワーにある古い2.3デバイスを掘り起こすときは、時間が長すぎると日付と時刻が正しく設定されていることを確認してください。
+0

ありがとう、エド。元の投稿に証明書チェーンを追加しました。 Javaキーツールを使用して、中間証明書とルート証明書の両方を1つのキーストアに含めました。それは物事について行く正しい方法ですか? – user1214401

+0

Thawteを使用している場合は、証明書をアプリに含める必要はありません。 ThawteはAndroidの2.3 [信頼できるCAのリスト](http://www.andreabaccega.com/blog/2010/09/23/android-root-certification-authorities-list/)にあります。また、Android 2.3では[Server Name Indication](http://en.wikipedia.org/wiki/Server_Name_Indication)がサポートされていないので、サーバーがそれに依存している場合、Androidが証明書を取得していない可能性があります。期待しています。 –

+0

今から1年後にそれを振り返ると、これは面白くないでしょう。私は壊れていないものを2週間かけて修理しました。はい、エド、あなたは正しいです。キーストアは不要でした。 thawte証明書はすでに信頼されています。それは2.3を実行している私のAndroid 4の携帯電話とエミュレータで動作しています。もともと私は証明書のエラー(それが何であったか覚えていない)を持っていましたが、それは私にこの道を導いたものです...しかし、私は過去2週間で多くを学んでいます...エド、あなたの編集を元のあなたの答えに私は答えとしてマークすることができます。感謝!! – user1214401

0

何の証明書は、あなたがR.raw.my_certからロードしていますか?このエラーは、Thawteのプライマリとセカンダリ中間CAをインストールしていない、または正しい証明書チェーンを読み込んで信頼していない、サーバーの設定ミスが原因です。

+0

私はそれを文字通り覚えていますか?私はルートのキーストアをロードしていましたが、次に中間ルートを含むキーストアを試しました。私は異なる組み合わせと注文(雹メアリー、誰ですか?)を持っていた5種類のキーストアをロードしようとしました。 これはあなたが求めていたものですが、正しいのでしょうか?あなたは私が実際の証明書を生のディレクトリに置くことを示唆していませんか? – user1214401

+0

編集済み...キーストアをロードしています。 –

3

私はまったく同じ問題を抱えていました。アンドロイド> 3.Xではすべてがうまくいっていましたが、2.3.Xデバイスの一部で試してみましたが、私はその有名な「ピア証明書エラーがありません」という例外があります。

私はstackoverflowや他のブログを通してたくさん掘り下げましたが、私はこれらの "不正な"デバイス(私の場合、トラストストアの正しい使用、必要なスニ、サーバー上の正しい証明書チェーンの順序など...)。

アンドロイドのApache HttpClientはちょうどが正しく動作していないようです。一部の2.3.Xデバイスではです。 「ピア証明書がありません」という例外が早過ぎてカスタムホスト名検証コードに到達していないので、that oneのような解決策は私のためには機能しませんでした。ここで

は私のコードだった:

KeyStore trustStore = KeyStore.getInstance("BKS"); 
InputStream is = this.getAssets().open("discretio.bks"); 
trustStore.load(is, "discretio".toCharArray()); 
is.close(); 

SSLSocketFactory sockfacto = new SSLSocketFactory(trustStore); 
sockfacto.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); 

SchemeRegistry schemeRegistry = new SchemeRegistry(); 
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 
schemeRegistry.register(new Scheme("https", sockfacto, 443)); 

SingleClientConnManager mgr = new SingleClientConnManager(httpParameters, schemeRegistry); 

HttpClient client = new DefaultHttpClient(mgr, httpParameters); 
HttpGet request = new HttpGet(url); 
HttpResponse response = client.execute(request); 

だから私はjavax.net.ssl.HttpsURLConnectionを使用して、すべてを書き直し、今では(2.3​​.3から4.Xに)私がテストしたすべてのデバイスに取り組んでいます。

KeyStore trustStore = KeyStore.getInstance("BKS"); 
InputStream is = this.getAssets().open("discretio.bks"); 
trustStore.load(is, "discretio".toCharArray()); 
is.close(); 

TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); 
tmf.init(trustStore); 
SSLContext context = SSLContext.getInstance("TLS"); 
context.init(null, tmf.getTrustManagers(), null); 

URL request = new URL(url); 
HttpsURLConnection urlConnection = (HttpsURLConnection) request.openConnection(); 

//ensure that we are using a StrictHostnameVerifier 
urlConnection.setHostnameVerifier(new StrictHostnameVerifier()); 
urlConnection.setSSLSocketFactory(context.getSocketFactory()); 
urlConnection.setConnectTimeout(15000); 

InputStream in = urlConnection.getInputStream(); 
//I don't want to change my function's return type (laziness) so I'm building an HttpResponse 
BasicHttpEntity res = new BasicHttpEntity(); 
res.setContent(in); 
HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, urlConnection.getResponseCode(), ""); 
resp.setEntity(res); 

証明書チェーンとホスト名の検証は、(私はそれらをテストした)作業している:

は、ここに私の新しいコードです。 誰かが変更をよりよく見たい場合は、diff

コメントは歓迎します。

+0

これは2.3以降では動作しますが、2.2では動作しません。 [この別の質問への回答](http://stackoverflow.com/a/5895320/912936)を参照してください。 – robpvn

1

このメッセージのもう1つの送信元は、無効な日付/時刻設定にすることができます。電源を切って数ヶ月過ぎたデバイスを使用しているとき。かなり簡単ですが、見つけにくいかもしれません。

0

(少なくとも)Android 2.3の証明書検証(またはより正確にはチェーン構築)ロジックに問題があります。ここで

は、私が観察したものである:

  • ことの証明書チェーンのTLSサーバは、(非自己署名または自己署名)のみ、サーバーの証明書を提供した場合は、キーストアおよび検証にサーバーの証明書を置くことができます成功します。

  • 証明書チェーン内のTLSサーバーが中間のCA証明書も提供する場合は、キーストアにルートCA証明書のみを置き、キーストアにサーバーおよび中間CA証明書が含まれていないことを確認する必要があります無作為に)。

  • 証明書チェーン内のTLSサーバーが中間およびルートCA証明書を正しい順序で提供する場合は、ルートCA証明書がキ​​ーストアに存在することを確認するだけです(サーバー証明書と中間CA証明書ある)。

だから、これを処理するために、どのように「正しい/信頼性」の方法は、キーストアのみのルートCA証明書に含まれ、「いいえピア証明書」のサーバー構成を非難することである - 場合には、サーバの証明書チェーンは、中間CAを提供していません。証明書または証明書の順序が間違っています。 https://www.ssllabs.com/ssltest/を使用してサーバーをテストできます。

関連する問題