基本概念

  • SSL(Secure Socket Layer)安全套接层诞生于1994年,Netscape在推出首版网页浏览器网景导航者时,推出HTTPS协议,以SSL进行加密,于1995年推出3.0版本后停止维护,贡献给了IETF
  • 1999年IETF对其进行标准化,提出TLS(Transport Layer Security)传输层安全协议,随后在2008年8月公布RFC 5246,在2011年3月公布RFC 6176,目前已成为互联网上保密通信的工业标准。
  • TLS 一共有三个版本,1.1、1.2 和 1.3 ,最广泛使用的是 1.2,所以接下来的探讨基于 TLS 1.2 的版本。不过在称呼时候,比起直接说TLS,人们还是习惯称呼为SSL,或者SSL/TLS。

Https

  • SSL简单理解就是在http这样的应用层协议基础上对数据进行加密。
  • 在使用https 时,http会先和 SSL 进行通信,然后再由 SSL 和 TCP 进行通信。也就是说,HTTPS 是经过加密的 HTTP,应用层看到的是明文,传输层传输的都是密文。
  • SSL 是一个独立的协议,其他应用层协议,比如 SMTP(电子邮件协议)Telnet(远程登录协议) 等都可以使用。
  • SSL保证了数据的机密性(内容不被其他人看到)、完整性(不被他人修改)、认证性(确保在正确的对象间发送),接下来将对实现方法进行说明。

加密方法

  • SSL通过对称加密进行数据传输,其密钥则是在握手阶段进行交换,此时则使用更安全的非对称加密进行保障,握手时也会交换网站信息,进行身份识别。

对称加密

  • 首先,对于加密过程几个部分,密码学称呼为:明文、密文、加密、解密。

    • 明文(Plaintext),一般认为是有意义的字符或者比特集,或者是通过某种公开编码就能获得的消息,通常用 m 或 p 表示;

    • 密文(Ciphertext),对明文加密后就变成了密文;

    • 加密(Encrypt),把原始的明文转换为密文的信息变换过程;

    • 解密(Decrypt),把已经加密的信息恢复成明文的过程。

  • 对称加密(Symmetrical Encryption)就是指加密和解密时使用的密钥都是同样的密钥。只要保证了密钥的安全性,那么整个通信过程也就具有机密性。

  • SSL里面有比较多的加密算法可供使用,目前最常用的是 AES-128, AES-192、AES-256 和 ChaCha20。

非对称加密

  • 非对称加密(Asymmetrical Encryption) 也被称为公钥加密,非对称加密中有两个密钥,一个是公钥,一个是私钥,公开密钥可供任何人使用,私钥只有你自己能够知道。使用公钥加密的文本只能使用私钥解密,使用私钥加密的文本也可以使用公钥解密。公钥需要在网络间进行传输,用户想要登录网站只要用公钥加密就行了,而密文只能由私钥持有者才能解密。黑客没有私钥,就无法破解密文。

  • 非对称加密算法常见的比如 DH、DSA、RSA、ECC 等。其中 RSA 加密算法是最重要的、最出名的一个,例如 DHE_RSA_CAMELLIA128_GCM_SHA256。它的安全性基于整数分解,使用两个超大素数的乘积作为生成密钥的材料,想要从公钥推算出私钥是非常困难的。

  • ECC(Elliptic Curve Cryptography)也是非对称加密算法的一种,它基于椭圆曲线离散对数的数学难题,使用特定的曲线方程和基点生成公钥和私钥, ECDHE 用于密钥交换,ECDSA 用于数字签名。

混合加密

  • RSA 的运算速度非常慢,而 AES 的加密速度比较快,SSL在通信刚开始的时候使用非对称算法,比如RSA、ECDHE,解决密钥交换的问题。随后用随机数产生对称加密使用的会话密钥(session key),再用公钥加密。对方拿到密文后用私钥解密,取出会话密钥。这样,双方就实现了对称密钥的安全交换。

