深入浅出解析 HTTPS 原理

简介: HTTPS是HTTP与SSL/TLS结合的安全协议,通过数字证书验证身份,利用非对称加密安全交换会话密钥,再以对称加密高效传输数据,确保通信的机密性、完整性和真实性。整个过程如同建立一条加密隧道,保障网络交互安全。

@TOC

概述

可以把 HTTPS 理解为 “穿着 SSL/TLS 盔甲的 HTTP”

它的核心目标很简单:
在不安全的网络(如互联网)上建立一个安全的通信通道,确保数据传输的三大安全属性:

  • 机密性:内容不会被窃听者解密
  • 完整性:内容在传输过程中未被篡改
  • 身份真实性:你连接的服务器就是你以为的那个服务器

一、非对称加密与对称加密的结合

HTTPS 的安全并非依赖单一技术,而是巧妙地结合了两种加密方式。

1. 对称加密

Symmetric Encryption

// 示例:AES 对称加密(简化版)
const crypto = require('crypto');

const secretKey = 'my-super-secret-key-32'; // 同一把密钥用于加/解密
const message = "这是要加密的敏感信息";

// 加密
const encrypted = crypto.AES.encrypt(message, secretKey);
console.log("密文:", encrypted); // 输出乱码字符串

// 解密
const decrypted = crypto.AES.decrypt(encrypted, secretKey);
console.log("明文:", decrypted); // 恢复原文
  • 概念:加密和解密使用同一把密钥。
  • 优点:速度快,适合加密大量数据(如网页内容)。
  • 缺点:如何安全地把 secretKey 发送给对方?如果密钥在网络中明文传输,攻击者可截获并解密所有消息。

2. 非对称加密

Asymmetric Encryption

// 示例:RSA 非对称加密(Node.js crypto 模拟)
const {
    generateKeyPairSync } = require('crypto');

// 生成公钥/私钥对(仅演示用,生产环境需更强参数)
const {
    publicKey, privateKey } = generateKeyPairSync('rsa', {
   
  modulusLength: 2048,
});

const data = "只有私钥能解开的信息";

// 客户端用公钥加密
const encryptedData = crypto.publicEncrypt(publicKey, Buffer.from(data));
console.log("公钥加密后:", encryptedData.toString('base64'));

// 服务器用私钥解密
const decryptedData = crypto.privateDecrypt(privateKey, encryptedData);
console.log("私钥解密后:", decryptedData.toString()); // 输出原文
  • 概念:一对密钥 —— 公钥私钥
    • 公钥加密 → 私钥解密(用于保密)
    • 私钥签名 → 公钥验证(用于认证)
  • 优点:解决了密钥分发问题。公钥可公开传播,即使被窃听也无法反向推导出私钥。
  • 缺点:运算慢,不适合频繁加密大块数据。

3. HTTPS 的智慧:混合加密机制

用非对称加密交换密钥,用对称加密传输数据

// TLS 握手阶段:客户端生成预主密钥,并用服务器公钥加密发送
let preMasterSecret = generateRandomBytes(48); // 如 48 字节随机数

// 使用从证书中提取的服务器公钥进行加密
let encryptedPreMaster = RSA.encrypt(preMasterSecret, serverPublicKey);

// 发送给服务器
sendToServer('/tls-handshake', {
    encryptedPreMaster });

// 后续通信全部使用基于此生成的对称密钥
let sessionKey = deriveSessionKey(clientRandom, serverRandom, preMasterSecret);
useSymmetricEncryption(sessionKey); // 开始 AES 加密通信

最终效果

  • 初始阶段通过非对称加密安全传递“会话密钥”
  • 后续通信采用高速对称加密(如 AES-GCM),兼顾安全性与性能

二、数字证书(Digital Certificate)

1. 问题引入

如何确保你拿到的公钥是真实服务器的,而不是中间人伪造的?

危险场景:

客户端 ──→ 攻击者(伪装成服务器)──→ 真实服务器
         ↑ 截获并替换公钥 ↑

攻击者用自己的公钥冒充服务器,即可完成“中间人攻击”。

2. 解决方案:数字证书

数字证书就像服务器的“网络身份证”,由可信第三方——CA 颁发。

证书的数据结构示例(JSON 形式)

