HTTPS 和 CA

  HTTPS 就是是 HTTP 加入了 SSL/TLS 协议,在 TCP 三次握手的前提下又进行了 SSL 验证,SSL 的验证依赖于证书,服务端证书经过 CA 签名后才会通过验证,被认为是可信的站点,然后通过协商出的密钥对通信过程进行加密,本文就详述一下这个过程。

HTTP 和 HTTPS

HTTP 的缺点

  我们知道 HTTP 协议是以明文传输信息的,信息很容易被劫持和篡改,为了防止信息被劫持,我们决定对传输的信息用一个密码加密,为了保证这个密码的安全,每次连接中我们让客户端生成一个随机数作为密码,但是考虑到这个密码在发送给服务端的时候也可能被劫持,我们还得对这个随机数进行加密,但是如何才能做到只有服务端才能解密呢?最安全的莫过于非对称加密,用服务端公钥加密,私钥只在服务端手里,方案有了,只需要服务端把公钥发过来就行了,流程如下:

  1. 客户端向服务端请求公钥;
  2. 服务端把自己公钥发给客户端;
  3. 客户端生成随机值,用服务端公钥加密发给服务端;
  4. 服务端用自己私钥解密获取该随机值;
  5. 服务端客户端的通信用该随机值加密。

但是这种方案 并不完美 ,如图:
MAN-IN-MIDDLE

  Client 把随机值发给 Server 时候是经过 Server 公钥加密的,但是 Client 获取 Server 公钥的时候, Server 是以明文将公钥发送给 Client 的,有可能被劫持,如上图 Server 发送公钥的过程中被劫持,公钥被劫持就无安全可言了,我们 无法确保首次公钥交换过程的安全,为了解决这个问题,就有了 HTTPS 协议。
  HTTPS 基于 SSL/TLS 协议,引入 SSL 后主要对上面第二步产生影响,这一步服务端会发送一个证书到客户端,证书里有服务端的公钥,客户端会先验证证书,验证可信后才会使用其公钥,中间人无法伪造可信的证书,也就无法劫持公钥(浏览器发现不可信的证书,会提示“您的链接不安全”,如果用户主动添加信任的话,还是有证书被劫持的风险,在用 Burp Suite 抓 HTTPS 包时候需要事先把 Burp 的证书导入浏览器,也是这个原因),为什么伪造的证书无法通过验证?这就是 CA 的功劳,CA 是一个权威的第三方机构,经过 CA 认证的证书才会通过客户端浏览器的验证,浏览器怎么知道某个证书是否被 CA 认证过?浏览器和操作系统都会内置一些可信的根证书颁发机构,也就是说这些机构的权威性是由浏览器或操作系统保证的。

HTTPS 协议

   HTTPS = HTTP + SSL,简单说运行在 SSL/TLS 上的 HTTP 就是 HTTPS,SSL 协议依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密,所以 SSL 验证过程中会有证书校验的步骤,在下一章节会详细介绍证书,先来看看这几个协议在计算机网络的 OSI 七层模型中的位置:

层级 层名 常用协议
7 应用层 HTTP/HTTPS、FTP、Socket、Telnet、SSH、SMTP、POP3、DHCP、DNS、NFS、SNMP
6 表示层 XDR、LPP
5 会话层 SSL/TLS、LDAP/DAP、RPC
4 传输层 TCP、UDP
3 网络层 IP、OSPF、ICMP
2 数据链路层 以太网、令牌环、PPP、PPTP、L2TP、ARP、ATMP
1 物理层 物理线路、光纤、无线电

  对于 SSL 属于哪一层网上争议挺多,有人说 SSL 协议并没有很好的对应 OSI 模型,所以具体属于哪一层没有严格的说法,会话层似乎更合适一些。

  客户端执行 HTTPS 请求时,需要由 TCP 协议建立和释放连接,这就涉及 TCP 协议的三次握手和四次挥手:
TCP 三次握手建立连接
Three-way Handshake

  1. 客户端发送一个唯一的数据包(SYNJ)给服务器,请求建立连接;
  2. 服务器收到客户端的请求后,生成一个 SYN J 的回应包(ack J+1)和一个新的数据包(SYN K),发给客户端;
  3. 客户端收到服务器返回的两个包后,针对服务器的 SYN K 包生成一个回应包(ack K+1),并发送给服务器。至此完成三次握手。

TCP 四次挥手断开连接
Three-way Handshake

  1. 客户端发送 FIN(M)报文给服务器,告诉对方,“我的数据发完了”;
  2. 服务器收到 FIN(M)报文后,回给客户端一个 ack(M+1)报文,告诉客户端,“好,我知道了”;
  3. 服务器发一个 FIN(N)报文给客户端,告诉对方,“我的数据也发完了”;
  4. 客户端回应 ack(N+1),告诉服务器,“好,我知道了”,至此,四次挥手断开连接。

