2016-09-01 25 views
7

Có ba máy chủ mà một ứng dụng Android thực hiện xác thực và ủy quyền. Máy chủ cuối cùng là REST API. Lần đầu tiên sử dụng quy trình xác thực và ủy quyền Oauth, nó hoạt động mà không có vấn đề gì.Android java.security.cert.CertPathValidatorException: Niềm tin neo cho đường dẫn chứng nhận không tìm thấy

Nhưng nếu người dùng giết ứng dụng sau khi đăng nhập và truy cập các dịch vụ được cung cấp bởi REST API và sau đó mở lại ứng dụng, vấn đề này phát sinh. Trong thời gian này quá trình xác thực và ủy quyền không xảy ra, chỉ có API REST. Nó gây ra java.security.cert.CertPathValidatorExceptionnhưng nó đã hoạt động trong lần sử dụng đầu tiên (đăng nhập và sau đó sử dụng ứng dụng).

Ai đó có thể giải thích tình huống đằng sau ngoại lệ này và có vấn đề gì với ứng dụng. Điều này hoạt động nếu ngoại lệ chứng nhận bị bỏ qua dưới đây theo 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; 

Tôi đang sử dụng Okhttp 3 cho các yêu cầu http. Bất kỳ đề xuất nào cũng sẽ giúp giải quyết vấn đề. Và vui lòng cho tôi biết nếu tôi sử dụng đoạn mã trên, liệu đó có phải là vi phạm bảo mật không? nó sẽ ảnh hưởng đến tính bảo mật của ứng dụng?

+0

Lưu ý: Việc triển khai 'checkServerTrusted' của bạn là vô ích khi bạn nắm bắt và bỏ qua CertificateException. Do đó tất cả các chứng chỉ máy chủ (đáng tin cậy và không tin cậy) đều được chấp nhận! – Robert

+0

@Robert cảm ơn vì đã làm rõ. Có ** điều này là không an toàn và vô dụng trong quan điểm bảo mật **, tôi đã tìm thấy giải pháp đúng như được mô tả trong câu trả lời của tôi. –

Trả lời

12

Tôi đang trả lời câu hỏi này để đưa ra ý tưởng về kịch bản và giải pháp theo trang web dành cho nhà phát triển Android cho những người khác được hưởng lợi. Tôi đã giải quyết vấn đề này bằng cách sử dụng trình quản lý tin cậy tùy chỉnh.

Sự cố xảy ra với chứng chỉ máy chủ, nó thiếu cơ quan cấp chứng chỉ trung gian. Tuy nhiên với đường dẫn chứng chỉ luồng đầu tiên được hoàn thành bằng cách nào đó và kết quả là xác thực đường dẫn chứng chỉ thành công.

Có giải pháp cho điều này trong android developer site. nó đề xuất sử dụng trình quản lý tin cậy tùy chỉnh tin tưởng chứng chỉ máy chủ này hoặc nó đề xuất với máy chủ để bao gồm CA trung gian trong chuỗi máy chủ.

trình quản lý tin cậy tùy chỉnh. nguồn: 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: Vấn đề của tôi đã được giải quyết sau khi cơ quan chứng nhận trung gian bổ sung vào chuỗi chứng chỉ từ phía máy chủ. Đây là giải pháp tốt nhất, Việc đóng gói chứng chỉ với ứng dụng yêu cầu ứng dụng phải được cập nhật khi chứng chỉ hết hạn hoặc bất kỳ vấn đề nào khác liên quan đến quản lý chứng chỉ.

CẬP NHẬT: 03/09/2017 Cách dễ nhất để tải tệp chứng chỉ mà tôi thấy là sử dụng tài nguyên thô.

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

trong đó certfilename là tệp chứng chỉ được đặt trong tài nguyên/thư mục thô. Ngoài ra, sslSocketFactory(SSLSocketFactory sslSocketFactory) của okhttp cũng không được chấp nhận và có thể sử dụng cách tiếp cận được đề xuất trong tài liệu api okhttp.

Ngoài ra khi nhận được chứng chỉ từ máy chủ, tốt hơn nên sử dụng openssl.

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

Vì tôi đã từng lấy thông tin đó từ firefox và gặp phải tình huống mà trình bảo vệ bị thay đổi.

1

Sự cố này cũng có thể xuất hiện khi cài đặt ngày/giờ của điện thoại không đồng bộ. Tôi đã thử nghiệm trên thiết bị Android khi tôi gặp sự cố này.

Khôi phục ngày & thời gian tới giá trị hiện tại đã giải quyết vấn đề này cho tôi.

Các vấn đề liên quan