Android: Trusting SSL certificates

http://blog.antoine.li/index.php/2010/10/android-trusting-ssl-certificates/

 

 

Two weeks ago I got the task to establish TLS secured connections via certificates to a service endpoint.
I thought it’s not a big deal, because the endpoint already uses an EV certificate from a trusted CA (SwissSign) in Switzerland. Therefore I shouldn’t have to worry that the certificate would be considered as untrusted so I don’t have to import it to the trusted certs in the  Java  key store etc.
FAIL! I’ve got a security exception, cert is not trusted. Same problem when I visit the website with the browser. Ok, that’s bad, SwissSign is not such a big player like thawte, so, it needs some time till it will be added to the android trusted CA list. But, when I visit thawte.com, their cert is also not trusted by android. WTF?
Windows Phone and iPhone trust my SwissSign CA and don’t complain.

So, let’s ask google, stackoverflow and the blogosphere. Found a lot of solutions how to disable certificate checking entirely.
Yeah, great, this will solve my problem, my connection will be “secure” and everyone will be able to intercept my connection and inject his own certificate. But I finally found the solution with help from other sites and some testing and debugging.

The Solution

The following main steps are required to achieve a secured connection from trusted Certification Authorities.

  1. Grab all required certificates (root and any intermediate CA’s)
  2. Create a keystore with keytool and the BouncyCastle provider and import the certs
  3. Load the keystore in your android app and use it for the secured connections
    • Don’t use the standard java.net.ssl.HttpsURLConnection for the secure connection. Use the Apache HttpClient (Version 4 atm) library, which is already built-in in android. It’s built on top of the java connection libraries and is, in my opinion, faster, better modularized and easier to understand.

Step 1: Grab the certs

You have to obtain all certificates that build a chain from the endpoint certificate the whole way up to the Root CA. This means, any (if present) Intermediate CA certs and also the Root CA cert. You don’t need to obtain the endpoint certificate.
You can obtain those certs from the chain (if provided) included in the endpoint certificate or from the official site of the issuer (in my case SwissSign).

Ensure that you save the obtained certificates in the Base64 encoded X.509 format. The content should look similar to this:

1
2
3
-----BEGIN CERTIFICATE-----
MIIGqTC.....
-----END CERTIFICATE-----

Step 2: Create the keystore

Download the BouncyCastle Provider and store it to a known location.
Also ensure that you can invoke the keytool command (usually located under the bin folder of your JRE installation).

Now import the obtained certs (don’t import the endpoint cert) into a BouncyCastle formatted keystore.
I didn’t tested it, but I think the order of importing the certificates is important. This means, import the lowermost Intermediate CA certificate first and then all the way up to the Root CA certificate.

With the following command a new keystore (if not already present) with the password mysecret will be created and the Intermediate CA certificate will be imported. I also defined the BouncyCastle provider, where it can be found on my file system and the keystore format. Execute this command for each certificate in the chain.

1
keytool -importcert -v -trustcacerts -file "path_to_cert/interm_ca.cer" -alias IntermediateCA -keystore "res/raw/myKeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret

Verify if the certificates were imported correctly into the keystore:

1
keytool -list -keystore "res/raw/myKeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret

Should output the whole chain:

1
2
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

Now you can copy the keystore as a raw resource in your android app under res/raw/

Step 3: Use the keystore in your app

First of all we have to create a custom Apache HttpClient that uses our keystore for HTTPS connections:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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.mykeystore);
             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);
         }
     }
}

We have created our custom HttpClient, now we can just use it for secure connections. For example when we make a GET call to a REST resource.

1
2
3
4
5
6
// Instantiate the custom HttpClient
DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpGet get = new HttpGet( "https://www.mydomain.ch/rest/contacts/23" );
// Execute the GET call and obtain the response
HttpResponse getResponse = client.execute(get);
HttpEntity responseEntity = getResponse.getEntity();

That’s it. Took me long to figure it out, hope this helps and saves you that time.

I really hope that the android platform will implement a better mechanism in future releases for defining which Certification Authorities should be trusted or not or just expand their own trusted CA list. If they don’t, I can’t believe they will get good acceptance from the business sector. Ok, you can control which certificates you want to trust in your app, but you still can’t add thawte as a trusted CA in the android keystore and your browser will always complain about an untrusted CA. The only way I know to eliminate this problem is to root your phone (very user friendly) and add your CA manually to the android keystore.

转载于:https://www.cnblogs.com/xyzlmn/archive/2011/04/09/3168184.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值