完整性验证

  • 在SSL中,实现完整性的手段主要是摘要算法(Digest Algorithm)

    消息摘要(Message Digest)又称为数字摘要(Digital Digest)。它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生。如果消息在途中改变了,则接收者通过对收到消息的新产生的摘要与原摘要比较,就可知道消息是否被改变了。因此消息摘要保证了消息的完整性。

  • 以MD5(Message Digest Algorithm 5)为例,它属于密码哈希算法(cryptographic hash algorithm),可用于从任意长度的字符串创建 128 位字符串值,最常用于验证文件的完整性。它还用于其他安全协议和应用程序中,例如 SSH、SSL 和 IPSec,一些应用程序通过向明文加盐值或多次应用哈希函数来增强 MD5 算法。

    在密码学中,就是一项随机数据,用作哈希数据,密码或密码的单向函数的附加输入。盐用于保护存储中的密码,没有密钥可以进行解密,只能进行单向加密,加密后的数据无法解密,不能逆推出原文。

  • SHA-1(Secure Hash Algorithm 1) 也是一种常用的加密算法,不过 SHA-1 是不安全的,在SSL里面被禁止使用。目前SSL推荐使用的是 SHA-1 的后继者:SHA-2。

  • SHA-2 的全称是Secure Hash Algorithm 2 ,它在 2001 年被推出,在SHA-1的基础上做了重大修改,SHA-2 系列包含六个哈希函数,其哈希值分别为 224、256、384 或 512 位:SHA-224, SHA-256, SHA-384, SHA-512,分别能够生成 28 字节、32 字节、48 字节、64 字节的哈希值。

  • 有了 SHA-2 的保护,就能够实现数据的完整性,哪怕文件改变一个标点符号,增加一个空格,生成的哈希值也会完全不同。但直接对文件进行Hash存在伪造Hash值的风险,安全性更高的加密方式是使用 HMAC,Hash+MAC。

    MAC (message authentication code,即消息认证码),与Hash 类似,它也用一段小的数据(称为MAC)来代表一段数据,不同的是,MAC 在生成消息认证码时使用对称加密来保证数据来自约定好的合法的伙伴,也就是将消息与共享密钥组合在一起作为输入,输出一个代表共享密钥和消息的数据。用数学描述如下:

    1
    mac_code = mac_algorithm(mac_key + message)
  • HMAC 使用两个密钥,并执行两轮哈希计算。该算法的第一轮从原始消息和第一个密钥 K1 生成内部哈希 HMAC1。然后,第二轮使用生成的内部哈希和第二个密钥 K2 创建最终的 HMAC。接收方计算自己的 HMAC,并将其与接收的 HMAC 进行比较,以验证消息的身份验证和完整性,用数学描述如下:

    1
    HMAC = Hash(key1 + Hash(key2 + message))

身份认证

  • 公钥加密、私钥解密可以保护用户发送给网站的信息,而网站拥有的私钥由于具有唯一性,所以可以私钥加密、公钥解密,再加上摘要算法,就能够实现数字证书交换,从而实现认证。

数字证书

  • 数字证书(Digital Certification)由证书颁发机构Certificate Authority(CA)所签发,唯一标识一个站点的身份信息。可以点击浏览器地址栏两边的锁头或设置,选择连接是安全的-证书有效进行查看,比如下面是github.com的数字证书:

证书申请

  • 申请者向CA机构发送证书申请请求(Certificate Signing Request,CSR),通常需包含网站域名、IP地址、公司名称、地理位置、邮箱地址、公钥等信息,CA对信息进行核实,完成后会为该站点签发一个证书,并发回给申请者,申请者部署到Web中即可。