TLS 和 SSL
  其实 TLS 就是从 SSL 发展而来的,只是 SSL 发展到 3.0 版本后改成了 TLS,版本演进大体为 SSL 2.0 -> SSL 3.0 -> TLS 1.0(可以看做是SSL 3.1版)。TLS 主要提供三个基本服务:

加密
身份验证,也可以叫证书验证吧
消息完整性校验

HTTPS 单向验证

  对于 HTTP 而言,TCP 连接建立好后服务器就可以发数据给客户端了。但是对于 HTTPS,它还要运行 SSL/TLS 协议,SSL/TLS 协议分两层,第一层是记录协议,主要用于传输数据的加密压缩;第二层是握手协议,它建立在第一层协议之上,主要用于数据传输前的双方身份认证、协商加密算法、交换密钥。
  HTTPS 验证过程就是 SSL 握手协议的交互过程。“HTTPS 验证”这个说法其实不准确的,应该是“SSL 验证”,过程如下:
1. 客户端发起 ClientHello
客户端向指定域名的服务器发起 HTTPS 请求,请求内容包括:

  1. 客户端支持的 SSL/TLS 协议版本列表;
  2. 支持的对称加密算法列表;
  3. 客户端生成的随机数 A。

2. 服务端回应 SeverHello
服务器收到请求后,回应客户端,回应的内容主要有:

  1. SSL/TLS 版本。服务器会在客户端支持的协议和服务器自己支持的协议中,选择双方都支持的 SSL/TLS 的最高版本,作为双方使用的 SSL/TLS 版本。如果客户端的 SSL/TLS 版本服务器都不支持,则不允许访问;
  2. 与1类似,选择双方都支持的最安全的加密算法;
  3. 从服务器密钥库中取出的证书;
  4. 服务器端生成的随机数 B。

3. 客户端回应
客户端收到后,检查证书是否合法,主要检查下面 4 点:

  1. 检查证书是否过期
  2. 检查证书是否已经被吊销。有 CRL 和 OCSP 两种检查方法。CRL 即证书吊销列表,证书的属性里面会有一个 CRL 分发点属性,这个属性会包含了一个 url 地址,证书的签发机构会将被吊销的证书列表展现在这个 url 地址中;OCSP 是在线证书状态检查协议,客户端直接向证书签发机构发起查询请求以确认该证书是否有效。
  3. 证书是否可信。浏览器会有一个信任库,里面保存了该客户端信任的 CA(证书签发机构)的证书,如果收到的证书签发机构不在信任库中,则浏览器会提示用户证书不可信。假如是 Java 程序,需要程序配置信任库文件,以判断证书是否可信,如果没设置,则默认使用 jdk 自带的证书库(jre\lib\security\cacerts,默认密码 changeit)。如果证书或签发机构的证书不在信任库中,则认为不安全,程序会报错。(你可以在程序中设置信任所有证书,不过这样并不安全)。
  4. 检查收到的证书中的域名与请求的域名是否一致。若客户端是程序,这一项可配置不检查。若为浏览器,则会出现警告,用户也可以跳过。证书验证通过后,客户端使用特定的方法又生成一个随机数 c,这个随机数有专门的名称“pre-master key”。接着客户端会用证书的公钥对“pre-master key”加密,然后发给服务器。

4. 服务器的最后回应
  服务器使用密钥库中的私钥解密后,得到这个随机数 c。此时,服务端和客户端都拿到了随机数 a、b、c ,双方通过这 3 个随机数使用相同的 DH 密钥交换算法 计算得到了相同的 对称加密的密钥。这个密钥就作为后续数据传输时对称加密使用的密钥。
  服务器回应客户端,握手结束,可以采用对称加密传输数据了。

如图:
one-way-authentication

这里注意几点:

  1. 整个验证过程,折腾了半天,其实是为了安全地得到一个双方约定的对称加密密钥,当然,过程中也涉及一些身份认证过程。既然刚开始时,客户端已经拿到了证书,里面包含了非对称加密的公钥,为什么不直接使用非对称加密方案呢,这是因为非对称加密计算量大、比较耗时,而对称加密耗时少。

  2. 为什么要用到 3 个随机数,1 个不行吗?这是因为客户端和服务端都不能保证自己的随机数是真正随机生成的,这样会导致数据传输使用的密钥就不是随机的,时间长了就很容易被破解。如果使用客户端随机数、服务端随机数、pre-master key 随机数这 3 个组合,就能十分接近随机。

