理解 HTTPS 原理,SSL/TLS 协议

本贴最后更新于 3290 天前,其中的信息可能已经时移世异

为什么要使用 HTTPS

当我们使用 HTTP 协议时,传输的数据是不安全的,因为所有在客户端和服务端往来的数据都是明文:

  • 第三方可以获取到真实数据
  • 第三方可以篡改数据
  • 第三方可以冒充服务端或客户端

为了解决这些问题,需要在 HTTP 协议中加入一个安全机制,由此并产生了 HTTPS,我们可以认为 HTTPS = HTTP + TLS/SSL。TLS/SSL 的引入解决了安全问题,而上层应用协议还是 HTTP。

历史

SSL(Secure Sockets Layer)中文称作“安全套接层”,TLS(Transport Layer Security),中文称作“传输层安全协议”。

  1. 1994 年,网景(NetScape)公司设计了 SSL 1.0
  2. 1995 年,SSL 2.0,存在严重漏洞
  3. 1996 年,SSL 3.0,得到大规模应用
  4. 1999 年,IETF 对 SSL 进行标准化,发布了 TLS 1.0
  5. 2006 年和 2008 年,TLS 进行了两次升级,分别为 TLS 1.1 和 TLS 1.2

在应用层,我们习惯将两者并称 TLS/SSL,因为它们设计大致相同,我们可以认为它们是同一个事物在不同历史阶段的名称。

前面我们介绍过,HTTPS 可以认为是 HTTP + TLS/SSL,所以我们只需要了解 TLS/SSL 原理即可。在进入原理之前,我们需要了解两个基础概念:数字证书、证书授权中心。

证书与授权

  • 数字证书(Digital Certificate)是用来证明公钥(非对称密钥算法中用于加密的密钥)所有者身份的。我们人人都可以自己生成一个公钥,但是这个公钥是否能代表是你的,这个认证的过程需要一个权威机构执行,这个机构就是证书授权中心。
  • 证书授权中心(Certificate Authority)负责证书颁发。CA 是行业内信得过的组织机构,它具有权威性,由它颁发的证书大家都相信是可靠的。

TLS/SSL 协议

  1. 由客户端发起握手,告诉服务器客户端支持的 TLS/SSL 版本、数据加密算法、以及一个随机数
    1.1. 服务端确认支持这个版本的 TLS/SSL、加密算法,并生成一个随机数
    1.2. 服务端将证书(带公钥)、服务端生成的随机数返回给客户端
  2. 客户端检查证书是否可信(和已有的 CA 列表对比,看是否是已有 CA 颁发的证书),并生成第三个随机数 PreMasterSecret
  3. 客户端使用证书带的公钥将 PreMasterSecret 进行加密,并通过之前交换的数据生成一个 Hash 值,发送给服务端,请求变更编码
    3.1. 服务端校验 Hash(确认不是假的客户端),并使用自己证书的私钥解密出 PreMasterSecret
    3.2. 服务端根据之前的随机数和约定的加密算法,生成用于加密后续传输数据的会话密钥 SessionSecret
    3.3. 服务端根据之前交换的数据生成一个 Hash 值,发送给客户端,确认开始变更编码
  4. 客户端校验 Hash (确认不是假的服务端),并生成会话密钥
  5. 客户端使用会话密钥加密数据,并发送给服务端
    5.1. 服务端使用会话密钥解密数据,执行业务逻辑后产生数据
    5.2. 服务端使用会话密钥加密数据,返回给客户端
  6. 客户端使用会话密钥解密数据,完成一次和服务端的数据交换

在这个过程中,有几个关键点:

  • 前两次的随机数(客户端随机数、服务端随机数)是明文传输的
  • 非对称密钥算法只被使用了一次,即客户端使用证书公钥加密 PreMasterSecret,服务端使用证书私钥解密出 PreMasterSecret
  • 应用数据的传输使用的是对称密钥算法,客户端/服务端都使用会话密钥进行加/解密

