2016-09-01 13 views
7

アンドロイドアプリが認証と承認を行うホストは3つあります。最終ホストはREST APIです。 Oauth認証と承認プロセスを初めて使用する場合、問題なく動作します。Android java.security.cert.CertPathValidatorException:認証パスの信頼アンカーが見つかりません

ログインしてREST APIで提供されているサービスにアクセスしてアプリを再度開いた後に、ユーザーがアプリを終了した場合、この問題が発生します。 この段階では、認証および承認プロセスは行われていませんが、REST APIのみが使用されます。これはjava.security.cert.CertPathValidatorExceptionになりましたが、最初の使用中にログインしてからアプリを使用していました。

誰かがこの例外の背後にあるシナリオについて説明し、アプリに間違いがありますか?これは、認証例外がthis SO answerに従って以下のように無視された場合に機能します。

SSLSocketFactory sslSocketFactory = null; 

     try { 
      TrustManagerFactory tmf = TrustManagerFactory.getInstance(
        TrustManagerFactory.getDefaultAlgorithm()); 
      // Initialise the TMF as you normally would, for example: 
      try { 
       tmf.init((KeyStore)null); 
      } catch(KeyStoreException e) { 
       e.printStackTrace(); 
      } 
      TrustManager[] trustManagers = tmf.getTrustManagers(); 

      final X509TrustManager origTrustmanager = (X509TrustManager)trustManagers[0]; 

      // Create a trust manager that does not validate certificate chains 
      TrustManager[] wrappedTrustManagers = new TrustManager[]{ 
        new X509TrustManager() { 
         public java.security.cert.X509Certificate[] getAcceptedIssuers() { 
          return origTrustmanager.getAcceptedIssuers(); 
         } 

         public void checkClientTrusted(X509Certificate[] certs, String authType) { 
          try { 
           origTrustmanager.checkClientTrusted(certs, authType); 
          } catch(CertificateException e) { 
           e.printStackTrace(); 
          } 
         } 

         public void checkServerTrusted(X509Certificate[] certs, String authType) { 
          try { 
           origTrustmanager.checkServerTrusted(certs, authType); 
          } catch(CertificateException e) { 
           e.printStackTrace(); 
          } 
         } 
        } 
      }; 
      //TrustManager[] trustAllCerts = TrustManagerFactory.getInstance("SSL").getTrustManagers(); 

      // Install the all-trusting trust manager 
      final SSLContext sslContext = SSLContext.getInstance("TLS"); 
      sslContext.init(null, wrappedTrustManagers, new java.security.SecureRandom()); 
      // Create an ssl socket factory with our all-trusting manager 
      sslSocketFactory = sslContext.getSocketFactory(); 
     } catch (NoSuchAlgorithmException | KeyManagementException e) { 
      e.printStackTrace(); 
     } 
     return sslSocketFactory; 

私はHTTPリクエストにOkhttp 3を使用しています。どんな提案も問題の解決に役立ちます。上記のコードスニペットを使用する場合は、セキュリティ違反ですか?それはアプリのセキュリティに影響を与えますか?

+0

注: 'checkServerTrusted'実装は、CertificateExceptionを捕まえて無視するので無用です。したがって、すべてのサーバ証明書(信頼できるものと信頼できないもの)が受け入れられます! – Robert

+0

@Robertは明確化のために感謝します。はい**これはセキュリティ上の観点からは安全で無用です**、私は私の答えに記述されている正しい解決策を見つけました。 –

答えて

12

私は他の人が恩恵を受けるためのAndroidデベロッパーサイトごとにシナリオとソリューションについてのアイデアを与えるためにこれに答えています。私はカスタムトラストマネージャを使用してこれを解決しました。

問題はサーバー証明書で発生し、中間の認証局が失われました。しかし、最初のフロー証明書パスが何らかの形で完了し、結果が証明書パスの検証に成功しました。

android developer siteにはこれに対する解決策があります。このサーバー証明書を信頼するカスタムのトラストマネージャーを使用するか、サーバーに中間CAをサーバーチェーンに含めることを提案します。

カスタムトラストマネージャー。ソース:https://developer.android.com/training/articles/security-ssl.html#UnknownCa

// Load CAs from an InputStream 
// (could be from a resource or ByteArrayInputStream or ...) 
CertificateFactory cf = CertificateFactory.getInstance("X.509"); 
// From https://www.washington.edu/itconnect/security/ca/load-der.crt 
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt")); 
Certificate ca; 
try { 
    ca = cf.generateCertificate(caInput); 
    System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN()); 
} finally { 
    caInput.close(); 
} 

// Create a KeyStore containing our trusted CAs 
String keyStoreType = KeyStore.getDefaultType(); 
KeyStore keyStore = KeyStore.getInstance(keyStoreType); 
keyStore.load(null, null); 
keyStore.setCertificateEntry("ca", ca); 

// Create a TrustManager that trusts the CAs in our KeyStore 
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); 
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); 
tmf.init(keyStore); 

// Create an SSLContext that uses our TrustManager 
SSLContext context = SSLContext.getInstance("TLS"); 
context.init(null, tmf.getTrustManagers(), null); 
// Tell the okhttp to use a SocketFactory from our SSLContext 
OkHttpClient okHttpClient client = new OkHttpClient.Builder().sslSocketFactory(context.getSocketFactory()).build(); 

UPDATE:中間認証局は、サーバ側からの証明書チェーンに追加した後私の問題を解決しました。それは最善の解決策です。アプリケーションで証明書をバンドルするには、証明書の有効期限や証明書管理に関連するその他の問題についてアプリを更新する必要があります。

UPDATE:03/09/2017証明書ファイルをロードする最も簡単な方法は、rawリソースの使用です。

InputStream caInput = new BufferedInputStream(context 
       .getResources().openRawResource(R.raw.certfilename)); 

ここで、certfilenameはresources/rawフォルダに置かれた証明書ファイルです。また、okhttpのsslSocketFactory(SSLSocketFactory sslSocketFactory)は推奨されず、okhttp api docの提案されたアプローチを使用することができます。

また、サーバーから証明書を取得するときは、opensslを使用する方がよいでしょう。

openssl s_client -connect {server-address}:{port} -showcerts 

私はこれをFirefoxから取得し、ウイルスガードによって変更された状況に直面していたためです。

1

この問題は、電話機の日付/時刻設定が同期していない場合にも表示されます。私はこの問題に直面したときにAndroid搭載端末でテストしていました。

日付を復元する現在の値にまでの時間は&です。この問題が解決されました。

関連する問題