2014-04-16 27 views
6

Tôi đang cố gắng thiết lập SSL được xác thực lẫn nhau giữa Máy chủ Linux và Android APP. Cho đến nay, tôi đã có thể làm cho ứng dụng hoạt động với chứng chỉ máy chủ giao tiếp qua SSL nhưng khi tôi đặt máy chủ chỉ chấp nhận chứng chỉ ứng dụng, nó sẽ ngừng hoạt động. Các cấu hình máy chủ có vẻ ok, nhưng tôi là một loại bị mắc kẹt. Đoán tốt nhất của tôi là chứng chỉ ứng dụng khách không được trình bày chính xác cho máy chủ nhưng không biết làm thế nào để kiểm tra nó tiếp theo. Tôi đã thử sử dụng .pem cho khách hàng trong keychain X OS của tôi nhưng các trình duyệt dường như không hoạt động với chứng chỉ đó. Sau đó, một lần nữa chứng chỉ máy chủ hoạt động hoàn hảo vì tôi có thể đạt được kết nối https và APP Chấp nhận chứng chỉ máy chủ chưa ký của tôi.Xác thực lẫn nhau SSL FAIL trên Máy khách Android chấp nhận chứng chỉ máy chủ nhưng máy chủ không nhận được chứng chỉ máy khách

Mã Tôi đã được tôi đang sử dụng là sự kết hợp các bài hướng dẫn khác nhau, câu trả lời này là những cái chính tôi đã đánh dấu:

Đây là hai lớp chính Tôi đang sử dụng cho kết nối: 1) Lớp này xử lý các phân tích cú pháp JSON và thực hiện các yêu cầu

package edu.hci.additional; 

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.InputStreamReader; 
import java.io.UnsupportedEncodingException; 
import java.util.List; 

import android.content.Context; 
import org.apache.http.HttpEntity; 
import org.apache.http.HttpResponse; 
import org.apache.http.NameValuePair; 
import org.apache.http.client.ClientProtocolException; 
import org.apache.http.client.entity.UrlEncodedFormEntity; 
import org.apache.http.client.methods.HttpGet; 
import org.apache.http.client.methods.HttpPost; 
import org.apache.http.client.utils.URLEncodedUtils; 
import org.json.JSONException; 
import org.json.JSONObject; 

import android.util.Log; 

public class JSONParser { 

    static InputStream is = null; 
    static JSONObject jObj = null; 
    static String json = ""; 

    // constructor 
    public JSONParser() { 

    } 

    // function get json from url 
    // by making HTTP POST or GET mehtod 
    public JSONObject makeHttpRequest(String url, String method, 
      List<NameValuePair> params, Context context) { 

     // Making HTTP request 
     try { 

      // check for request method 
      if(method == "POST"){ 
       // request method is POST 
       // defaultHttpClient 
       SecureHttpClient httpClient = new SecureHttpClient(context); 
       HttpPost httpPost = new HttpPost(url); 
       httpPost.setEntity(new UrlEncodedFormEntity(params)); 

       HttpResponse httpResponse = httpClient.execute(httpPost); 
       HttpEntity httpEntity = httpResponse.getEntity(); 
       is = httpEntity.getContent(); 

      }else if(method == "GET"){ 
       // request method is GET 
       SecureHttpClient httpClient = new SecureHttpClient(context); 
       String paramString = URLEncodedUtils.format(params, "utf-8"); 
       url += "?" + paramString; 
       HttpGet httpGet = new HttpGet(url); 

       HttpResponse httpResponse = httpClient.execute(httpGet); 
       HttpEntity httpEntity = httpResponse.getEntity(); 
       is = httpEntity.getContent(); 
      }   


     } catch (UnsupportedEncodingException e) { 
      e.printStackTrace(); 
     } catch (ClientProtocolException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

     try { 
      BufferedReader reader = new BufferedReader(new InputStreamReader(
        is, "iso-8859-1"), 8); 
      StringBuilder sb = new StringBuilder(); 
      String line = null; 
      while ((line = reader.readLine()) != null) { 
       sb.append(line + "\n"); 
      } 
      is.close(); 
      json = sb.toString(); 
     } catch (Exception e) { 
      Log.e("Buffer Error", "Error converting result " + e.toString()); 
     } 

     // try parse the string to a JSON object 
     try { 
      jObj = new JSONObject(json); 
     } catch (JSONException e) { 
      Log.e("JSON Parser", "Error parsing data " + e.toString()); 
     } 

     // return JSON String 
     return jObj; 

    } 
} 

lớp thứ hai này xử lý các xác thực SSL:

package edu.hci.additional; 

import android.content.Context; 
import android.util.Log; 
import edu.hci.R; 
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.params.HttpParams; 

import java.io.IOException; 
import java.io.InputStream; 
import java.security.*; 


public class SecureHttpClient extends DefaultHttpClient { 