在握手阶段,安全与否的关键在于 PreMasterSecret 是否能够被破解,虽然理论上通过 RSA 算法加密是比较安全的,但还是有破解的可能性。最安全的做法是不发送 PreMasterSecret,而是根据一系列参数由客户端和服务端分别计算出 PreMasterSecret,这个算法就是迪菲-赫尔曼密钥交换

另外,TLS/SSL 不只适用于 HTTP 的安全问题,它也可以和其他应用层协议搭配使用。

应用

通过上述介绍,我们得知使用 SSL/TLS 需要做如下准备:

  • 数字证书:可以找权威的证书授权中心颁发证书(有付费的,也有免费的),也可以自己做 CA,然后自己给自己颁发证书
  • 服务端使用证书,比如配置 NGINX 来使用
  • 客户端使用 SSL/TLS 协议和服务端进行通讯,如果是自己的 CA 颁发的证书,还需要在客户端导入 CA 的根证书

下面我们针对自建 CA 做一个完整的示例(证书相关使用 Linux 的 openssl 命令,客户端、服务端使用 Golang)。

建立 CA

  • 生成 CA 私钥:openssl genrsa -out ca.key 2048
  • 生成 CA 根证书:openssl req -new -x509 -days 3650-key ca.key -out ca.crt

颁发证书

  • 生成证书私钥:openssl genrsa -out auto.pem 1024
  • 生成证书公钥:openssl rsa -in auto.pem -out auto.key
  • 生成签名请求:openssl req -new -key auto.pem -out auto.csr,其中的 Common Name 一定要填写客户端访问时的域名,并且不能是 IP
  • CA 签名(颁发)证书:openssl x509 -req -sha256 -in auto.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -out auto.crt

最终我们需要的就是公钥 auto.key 以及证书 auto.crt

服务端使用证书

http.ListenAndServeTLS(Auto.Server, "auto.crt", "auto.key", nil)

客户端发起请求

这里假设客户端已经获得了服务端的证书。

// getClient 根据传入的 url 实参中协议部分返回适当的 HTTP 客户端.
func getClient(url string) *http.Client {
	if !strings.Contains(url, "https://") {
		return &http.Client{}
	}

	certName := strings.Split(url, "https://")[1]
	certName = strings.Split(certName, ":")[0]

	// Load client cert
	cert, err := tls.LoadX509KeyPair("certs/"+certName+".crt", "certs/"+certName+".key")
	if err != nil {
		logger.Error(err)
	}

	// Load CA cert
	caCert, err := ioutil.ReadFile("certs/ca.crt")
	if err != nil {
		logger.Error(err)
	}
	caCertPool := x509.NewCertPool()
	ok := caCertPool.AppendCertsFromPEM(caCert)
	if !ok {
		logger.Error("Load CA cert failed")
	}

	// Setup HTTPS client
	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
		RootCAs:      caCertPool,
	}
	tlsConfig.BuildNameToCertificate()

	transport := &http.Transport{TLSClientConfig: tlsConfig}

	return &http.Client{Transport: transport}
}

其中载入了 CA 证书,原因就是因为我们自建的 CA 不在系统自带的“受信任的根证书颁发机构”中。

参考

打赏 50 积分后可见
50 积分 • 3 打赏
  • SSL

    SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS 与 SSL 在传输层对网络连接进行加密。

    70 引用 • 193 回帖 • 432 关注
  • HTTPS
    99 引用 • 274 回帖 • 3 关注
  • CA
    2 引用 • 13 回帖
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    497 引用 • 1387 回帖 • 285 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • kuuyee

    好文! 👍

    有完整的例子吗?go 语言的

  • 88250

    @kuuyee 上面的示例代码已经比较完整了,改下证书的实参就可以的。

  • someone

    我也想学下,但好难啊

  • someone

    其实博主这样写还是太粗糙了,其实可以这样,直接看 open ssl 源代码,从底层分析 ssl 协议的几个过程,里面的加密套件或则加密算法等等,这样更好理解.
    我的微信:chenyu_920310 求加

    1 回复
  • lemon996

    加不了 v 了