真正搞懂Https

本文主要是为了搞懂https相关知识以及应用,语言保证通俗易懂,清晰。网上有很多文章但都太碎片,这种文章看多了反倒会把自己搞晕

什么是https?

传统的http都是明文传播,且不验证服务器安全性。所以在http协议层下加了个ssl层。ssl层在3.0以上又叫tls层。

SSL/TLS如何保证安全?原理?

保证安全的前置问题是,http传递信息的不安全在哪里?思考清楚这个问题我们才能理解ssl的思想。

  1. 危险站点,我们通过浏览器浏览的网页需要被确保是安全正规的,否则其中可能有木马或偷取用户隐私或钓鱼网站等。
  2. 中间人攻击,传输的数据很容易被代理服务器获取和修改
  3. 冒充客户端,对于部分服务器来说,客户端的验证也是必要的,比如银行交易相关数据请求,必须要求规定的客户端发出才安全。

如何解决这些问题就是ssl要做的事情。看下ssl握手(不同于http的三次握手四次挥手)的过程。握手过程有五步

第一步,爱丽丝给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法。
第二步,鲍勃确认双方使用的加密方法,并给出数字证书、以及一个服务器生成的随机数(Server random)。
第三步,爱丽丝确认数字证书有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给鲍勃。
第四步,鲍勃使用自己的私钥,获取爱丽丝发来的随机数(即Premaster secret)。
第五步,爱丽丝和鲍勃根据约定的加密方法,使用前面的三个随机数,生成"对话密钥"(session key),用来加密接下来的整个对话过程。

爱丽丝表示客户端,鲍勃表示服务器。这个过程看着简洁,但是我最初看的时候,感觉不够深刻,一看就会,一问就懵。那是因为这里有一些细节没弄懂,觉得懂了,实际没懂。

  1. 客户端支持的加密方法是什么?
    看这张charles抓包的截图,实际上就是一个对称加密的方法,服务器会从其中选择一个(Server Chosen)。秘钥就是三个随机数生成的对话秘钥。

    20191104205421.png

  2. 客户端如何证明服务器的证书有效的呢?
    证书包含以下信息:申请者公钥、申请者的组织信息和个人信息、签发机构 CA 的信息、有效时间、证书序列号等信息的明文,同时包含一个签名;
    签名的产生算法:首先,使用散列函数计算公开的明文信息的信息摘要,然后,采用 CA 的私钥对信息摘要进行加密,密文即签名;
    只要用证书公钥解开签名信息,然后将解密的信息和证书中的信息进行对比。同时保证本地信任该组织证书。浏览器或手机支持的证书都可以在手机中查到,也可以手动禁用他们,或安装其他证书。

  3. 公钥和私钥?
    相信很多人这个都懂,但是我还是想说下我对私钥公钥的理解,其实很简单。
    公钥是给别人用的,私钥是你自己保存的。所以你想证明你的身份 私钥加密下,别人用你的公钥能解密那就证明你的身份了。同样还有个作用就是我用你的公钥加密,除了你自己能解开,别人是解不开的,这也保证了数据安全性。看到了吧,就这两把钥匙就保证了数据安全和对方身份。

搞懂这三点再看流程是不是更简单了。简单的说就是 1. 客户端校验了服务器的证书,保证服务器安全 2. 客户端和服务器约定算法保证数据安全。那么开篇的三个问题前两个就解决掉了。第三个就涉及到双向认证了,后面再说。

到这儿还是想考下,为什么这样就解决中间人攻击了呢
首先客户端发送加密算法套件和随机数给服务器。代理服务器拿到了你发送的这些东西,原封不动再将这些数据发给服务器。服务器收到后,将自己的证书及生成的随机数发给代理服务器。代理服务器又原封不动的将证书和随机数发给客户端。客户端使用服务器证书加密自己生成的随机数,然后发送给代理服务器。这个时候代理服务器由于没有证书私钥,无法解密内容。代理服务器只能依然原封不动的发给服务器。服务器用私钥成功解密,然后选择其中一个加密算法,用三个随机数作为数据加密密钥。客户端和服务器开始了真正的数据交流,然后处在中间的代理服务器只能懵逼的传递数据。