工作原理

  • 证书指纹:用于唯一标识一个证书(确保证书的完整性)

  • Hash加密:单向加密不可逆,且具有唯一性

  • 数字签名:用于验证证书身份的真实性

  • CA会对证书的内容和公钥分别进行hash计算,将得到的hash值附加到证书中,同时说明使用的hash算法,服务端在收到证书后,会使用相同的hash算法对证书进行has计算,并将结果与附加在其中的指纹进行对比,如果相同,则表明证书在传输的过程中没有被篡改过内容,证明了证书的完整性。

  • 接下来需要保证证书的真实性,即是由申请的CA所颁发的,这一步通过证书签名实现。CA通过自己的私钥对证书进行加密,附加到证书中,客户端收到证书后用CA公钥进行解密,内容一致即可证明CA的身份。

  • 接下来又有问题,怎么保证客户端安全获得CA公钥?这一步就拨到洋葱芯了,CA在对证书进行签名时,不仅用到私钥,还会用到另一个证书文件——根证书,根证书最主要的作用是为其他证书签名,CA的公钥也在CA的根证书中。每个操作系统都会维护一个根证书库,并默认安装好了全球受信任的CA根证书,因此当需要验证签名时,直接从系统中找到对应的CA根证书即可,从而保证访问网站的真实有效。

协议结构

  • TLS主要分为两层,底层的是TLS记录协议,主要负责使用对称密码对消息进行加密,上层的是TLS握手协议,主要分为握手协议,密码规格变更协议和应用数据协议4个部分。

    • 握手协议负责在客户端和服务器端商定密码算法和共享密钥,包括证书认证,是4个协议中最最复杂的部分。

    • 密码规格变更协议负责向通信对象传达变更密码方式的信号。

    • 警告协议负责在发生错误的时候将错误传达给对方。

    • 应用数据协议负责将TLS承载的应用数据传达给通信对象的协议。

握手协议

  • 握手协议是SSL协议中非常重要的协议,通过客户端和服务器端的交互共享一些必要信息,在用户和网站TCP三次握手之后:

    1. 客户端通过发送Client Hello报文开始SSL通信。报文中包含客户端支持的SSL的指定版本、加密组件(Cipher Suite)列表(所使用的加密算法及密钥长度等),客户端还会附加一个随机数,记为R1
    2. 服务器可进行SSL通信时,会以Server Hello报文作为应答。在客户端发送的信息中选择后,在报文中通知要使用的SSL版本以及加密组件,这里服务器同样会附加一个随机数,记为R2
    3. 服务器发送Certificate数字证书报文,客户端首先验证证书中的指纹和签名是否正确,成功后用CA公钥从中提取服务器端的公钥
    4. 如需客户端证明身份,发送证书请求Certificate Request。
    5. 最后服务器发送Server Hello Done报文通知客户端,最初阶段的SSL握手协商部分结束。
    6. 对应步骤4,发送数字证书Certificate。
    7. SSL第一次握手结束后,客户端以Client Key Exchange报文作为回应。报文中包含随机密码串预主密钥的,该报文使用服务器公钥进行加密。
    8. 对应步骤6,客户端证明自己是证书持有者Certificate Verify。
    9. 客户端继续发送Change Cipher Spec报文。用于告知服务端,客户端已经切换到之前协商好的加密套件(Cipher Suite)的状态,准备使用之前协商好的加密套件加密数据并传输了。
    10. 客户端发送Finished报文。该报文包含连接至今全部报文的整体校验值(也就是HASH值),用来供服务器校验。
    11. 服务器接收到客户端的请求之后,使用私钥解密报文,把预主密钥取出来。接着,服务器同样发送Change Cipher Spec报文。
    12. 服务器同样发送Finished报文,用来供客户端校验。
    13. 服务器和客户端的Finished报文交换完毕之后,SSL连接就算建立完成。当然,通信会受到SSL的保护。从此处开始进行应用层协议的通信,即发送HTTP请求。
    14. 最后由客户端断开连接。断开连接时,发送close_notify报文。
  • 至此再发送TCP FIN报文来关闭与TCP的通信。

  • 可以通过wireshark抓包可以清晰地看到数据交流的过程:

image-20250726231637596

补充说明

问题一为什么最后客户端和服务端都要发送一个Finish报文?

  • Finish报文是用来校验双方身份的,是对全部报文的校验值(也就是HASH值)。当客户端把这个值通过得到的公钥进行加密的时候,服务器得到之后对其进行解密,然后再对全部报文进行一个HASH求值。如果这个值跟解密得到的值相等的话,那么说明客户端是可信赖的。同样的,服务器发送这样的一个整体校验值,用来客户端验证服务器是否是真正要进行通信的那一个。