HTTPS 双向验证

  单向验证过程中,客户端会验证自己访问的服务器,服务器对来访的客户端身份不做任何限制。如果服务器需要限制客户端的身份,则可以选择开启服务端验证,这就是双向验证。从这个过程中我们不难发现,使用单向验证还是双向验证,是服务器决定的。
  一般而言,我们的服务器都是对所有客户端开放的,所以服务器默认都是使用单向验证。如果你使用的是 Tomcat 服务器,在配置文件 server.xml 中,配置 Connector 节点的 clientAuth 属性即可。若为 true 则使用双向验证,若为 false 则使用单向验证。如果你的服务只允许特定的客户端访问,那就需要使用双向验证了。

双向验证基本过程与单向验证相同,不同在于:

  1. 第二步服务器第一次回应客户端的 SeverHello 消息中,会要求客户端提供“客户端的证书”;
  2. 第三步客户端验证完服务器证书后的回应内容中,会增加两个信息:

    1. 客户端的证书;
    2. 客户端证书验证消息(CertificateVerify message):客户端将之前所有收到的和发送的消息组合起来,并用 hash 算法得到一个 hash 值,然后用客户端密钥库的私钥对这个 hash 进行签名,这个签名就是 CertificateVerify message。
  3. 服务器收到客户端证书后:

    1. 确认这个证书是否在自己的信任库中(当然也会校验是否过期等信息),如果验证不通过则会拒绝连接;
    2. 用客户端证书中的公钥去验证收到的证书验证消息中的签名。这一步的作用是为了确认证书确实是客户端的。

如图:
two-way-authentication

说明:
  关于第二步中客户端私钥的使用,网上有很多文章认为:在协商对称加密方案时,服务端先用客户端公钥加密服务器选定的对称加密方案,客户端收到后使用私钥解密得到。首先,对称加密方案就那么几种,逐个试试就能试出来,没必要为了这个增加一个客户端和服务端的交互过程。而这里关于 CertificateVerify message 的说法参考了维基百科关于“Transport Layer Security”一文中“Client-authenticated TLS handshake”的描述。

  所以,在双向验证中,客户端需要用到密钥库,保存自己的私钥和证书,并且证书需要提前发给服务器,由务器放到它的信任库中。

CA 和证书

什么是 CA 证书

  CA(Certificate Authority),也叫“证书授权中心”,它是负责管理和签发证书的第三方机构,CA 证书顾名思义就是 CA 颁发的证书,平常说的 CA 证书应该是指 CA 根证书,但 CA 根证书并不会直接对申请者签名,而是通过中间证书进行签名,经过签名认证的证书,通常也叫 SSL 证书,当然具体怎么称呼还是看场景,CA 自签名的证书叫根证书;针对客户端/服务端分别有客户端证书、服务端证书;但是对于 CA 来说无论客户端/服务端都是其用户,可以统称用户证书。

证书的签发过程

  1. 服务方 S 向第三方机构 CA 提交公钥、组织信息、个人信息(域名)等信息并申请认证;

  2. CA 通过线上、线下等多种手段验证申请者提供信息的真实性,如组织是否存在、企业是否合法,是否拥有域名的所有权等;

  3. 如信息审核通过,CA 会向申请者签发认证文件-证书。证书包含以下信息:申请者公钥、申请者的组织信息和个人信息、签发机构 CA 的信息、有效时间、证书序列号等信息的明文,同时包含一个签名;
    签名的产生算法:首先,使用散列函数计算公开的明文信息的信息摘要,然后该散列函数和信息摘要经过 CA 的私钥进行加密,密文即是签名;

  4. 客户端 C 向服务器 S 发出请求时,S 返回证书文件;

  5. 客户端 C 读取证书中的相关的明文信息,然后利用对应 CA 的公钥解密签名数据,获取散列函数后用该函数计算证书中明文信息得到信息摘要,对比证书的信息摘要,如果一致,则可以确认证书的合法性,即公钥合法;

  6. 客户端然后验证证书相关的域名信息、有效时间等信息;

  7. 客户端会内置信任 CA 的证书信息(包含公钥),如果CA不被信任,则找不到对应 CA 的证书,证书也会被判定非法。

证书格式如下:
Certificate frame

在这个过程注意几点:

  1. 申请证书不需要提供私钥,确保私钥永远只能服务器掌握;

  2. 证书的合法性仍然依赖于非对称加密算法,证书主要是增加了服务器信息以及签名;

  3. 内置 CA 对应的证书称为根证书,颁发者和使用者相同,自己为自己签名,即自签名证书;

  4. 证书 = 公钥 + 申请者与颁发者信息 + 签名。

