当前位置: 首页 > news >正文

HTTPS协议与HTTP协议的区别

一:差别对比

层级HTTPHTTPS
应用层明文文本(可直接 telnet 阅读)加密二进制(Wireshark 需导入私钥才能解密)
传输层TCP 80 端口TCP 443 端口
安全层TLS 1.2/1.3(身份验证 + 机密性 + 完整性)
维度要点示例/验证
1. 端口80 vs 443curl -v http://example.com vs https://example.com
2. 握手三次握手即可发报文HTTPS 额外 TLS 握手(1-RTT 或 0-RTT)
3. 加密明文可被中间人窃听Wireshark 抓 HTTP 直接看到密码;HTTPS 只能看到密文
4. 证书服务器必须提供 CA 签发的 X.509 证书
5. SEO谷歌优先收录 HTTPSChrome 地址栏对 HTTP 显示“不安全”
6. 性能旧时代 HTTPS 慢TLS 1.3 + HTTP/2 使 HTTPS 更快(多路复用 + 0-RTT)
7. 成本免费Let’s Encrypt 提供零费用证书

二:HTTPS连接建立的详细过程

阶段1. TCP 三次握手 14:00:00.021-0.041 RTT≈20 ms
Client → SYN(seq=0xabcd1234, MSS=1460, SACK_PERM=1)
Server → SYN-ACK(seq=0x567890ef, ack=0xabcd1235)
Client → ACK(seq=0xabcd1235, ack=0x567890f0)
窗口 65535 → 28960 → 28960

阶段2 TLS1.3 握手 14:00:00.042-0.083 RTT≈41 ms

3.1 ClientHello(明文)
HandshakeType=01
Length=0x01c8
ClientVersion=0x0303(向下兼容)
Random=0x9f8e…(32字节)
SessionID=32字节(兼容 0x20)
CipherSuites=TLS_AES_128_GCM_SHA256(0x1301),TLS_CHACHA20_POLY1305_SHA256(0x1303)…
Extensions:server_name: "example.com"supported_groups: x25519(0x001d),secp256r1(0x0017)key_share: x25519 pub_A=0x1a2b…(32字节)signature_algorithms: ecdsa_secp256r1_sha256,rsa_pss_rsae_sha256application_layer_protocol_negotiation: h2,http/1.1

备注:主要数据就是支持的TLS协议版本,支持的加密套件,ClientRandom随机数,以及key_share

问题1:加密套件是什么,包含哪些东西?

加密套件(Cipher Suite)本质上是一份“算法清单”,告诉通信两端:

  1. 用什么算法得到共享密钥(Key Exchange)

  2. 用什么算法验证身份(Authentication)

  3. 用什么算法加密数据(Bulk Encryption / AEAD)

  4. 用什么算法做完整性校验(MAC / PRF / Hash)

TLS 1.2 与 TLS 1.3 的写法不同,下面分开讲,并给出可抓包看到的真实例子。
TLS1.2格式:
密钥交换_身份验证_批量加密_消息认证

  1. ECDHE-ECDSA-AES256-GCM-SHA384

    • 密钥交换:ECDHE

    • 身份验证:ECDSA 证书(P-256/P-384)

    • 加密:AES-256-GCM

    • MAC/PRF:SHA384

TLS 1.3 加密套件的 2 段式命名
TLS 1.3 把密钥交换和身份验证从套件里拿掉,改为“协商算法 + 证书公钥算法”独立字段

示例:
TLS_AES_128_GCM_SHA256

字段含义
AEAD 算法实际加密/完整性一次完成AES-128-GCM
HKDF Hash用于密钥派生SHA256

问题2:如何使用密钥协商算法得到共享密钥,key_share和随机数有什么作用?

0. 预备
曲线:X25519(RFC 7748,素数 p = 2²⁵⁵ − 19,基点 G 已标准化)
私钥长度:32 字节随机数,需做掩码
输出:32 字节共享密钥 SS(也叫 K)