问题二:整个过程中产生的三个随机数有什么用呢?

  • 对于客户端:

    • 当其生成了预主密钥之后,会结合原来的R1、R2随机数,用DH算法计算出一个主密钥,紧接着根据这个主密钥推导出hash密钥会话密钥
  • 对于服务端:

    • 当其解密获得了预主密钥之后,会结合原来的R1、R2随机数,用DH算法计算出相同的主密钥,紧接着推导出相同的hash密钥会话密钥

问题三:为什么要使用三个随机数呢?

网友dog250是这么解释的:

“不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。由于SSL协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出来的密钥的随机性。对于RSA密钥交换算法来说,pre-master secret本身就是一个随机数,再加上hello消息中的随机,三个随机数通过一个密钥导出器最终导出一个对称密钥。pre-master secret的存在在于SSL协议不信任每个主机都能产生完全随机的随机数,如果随机数不随机,那么pre-mastersecret就有可能被猜出来,那么仅适用pre-master secret作为密钥就不合适了,因此必须引入新的随机因素,那么客户端和服务器加上pre-master secret三个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能完全不随机,可是是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的可不是一。”

  • 意思是计算机所使用的随机数都是伪随机,而不是物理上所说的真正随机。
  • 另外,黑客可能拦截了这样的一个加密的报文,他不对报文进行修改,而是不停的向报文的接受者发送重复的报文,以扰乱通信的建立。于是,就可以加这么一个随机数。只要任何一个通信方,接收到的报文中的随机数出现重复的情况,就可以知道有中间者对通信的过程进行了扰乱,可以立即中断通信。

问题四:使用对称算法加密来进行通讯使用哪个作为共享的密钥?

  • 双方使用对称加密算法进行加密,用hash密钥对HTTP报文做一次运算生成一个MAC,附在HTTP报文的后面,然后用会话密钥加密所有数据(HTTP+MAC),然后发送。

问题五:怎么保证报文的完整性?

  • 接收方用会话密钥解密数据,得到HTTP+MAC,再用相同的算法计算出自己的MAC,如果两个MAC相等,证明数据没有被篡改。

问题六:如果黑客拦截了服务器把证书发送给客户端,并对证书进行恶意修改,会出现什么情况?

  • 第一种情况,假如黑客只是单纯的修改数字证书中的内容,那么由于数字签名的存在,客户端会很容易的判断出报文是否被篡改。

  • 第二种情况,黑客不仅修改了数字证书的内容,并且把数字签名替换掉了,由于黑客不可能知道CA的私钥,于是在客户端用CA的公钥进行解密的时候,解密之后得不到正确的信息,也很容易判断出报文是否被修改。

  • 第三种情况,黑客恶意的从相同的第三方CA申请了一个数字证书。由于这个CA是真实存在的,所以客户端是可以用CA的公钥进行解密,得到了黑客提供的数字证书中的公钥。但是,由于数字证书在申请的时候,会绑定一个域名,当客户端比如说浏览器,检测到这个数字证书中的域名和我们现在网页访问的域名不一致,便会发出警告,此时我们也能得知数字证书被替换了。

TLS记录协议

  • TLS记录协议主要负责消息的压缩,加密及数据的认证:
  • 消息会被分段,压缩,计算其消息验证码,使用会话密钥进行加密,得到密文之后会附加类型、版本、长度等其他信息,组成最后的报文数据。

自签名证书实战

创建CSR

  • 在线创建:便捷但可能会泄露密钥文件。
  • 本地创建:可以使用MacOS命令
    • OpenSSL
    • cfssl