{
   
  "subject": {
   
    "commonName": "www.example.com",
    "organization": "Example Inc.",
    "country": "US"
  },
  "issuer": {
   
    "commonName": "DigiCert TLS RSA SHA256 2020 CA1",
    "organization": "DigiCert Inc",
    "country": "US"
  },
  "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----",
  "validFrom": "2025-01-01T00:00:00Z",
  "validTo": "2026-01-01T23:59:59Z",
  "signatureAlgorithm": "sha256WithRSAEncryption",
  "fingerprint": "A1:B2:C3:D4:E5:F6:78:90...",
  "extensions": [
    {
    "name": "subjectAltName", "altNames": ["example.com", "*.example.com"] }
  ],
  "caSignature": "3045022100b9d...fcda" // CA 对整个证书内容的数字签名
}

浏览器收到该证书后,会使用内置的 CA 根证书公钥来验证 caSignature 是否有效。

3.信任链机制(Certificate Chain)

// 伪代码:浏览器验证证书流程
function validateCertificate(cert, rootCAs) {
   
  // 1. 检查有效期
  if (new Date() < cert.validFrom || new Date() > cert.validTo) {
   
    throw new Error("证书已过期");
  }

  // 2. 检查域名匹配
  if (!cert.extensions.altNames.includes(currentDomain)) {
   
    throw new Error("域名不匹配");
  }

  // 3. 验证签名:使用签发者(Issuer)的公钥验证签名
  const issuerPublicKey = getTrustedCAPublicKey(cert.issuer);
  const dataToVerify = serializeWithoutSignature(cert);

  if (!RSA.verifySignature(dataToVerify, cert.caSignature, issuerPublicKey)) {
   
    throw new Error("证书签名无效,可能被篡改");
  }

  console.log(" 证书验证通过!");
  return true;
}

若任一检查失败 → 浏览器弹出警告:“您的连接不是私密连接”

三、HTTPS 通信全过程(TLS 握手详解)

这是 HTTPS 安全通信的核心环节。以下每一步都配有模拟代码或数据交互示意

步骤 1:Client Hello

客户端发起连接请求:

GET / HTTP/1.1
Host: www.example.com
Connection: Upgrade
Upgrade: tls/1.3

同时发送 TLS 层信息(不在 HTTP 头中):

{
   
  "tlsVersion": ["TLSv1.2", "TLSv1.3"],
  "cipherSuites": [
    "TLS_AES_128_GCM_SHA256",
    "TLS_RSA_WITH_AES_256_CBC_SHA"
  ],
  "clientRandom": "a1b2c3d4e5f6...", // 32 字节随机数
  "extensions": {
   
    "supported_groups": ["secp256r1", "x25519"],
    "signature_algorithms": ["rsa_pkcs1_sha256"]
  }
}

这些信息告诉服务器:“我能支持哪些加密方式,请你选一个。”

步骤 2:Server Hello

服务器选择最优配置并回应:

{
   
  "selectedTlsVersion": "TLSv1.3",
  "selectedCipherSuite": "TLS_AES_128_GCM_SHA256",
  "serverRandom": "f6e5d4c3b2a1...", // 服务器生成的随机数
  "certificate": {
   
    "subject": "www.example.com",
    "publicKey": "-----BEGIN PUBLIC KEY-----...",
    "caSignature": "3045022100b9d..."
  }
}

至此,客户端已获得服务器证书和两个随机数。

步骤 3:证书验证

客户端执行自动验证(无需用户干预,除非出错):

try {
   
  verifyCertificate(serverCert);
} catch (error) {
   
  showBrowserWarning(` 安全警告:${
     error.message}`);
  blockPageLoad();
}

常见错误提示:

  • “此网站的安全证书有问题”
  • “NET::ERR_CERT_DATE_INVALID”
  • “无法验证此证书是由受信任的机构颁发的”

步骤 4:生成预主密钥(Pre-Master Secret)

// 客户端行为
const preMasterSecret = generateRandomBytes(48); // 如 ECDHE 中的共享密钥

// 从证书中提取公钥
const serverPublicKey = parsePublicKeyFromCert(serverCert);

// 用公钥加密预主密钥(TLS 1.2 及以前)
const encryptedPreMaster = RSA.encrypt(preMasterSecret, serverPublicKey);

// 发送给服务器
sendEncryptedPreMaster(encryptedPreMaster);

注意:现代 TLS(如 1.3)通常使用 ECDHE 密钥交换,不再需要加密预主密钥,而是通过椭圆曲线计算达成共识,具备前向安全性。

步骤 5:服务器解密预主密钥

// 服务器行为(TLS 1.2)
const encryptedPreMaster = receiveFromClient();

// 使用自己的私钥解密
const preMasterSecret = RSA.decrypt(encryptedPreMaster, privateKey);

