密钥派生:安全通信的密码学生命线
问题
- 若双方采用RSA密钥协商算法,经过阶段3后,双方共享什么值? 预主密钥
- 若双方采用D-H密钥协商算法,经过阶段3后,双方共享什么值? 预主密钥
问:以上密钥用于加密应用层数据么?
答:
- 不是,预主密钥是密钥原料,还需要在进行安全加工,产生用于记录协议处理的6个密钥: 发送端写方向的加密密钥、MAC值、IV 接收端写方向的加密密钥、MAC值、IV
计算密码参数
- 密码参数包括:
客户端写入加密密钥:客户端用来加密数据,服务器用来解密数据
服务器写入加密密钥:服务器用来加密数据,客户端用来解密数据
客户端写入MAC密钥:客户端用来创建MAC,服务器用来验证MAC
服务器写入MAC密钥:服务器用来创建MAC,客户端用来验证MAC
客户端写入IV:客户端在分组加密时使用的IV初始向量,服务端用于解密
服务端写入IV:服务端在分组加密时使用的IV初始向量,客户端用于解密
密钥派生
- 密钥派生是通过密码学算法,将少量原始密钥材料(如共享密钥、密码、随机数),转化为多个满足特定安全需求的专用密钥的过程。
- 核心是从单一原始材料生成多组安全密钥。
- 常用工具包括TLS 1.2的PRF(伪随机函数),本质是通过哈希、迭代等操作增强密钥的随机性和专用性。
密钥原料的三重来源
密钥派生过程建立在三个核心原料之上:
随机数基础:
ClientRandom:客户端在ClientHello中生成的32字节加密级随机数ServerRandom:服务器在ServerHello中生成的32字节加密级随机数- 安全要求:不可预测、不可重复,打破密钥与明文的固定映射
核心原料——预主密钥:
- RSA密钥交换:客户端生成48字节随机数,用服务器公钥加密传输
- DH密钥交换:双方交换公钥,各自计算相同的PMS,无需传输
- 安全地位:密钥生成的"根原料",必须严格保密
辅助输入:
- 会话标识:用于会话复用
- 扩展字段:增强密钥推导的唯一性
- 作用:作为PRF输入,增强密钥推导的唯一性(避免不同会话密钥冲突)
三层派生架构:精密的密码学工程
密钥派生过程采用精心设计的三层架构,每一层都有特定的安全目标:
第一层:预主密钥(PMS) → 主密钥(MS)
推导工具:TLS伪随机函数
- 定义:将固定长度原料转化为高随机性、固定长度中间密钥的复合函数
- 核心公式:
MS = PRF(PMS, "master secret", ClientRandom + ServerRandom)
输入参数
- 第一个参数:核心原料,密钥安全的基础
- 第二个参数:固定标签“master secret”,避免与其他PRF推导场景混淆
- 第三个参数:随机因子,确保不同会话MS不同
输出结果
- 长度:固定48字节
- 作用:将“易受攻击的PMS”转化为更安全的中间密钥,隔离密钥交换算法的风险
第二层:主密钥 → 密钥块
- 推导工具:仍使用TLS PRF(与第一层一致)
- 核心公式:
Key Block = PRF(MS, "key expansion", ServerRandom + ClientRandom) - 关键差异:输入顺序反转与MS推导的输入顺序对比:
ClientRandom + ServerRandom→ServerRandom + ClientRandom设计目的:进一步提升随机性,避免攻击者通过输入顺序猜测推导逻辑 - 输出长度:按需生成 由“加密套件+安全需求”决定,公式:
密钥块长度 = 客户端写密钥长度 + 服务器写密钥长度 + 客户端MAC密钥长度 + 服务器MAC密钥长度 + IV长度(CBC模式)示例:AES-256-CBC + HMAC-SHA256 → 32+32+32+32+16=96字节 - 作用:生成足够长度的“密钥原材料”,为拆分具体会话密钥做准备
第三层:密钥块 → 会话密钥
- 拆分规则:按加密套件定义的顺序拆分(固定顺序,不可颠倒)
- 拆分顺序客户端写密钥:客户端加密数据使用服务器写密钥:服务器加密数据使用客户端MAC密钥:客户端验证数据完整性服务器MAC密钥:服务器验证数据完整性初始化向量:仅CBC模式需要(GCM模式无需单独生成)
伪随机函数
为什么需要PRF?(避免误解为“简单哈希”)
- 哈希算法:固定输入→固定输出,无法生成任意长度数据
- PRF:固定输入→任意长度、高随机性输出,抵御字典攻击和碰撞攻击
PRF的实现逻辑
- 基于哈希算法(如SHA256、MD5),采用嵌套哈希+迭代推导
- 实现模式: 单哈希模式→如加密套件指定SHA256,PRF完全基于SHA256
安全设计
- 固定标签:避免不同推导场景(MS生成、密钥块生成)的输出混淆
- 随机因子:
ClientRandom/ServerRandom确保不同会话的推导结果完全独立 - 迭代特性:即使部分输入泄露,也难以反推原始原料
会话密钥的生命周期管理
- 密钥使用场景(衔接TLS记录层)
- 加密密钥:用于加密应用数据(如HTTP请求/响应)
- MAC密钥:用于生成HMAC值,验证数据完整性和真实性(防止篡改)
- IV:仅CBC模式使用,需满足“随机且唯一”(呼应AES-CBC的IV安全要求)
- 密钥生命周期(安全使用的关键)
- 有效期:仅当前会话有效(会话建立→会话结束)
- 存储要求:仅在内存中临时存储,会话结束后立即销毁(禁止持久化到磁盘)
- 销毁方式:内存数据清零,避免被恶意程序窃取
- 会话复用的密钥生成(高效且安全)
- 场景:同一客户端再次连接同一服务器,无需重复完整握手
- 两种复用方式:
- 方式1:Session ID 复用→直接使用之前的MS 重新推导密钥块
- 方式2:Session Ticket 复用→服务器用长期密钥加密“MS+会话信息”生成Ticket,客户端复用Ticket 解密获取MS
- 核心优势:兼顾效率(减少握手耗时)与安全(无需重新交换PMS)
密钥安全管理体系总结
SSL/TLS的密钥管理体系体现了纵深防御的思想:
原料安全:随机性是根基,ClientRandom和ServerRandom必须不可预测
过程安全:分层推导+PRF增强,隔离风险、提升攻击难度
使用安全:按需拆分+生命周期管理,实现职责分离和及时销毁
权衡思维:在兼容性、安全性、效率之间寻求平衡,如会话复用的设计
SSL/TLS攻击案例深度分析
POODLE攻击概述:SSL 3.0的致命一击
POODLE(Padding Oracle On Downgraded Legacy Encryption,基于降级旧加密协议的填充提示攻击)是2014年公开的针对SSL/TLS协议的重大安全漏洞。该攻击巧妙结合了降级攻击和填充预言机攻击两种技术,成功解密SSL 3.0协议加密的通信内容,特别是会话Cookie等敏感信息。
攻击的核心逻辑可以分解为三个关键部分:
- 降级攻击:迫使客户端和服务器使用不安全的SSL 3.0协议
- 填充漏洞:利用SSL 3.0中CBC模式填充验证的缺陷
- 预言机机制:服务器对错误填充的响应差异成为攻击者的"助手"
第一阶段:降级攻击 - 迫使使用不安全的SSL 3.0
协议降级的机制与实现
现代浏览器和服务器默认优先使用TLS 1.2或更高版本进行通信。然而,POODLE攻击的第一步就是强制双方"降级"到存在已知漏洞的SSL 3.0协议。
攻击过程详解:
- 攻击者作为中间人(Man-in-the-Middle),在客户端和服务器之间拦截TLS握手过程
- 当客户端尝试与服务器协商使用TLS 1.0/1.1/1.2时,攻击者伪装成服务器,向客户端返回"握手失败"消息
- 攻击者声称自己只支持SSL 3.0协议,利用客户端的向后兼容性
- 客户端收到失败消息后,尝试使用更低版本的协议(SSL 3.0)重新连接
- 由于许多服务器为了兼容性确实支持SSL 3.0,连接最终成功建立在SSL 3.0上
技术背景:
这种降级攻击之所以可能,是因为SSL/TLS协议设计中的向后兼容性机制。客户端在遇到握手失败时,会自动尝试更早的协议版本,这种"优雅降级"的设计原本是为了提高互操作性,但在安全上却成为致命弱点。
第二阶段:SSL 3.0的填充漏洞
CBC模式与填充机制
SSL 3.0使用块加密模式(如AES-CBC或3DES-CBC)对数据进行加密。块加密要求明文必须被分成固定大小的块(例如16字节)。如果最后一块数据不足16字节,就需要进行填充。
PKCS#7填充规则:
- 如果明文长度是16的整数倍,则添加一个完整的16字节块,每个字节的值都是0x10
- 如果明文长度比16的倍数少n字节(1≤n≤16),则填充n个字节,每个字节的值都是0x0n
示例:
明文:"hello world"(11字节) 缺少字节数:16-11=5 填充后:"hello world" + 0x05 0x05 0x05 0x05 0x05漏洞本质:不完整的填充验证
SSL 3.0填充验证的关键缺陷在于:服务器仅检测填充字节是否在合法范围内,但没有验证所有填充字节的一致性。
具体来说:
- 服务器解密数据后,会检查最后一个字节的值
- 如果该值在1-16范围内,则接受该值为填充长度
- 但是,服务器不会验证从末尾开始的所有填充字节是否都等于声明的填充长度
这意味着,只要最后一个字节是合法的填充值(如0x01、0x02等),即使前面的填充字节不符合规范,服务器也可能接受这个填充。
第三阶段:填充预言机机制
服务器响应的差异:攻击者的"预言机"
填充预言机攻击的核心在于服务器对不同填充错误的响应差异,这些差异为攻击者提供了判断填充是否合法的线索。
服务器响应逻辑:
- 收到有效的密文(正确填充并包含有效数据的密文)后,应用程序将正常响应(
200 OK) - 收到无效的密文(解密后不会以有效填充结尾)时,应用程序将引发加密异常(
500 Internal Server Error) - 收到有效的密文(正确填充的密文)但解密为无效值时,应用程序将显示自定义错误消息(
200 OK)
关键观察:
- 响应
500 Internal Server Error表示填充完全无效 - 响应
200 OK或自定义错误消息表示填充格式正确(即使内容无效)
这种响应差异构成了一个"预言机":攻击者可以通过观察服务器响应来判断自己构造的密文是否具有合法的填充格式。
第四阶段:完整的POODLE攻击过程
攻击前提与场景设置
假设攻击场景如下:
- 用户在公共WiFi环境下访问HTTPS网站
- 攻击者已成功实施中间人攻击,并迫使连接降级到SSL 3.0
- 攻击者能够截获加密的HTTP请求(包含会话Cookie)
- 攻击者的目标是解密Cookie值,获取用户会话
攻击的密码学基础
在CBC加密模式中,解密过程遵循以下公式:
明文块 P_i = 解密函数(密文块 C_i) ⊕ 前一个密文块 C_{i-1}其中,对于第一个块,C_{i-1}就是初始化向量IV。
攻击者可以操纵C_{i-1}(或IV)来影响解密后的明文P_i。这是填充预言机攻击的基础。
攻击步骤详解
步骤1:获取单个字节
假设攻击者想要解密密文块C_i的第15个字节(从0开始计数,即最后一个字节)P_i[15]。
攻击过程:
- 攻击者准备两个连续的密文块:
C_{i-1}和C_i - 攻击者篡改
C_{i-1}的最后一个字节,设篡改后的值为g - 攻击者将篡改后的
C_{i-1}'和原始的C_i一起发送给服务器 - 服务器解密后得到修改后的明文
P_i'
根据解密公式:
P_i'[15] = Dec_k(C_i)[15] ⊕ g其中Dec_k(C_i)[15]是C_i解密后的中间值(与IV异或前)的第15个字节。
关键观察:
如果P_i'[15]恰好等于0x01(表示单字节填充),那么服务器将认为填充合法,不会返回填充错误。
攻击者可以尝试所有256个可能的g值(0x00到0xFF),直到服务器不报填充错误。此时:
P_i'[15] = 0x01 = Dec_k(C_i)[15] ⊕ g因此:
Dec_k(C_i)[15] = g ⊕ 0x01由于P_i[15] = Dec_k(C_i)[15] ⊕ C_{i-1}[15],攻击者最终可以计算出:
P_i[15] = (g ⊕ 0x01) ⊕ C_{i-1}[15]这样,攻击者就成功解密了一个字节。
步骤2:解密更多字节
解密倒数第二个字节P_i[14]的过程类似,但需要构造双字节填充(0x02 0x02):
- 攻击者已经知道
Dec_k(C_i)[15]的值 - 要使得
P_i'[15] = 0x02,需要设置:g_15 = Dec_k(C_i)[15] ⊕ 0x02 - 攻击者还需要同时调整
C_{i-1}[14]的值g_14,使得P_i'[14]也等于0x02 - 攻击者尝试所有256个可能的
g_14值,直到服务器接受填充
当服务器接受填充时,表明:
P_i'[14] = 0x02 = Dec_k(C_i)[14] ⊕ g_14因此:
Dec_k(C_i)[14] = g_14 ⊕ 0x02最终:
P_i[14] = Dec_k(C_i)[14] ⊕ C_{i-1}[14] = (g_14 ⊕ 0x02) ⊕ C_{i-1}[14]步骤3:迭代解密整个块
重复上述过程,可以逐字节解密整个密文块:
- 解密第n个字节时,需要构造n字节填充(所有填充字节都等于n)
- 对于每个字节,最多需要尝试256次请求
- 解密一个16字节块最多需要16×256=4096次请求
对于现代网络环境,这完全在可行范围内。
攻击示例:解密HTTP请求
假设攻击者截获了一个加密的HTTP请求,其中包含Cookie:
GET /private HTTP/1.1 Cookie: session=abc123def456攻击者可以:
- 定位Cookie值的密文块
- 使用上述方法逐字节解密
- 获得明文Cookie值后,可以劫持用户会话