证书链和中间证书

证书是以证书链的形式存在的:

最上层为 root,也就是通常所说的 CA ,用来颁发证书;
最下层为 end-user,对应每个网站购买使用的证书;
中间一层为 intermediates,是二级 CA ,这一层可以继续划分为多层,用来帮助 root 给 end-user 颁发证书,这样 root 只需向 intermediates 颁发证书;
只有当整个证书链上的证书都有效时,才会认定当前证书合法 。

  中间层 intermediates 实际上是一个中间证书,其实也叫中间 CA(中间证书颁发机构,Intermediate certificate authority, Intermedia CA),对应的是根证书颁发机构(Root certificate authority ,Root CA)。为了验证证书是否可信,必须确保证书的颁发机构在设备的可信 CA 中。如果证书不是由可信 CA 签发,则会检查颁发这个 CA 证书的上层 CA 证书是否是可信 CA ,客户端将重复这个步骤,直到证明找到了可信 CA(将允许建立可信连接)或者证明没有可信 CA(将提示错误)。

  为了构建信任链,每个证书都包括字段:“使用者”和“颁发者”。 中间 CA 将在这两个字段中显示不同的信息,显示设备如何获得下一个 CA 证书,重复检查是否是可信 CA 。

  根证书,必然是一个自签名的证书,“使用者”和“颁发者”都是相同的,所以不会进一步向下检查,如果根 CA 不是可信 CA ,则将不允许建立可信连接,并提示错误。

例如:一个服务器证书 domain.com,是由 Intermedia CA 签发,而 Intermedia CA 的颁发者 Root CA 在 WEB 浏览器可信 CA 列表中,则证书的信任链如下:

证书 1 - 使用者:domain.com;颁发者:Intermedia CA
证书 2 - 使用者:Intermedia CA;颁发者: Root CA
证书 3 - 使用者:Root CA ; 颁发者: Root CA

  当 Web 浏览器验证到 Root CA 时,发现是一个可信 CA ,则完成验证,允许建立可信连接。当然有些情况下,Intermedia CA 也在可信 CA 列表中,这个时候就可以直接完成验证,建立可信连接。

为何需要中间证书

  1. 保护根证书。如果直接采用根证书签发证书,一旦发生根证书泄露,将造成极大的安全问题。所以目前根证书都要求离线保存,如果需要用根证书签名,则必须通过人手工方式,直接用根证书在线签发证书是不允许的。
  2. 区分不同类型的产品。针对 DV、OV、EV 等不同类型,不同安全级别的证书,CA 会采用不同的根证书,一来便于区分,二来一旦出现问题,也便于区别处理,降低影响。中间 CA 证书一般都是支持在线签发证书的。
  3. 交叉验证。为了获得更好的兼容性,支持一些很古老的浏览器,有些根证书本身,也会被另外一个很古老的根证书签名,这样根据浏览器的版本,可能会看到三层或者是四层的证书链结构,如果能看到四层的证书链结构,则说明浏览器的版本很老,只能通过最早的根证书来识别。

证书验证流程

如图:
certificate-verify

  1. 客户端获取到了站点证书,拿到了站点的公钥;
  2. 要验证站点可信后,才能使用其公钥,因此客户端找到其站点证书颁发者的信息;
  3. 站点证书的颁发者验证了服务端站点是可信的,但客户端依然不清楚该颁发者是否可信;
  4. 再往上回溯,找到了认证了中间证书商的源头证书颁发者。由于源头的证书颁发者非常少,我们浏览器之前就认识了,因此可以认为根证书颁发者是可信的;
  5. 一路倒推,证书颁发者可信,那么它所颁发的所有站点也是可信的,最终确定了我们所访问的服务端是可信的;
  6. 客户端使用证书中的公钥,继续完成TLS的握手过程。

  那么,客户端是是如何验证某个证书的有效性,或者验证策略是怎样的?证书颁发者一般提供两种方式来验证证书的有效性:
CRL
  CRL(Certificate Revocation List)即 证书撤销名单。证书颁发者会提供一份已经失效证书的名单,供浏览器验证证书使用。当然这份名单是巨长无比的,浏览器不可能每次TLS都去下载,所以常用的做法是浏览器会缓存这份名单,定期做后台更新。这样虽然后台更新存在时间间隔,证书失效不实时,但一般也OK。

OCSP
  OCSP(Online Certificate StatusProtocol)即 在线证书状态协议。除了离线文件,证书颁发者也会提供实时的查询接口,查询某个特定证书目前是否有效。实时查询的问题在于浏览器需要等待这个查询结束才能继续TLS握手,延迟会更大。