if (!preMasterSecret) {
   
  terminateConnection("预主密钥解密失败");
}

私钥始终保存在服务器本地,绝不外泄!

步骤 6:生成会话密钥(Session Key)

双方使用三个随机数生成相同的会话密钥:

// 伪函数:PRF 是伪随机函数(Pseudo-Random Function)
function generateMasterSecret(preMasterSecret, clientRandom, serverRandom) {
   
  return PRF(preMasterSecret, 'master secret', clientRandom + serverRandom, 48);
}

function deriveSessionKey(masterSecret, label = 'key expansion') {
   
  return PRF(masterSecret, label, serverRandom + clientRandom, 32); // 例如 AES-256 密钥
}

// 双方独立计算,结果一致
const masterSecret = generateMasterSecret(preMasterSecret, clientRandom, serverRandom);
const sessionKey = deriveSessionKey(masterSecret);

console.log("🎯 会话密钥生成完成:", sessionKey.toString('hex'));

只要输入相同,输出就相同 —— 这是数学的魅力!

步骤 7:握手完成,开始安全通信

// 客户端发送加密的 Finished 消息
const finishedMessage = "handshake_finished";
const encryptedFinished = AES.encrypt(finishedMessage, sessionKey, iv);

send(encryptedFinished);

// 服务器解密并验证
const received = AES.decrypt(encryptedFinished, sessionKey, iv);
if (received === "handshake_finished") {
   
  sendAck(); // 回应确认
  startSecureHTTP(); // 正式进入 HTTPS 通信
}

从此以后:

所有 HTTP 请求和响应都被封装在 TLS 记录层中,经过对称加密传输:

[TLS RECORD LAYER]
Content-Type: ApplicationData
Encrypted Payload: a1b2c3d4e5f6...

四、HTTPS 如同寄密信(带代码类比)

想象你要通过一个不安全的邮递系统(互联网)给朋友(服务器)寄一封密信。

1. 获取公钥(证书)

朋友先通过权威公证处(CA)公证了他的公开信箱(公钥),并把公证书寄给你。

// 你收到证书后验证它是否可信
const cert = downloadCertificate("https://friend.com");
validateCertificate(cert, trustedCAList); // 返回 true 表示可信

2. 传递密钥(握手)

你写一封信,里面装着一把保险箱的钥匙(会话密钥),然后放进朋友的公开信箱(用公钥加密),寄出去。

const sessionKey = generateSessionKey();
const encryptedKey = RSA.encrypt(sessionKey, friendPublicKey);
mail.send("公开信箱", encryptedKey);

即使信被截获,别人没有朋友的私人钥匙(私钥),也打不开信箱。

3. 朋友拿到钥匙

朋友用他的私人钥匙打开信箱,取出保险箱钥匙。

const encryptedKey = mail.receive();
const sessionKey = RSA.decrypt(encryptedKey, friendPrivateKey);
console.log("🔑 会话密钥已就位!");

4. 安全通信开始

现在你们都有了同一把保险箱钥匙。之后所有的信件都放进这个保险箱邮寄,既安全又快捷。

// 日常通信
function sendSecureMessage(msg, key) {
   
  const encrypted = AES.encrypt(msg, key);
  return transmitOverInternet(encrypted);
}

function readSecureMessage(encryptedMsg, key) {
   
  const plainText = AES.decrypt(encryptedMsg, key);
  return plainText;
}

五、关键要点回顾(附代码说明)

要点 说明 关键代码体现
混合加密 非对称加密用于交换密钥,对称加密用于传输数据 RSA.encrypt(preMaster) + AES.encrypt(payload)
数字证书 包含公钥 + CA 签名,防止伪造 verifySignature(cert.data, cert.signature, caPubKey)
TLS 握手 建立安全连接的关键过程 ClientHello → ServerHello → Certificate → PreMaster → SessionKey
三重随机数 客户端随机 + 服务器随机 + 预主密钥 → 唯一会话密钥 PRF(preMaster, randomA + randomB)
前向安全性 即使未来私钥泄露,也无法解密过去的通信 使用 ECDHE 密钥交换,每次会话密钥独立

现代 TLS 1.3 已默认启用前向安全性,废弃静态 RSA 密钥交换。

六、总结

HTTPS 并不是一种全新的协议,而是 HTTP + TLS/SSL 的安全组合。

它通过:

  • 数字证书验证身份( 我连的是真的银行网站)
  • 非对称加密建立信任( 安全传递密钥)
  • 对称加密保障性能( 快速加密网页内容)

