Android Https心得

Https通信过程

366784-20160127222221785-258650029.png

1 客户端发起一个https的请求,把自身支持的一系列Cipher Suite(密钥算法套件,简称Cipher)发送给服务端

2 服务端,接收到客户端所有的Cipher后与自身支持的对比,如果不支持则连接断开,反之则会从中选出一种加密算法和HASH算法

以证书的形式返回给客户端 证书中还包含了 公钥 颁证机构 网址 失效日期等等。

3 客户端收到服务端响应后会做以下几件事

  • 验证证书的合法性
    颁发证书的机构是否合法与是否过期,证书中包含的网站地址是否与正在访问的地址一致等证书验证通过后,在浏览器的地址栏会加上一把小锁(每家浏览器验证通过后的提示不一样 不做讨论)

  • 生成随机密码
    如果证书验证通过,或者用户接受了不授信的证书,此时浏览器会生成一串随机数,然后用证书中的公钥加密。

  • HASH握手信息
    用最开始约定好的HASH方式,把握手消息取HASH值, 然后用 随机数加密 “握手消息+握手消息HASH值(签名)” 并一起发送给服务端

     在这里之所以要取握手消息的HASH值,主要是把握手消息做一个签名,用于验证握手消息在传输过程中没有被篡改过。
    
  • 服务端拿到客户端传来的密文,用自己的私钥来解密握手消息取出随机数密码,再用随机数密码 解密 握手消息与HASH值,并与传过来的HASH值做对比确认是否一致。然后用随机密码加密一段握手消息(握手消息+握手消息的HASH值 )给客户端。

  • 客户端用随机数解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密因为这串密钥只有客户端和服务端知道,所以即使中间请求被拦截也是没法解密数据的,以此保证了通信的安全

非对称加密算法:RSA,DSA/DSS 在客户端与服务端相互验证的过程中用的是对称加密

对称加密算法:AES,RC4,3DES 客户端与服务端相互验证通过后,以随机数作为密钥时,就是对称加密

HASH算法:MD5,SHA1,SHA256 在确认握手消息没有被篡改时

Android OKHttp Https配置

X509TrustManager:

在JSSE中,证书信任管理器类就是实现了接口X509TrustManager的类。

接口X509TrustManager有下述三个公有的方法需要我们实现:

⑴ void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException
该方法检查客户端的证书,若不信任该证书则抛出异常。由于我们不需要对客户端进行认证,因此我们只需要执行默认的信任管理器的这个方法。JSSE中,默认的信任管理器类为TrustManager。

⑵ void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException
该方法检查服务器的证书,若不信任该证书同样抛出异常。通过自己实现该方法,可以使之信任我们指定的任何证书。在实现该方法时,也可以简单的不做任何处理,即一个空的函数体,由于不会抛出异常,它就会信任任何证书。

⑶ X509Certificate[] getAcceptedIssuers()
返回受信任的X509证书数组。
  • 若server使用的证书是由认证的CA机构颁发的,则如此配置即可(OkHttp等均已支持):
    private X509TrustManager systemDefaultTrustManager() {
    try {
      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
          TrustManagerFactory.getDefaultAlgorithm());
      trustManagerFactory.init((KeyStore) null);
      TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
      if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
        throw new IllegalStateException("Unexpected default trust managers:"
            + Arrays.toString(trustManagers));
      }
      return (X509TrustManager) trustManagers[0];
    } catch (GeneralSecurityException e) {
      throw new AssertionError(); // The system has no TLS. Just give up.
    }
  }

  private SSLSocketFactory systemDefaultSslSocketFactory(X509TrustManager trustManager) {
    try {
      SSLContext sslContext = SSLContext.getInstance("TLS");
      sslContext.init(null, new TrustManager[] { trustManager }, null);
      return sslContext.getSocketFactory();
    } catch (GeneralSecurityException e) {
      throw new AssertionError(); // The system has no TLS. Just give up.
    }
  }

  • 若使用自签名证书,则客户端需要将server生成的xx.cer放置到assets目录下:
    /**
     * 设置https certificate
     *
     * @param context
     * @param certificateName must be in assets directory such as "abf.cer"
     */
    public void setSSLSocketFactory(Context context, String certificateName) {
        try {
            CertificateFactory tCertFactory = CertificateFactory.getInstance("X.509");
            X509Certificate tJDBCert = readJDBCertFromAsset(context, certificateName, tCertFactory);
            X509TrustManager tTrustMgr = newTrustManager(tJDBCert);
            SSLContext tSSLContext = newSSLContext(tTrustMgr);
            mOkHttpClient.newBuilder().sslSocketFactory(new NoSSLv3SocketFactory(tSSLContext.getSocketFactory()), tTrustMgr);
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
    }
    /**
     * xxx.cer证书 -> X509Certificate
     */
    private X509Certificate readJDBCertFromAsset(Context context, String certificateName, CertificateFactory pCertFactory) throws IOException, CertificateException {
        InputStream caInput = context.getAssets().open(certificateName);
        if (caInput == null) {
            return null;
        }
        try {
            X509Certificate tCert = (X509Certificate)pCertFactory.generateCertificate(caInput);
            tCert.checkValidity();
            return tCert;
        } finally {
            caInput.close();
        }
    }
    /**
     * X509TrustManager实例化 Android 自带的一套https认证机制
     */
    private X509TrustManager newTrustManager(final X509Certificate privateCert) {
        // Accepted CA is only the one installed in the store.
        return new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                if (null == chain || 0 == chain.length) {
                    throw new CertificateException("Certificate chain is invalid.");
                }

                if (null == authType || 0 == authType.length()) {
                    throw new CertificateException("Authentication type is invalid.");
                }

                for (X509Certificate cert : chain) {

                    // Make sure that it hasn't expired.
                    cert.checkValidity();

                    // Verify the certificate's public key chain.
                    try {
                        cert.verify(privateCert.getPublicKey());
                    } catch (NoSuchAlgorithmException e) {
                        e.printStackTrace();
                    } catch (InvalidKeyException e) {
                        e.printStackTrace();
                    } catch (NoSuchProviderException e) {
                        e.printStackTrace();
                    } catch (SignatureException e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[]{};
            }
        };
    }
    /**
     * X509TrustManager -> SSLContext
     */
    private SSLContext newSSLContext(X509TrustManager pTrustManager) throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext tSSLContext = SSLContext.getInstance("TLS");
        tSSLContext.init(null, new TrustManager[]{pTrustManager}, null);
        return tSSLContext;
    }

解释一下代理如何抓取Https请求:

一般我们可以抓取的https请求,server端的证书都是由认证CA颁发的,所以
X509TrustManager可以使用默认的那套,从系统根证书信任列表中去挨个认证。
所以那Charles为例,如何抓取https信息呢? 首先电脑上(由于电脑是代理,相当于服务器)
信任Charles证书,然后在手机上安装Charles证书,两端证书的指纹是一样的。当手机发出
https请求时,根据https通信过程,电脑会把charles的证书下发给手机,而手机上由于之前
安装了Charles证书显然可以验证通过,所以手机端会生成对称秘钥,从此以它进行加解密。但
是,对于自签名的证书,由于使用的X509TrustManager是自定义的,也就是只能验证自签名
证书,不会向手机的根信任列表去挨个校验,所以这种方式无法抓取https。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容