────────────────

  1. 生成私钥 & 公钥
    客户端 Alice

  • 随机 32 字节(示例)
    priv_A = 0x6a2cb65d7b5a1e8c25f1e8c25f1e8c25f1e8c25f1e8c25f1e8c25f1e8c25f

  • 掩码(RFC 7748):
    priv_A[0] &= 0xf8
    priv_A[31] &= 0x7f
    priv_A[31] |= 0x40
    → 最终 priv_A = 0x68acb65d7b5a1e8c25f1e8c25f1e8c25f1e8c25f1e8c25f1e8c25f1e8c240

  • 公钥 pub_A = X25519(priv_A, G)#备注:这个公钥就是key_share
    → pub_A = 0x1a2b3c4d5e6f7890123456789abcdef0123456789abcdef0123456789abcdef

服务端 Bob

  • 随机 32 字节
    priv_B = 0x2037437a4c0550b24c0550b24c0550b24c0550b24c0550b24c0550b24c0550b2

  • 掩码后 priv_B = 0x2037437a4c0550b24c0550b24c0550b24c0550b24c0550b24c0550b24c0550b2a

  • 公钥 pub_B = X25519(priv_B, G)
    → pub_B = 0x3c4d5e6f7890123456789abcdef0123456789abcdef0123456789abcdef0e1f

──────────────── 2. 交换公钥
Alice 把 pub_A 发给 Bob
Bob 把 pub_B 发给 Alice
(网络字节序:小端 32 字节)

──────────────── 3. 计算共享密钥
Alice 侧

SS = X25519(priv_A, pub_B)= X25519(0x68acb65d..., 0x3c4d5e6f...)→ 32 字节结果= 0x4a5d9d5ba4ce3d8f6a0b2c8e7d1f7b8d

Bob 侧

SS = X25519(priv_B, pub_A)= X25519(0x2037437a..., 0x1a2b3c4d...)→ 32 字节结果= 0x4a5d9d5ba4ce3d8f6a0b2c8e7d1f7b8d

两侧结果完全一致,这就是 ECDHE 共享密钥 K。

0. 输入

  • SS:32 字节 ECDHE 共享密钥(上一题已算得 0x4a5d9d5b…)。

  • Hello.Random:ClientHello.random || ServerHello.random(64 字节)。

  • CipherSuite:TLS_AES_128_GCM_SHA256 → Hash=SHA256 → 输出长度 L=32 字节。

────────────────

  1. 提取(HKDF-Extract)

salt = 0x00 * 32
early_secret = HKDF-Extract(salt, SS)

得到 32 字节 early_secret

──────────────── 2. 派生握手密钥

handshake_secret = HKDF-Extract(early_secret, Derive-Secret(early_secret,"derived",""))

其中 Derive-Secret 调用:

Derive-Secret(Secret, Label, Messages) =HKDF-Expand-Label(Secret, Label, Transcript-Hash(Messages), Hash.length)

Transcript-Hash = SHA256(ClientHello…ServerHello)
结果仍是 32 字节,记为 handshake_secret

──────────────── 3. 计算客户端/服务端握手密钥

client_handshake_key =HKDF-Expand-Label(handshake_secret, "client handshake key",Transcript-Hash(ClientHello…ServerHello), 32)server_handshake_key =HKDF-Expand-Label(handshake_secret, "server handshake key",Transcript-Hash(ClientHello…ServerHello), 32)

这两个 32 字节直接作为 AES-128-GCM 的 128-bit 密钥。

──────────────── 4. 计算应用数据密钥(真正加密 HTTP/2 流量)

master_secret = HKDF-Extract(handshake_secret, Derive-Secret(handshake_secret,"derived",""))client_application_traffic_secret =HKDF-Expand-Label(master_secret, "client application traffic secret",Transcript-Hash(ClientHello…ServerFinished), 32)server_application_traffic_secret =HKDF-Expand-Label(master_secret, "server application traffic secret",Transcript-Hash(ClientHello…ServerFinished), 32)

