使用自簽名證書在 android 中創建安全的客戶端-服務器連接 (Using a self-signed certificate to create a secure client-server connection in android)


問題描述

使用自簽名證書在 android 中創建安全的客戶端‑服務器連接 (Using a self‑signed certificate to create a secure client‑server connection in android)

I am developing an enterprise android application, thus it is necessary to create a secure connection between the client (android emulator / test phone) and server during my testing phase, even though the server's certificate is self‑signed while a legitimate certificate is being bought by the company (something outside my control for now).  

I need to trust the server's self‑signed certificate and its certificate‑authority, which is not trusted by the android OS natively, of course.  I am following google's suggestion for creating an HTTPS environment in this scenario almost verbatim.  

The problem I'm currently facing is that I can't access my .crt file as in this line from google's example:

InputStream caInput = new BufferedInputStream(
    new FileInputStream("load‑der.crt"));

In place of the above, I am using: 

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

to open the InputStream derived from mycrtfile.crt, where the .crt file exists in /res/raw/mycrtfile.crt.  However, I get a NullPointerException on that line.   

Is there a better way to store and access the cert file which I need to load as an InputStream or FileInputStream than as a raw resource stored inside the res directory?


參考解法

方法 1:

There is different ways to solve your problem but here is the one I use: All the steps are in this link http://blog.antoine.li/2010/10/22/android‑trusting‑ssl‑certificates/  but some parts  can be confused so I will explain all the process: 

1.‑Store your mycrtfile.crt in a know path I will say c:BKS/mycrtfile.crt.

2.‑To create your BKS or key store you will need the file bcprov‑jdk15on‑146.jar, this class will do all the work for us, there are different versions but this one works for me http://www.bouncycastle.org/download/bcprov‑jdk15on‑146.jar also store this file into C:BKS/

3.‑Now you will use the Keytool (keytool comes with the Java SDK. You should find it in the directory that contains javac) to generate our keystore and to make sure that is working go to your cmd and type "Keytool", you will see the available commands which means is working, or you can access trough "C:\Program Files (x86)\Java\jre7\bin>keytool".

4.‑Now that everything is in place we can generate the keystore with this command line:

keytool ‑importcert ‑v ‑trustcacerts ‑file "c:\BKS/mycrtfile.crt" ‑alias certificate  ‑keystore "c:\BKS/keystore.bks" ‑provider org.bouncycastle.jce.provider.BouncyCastleProvider ‑providerpath "c:\BKS/prov‑jdk15on‑146.jar" ‑storetype BKS ‑storepass mysecret

Lets see what is in this line (I was really confused in this part): ‑"c:\BKS/mycrtfile.crt" : this is the path to your certificate. ‑"c:\BKS/keystore.bks" this is the path where we will store the keystore and you can change the out‑put name I use keystore, just make sure that the extension file is .bks ‑"c:\BKS/prov‑jdk15on‑146.jar": this is the path to our file that will do all the job.  ‑mysecret: this is the password to use the key store you will need this password so don't forget about this.

EDITED: 4.1‑ Also use this command line to Verify if the certificate were imported correctly into the keystore:

keytool ‑list ‑keystore "res/raw/Keystore.bks" ‑provider org.bouncycastle.jce.provider.BouncyCastleProvider ‑providerpath "c:\BKS/prov‑jdk15on‑146.jar" ‑storetype BKS ‑storepass mysecret

4.2‑ After this you should see a output like this:

RootCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 24:77:D9:A8:91:D1:3B:FA:88:2D:C2:FF:F8:CD:33:93 IntermediateCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 98:0F:C3:F8:39:F7:D8:05:07:02:0D:E3:14:5B:29:43  Which means it was imported correctly. 

5.‑ After this if you go to your BKS folder you will se a Keystore.bks file which means we are on the way.

6.‑ Now lets go to the ANDROID part. IN your project check if you have the "raw" folder it has to be in Yourproject/res/raw if not create this folder under res.

7.‑copy your Keystore.bks file in to the raw folder. Everything is on place now lets go to the code.

8.‑‑‑ Now we will create a class to read and trust  our Keystore:

import java.io.InputStream;
import java.security.KeyStore;
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 org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import com.futureconcepts.anonymous.R;
import android.content.Context;


public class Client extends  DefaultHttpClient   {
final Context context;
  public Client(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));

    HttpParams httpParams = new BasicHttpParams();
     HttpConnectionParams.setConnectionTimeout(httpParams,9000);
     HttpConnectionParams.setSoTimeout(httpParams, 9000);

      return new SingleClientConnManager(httpParams, registry);
  }


  private SSLSocketFactory newSslSocketFactory() {
      try {

          // Get an instance of the Bouncy Castle KeyStore format
            KeyStore trusted = KeyStore.getInstance("BKS");//put BKS literal  
            // Get the raw resource, which contains the keystore with
            // your trusted certificates (root and any intermediate certs)
            InputStream in =context.getResources().openRawResource(R.raw.keystore);
            try {
                // Initialize the keystore with the provided trusted certificates
                // Also provide the password of the keystore
                trusted.load(in, "mysecret".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

           sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
          return sf;
      } catch (Exception e) {
          throw new AssertionError(e);
      }
  }

}

9.And we done now to make a request just do this:     HttpClient client= new Client(this);     ///setupo your Httpclient.

That's it now you will only trust your certificate. I hope this explanation help you or any one with the same problem.

方法 2:

Keystore (Here is a great article on Keystore and related issues)  and KeyChain do the trick but you might need to check the API level you targeting with. Furthermore, since you mentioned Enterprise Application, you may need to consider Certificate pinning. This helps you for the certificate pinning.  

方法 3:

1) Create directory "assets" in app/src/main/

2) Put your certificate in this directory.

3) Now you can get InputStream  :

InputStream is = this.getAssets().open("mycrtfile.crt");

To you use this.getAssets() you have to be in Activity, because in Activity "this" correspond to the "Context". If you are not in Activity you have to pass the Context (this) as argument.

(by ironicaldictionGocasamsonigor)

參考文件

  1. Using a self‑signed certificate to create a secure client‑server connection in android (CC BY‑SA 3.0/4.0)

#Security #Android #SSL






相關問題

只允許 oracle db 登錄到特定的應用程序? (Allowing oracle db login only to specific application?)

在桌面應用程序中保存用戶名和密碼 (Saving username & password in desktop app)

如何使用算法 RSA/ECB/PKCS1Padding 通過 JavaScript 解密加密字符串 (How to decrypt through JavaScript of encrypted string using algorithm RSA/ECB/PKCS1Padding)

wcf:將用戶名添加到消息頭是否安全? (wcf: adding username to the message header is this secure?)

沒有 .htaccess 的安全目錄密碼保護 (Secure directory password protection without .htaccess)

無法在 Oracle 表上創建簡單視圖 (Unable to create a simple view on Oracle table)

當請求來自調度程序時,無法寫入 App_Data (Cannot write in App_Data when request is from scheduler)

安全的 PHP 文件上傳 (Secure PHP file uploading)

Grails Spring 安全配置通過 xml (Grails Spring Security Configuration thru xml)

醫療應用的安全要求 (Security Requirements for Medical Applications)

如何保護 Silverlight 應用程序 (How to Secure Silverlight Application)

在使用 azure 流量管理器和 azure 應用程序網關與 WAF 時實現國家級阻止 (Achieve country level blocking while using azure traffic manager and azure application gateway with WAF)







留言討論