Retrofit/OkHttp SSL Pining

这周在做竞品研究的时候发现,只要安装抓包工具的相关CA证书,就可以看到SSL加密的数据了。为了防止request结构被其他人看到,决定使用SSL Pinning,使得App只认绑定证书。这样即便安装了抓包工具的证书,也会因为证书的key不对而不会建立链接。

OkHttp提供了一个CertificatePinner类可以方便的设置SSL Pinning,但实验下来发现在Android中不行。使用抓包工具还是可以看到加密内容。或许是我的使用方法有问题。不过我还是找另外一种方法做SSL Pinning。

这里使用的方法是从CA证书中读取相关的信息,并创建一个只信任指定CA证书的SSLSocketFactory对象,注入到OkHttp中。这样OkHttp会使用注入的SSLSocketFactory去创建SSL Socket了。

第一步提取CA证书(以Google为例):

Get Google CA
Get Google CA
Click “View Certificate”
Click “Export”

导出后得到一个后缀名为.crt的文件,该文件既是Google的CA证书。

 

第二步读取CA证书并创建创建SSLSocketFactory:

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
SSLSocketFactory = sslSocketFactory = null;
try {
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
 
    InputStream caInput = context.getResources().openRawResource(R.raw.ca);
    Certificate ca = null;
    try {
        ca = certificateFactory.generateCertificate(caInput);
    } catch (CertificateException e) {
        e.printStrackTrace();
    } finally {
        caInput.close();
    }
 
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null, null);
    if (ca == null) {
        return null;
    }
    keyStore.setCertificateEntry("ca", ca);
 
    String algorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
    trustManagerFactory.init(keyStore);
 
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
 
    sslSocketFactory = sslContext.getSocketFactory();
} catch (CertificateException|IOException|KeyStoreException|NoSuchAlgorithmException|KeyManagementException e) {
    e.printStackTrace();
}

 

第三步在创建OkHttpClient时将SSLSocketFactory注入到Builder:

1
2
3
OkHttpClient client = new OkHttpClient.Builder()
        .sslSocketFactory(SslSocketFactoryHelper.getSslSocketFactory(context.get()))
        .build();

这样就大功告成了。

再测试一下会发现因为CA证书不对,链接会无法建立。也就达到了SSL Pinning的效果。