同样 32 字节 → AES-128-GCM 密钥。

──────────────── 5. 每字节公式(伪代码)

# HKDF-Expand-Label 定义
HKDF-Expand-Label(Secret, Label, Context, Length):info = 0x00 0x00 || Length(2B) || "tls13 " || Label || 0x00 || Contextreturn HKDF-Expand(Secret, info, Length)

OpenSSL 打印示例

client_application_traffic_secret=5d0c2e5f 0a4e8f6b 7b8a9c1e 2f3d4b5a 6e7f8c9d 0e1f2a3b 4c5d6e7f 8a9b0c1d

──────────────── 一句话总结
TLS 1.3 用 HKDF-Extract → HKDF-Expand-Label 把 32 字节共享密钥层层派生出 握手密钥 → 应用密钥,每一步都绑定握手摘要,确保“改一字节就全变”。

3.2ServerHello+EncryptedExtensions+Certificate+CertificateVerify+Finished(明文→加密)
HandshakeType=02
Random=0x4d5e…
CipherSuite=0x1301(TLS_AES_128_GCM_SHA256)
key_share: x25519 pub_B=0x3c4d…(32字节)

server hello返回的重要数据包含了选定的TLS版本,加密套件,server random,以及key_share。

问题3:明明到了server hello才选定了使用的加密套件,那凭什么clienthello的时候就发出了key_share呢,这个明明要经过加密套件中的密钥协商算法才能得出来的?

客户端在 ClientHello 里给出的 Key Share 并不是“最终答案”,而是一份 预选的提议(draft key share)。它遵循 “先送一个最常见曲线,避免往返” 的设计:

  1. 提前发送 ≠ 最终确定
    ClientHello 可以同时列出:

    • 自己支持的所有加密套件(cipher suites)

    • 同时也给出其中某一条曲线(如 X25519)的公钥(key_share 扩展)

  2. 服务端有否决权

    • 如果服务端也支持这条曲线 → 直接用它算共享密钥,握手立刻成功(1-RTT)。

    • 如果服务端想选别的曲线 → HelloRetryRequest 要求客户端重新发对应曲线的公钥(额外 1 个 RTT),但连接不会中断。

  3. 好处:减少 RTT
    90% 的现代服务器都支持 X25519,因此客户端先送它的公钥,大概率一次往返就完成握手;只有在少数场景(强制 P-384、国密 SM2 等)才触发 HelloRetryRequest。

一句话:Key Share 只是“先发制人”的提议,最终曲线/套件仍由服务端决定,客户端随时可改。

certificate消息中主要包含的是证书验证链如下所示:

Certificate链(ASN.1 DER)
[0] leaf: CN=example.com, SAN=DNS:example.com, DNS:*.example.comPublicKey=ecdsa-p256 0x04ab…Signature=ecdsa-with-SHA256 0x021f…
[1] intermediate: CN=Let's Encrypt R3
[2] root: CN=ISRG Root X1(本地已信任)
问题4:如何快速了解什么叫做证书链,证书链的验证过程是什么?

把“证书链”想成一份可逐层验证的「官方介绍信」,就能立即理解它的作用与结构。下面用生活类比 → 技术细节 → 浏览器验证流程三步彻底讲清。

  1. 生活类比:介绍信的三级盖章 你(浏览器)拿到一份“Bob 的介绍信”:

  • 第一层:Bob 自己写的
    “我叫 Bob,身份证 123456,公钥是 0xABCD…”
    但任何人都能伪造,因此需要第二层。

  • 第二层:派出所(中级 CA)盖章
    “经核查,Bob 的身份证确实 123456,公章属实。”
    派出所的公章大家信不过?那就再往上。

  • 第三层:市公安局(根 CA)再盖章
    “派出所的公章是真的,特此证明。”
    市公安局的章直接预装在每个人(浏览器/操作系统)的“信任保险箱”里,无需再验证。