    private static Context appContext = null; 
    private static HttpParams params = null; 
    private static SchemeRegistry schmReg = null; 
    private static Scheme httpsScheme = null; 
    private static Scheme httpScheme = null; 
    private static String TAG = "MyHttpClient"; 

    public SecureHttpClient(Context myContext) { 

     appContext = myContext; 

     if (httpScheme == null || httpsScheme == null) { 
      httpScheme = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80); 
      httpsScheme = new Scheme("https", mySSLSocketFactory(), 443); 
     } 

     getConnectionManager().getSchemeRegistry().register(httpScheme); 
     getConnectionManager().getSchemeRegistry().register(httpsScheme); 

    } 

    private SSLSocketFactory mySSLSocketFactory() { 
     SSLSocketFactory ret = null; 
     try { 

      final KeyStore clientCert = KeyStore.getInstance("BKS"); 
      final KeyStore serverCert = KeyStore.getInstance("BKS"); 

      final InputStream client_inputStream = appContext.getResources().openRawResource(R.raw.authclientcerts); 
      final InputStream server_inputStream = appContext.getResources().openRawResource(R.raw.certs); 

      clientCert.load(client_inputStream, appContext.getString(R.string.client_store_pass).toCharArray()); 
      serverCert.load(server_inputStream, appContext.getString(R.string.server_store_pass).toCharArray()); 

      String client_password = appContext.getString(R.string.client_store_pass); 

      server_inputStream.close(); 
      client_inputStream.close(); 

      ret = new SSLSocketFactory(clientCert,client_password,serverCert); 
     } catch (UnrecoverableKeyException ex) { 
      Log.d(TAG, ex.getMessage()); 
     } catch (KeyStoreException ex) { 
      Log.d(TAG, ex.getMessage()); 
     } catch (KeyManagementException ex) { 
      Log.d(TAG, ex.getMessage()); 
     } catch (NoSuchAlgorithmException ex) { 
      Log.d(TAG, ex.getMessage()); 
     } catch (IOException ex) { 
      Log.d(TAG, ex.getMessage()); 
     } catch (Exception ex) { 
      Log.d(TAG, ex.getMessage()); 
     } finally { 
      return ret; 
     } 
    } 

} 

Để tạo các phím tôi sử dụng openssl với lệnh này:

openssl req -nodes -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 500 

để có được những chìa khóa để BKS dành cho Android tôi đã sử dụng lâu đài bouncy bcprov-jdk15on-150.jar nằm tại địa chỉ: http://www.bouncycastle.org/latest_releases.html

Và sử dụng lệnh:

keytool -import -v -trustcacerts -alias 0 -file ~/cert.pem -keystore ~/Downloads/authclientcerts.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath ~/Downloads/bcprov-jdk15on-150.jar -storepass passWORD 

Cuối cùng những dòng tôi thêm vào /etc/httpd/conf.d/ssl.conf để yêu cầu giấy chứng nhận của khách hàng và kiểm tra tính hợp lệ giấy chứng nhận (mà phù hợp với giấy chứng nhận client tôi tạo ra) trong Fedora 19 là:

... 
SSLVerifyClient require 
SSLVerifyDepth 5 
... 
<Location /> 
SSLRequire ( %{SSL_CLIENT_S_DN_O} eq "Develop" \ 
      and %{SSL_CLIENT_S_DN_OU} in {"Staff", "Operations", "Dev"}) 
</Location> 
... 
SSLOptions +FakeBasicAuth +StrictRequire 

