TLS-Pinning


什么是SSL/TLS Pinning?

将服务端提供的TLS/SSL证书内置到移动端开发的APP客户端中,当客户端发起请求时,通过比对内置证书和服务器端证书的内容,以确定这个连接的合法性.

我这里把Pinning翻译成“固定”

Pinning 方式

  • Certificate Pinning (证书固定)
  • Public Key Pinning (公钥固定)

在Android中有下面这些具体的方式执行TLS固定:

OkHttp中设置 TLS Pinning

OkHttp中专门提供了对应的方法设置TLS Pinning:

String hostname = "publicobject.com";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
    .add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
    .add("publicobject.com", "sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=")
    .add("publicobject.com", "sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=")
    .add("publicobject.com", "sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=")
    .build();
OkHttpClient client = OkHttpClient.Builder()
    .certificatePinner(certificatePinner)
    .build();

Request request = new Request.Builder()
    .url("https://" + hostname)
    .build();
client.newCall(request).execute();

可以设置多个域名对应的sha256/base64编码的map,用于多版本兼容.

OkHttp中的CertificatePinner的原理是:

  • 将证书中的公钥进行SHA256加密,然后进行base64编码,得到一串与hostname对应的字符串
  • 在建立TLS连接的时候与服务端获取的证书信息中的公钥,并进行SHA256编码,然后与通过base64解码之后的字节字符串进行对比

简而言之,就是一用的是将公钥进行SHA256加密后进行对比.

之所以本地自己写的时候要用base64编码的字符串,当然是为了容易书写和比较.

自定义TrustManager

  1. 使用KeyStore加载证书

    val inputStream = resources.openRawResource(R.raw.my_cert)
    val keyStoreType = KeyStore.getDefaultType()
    val keyStore = KeyStore.getInstance(keyStoreType)
    keyStore.load(inputStream, null)
  2. 创建一个TrustManager

    val tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
    val trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm)
    trustManagerFactory.init(keyStore)
  3. 创建SSLContext,并绑定上面创建的TrustManager对象,OKhttp中也可以自定义

    val sslContext = SSLContext.getInstance("TLS")
    sslContext.init(null, trustManagerFactory.trustManagers, null)
    val url = URL("http://www.yourdomain.com/")
    val urlConnection = url.openConnection() as HttpsURLConnection
    urlConnection.sslSocketFactory = sslContext.socketFactory

这种方式相当于上面所说的证书固定.会在客户端内置一个证书文件,但是这种方式对服务的证书更换和续期的限制比较大.

Android7.0之后使用Network Security Config配置

  1. manifest文件中指定网络配置资源

        <?xml version="1.0" encoding="utf-8"?>
        <manifest ... >
            <application android:networkSecurityConfig="@xml/network_security_config"
                            ... >
                ...
            </application>
        </manifest>
    
  2. 网络安全配置:可以配置自定义信任的CA(缩小/拓展系统信任的CA列表),配置用于调试的CA,当然还有固定证书配置:

    具体的文档在网络安全配置说明

        <?xml version="1.0" encoding="utf-8"?>
        <network-security-config>
            <domain-config>
                <domain includeSubdomains="true">example.com</domain>
                <pin-set expiration="2018-01-01">
                    <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
                    <!-- backup pin -->
                    <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
                </pin-set>
            </domain-config>
        </network-security-config>
    

    使用证书的公钥的SHA-256值经过base64编码后填入.


文章作者: 姜康
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 姜康 !
评论
 上一篇
Android启动流程-init进程分析 Android启动流程-init进程分析
基于Android 10 源码分析 之前在Android启动流程分析中已经大致分析了一下Android系统启动的过程,这里回顾一下: 上电,从Boot ROM中执行一段烧录好的代码加载bootloader bootloader执行,引
2020-06-22
下一篇 
FFmpeg简介 FFmpeg简介
模块库 avcodec 用于音视频编解码,支持自带的MPEG4,AAC,MJPEG等编码格式,还支持第三方的编解码,比如H.264(AVC,使用X264编解码器),H.265(HEVC,使用X265编解码器) avdevice 多媒体设备
  目录