这三张纸叠在一起就是“证书链”:Bob 的实体证书 + 中级 CA 证书 + 根 CA 证书

证书中包含了这个被签目标的域名,有效期,公钥以及签发机构ca的数字签名(即用ca的私钥去对出数字签名外所有证书内容的一个加密)。

CertificateVerify

  这个存在主要是为了确保握手过程中的身份验证,这里就要用到身份验证算法,所谓certificateverify包含了服务端用私钥把从clienthello到serverclient所有消息的哈希码进行加密的密文,然后客户端收到之后会事先把从clienthello到serverclient所有消息用哈希算法进行加密,然后用之前收到的证书上的公钥对密文进行解密,比对内容后就能确定对方是不是正确的对象,杜绝握手过程中的中间人攻击。

SignatureScheme=ecdsa_secp256r1_sha256
Signature=ECDSA_sign(SK_server, Hash(ClientHello..ServerHello))

Finished

verify_data=HMAC(handshake_secret, Hash(ClientHello..CertificateVerify))

这个过程主要服务端使用加密套件中的确保完整性算法HMAC算法对握手摘要进行了加密,然后客户端也会同步进行加密,服务端把密文发过去之后进行比对两边密文是否一致,来确保最后不存在中间人攻击。

3.3 客户端验证(下面很多过程贴合上面提到的)
  • 链式验证:根证书信任 → 中间证书签名 → 叶子证书签名 OK

  • 域名匹配:SAN 包含 example.com OK

  • 有效期:2024-06-01 至 2024-08-30 OK

  • 吊销检查:OCSP Stapling 已附带 OK

3.4 计算共享密钥
SS = X25519(priv_A, pub_B) = 32字节共享秘密
handshake_secret = HKDF-Extract(salt=0x00, SS)
client_handshake_key = HKDF-Expand(handshake_secret, "client handshake key", 32)
server_handshake_key = HKDF-Expand(handshake_secret, "server handshake key", 32)
3.5 ClientFinished(加密)

verify_data=HMAC(handshake_secret, Hash(ClientHello..ClientFinished-1))

至此握手完成,后续流量用 client_app_key / server_app_key (AES-128-GCM) 加密。

问题5:https传输密文的时候如何确保完整性和真实性?

HTTPS 把“完整性 + 真实性”做成一道带密钥的防伪封条,贴在每一条加密记录上,任何比特级篡改都会立刻被检测并终止连接。下面用 TLS 1.3 为例,把机制拆成“三张牌”:

1️⃣ 只有双方知道的对称密钥
• 来源:TLS 1.3 派生出的 client_application_traffic_secret / server_application_traffic_secret
• 作用:既做加密也做 MAC 的密钥,绝不外传

2️⃣ AEAD 模式:一次性完成“加密 + 认证”
TLS 1.3 强制使用 AEAD(AES-128-GCM、ChaCha20-Poly1305 等),每个加密记录包含:

[ 16-byte 随机数 nonce ]
[ 原始明文 ]
[ 16-byte 认证标签 Auth Tag ]

Auth Tag = AEAD_Encrypt(key, nonce, plaintext, 附加数据 AAD)
AAD 里固定包含记录序号 + 类型 + 版本 + 长度,哪怕改 1 bit,Auth Tag 立刻失效。

3️⃣ 隐式序号:防重放
每条记录都有一个单调递增的 64-bit 序号,参与 AAD 计算,但不随包发送
→ 乱序、重复、丢包都会被 Auth Tag 拒绝。

一条 TLS 记录的格式分两层:

  1. 固定 5 字节的记录头(所有版本统一,也叫AAD)

  2. 载荷区(Fragment),根据握手阶段是否加密,内部结构完全不同。

  3. 固定 5 字节头(大端序)

字节字段长度取值/说明
0ContentType1 B0x14=ChangeCipherSpec