tôi đã cố gắng rất nhiều sự kết hợp trên tập tin cấu hình này và tất cả đã kết thúc trong cùng một kết quả twrowing cho tôi một "SSLPeerUnverifiedException: Không có giấy chứng nhận ngang hàng" ngoại lệ. Tôi bình luận dòng này trên tập tin cấu hình SSL của máy chủ và tất cả các công trình lớn nhưng máy chủ chấp nhận tất cả các khách hàng mà không phải là những gì tôi cần.

Cảm ơn trước :)

CẬP NHẬT

trả lời @EJP 's đã làm các trick

Đầu tiên tôi đã có thêm giấy chứng nhận cho đúng (/ etc/PKI/tls/certs /) đường dẫn và tải nó bằng cách sử dụng: đổi tên cert: mv ca-andr.pem ca-andr.crt Và bây giờ tải các chứng chỉ:

ln -s ca-andr.crt $(openssl x509 -hash -noout -in ca-andr.crt)".0" 

này sẽ tạo ra một liên kết tượng trưng có thể đọc được OpenSSL với một tên tương tự như “f3f24175.0”

Sau đó, tôi thiết lập các tập tin Giấy chứng nhận mới trong thư mục/etc/httpd/Tệp cấu hình conf.d/ssl.conf.

… 
SSLCACertificateFile /etc/pki/tls/certs/f2f62175.0 
… 

Bây giờ khởi động lại dịch vụ http và kiểm tra nếu các chứng chỉ được nạp với:

openssl verify -CApath /etc/pki/tls/certs/ f2f62175.0 

Nếu tất cả là ok bạn sẽ thấy:

f3f24175.0: OK

Và bạn có thể kết thúc thử nghiệm với:

openssl s_client -connect example.com:443 -CApath /etc/pki/tls/certs 

Điều này sẽ trả về danh sách chứng chỉ ứng dụng đáng tin cậy (Nếu bạn thấy chứng chỉ bạn đã thêm, đang hoạt động)

Bây giờ phần thứ hai của vấn đề là authclientcerts.BKS của tôi không chứa khóa riêng, vì vậy mật khẩu mà tôi cung cấp không bao giờ được sử dụng và máy chủ sẽ không xác thực chứng chỉ. Vì vậy, tôi đã xuất khóa và cert của mình thành pkcs12 và cập nhật mã JAVA cho phù hợp.

khẩu lệnh:

openssl pkcs12 -export -in ~/cert.pem -inkey ~/key.pem > android_client_p12.p12 

Sau đó, tôi đã thay đổi các bộ phận của lớp SecureHttpClient.java để làm giấy chứng nhận của khách hàng với pkcs12 thay vì BKS.

Để thay đổi kiểu kho khóa từ BKS để pkcs12 tôi thay thế:

final KeyStore clientCert = KeyStore.getInstance("BKS”); 

Đối với điều này:

final KeyStore clientCert = KeyStore.getInstance("PKCS12"); 

Và sau đó tôi được cập nhật các tham chiếu đến các tệp kho khóa thực tế nằm trên res/raw/ Bằng cách thay thế:

final InputStream client_inputStream = appContext.getResources().openRawResource(R.raw.authclientcerts); 

Đối với điều này:

final InputStream client_inputStream = appContext.getResources().openRawResource(R.raw.android_client_p12); 

Và đó đã làm các trick: D

Trả lời

3

Khi máy chủ yêu cầu giấy chứng nhận của khách hàng, nó cung cấp một danh sách các CA rằng nó sẽ chấp nhận giấy chứng nhận có chữ ký của. Nếu khách hàng không có chứng chỉ được ký bởi một trong số họ, nó sẽ không gửi chứng chỉ trả lời. Nếu máy chủ được cấu hình để yêu cầu chứng chỉ ứng dụng khách, thay vì chỉ muốn một chứng chỉ, nó sẽ đóng kết nối.

Vì vậy, hãy đảm bảo rằng ứng dụng khách có chứng chỉ có thể chấp nhận được với máy chủ của máy chủ.

+1

Cảm ơn câu trả lời đã giải quyết mọi thứ cho tôi. Tôi đã có thể kết nối thành công Ứng dụng Android với máy chủ bằng cách sử dụng Xác thực lẫn nhau. Cảm ơn bạn rất nhiều vì đã dành thời gian để trả lời. Tôi sẽ bổ sung thêm một vài phút để cập nhật với giải pháp đầy đủ và những thứ tôi phải sửa. – jmarrero

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