证书类型

  能够受浏览器默认信任的 CA 大厂商有很多,Verisign 是第一家 CA 厂商,创办于 1995 年,当时得到了 RSA 算法的使用授权,是全世界最大的 CA 厂商,在 2010 年以 12.8 亿美元卖给了赛门铁克。当前 TOP5 的 CA 厂商分别是 Symantec、Comodo、Godaddy、GlobalSign 和 Digicert,占据了 90% 以上的市场份额。

dv-ov-ev

  需要强调的是,不论是 DV、OV 还是 EV 证书,其加密效果都是一样的! 它们的区别在于:
  DV(Domain Validation),面向个体用户,安全体系相对较弱,验证方式就是向 whois 信息中的邮箱发送邮件,按照邮件内容进行验证即可通过;
  OV(Organization Validation),面向企业用户,证书在 DV 证书验证的基础上,还需要公司的授权,CA 通过拨打信息库中公司的电话来确认;
  EV(Extended Validation),打开 Github 的网页,你会看到 URL 地址栏展示了注册公司的信息,这会让用户产生更大的信任,这类证书的申请除了以上两个确认外,还需要公司提供金融机构的开户许可证,要求十分严格。
  OV 和 EV 证书相当昂贵,使用方可以为这些颁发出来的证书买保险,一旦 CA 提供的证书出现问题,一张证书的赔偿金可以达到 100w 刀以上。

总结

为什么需要 CA
   HTTPS 要使客户端与服务器端的通信过程得到安全保证,必须使用的对称加密算法,但是协商对称加密算法的过程,需要使用非对称加密算法来保证安全,然而直接使用非对称加密的过程本身也不安全,会有中间人篡改公钥的可能性,所以客户端与服务器不直接使用公钥,而是使用数字证书签发机构颁发的证书来保证非对称加密过程本身的安全。这样通过这些机制协商出一个对称加密算法,就此双方使用该算法进行加密解密,从而解决了客户端与服务器端之间的通信安全问题。

浏览器证书校验大致过程:

  1. 首先证书包括证书机构、有效期、服务器公钥等一些详细信息,以及一个签名。把前面详细信息经过 hash 得到一个字串,这个字串和 hash 算法经过 CA 私钥加密就是前面提到的签名了;
  2. 浏览器读取证书中的证书所有者、有效期等信息进行一一校验;
  3. 浏览器开始查找操作系统中已内置的受信任的证书发布机构CA,与服务器发来的证书中的颁发者CA比对,用于校验证书是否为合法机构颁发;
  4. 如果找不到,浏览器就会报错,说明服务器发来的证书是不可信任的;
  5. 如果找到,那么浏览器就会从操作系统中取出颁发者CA 的公钥,然后对服务器发来的证书里面的签名进行解密,解密后得到一个 hash 算法,浏览器使用该算法对证书中详细信息计算 hash 值,将这个 hash 值与签名中的 hash 值做对比;
  6. 对比结果一致,则证明服务器发来的证书合法,没有被冒充;
  7. 此时浏览器就可以读取证书中的公钥,用于后续加密了。

用于对称加密的 key 的生成过程:

  1. 客户端发送 ClientHello 消息、支持的加密算法和生成的一个随机数 random1 到服务器端(Say Hello);
  2. 服务端会在客户端加密算法中选择一个自己也支持的加密算法作为后面通信的加密算法、生成一个随机数 random2,还有服务器证书(包含公钥)一起回馈给客户端(I got it);
  3. 证书验证通过后,客户端生成随机数 random3 ,通过 server 的公钥加密并传递给服务端;
  4. 服务器端使用私钥解密,拿到 random3;
  5. 这三个随机数通过某种算法算出 session key。

参考

HTTPS原理和CA证书申请(满满的干货)
HTTPS实战之单向验证和双向验证
Https单向认证和双向认证

CA证书扫盲,https讲解。
浅析HTTPS与SSL原理
https之证书验证
中间证书的使用
HTTPS加密过程和TLS证书验证
HTTPS 通信过程详解(含CA证书验证)

文章目录
  1. 1. HTTP 和 HTTPS
    1. 1.1. HTTP 的缺点
    2. 1.2. HTTPS 协议
    3. 1.3. HTTPS 单向验证
    4. 1.4. HTTPS 双向验证
  2. 2. CA 和证书
    1. 2.1. 什么是 CA 证书
    2. 2.2. 证书的签发过程
    3. 2.3. 证书链和中间证书
    4. 2.4. 证书验证流程
    5. 2.5. 证书类型
    6. 2.6. 总结
  3. 3. 参考