1-2LegacyVersion2 B0x0303(TLS 1.3 也写 0x0303,兼容)
3-4Length2 B载荷长度 0‒16383(不含这 5 B)

2. Fragment(载荷区)——两种形态

A. 明文阶段 / 握手早期

+---------+--------------+
| 明文数据 | 无额外结构   |
| (变长)   |              |
+---------+--------------+

例:ClientHello 直接放在 Fragment 里。

B. TLS 1.3 加密阶段(AEAD 封装)

+----------------+------------------+----------------+
| 12 B IV/nonce  | Ciphertext       | 16 B Auth Tag  |
| (显式)         | (明文+填充)      | (MAC)          |
+----------------+------------------+----------------+
  • IV/nonce 由 TLS 1.3 规定 12 字节显式发送

  • Ciphertext 长度 = Length – 28

  • Auth Tag 固定 16 字节

它只需要用同一把密钥、同一次 nonce、同一份 AAD 把整条 fragment 丢进 AEAD 解密函数
如果 Auth Tag 不对,函数会立即报错bad_record_mac),这就是完整性/真实性校验的全部动作。

────────────────

输入材料(全部来自刚才收到的 TLS 记录)

key      = client_application_traffic_secret  (32 B 对称密钥)
nonce    = 12 B explicit IV + 8 B implicit seq  (共 12 B)
aad      = 5 B 固定结构:0x17 0x0303 Length
ciphertext_with_tag = IV 之后的所有字节(len-28)
    int rc = EVP_AEAD_CTX_open(ctx,               // 已装载 keyout,               // 输出明文缓冲区&out_len,          // 实际明文长度MAX_OUT,           // 缓冲区大小nonce, 12,         // 12 B nonceciphertext, len,   // 完整 fragment(含 tag)aad, 5             // 5 B AAD);
    • 成功:rc=1out 里是原始明文,可直接交给 HTTP/2。

    • 失败:rc=0,TLS 立即发 Alert(fatal: bad_record_mac),连接 RST。

    http://www.dtcms.com/a/340708.html

    相关文章:

  1. Web前端调试与性能优化,Charles抓包工具的高效应用
  2. 计算机视觉(二)------OpenCV图像视频操作进阶:从原理到实战
  3. vscode连接docker
  4. 【网络运维】Linux:正则表达式
  5. Gin自定义Error中间件
  6. 【C++】--指针与引用深入解析和对比
  7. Gin传参和接收参数的方式
  8. K8S-Secret资源对象
  9. 如何代开VSCode的settigns.json文件
  10. 【运维】githubvercel学习使用
  11. 数据结构--2:ArrayList与顺序表
  12. 【机器学习深度学习】AI大模型高并发挑战:用户负载部署策略
  13. 26_基于深度学习的茶叶等级检测识别系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
  14. 【JavaEE】多线程 -- CAS机制(比较并交换)
  15. iPhone17系列超全准确预告
  16. 【windows】只需两步繁杂的桌面开启清爽模式
  17. 大数据常见问题分析与解决方案
  18. 对抗式域适应 (Adversarial Domain Adaptation)
  19. C++继承中的虚函数机制:从单继承到多继承的深度解析
  20. VLN领域的“ImageNet”打造之路:从MP3D数据集、MP3D仿真器到Room-to-Room(R2R)、VLN-CE
  21. Linux-文件查找find
  22. pyqt 的自动滚动区QScrollArea
  23. electron进程间通信-从主进程到渲染器进程
  24. 康师傅2025上半年销售收入减少超11亿元,但净利润增长20.5%
  25. qwen 千问大模型联网及json格式化输出
  26. Https之(一)TLS介绍及握手过程详解
  27. 【数据结构】排序算法全解析:概念与接口
  28. 从0开始学习Java+AI知识点总结-20.web实战(多表查询)
  29. HTTPS 原理
  30. 模拟tomcat接收GET、POST请求