代理服务器就真的没办法解密数据了吗?如果第二步中服务器将证书发给代理服务器,而代理服务器偷梁换柱将自己证书发给客户端。客户端将生成的随机数加密并发送给代理服务器。代理服务器用自己的私钥解开,然后用服务器公钥加密下,再发送给服务器。这样就可以操作客户端数据了。

那么怎么解决中间人替换证书呢?
客户端不知道真正的服务器证书是什么,所以才会信任中间人给的证书。那么问题也好解决,就是要客户端确定服务器证书,将证书放到本地。这样第二步中代理服务器替换证书后,客户端不理会代理服务器给的证书,而直接用本地证书去加密。然后再发送给代理服务器,代理服务器由于没有私钥,也解不开,所以依然只能懵逼的做一个中间人。

可能这个时候你还是有疑问,那服务器证书的验证还有意义吗?内置证书的情况,就默认信任该服务器证书了,所以证书也就没有验证的必要了。

如何设置本地证书?

java的okHttp中设置代码如下:

  public CustomTrust() {
    X509TrustManager trustManager;
    SSLSocketFactory sslSocketFactory;
    try {
      trustManager = trustManagerForCertificates(trustedCertificatesInputStream());
      SSLContext sslContext = SSLContext.getInstance("TLS");
      sslContext.init(null, new TrustManager[] { trustManager }, null);
      sslSocketFactory = sslContext.getSocketFactory();
    } catch (GeneralSecurityException e) {
      throw new RuntimeException(e);
    }

    client = new OkHttpClient.Builder()
        .sslSocketFactory(sslSocketFactory, trustManager)
        .build();
  }

 private InputStream trustedCertificatesInputStream() {
    // PEM files for root certificates of Comodo and Entrust. These two CAs are sufficient to view
    // https://publicobject.com (Comodo) and https://squareup.com (Entrust). But they aren't
    // sufficient to connect to most HTTPS sites including https://godaddy.com and https://visa.com.
    // Typically developers will need to get a PEM file from their organization's TLS administrator.
    String comodoRsaCertificationAuthority = ""
        + "-----BEGIN CERTIFICATE-----\n"
        + "MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB\n"
        + ...
        + "NVOFBkpdn627G190\n"
        + "-----END CERTIFICATE-----\n";
    String entrustRootCertificateAuthority = ""
        + "-----BEGIN CERTIFICATE-----\n"
        + "MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC\n"
        + ...
        + "-----END CERTIFICATE-----\n";
    return new Buffer()
        .writeUtf8(comodoRsaCertificationAuthority)
        .writeUtf8(entrustRootCertificateAuthority)
        .inputStream();
  }

 private X509TrustManager trustManagerForCertificates(InputStream in)
      throws GeneralSecurityException {
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
    Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in);
    if (certificates.isEmpty()) {
      throw new IllegalArgumentException("expected non-empty set of trusted certificates");
    }

    // Put the certificates a key store.
    char[] password = "password".toCharArray(); // Any password will work.
    KeyStore keyStore = newEmptyKeyStore(password);
    int index = 0;
    for (Certificate certificate : certificates) {
      String certificateAlias = Integer.toString(index++);
      keyStore.setCertificateEntry(certificateAlias, certificate);
    }

    // Use it to build an X509 trust manager.
    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
        KeyManagerFactory.getDefaultAlgorithm());
    keyManagerFactory.init(keyStore, password);
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
        TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init(keyStore);
    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];
  }

  private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
    try {
      KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
      InputStream in = null; // By convention, 'null' creates an empty key store.
      keyStore.load(in, password);
      return keyStore;
    } catch (IOException e) {
      throw new AssertionError(e);
    }
  }

代码不多解释了,不是重点。
这样基本可以保证数据安全传输了,解决了中间人攻击和服务器验证问题。但是还不够完美,最好是能对客户端也进行验证。那么这就是最后一个问题了 如何实现双向认证?

sslContext.init(KeyManager[],TrustManager[]null);
//KeyManager[] 第一个参数是授权的密钥管理器,用来授权验证。TrustManager[]第二个是被授权的证书管理器,用来验证服务器端的证书。
//第三个参数是一个随机数值,可以填写null。如果只是服务器传输数据给客户端来验证,就传入第一个参数就可以,客户端构建环境就传入第二个参数。
//双向认证的话,就同时使用两个管理器。

到这里就结束了,希望以后遇到https问题能够更好的解决

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

推荐阅读更多精彩内容