移动安全之Certificate Pinning

移动安全之CertificatePinning



背景:

项目组是为美国一商业银行开发移动支付类软件.提到银行类软件,大家第一反应都是安全性的要求特别高,的确在开发过程中,我们对安全方面也做了非常多的处理跟防护.这里先介绍下比较重要的一个——证书锁定 (CertificatePinning)



什么是CertificatePinning

SSL/TLS通信中,客户端通过数字证书判断服务器是否可信,并采用证书的公钥与服务器进行加密通信.

然而在很多移动应用中,开发者不检查服务器证书的有效性,或选择接受所有的证书,这样就会导致中间人攻击.

关于这点,举个例子.在钓鱼WiFi网络中,攻击者可以通过设置DNS服务器使客户端与指定的服务器进行通信。攻击者在服务器上部署另一个证书,在会话建立阶段,客户端会收到这张证书。如果客户端忽略这个证书的异常,或者接受这个证书,就会成功建立会话、开始加密通信。因为攻击者拥有私钥,他可以解密得到客户端发来数据的明文。攻击者还可以模拟客户端,与真正的服务器联系,充当中间人做监听.(摘取自互联网)

解决问题的一种方法是从可信CA申请一个证书.但在移动软件开发中,不推荐这种方法.这种验证只判断了证书是否CA可信的,并没有验证服务器本身是否可信.攻击者可以盗用其他可信证书,或者盗取CA私钥为自己颁发虚假证书.

事实上,移动软件大多只和固定的服务器通信,因此可以在代码更精确地直接验证某张特定的证书,这种方法称为“证书锁定”(certificatepinning



如何CertificatePinning

  1. 首先服务器端使用RSA算法生成一对公钥私钥对,服务器端持有私钥,线下将公钥传给客户端。App中将这个值硬编码到本地。

  2. App端可以自己实现一个X509TrustManager接口,在其中的CheckServerTrusted()方法里通过证书链拿到PublicKey,

  3. 比较12中进行md5的值,如果匹配则服务器验证通过,否则立即终止与此服务器的通信

示例代码(Android)

public final class PubKeyManager implements X509TrustManager
{
  private static String PUB_KEY = "30820122300d06092a864886f70d0101" +
    "0105000382010f003082010a0282010100b35ea8adaf4cb6db86068a836f3c85" +
    "5a545b1f0cc8afb19e38213bac4d55c3f2f19df6dee82ead67f70a990131b6bc" +
    "ac1a9116acc883862f00593199df19ce027c8eaaae8e3121f7f329219464e657" +
    "2cbf66e8e229eac2992dd795c4f23df0fe72b6ceef457eba0b9029619e0395b8" +
    "609851849dd6214589a2ceba4f7a7dcceb7ab2a6b60c27c69317bd7ab2135f50" +
    "c6317e5dbfb9d1e55936e4109b7b911450c746fe0d5d07165b6b23ada7700b00" +
    "33238c858ad179a82459c4718019c111b4ef7be53e5972e06ca68a112406da38" +
    "cf60d2f4fda4d1cd52f1da9fd6104d91a34455cd7b328b02525320a35253147b" +
    "e0b7a5bc860966dc84f10d723ce7eed5430203010001";

  public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException
  {
    if (chain == null) {
      throw new IllegalArgumentException("checkServerTrusted: X509Certificate array is null");
    }

    if (!(chain.length > 0)) {
      throw new IllegalArgumentException("checkServerTrusted: X509Certificate is empty");
    }

    if (!(null != authType && authType.equalsIgnoreCase("RSA"))) {
      throw new CertificateException("checkServerTrusted: AuthType is not RSA");
    }

    // Perform customary SSL/TLS checks
    try {
      TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
      tmf.init((KeyStore) null);
      
      for (TrustManager trustManager : tmf.getTrustManagers()) {
        ((X509TrustManager) trustManager).checkServerTrusted(chain, authType);
      }
    } catch (Exception e) {
      throw new CertificateException(e);
    }

    // Hack ahead: BigInteger and toString(). We know a DER encoded Public Key begins
    // with 0x30 (ASN.1 SEQUENCE and CONSTRUCTED), so there is no leading 0x00 to drop.
    RSAPublicKey pubkey = (RSAPublicKey) chain[0].getPublicKey();
    String encoded = new BigInteger(1 /* positive */, pubkey.getEncoded()).toString(16);

    // Pin it!
    final boolean expected = PUB_KEY.equalsIgnoreCase(encoded);
    if (!expected) {
      throw new CertificateException("checkServerTrusted: Expected public key: "
                + PUB_KEY + ", got public key:" + encoded);
      }
    }
  }
}

CertificatePinning的应用
在实际应用中,一款移动应用往往不止一个后台,尤其是在支付类产品中,经常需要去集成第三方支付网关。

但是第三方的服务什么时候会更改证书,这个就说不准了。初期,我们会把所有的这些第三方的服务提供的PublicKey都硬编码在本地,但是有次其中一个服务商自己改掉了,造成用户手上的产品直接不能使用了, 这个就给我们带来了不必要的麻烦。

解决方案:

  1. 自己的服务端使用RSA算法生成一对公钥私钥对,服务器端持有私钥,线下将公钥传给客户端。App中将这个值硬编码到本地。

  2. 自己的服务端提供API,获得当前所有服务器(包括第三方)公钥的SHASUM.当然这个值必须通过1中存在本地PublicKey签名验证得到

  3. 再通过最基本CertificatePinning的办法(第二章提到的),直接从各服务端拿到各自公钥, 连同本地PublicKey一起计算SHASUM

  4. 比较23中的值,如果相同则CertificatePinning通过,否则终止app.

这种方案将解决由第三方服务突然更换公钥,而导致App不能使用的情况。后期,我们还将优化这个方案,在最后一步如果不同的时候再次发送请求通知自己的服务器端,将错误数据发送让其检验是否是真更改还是存在不安全的环境。如果确实是第三方服务器上的证书更改了,服务器应该返回最新的公钥SHASUM值,如果没有更改,则返回原来的SHASUM值。



附录:概念解释

Concept

Description

SSL

http://baike.baidu.com/link?url=el1VbbUH0_HGol9_Gx36L_oiwMwaJiQmKz94iAFyVJRJgkNLI_lS54MKVo7ERwil7mBcSWsAZ3pwZnNfyyxehK

CA

http://baike.baidu.com/link?url=6uOhzRjx7XBPhqbrHtTeNwWjhwsjsAabU_Uwj3Tsh7zCDZPz6fknzW8yFP8YVfIXATkehdZSgr8ziPPHWVvAF_

RSA

http://baike.baidu.com/link?url=cvzT7VjNxiYJVeRVHAQkTRGb1Adq74aSkjYO5qPZAYeaLehtE2RVeg9UK4i3h9qMTuNCta9ND5MWv6iGkoZnNBMKbXm3hrClyXvyNf43u-BmI3EJ4HLwpGRq16tCY2Wo

PublicKey /PrivateKey

http://baike.baidu.com/link?url=yJmN72KB_gczeCEj4ocDiPxVPoJKJrpqmdYIuBD9Lc4gt7oEJ34M6Bf5bj3Kmm68RHSi4Ovt79tleMe9GtM96a



附录:参考文档

https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning

你可能感兴趣的:(Android)