OpenSSL

  • 涉及许多密码学知识,只记录使用方式。

  • 建议下载最新版本3.0及以上,老版本已停止维护。可以先查看是否有已安装的版本:

    1
    openssl version

    也可以增加-a,输出更详细的信息:

    1
    openssl version -a

    image-20250723114116616

    系统如果没有安装openssl,先安装新的版本,然后将环境配置成最新的版本。

    OPENSSLDIR是ssl的默认配置路径,其中openssl.cnf是默认配置文件:

    image-20250723114721088

    其中包括支持的加密算法、创建csr文件时的默认属性、证书相关的配置等等。

  • 创建PKI密钥

    • 加密算法:最广泛使用的是RSA。

    • 密钥大小:越大越安全,加解密时间越长,至少要确保2048位。

    • 密钥密码:为密钥指定密码,但同时带来密钥使用的复杂性,本文不使用。

    • 可以使用genpkey进行创建:

      1
      openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem

      其中,-algorithm指定加密算法,-pkeyopt添加信息,比如密钥长度rsa_keygen_bits,最后定义输出文件-out。输出信息如下:image-20250723120024181在当前目录可以查看密钥文件,其内容是普通的文本文件,可以使用任何文本命令,cat、vim、bat等查看其内容:

      如果希望以可读的方式阅读密钥,可以通过pkey命令,用-in指定文件,-text指以可读的格式输出,-noout不要在末尾输出密钥的原始内容,-check验证生成的密钥:

      1
      openssl pkey -in private.pem -text -noout -check

      运行指令,第一行会告诉生成的密钥没有问题,其后可以看到密钥长度为2048位,并以模块的形式输出:

      私钥和公钥均包含在密钥文件中,可以将公钥提取出来:

      1
      openssl pkey -in private.pem -pubout -out public.pem

      通过-pubout提取公钥,-out指定公钥文件名:

      一般csr创建会自动从密钥中提取公钥,因此这一步不是必须的。

    • 也可以使用genrsa创建密钥,并且命令更简洁:

      我在同一目录下删除之前的文件后,运行指令进行创建:

      1
      openssl genrsa -out private.pem 4096

      查看密钥内容也可以使用pkey,rsa也有自己的查看工具:

      1
      openssl rsa -in private.pem -text -noout -check 

      其验证信息在末尾,无误则会输出:RSA key ok

      提取公钥为:

      1
      openssl rsa -in private.pem -pubout -out public.pem
  • 创建CSR文件

    • 通过req创建:

      1
      openssl req -new -key private.pem -out cert.csr

      会提供交互输入信息,依次是国家、省份、城市、公司、部门、域名、邮箱,还有密钥密码、公司别名:

      image-20250724224435633

      证书内容同样可以直接查看,或者以可读形式查看:

      1
      openssl req -in cert.csr -text -noout -verify
    • 也可以使用-subj一次性提供信息:

      1
      2
      3
      4
      openssl req -new \
      > -key private.pem \
      > -out cert.csr \
      > -subj "/C=CN/ST=Hu Bei/L=Wuhan/O=SSL Learn/OU=IT Department/CN=jihefx.github.io/emailAddress=986748913@qq.com"

      image-20250724225545632

    • 还可以将信息保存在配置文件中,通过配置文件进行创建。可以使用vim创建文件,并填写信息:

      通过配置文件创建CSR:

      1
      openssl req -new -key private.pem -out cert.csr -config cert.conf

      配置文件可以记录信息,在实践环境中更常用。

  • 最后在网络上传给对应的CA组织就可以拿到证书。

  • 有时个人网站不会向CA申请证书,会使用自签名证书。

    • 首先创建密钥和CSR文件,可以通过一步完成:

      1
      2
      3
      4
      5
      6
      openssl req -new \
      -newkey rsa:2048 \
      -keyout cert-key.pem \
      -nodes \
      -out cert.csr \
      -subj "/C=CN/ST=Hu Bei/L=Wuhan/O=SSL Learn/OU=IT Department/CN=jihefx.github.io/emailAddress=986748913@qq.com"

      通过-nodes指令使密钥不被加密:

      image-20250724232002633

    • 接下来使用x509命令创建证书:

      1
      2
      3
      4
      5
      6
      7
      8
      openssl x509 \
      -req \
      -days 365 \
      -in cert.csr \
      -sha256 \
      -signkey cert-key.pem \
      -extfile <(printf "subjectAltName=DNS:jihefx.github.io") \
      -out cert.crt

      -req使用自己的CSR文件创建证书,否则还需以交互的形式输入主体信息,-days证书有效期,以天为单位,-sha256指定证书加密算法,-extfile为证书指定扩张选项,比如subjectAltName属性,很多浏览器会验证该属性,最好进行设置,也可以直接保存在配置文件中导入-extfile ext.conf。执行命令:

      image-20250725115322011

    • 也可以使用req命令,一次性完成密钥文件和证书的创建:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      openssl req \
      -x509 \
      -newkey rsa:4096 \
      -sha256 \
      -days 3650 \
      -nodes \
      -keyout cert-key.pem \
      -subj "/C=CN/ST=Hu Bei/L=Wuhan/O=SSL Learn/OU=IT Department/CN=jihefx.github.io/emailAddress=986748913@qq.com" \
      -addext "subjectAltName=DNS:jihefx.github.io,IP:127.0.0.1" -\
      -out cert.crt

      添加x509参数,执行命令:

      image-20250725120806706

      可读形式阅读证书内容与上面方式类似:

      1
      openssl x509 -in cert.crt -noout -text

      阅读内容前可以先了解证书的相关知识。

      x.509是最为广泛使用的证书标准,用于统一证书的格式、编码等内容,以便在不同用途中进行认证,需要包含以下信息:

      1. 版本号:证书的版本信息
      2. 序列号:用于唯一标识证书
      3. 颁发者:标识证书颁发机构(CA)的信息
      4. 有效期:指定证书的有效起始日期和截止日期
      5. 主体:证书申请者的信息
      6. 公钥信息:公钥及其算法
      7. 数字签名:由颁发机构使用其私钥生成的数字签名,用于验证证书的完整性和真实性
      8. 扩展信息:包含其他可选信息,包括密钥用途、基本约束等

      这些信息都可以在输出中看到:

  • 自签名证书由于系统中没有,所以使用自签名证书认证的网站依然不被信任,我们可以把它添加进电脑中:

    • 双击证书文件即可添加:

      证书导入成功:

      默认情况下证书是不被信任的,需要手动修改该证书的信任关系,双击该证书,在信任中改为始终信任:

      此时证书已经被信任了:

  • 如果有多个网站,可以使用根证书,只要系统中储存了根证书,其他证书只要使用根证书也可被信任:

    • 创建根证书:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      openssl req \                         
      -x509 \
      -newkey rsa:4096 \
      -sha256 \
      -days 3650 \
      -nodes \
      -keyout rootCA-key.pem \
      -subj "/C=CN/ST=Hu Bei/L=Wuhan/O=Personal CA/OU=IT/CN=My Personal CA/emailAddress=986748913@qq.com" \
      -out rootCA.crt

      与自签名证书基本一致:

      使用根证书创建证书:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      openssl req \
      -x509 \
      -newkey rsa:2048 \
      -days 365 \
      -nodes \
      -keyout cert-key.pem \
      -CA rootCA.crt \
      -CAkey rootCA-key.pem \
      -subj "/C=CN/ST=Hu Bei/L=Wuhan/O=SSL Learn/OU=IT Department/CN=jihefx.github.io/emailAddress=986748913@qq.com" \
      -addext "subjectAltName=DNS:jihefx.github.io,IP:127.0.0.1" -\
      -out cert.crt

      添加证书文件和密钥文件即可:

参考文献

  1. 三分钟带你详解SSL认证与加密技术
  2. 密码学基础-Hash、MAC、HMAC 的区别与联系
  3. 一篇文章让你彻底弄懂SSL/TLS协议
  4. HTTPS通信的过程的三个随机数的作用
  5. 带你详细了解TLS、HTTPS以及证书的底层原理-上
  6. 带你详细了解TLS、HTTPS以及证书的底层原理-下