在开放网络中构建出一条“加密隧道”,让我们可以安全地浏览网页、登录账号、进行支付。

下次当你看到浏览器地址栏的小锁图标时,就知道:
背后正有一场精密而优雅的加密握手在悄然完成。

本文参考 TLS 1.2/1.3 协议设计原理,结合 Node.js crypto API 风格编写示例代码,旨在帮助初学者理解 HTTPS 底层机制。实际实现涉及 C/C++ 和底层协议栈,此处为高度简化模型。

相关文章
|
3月前
|
安全 Java Android开发
深度解析 Android 崩溃捕获原理及从崩溃到归因的闭环实践
崩溃堆栈全是 a.b.c?Native 错误查不到行号?本文详解 Android 崩溃采集全链路原理,教你如何把“天书”变“说明书”。RUM SDK 已支持一键接入。
1068 253
|
2月前
|
存储 自然语言处理 测试技术
一行代码,让 Elasticsearch 集群瞬间雪崩——5000W 数据压测下的性能避坑全攻略
本文深入剖析 Elasticsearch 中模糊查询的三大陷阱及性能优化方案。通过5000 万级数据量下做了高压测试,用真实数据复刻事故现场,助力开发者规避“查询雪崩”,为您的业务保驾护航。
1534 89
|
3月前
|
人工智能 前端开发 算法
大厂CIO独家分享:AI如何重塑开发者未来十年
在 AI 时代,若你还在紧盯代码量、执着于全栈工程师的招聘,或者仅凭技术贡献率来评判价值,执着于业务提效的比例而忽略产研价值,你很可能已经被所谓的“常识”困住了脚步。
1958 89
大厂CIO独家分享:AI如何重塑开发者未来十年
|
2月前
|
人工智能 安全 数据可视化
面向业务落地的AI产品评测体系设计与平台实现
在AI技术驱动下,淘宝闪购推进AI应用落地,覆盖数字人、数据分析、多模态创作与搜推AI化四大场景。面对研发模式变革与Agent链路复杂性,构建“评什么、怎么评、如何度量”的评测体系,打造端到端质量保障平台,并规划多模态评测、可视化标注与插件市场,支撑业务持续创新。
565 38
|
3月前
|
设计模式 Java 数据库连接
10大 spring源码设计模式 (图解+秒懂+史上最全)
10大 spring源码设计模式 (图解+秒懂+史上最全)
10大 spring源码设计模式 (图解+秒懂+史上最全)
|
3月前
|
机器学习/深度学习 SQL 关系型数据库
TRUNCATE、DELETE、DROP 的区别?
MySQL中DELETE、TRUNCATE和DROP均用于删除数据,但作用不同:DELETE删除行记录,支持WHERE条件和事务回滚,速度慢;TRUNCATE快速清空表并重置自增ID,不可回滚;DROP则彻底删除表结构与数据,操作不可逆。三者在日志记录、速度及功能上有显著差异。
495 0
|
3月前
|
Cloud Native Java API
Spring Boot 3.0 vs. 2.0
Spring Boot 3.0 带来革命性升级:全面支持 Java 17+ 与 Jakarta EE,引入原生编译、增强可观测性,推动云原生转型。相比 2.0,性能更强、启动更快、更现代。新项目应首选 3.0,老项目需逐步迁移,拥抱未来。
|
3月前
|
存储 Kubernetes 数据库
K3S ——轻量化K8S 入门指南
本文介绍轻量级Kubernetes发行版K3s,适用于边缘计算、IoT等场景。涵盖其架构、安装部署(单节点/高可用/离线)、核心组件、网络存储配置及生产建议,助力快速构建轻量化容器平台。
682 4
|
5月前
|
存储 人工智能 运维
AI 网关代理 RAG 检索:Dify 轻松对接外部知识库的新实践
Higress AI 网关通过提供关键桥梁作用,支持 Dify 应用便捷对接业界成熟的 RAG 引擎。通过 AI 网关将 Dify 的高效编排能力与专业 RAG 引擎的检索效能结合,企业可在保留现有 Dify 应用资产的同时,有效规避其内置 RAG 的局限,显著提升知识驱动型 AI 应用的生产环境表现。
2801 113
|
3月前
|
Java 程序员 持续交付
Git 从入门到进阶:常用命令与高级用法全解析
本文系统梳理Git常用命令与高级技巧,涵盖初始化、分支管理、变基、储藏、标签、差异对比、二分查找及reflog等核心功能,结合最佳实践与避坑指南,助你从入门到精通,提升代码管理与团队协作效率。
472 74