ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从哈希函数到块密码:Davies-Meyer结构与SHACAL实例解析

2026/8/8 5:09:56 拓冰建站 浏览量
从哈希函数到块密码:Davies-Meyer结构与SHACAL实例解析

1. 从“散列”到“加密”:一个被误解的密码学概念

在密码学的世界里,哈希函数和块密码是两大基石,但它们解决的问题和实现方式截然不同。当“基于哈希函数的块密码”这个标题出现时,很多人的第一反应可能是困惑:哈希函数不是单向的、不可逆的吗?怎么能用来构建可逆的块密码呢?这听起来像是一个技术悖论。实际上,这个标题指向的是一种特定且重要的密码学构造思路——利用哈希函数的设计理念和组件,来构建一个安全的、可逆的加密算法。这并非天方夜谭,而是密码学历史上一个经典的“借力打力”案例,它完美诠释了如何将一种密码原语的核心强度,转化为另一种原语的构建材料。

简单来说,这种思路的核心在于:块密码需要一个强大的、混乱的、非线性的轮函数来对数据进行多轮混淆和扩散。而现代密码学哈希函数(如SHA-256)的内部压缩函数,恰恰就是一个经过精心设计、能抵抗各种密码分析攻击的、高度非线性的复杂函数。那么,一个很自然的想法就是:能不能把这个现成的、强大的“轮函数”拿过来,稍作修改和包装,让它变成一个块密码的加密轮次呢?答案是肯定的,并且已经有成熟的设计模式,比如著名的Davies-Meyer结构,它本身就是许多哈希函数(如MD5, SHA-1, SHA-2家族)的核心构造模块,而这个结构反过来又可以用于构建块密码。

理解这个概念,不仅能让你看清哈希函数和块密码之间深刻的联系,更能让你掌握一种“降维”分析复杂密码系统的方法。当你下次看到一个陌生的加密算法时,如果能识别出它内部可能采用了类似哈希函数组件的结构,你对它的安全性和性能会有一个更直观的判断。本文将深入拆解这一技术路径,从原理、经典构造到实际考量,为你呈现一个完整的知识图谱。

2. 基石解析:哈希函数与块密码的本质差异与内在联系

要理解“基于哈希函数构建块密码”,首先必须厘清这两者的根本区别,然后才能找到它们之间的桥梁。很多人容易混淆,是因为它们都处理数据并输出固定长度的结果,但目标和性质完全不同。

2.1 哈希函数的单向性与抗碰撞性

哈希函数,更准确地说是密码学哈希函数,其核心特性是单向性抗碰撞性。你输入任意长度的数据(消息),它输出一个固定长度的“指纹”(哈希值)。这个过程是单向的:从哈希值反推原始消息在计算上是不可行的。同时,找到两个不同的消息产生相同哈希值(即碰撞)也应该是极其困难的。

它的工作模式通常是迭代的:将长消息分割成固定大小的块,然后与一个内部状态(称为链接变量)一起,送入一个压缩函数中进行处理。这个压缩函数是哈希函数的心脏,它接收两个固定长度的输入(一个消息块和一个中间状态),输出一个新的固定长度的中间状态。经过多轮迭代,最终的中间状态就是哈希值。哈希函数的设计目标不是可逆,而是最大限度地制造混乱和扩散,确保输入哪怕有一比特的改变,输出也会发生雪崩效应,变得面目全非。

2.2 块密码的可逆性与混淆扩散

块密码则不同。它是一个可逆的变换。它接收一个固定长度的明文块和一个密钥,输出一个相同长度的密文块。并且,存在一个解密函数,使用相同的密钥(对称密码)可以将密文块恢复为明文块。块密码的核心是混淆扩散。混淆指密文与密钥之间的关系尽可能复杂,防止密钥被轻易推导;扩散指明文或密钥中一位的改变,会影响密文中许多位的变化。

块密码通常通过多轮迭代实现。每一轮都包含一个轮函数,轮函数接收当前的数据块和该轮的轮密钥,对数据块进行代换和置换操作。一个安全的块密码,其轮函数必须提供足够的非线性和扩散能力。

2.3 联系的纽带:压缩函数与轮函数

现在,关键的联系点出现了。哈希函数的压缩函数和块密码的轮函数,在抽象层面上承担着相似的角色:它们都是一个接收固定长度输入、产生固定长度输出的密码学原语,并且都需要具备强大的非线性和扩散特性。哈希函数的压缩函数为了抵抗碰撞和原像攻击,已经被设计得非常坚固。那么,一个很直接的想法是:能否将这个现成的、坚固的压缩函数,当作一个块密码的轮函数来使用?

这个想法是可行的,但需要解决一个核心矛盾:哈希函数的压缩函数是单向的,而块密码的轮函数需要是可逆的(至少在使用相同密钥进行解密时)。解决方案不是让压缩函数本身可逆,而是通过一种巧妙的构造模式,将整个加密过程设计成可逆的。这种模式通常会将密钥作为压缩函数的一部分输入,并利用类似Feistel网络的结构或特定的操作顺序,使得在知晓密钥的情况下,能够逆向执行整个流程,从而实现解密。接下来,我们就深入最经典的构造模式。

3. 核心构造模式:Davies-Meyer与单向压缩函数的再利用

在“基于哈希函数构建块密码”的诸多方案中,Davies-Meyer (DM) 结构是最著名、最直接的一种。它清晰地展示了如何将一个单向的压缩函数,转化为一个可逆的加密/解密过程。有趣的是,DM结构本身也是构建Merkle-Damgård结构哈希函数(如MD5, SHA-1, SHA-2)的核心。这里我们讨论的是它的“反向”应用。

3.1 Davies-Meyer结构的工作原理

假设我们有一个现成的、单向的压缩函数,记作Compress(State, MessageBlock)。它接收一个状态State和一个消息块MessageBlock,输出一个新的状态。在哈希函数中,State是之前的中间哈希值,MessageBlock是当前要处理的数据块。

现在,我们要用这个Compress函数来构建一个块密码。我们将块密码的明文对应为压缩函数的状态State,将块密码的密钥(或由密钥派生出的轮密钥)对应为压缩函数的消息块MessageBlock。那么,一轮简单的加密可以定义为:

Ciphertext = Compress(Plaintext, Key) ⊕ Plaintext

这里引入了一个异或操作。加密过程是:将明文和密钥一起送入压缩函数,得到的结果再与原始的明文进行异或,输出作为密文。

为什么这样设计就能实现可逆的解密呢?解密过程如下:

Plaintext = Compress(Ciphertext ⊕ Key, Key) ⊕ (Ciphertext ⊕ Key)

这个式子看起来复杂,但原理是关键。注意,在加密时,Compress的输入是(Plaintext, Key)。在解密时,我们无法直接逆向计算Compress,但我们可以利用一个技巧:我们构造一个输入,使得Compress的输出能与密文运算后还原出明文。通过代入推导可以发现,如果解密者知道密钥,他可以通过计算Ciphertext ⊕ Key得到一个中间值,然后将这个中间值同时作为Compress函数的State和MessageBlock输入(具体形式可能因变种而异),最后再与同一个中间值异或,就能神奇地得到原始明文。

注意:上述是最简化的单轮DM描述。实际安全的块密码需要多轮迭代。每一轮都会使用一个由主密钥通过密钥调度算法生成的轮密钥,来代替上面公式中的Key。并且,为了增强安全性,通常在每一轮前后还会增加固定的置换或加解密步骤。

3.2 安全性讨论:固定点与碰撞攻击的关联

使用DM结构或其他类似结构基于哈希函数构建块密码时,其安全性与底层压缩函数的安全性紧密绑定,但威胁模型发生了变化。

在哈希函数中,攻击者的目标是找到碰撞或原像。在基于DM的块密码中,攻击者的目标可能包括:

  1. 密钥恢复攻击:在已知若干明文-密文对的情况下,推导出密钥。
  2. 区分攻击:区分这个密码算法与一个理想的随机置换。

一个著名的潜在问题是固定点攻击。如果一个攻击者能找到一个(Plaintext, Key)对,使得Compress(Plaintext, Key) = 0,那么根据加密公式Ciphertext = 0 ⊕ Plaintext = Plaintext。这意味着明文和密文相同!攻击者如果能为特定密钥找到这样一个固定点,就获得了一个特殊的明文-密文对,这可能降低算法的有效密钥空间,或为其他攻击打开突破口。

而寻找这样的固定点,在某种程度上类似于为压缩函数寻找碰撞或原像。因此,如果底层哈希函数的压缩函数被发现有严重的弱点(如易于找到碰撞),那么基于它构建的块密码的安全性也会大打折扣。这反过来也强调了使用经过充分密码学分析、强度足够的哈希函数(如SHA-2、SHA-3的组件)作为基础的重要性。

4. 从理论到实例:SHACAL与SHA-256的华丽转身

理论需要实例来佐证。在密码学历史上,确实存在将哈希函数标准直接“改造”为块密码的著名案例,其中最典型的就是SHACAL家族密码。

4.1 SHACAL-1与SHACAL-2的设计精髓

SHACAL(Secure Hash Algorithm Cipher Algorithm)是一组基于SHA哈希函数系列的块密码。其中最具代表性的是:

  • SHACAL-1:基于SHA-1的压缩函数。
  • SHACAL-2:基于SHA-256和SHA-224的压缩函数。

它们的设计思想非常直接:将SHA的压缩函数几乎原封不动地用作块密码的加密轮函数。具体来说:

  • 数据块:SHA-256压缩函数处理一个256位的中间哈希值和512位的消息块。在SHACAL-2中,这256位的中间哈希值就被当作明文/密文块(块大小为256位),而那512位的消息块则被当作轮密钥
  • 加密过程:将256位的明文块作为初始的“链接变量”,将扩展后的密钥(或其一部分)作为“消息块”,代入SHA-256压缩函数执行一轮计算,输出结果作为新的中间状态。这个过程重复多轮(例如SHACAL-2标准建议80轮,与SHA-256的轮数一致)。最终的状态就是密文块。
  • 密钥调度:主密钥(最长512位)通过一个类似但可能简化的密钥扩展算法,生成80个512位的轮密钥。这保证了每一轮都有新鲜的密钥材料注入。
  • 解密过程:由于SHA压缩函数本身是单向的,SHACAL的解密并非简单逆运算。它需要利用压缩函数内部操作的特性(主要是位运算和模加运算),从最后一轮开始,逆向推导出上一轮的中间状态。这个过程是精心设计的,确保在知道全部轮密钥的情况下是可行的,但计算量通常比加密稍大。

4.2 实战中的考量:性能、密钥长度与标准化

虽然SHACAL在概念上很优雅,但在实际应用中需要权衡几个关键点:

  1. 性能:SHA-256压缩函数设计时优先考虑的是抗碰撞性,而非加解密速度。它包含大量复杂的位运算、逻辑函数和模加法。因此,基于它构建的SHACAL-2,其加解密速度通常低于那些为高效加密而专门设计的块密码(如AES)。在软件实现上,这可能是一个劣势。
  2. 块大小与密钥长度:SHACAL-2提供256位的块大小和最长512位的密钥。256位的块大小比AES的128位更大,在某些特定模式下(如某些认证加密模式)可能提供更好的安全性边界。超长的密钥长度(512位)远远超出了当前及可预见的未来计算能力的破解范围,提供了“过度安全”。但这也意味着密钥管理开销更大。
  3. 标准化与采纳度:SHACAL曾提交给NESSIE(欧洲密码学项目)和CRYPTREC(日本密码技术评估项目)进行评估。虽然它被认为在算法设计上是安全的,但最终未能像AES那样成为全球广泛采纳的标准。原因包括性能因素、AES的先发优势以及足够的成熟度。因此,在大多数通用场景下,AES仍是首选。SHACAL更可能出现在一些对基于哈希构造有特殊偏好、或需要与SHA-256硬件实现高度集成的 niche 场景中。

从SHACAL这个例子我们可以学到,一个技术上可行的方案,要成为工业标准,需要在天时、地利、性能、生态等多个维度都具有竞争力。

5. 另一种路径:海绵结构与可调密码

除了基于传统Merkle-Damgård结构哈希函数(如SHA-2)的构造,现代密码学,特别是SHA-3(Keccak)的获胜,带来了另一种强大的密码学结构——海绵结构。这种结构为“基于哈希函数的密码”提供了更统一、更灵活的视角。

5.1 海绵结构:一种通用的密码学原语工厂

海绵结构本身不特指哈希函数。它是一个通用的框架,可以实例化为哈希函数、消息认证码、流密码,当然也包括块密码。其核心是一个固定大小的内部状态(海绵),以及一个被称为置换函数的核心组件。

工作分为两个阶段:“吸收”和“挤压”。在吸收阶段,输入数据被分块与内部状态进行混合;在挤压阶段,从内部状态中提取输出块。整个过程由置换函数驱动,该函数在每一轮(或每吸收/挤压一个数据块后)对整个内部状态进行一个固定的、可逆的置换操作。

Keccak-f置换函数是SHA-3的核心,它是一个对1600位状态进行操作的复杂置换。它的设计目标之一就是高效和灵活性。

5.2 从海绵结构到可调块密码

基于海绵结构,可以非常自然地构造一种称为可调块密码的模式。在这种模式下:

  • 算法的内部状态被分为两部分:一部分对外保密(相当于密钥),另一部分公开(相当于“调整值”或“tweak”)。
  • 明文/密文在吸收/挤压过程中与状态交互。
  • 核心的加密动力源,就是那个强大的、固定的置换函数(例如Keccak-f)。

你可以这样理解:在这个构造中,置换函数扮演了传统块密码中“轮函数”的角色。只不过这个“轮函数”不是基于密钥变化的,而是固定的、公开的。安全性来自于初始状态中保密的密钥部分,以及置换函数本身极强的扩散和混淆能力。

这种构造的优势在于:

  • 简洁与统一:同一个核心置换(Keccak-f)可以用于哈希、认证加密、流密码和块密码,简化了系统设计。
  • 可证明安全性:在理想置换模型下,海绵结构的安全性证明相对清晰。
  • 灵活性:通过调整“tweak”,可以很容易地从一个主密钥派生出大量不同的、关联性可控的“次密钥”,适用于磁盘加密等需要大量不同密钥的场景。

因此,当我们谈论“基于哈希函数的块密码”时,在现代语境下,很可能指的就是这种基于SHA-3/Keccak海绵结构和置换函数的可调密码设计。这代表了比SHACAL更现代、更通用的一种设计哲学。

6. 设计权衡与安全实践:何时考虑,如何选择

了解了原理和实例后,作为一个实践者,你可能会问:我到底该不该使用或者设计一个基于哈希函数的块密码?这需要冷静的权衡。

6.1 优势分析

  1. 信任传递:如果整个系统已经重度依赖并信任某个哈希函数(例如SHA-256),那么使用基于其构建的块密码,可以在一定程度上简化系统的密码学信任基础。你不需要再引入一个全新的、需要单独评估的密码算法。
  2. 实现一致性:在硬件或深度优化的软件库中,如果已有高效的SHA-256指令集或代码,那么实现SHACAL-2的加密可能会共享大部分底层操作,节省代码体积和开发成本。
  3. 抗特定攻击:由于设计根源不同,基于哈希的密码可能对某些针对传统Feistel或SPN结构密码的专用攻击(如某些线性或差分攻击的变种)具有天然的抵抗力。
  4. 大块与长密钥:如SHACAL-2提供的256位块和512位密钥,为需要极高安全边际的应用提供了选项。

6.2 劣势与风险

  1. 性能瓶颈:这是最主要的顾虑。哈希函数为抗碰撞优化,其操作(如模加、复杂布尔函数)通常比AES等专用加密算法的操作(查表、字节置换)在通用CPU上更慢。加解密吞吐量可能成为系统瓶颈。
  2. 侧信道攻击风险:哈希函数的实现可能并未像AES那样,经过广泛的侧信道攻击(如计时攻击、功耗分析)防护设计和审查。直接将其实施为密码,可能引入新的侧信道漏洞。
  3. 缺乏广泛审查:像AES这样的标准,经历了全球密码学界近20年的高强度、公开的分析攻击。而基于哈希的密码(如SHACAL)所受到的审查强度和广度远不及前者。可能存在尚未被发现的设计弱点。
  4. 生态支持弱:在主流密码库(如OpenSSL, libsodium)中,AES的支持是最高优先级的,通常有硬件加速。而基于哈希的密码往往只有软件实现,且优化程度不高,缺乏标准的操作模式支持。

6.3 实操建议与决策指南

基于以上分析,我的个人建议是:

对于绝大多数应用,请坚持使用标准化的、广泛采用的块密码,如AES。

AES在性能、安全性证明、硬件支持、库生态、抗侧信道实现经验等方面,拥有无与伦比的优势。它是经过时间考验的工业标准。

仅在以下非常特定的场景中,才考虑基于哈希函数的块密码:

  1. 极端受限的环境:在一个已经内置了SHA-256硬件加速,但没有任何对称加密硬件加速的极端嵌入式设备上,为了节省门电路或代码空间,使用SHACAL-2可能是合理的。但必须自行全面评估侧信道风险。
  2. 密码学协议研究:在设计需要与哈希函数进行某种形式“同态”操作或证明的先进密码协议时,使用基于同一原语的密码可能简化安全证明。
  3. 教育与实践:为了深入理解哈希函数和块密码的构造原理,亲手实现或分析一个类似SHACAL的算法,是绝佳的学习方式。这能极大地加深你对密码学组件复用和模式设计的理解。

如果你决定使用,务必做到:

  • 选择经过一定分析的算法:如SHACAL-2,而不是自己临时用某个哈希函数拼凑一个。
  • 进行严格的性能测试:与AES等标准算法在目标平台上进行对比。
  • 关注侧信道防护:确保实现是常数时间的,并考虑必要的防护措施。
  • 明确记录决策原因:在系统设计文档中,清晰说明为何不采用AES而选择此方案。

7. 深入轮函数:剖析SHA-256压缩函数如何充当加密引擎

要真正理解“基于哈希函数的块密码”如何工作,我们需要钻进去,看看SHA-256的压缩函数这个“引擎”的内部构造,并理解它如何被“驾驶”来完成加密任务。这对于评估其安全性和性能至关重要。

SHA-256压缩函数的核心是一个256位的中间状态(8个32位字,记作A, B, C, D, E, F, G, H)和一个512位的输入消息块(扩展为64个32位字,记作W_t)。每一轮(共64轮)的操作可以概括为以下步骤:

  1. 消息调度:512位输入块被扩展成64个32位字W_t。这个扩展过程本身包含移位和异或等操作,引入了额外的非线性。
  2. 轮运算:每一轮t,进行如下计算:
    T1 = H + Σ1(E) + Ch(E, F, G) + K_t + W_t T2 = Σ0(A) + Maj(A, B, C) H = G G = F F = E E = D + T1 D = C C = B B = A A = T1 + T2
    其中:
    • Ch(E, F, G)是选择函数(E & F) ^ (~E & G)
    • Maj(A, B, C)是多数函数(A & B) ^ (A & C) ^ (B & C)
    • Σ0,Σ1是循环右移和异或构成的函数。
    • K_t是第t轮的常量。
    • +是模 2^32 加法。

当这个压缩函数被用于SHACAL-2加密时:

  • 中间状态(A-H)被初始化为明文
  • 消息块W_t被替换为由主密钥扩展生成的轮密钥
  • 执行完64轮运算后,最终的中间状态(A-H)就是密文

可以看到,每一轮中,轮密钥W_t和轮常量K_t通过模加操作注入到状态中。ChMaj函数提供了关键的非线性。而Σ0Σ1以及状态的移位传递(B->C, C->D等)提供了比特的扩散。整个结构是一个复杂的、高度非线性的状态更新函数。

为什么解密是可能的?仔细观察上述轮运算。除了模加法D + T1T1 + T2,其他操作(赋值、位运算)在已知所有轮密钥和轮常量的情况下,都是可逆的。模加法的逆运算是模减法。因此,从最后一轮的状态(密文)开始,如果已知该轮的轮密钥W_t,我们可以通过逆向计算(进行相反的赋值顺序和模减法)来恢复出前一轮的状态。如此逐轮逆向,最终就能得到初始状态(明文)。这个过程要求解密者必须知道加密时使用的所有轮密钥,这正是对称密码的要求。

这种可逆性依赖于对压缩函数每一步的精确了解和控制。这也意味着,如果你试图使用一个“黑盒”压缩函数(只知其输入输出,不知内部细节),是无法构建出可解密的块密码的。你必须拥有其完整的算法描述。

8. 超越加密:构造模式与认证加密的启示

“基于哈希函数的块密码”这一思想的价值,不仅在于创造出一个新的加密算法,更在于它启发了更广泛的密码学构造模式。这些模式将哈希函数或类似组件视为一种通用的密码学“乐高积木”。

8.1 从块密码到哈希函数:逆向思维

我们讨论了用哈希函数造块密码。有趣的是,反过来也是成立的,并且更早被广泛应用:用块密码来构造哈希函数。例如,常见的Davies-MeyerMiyaguchi-Preneel等结构,就是用一个块密码(如AES)的加密函数作为压缩函数,来构建哈希函数。这在AES成为标准后,催生了如AES-based SHA-3候选算法等提案。

这种双向可构造性深刻地揭示了密码学原语之间的内在统一性。一个强大的、具有良好混淆扩散性质的变换,既可以作为保护数据机密性的工具(加密),也可以作为生成数据指纹的工具(哈希),关键在于如何使用它。

8.2 海绵结构:统一点的火花

如前所述,SHA-3的海绵结构将这种统一性推向了极致。在 sponge 构造中,同一个核心置换Keccak-f可以实例化为:

  • 哈希函数:吸收消息后,挤压出哈希值。
  • 消息认证码:将密钥作为初始状态的一部分吸收。
  • 流密码:在吸收密钥和初始向量后,持续挤压出密钥流。
  • 可调块密码:如前所述,将密钥和调整值吸收进状态,然后吸收/挤压明文/密文。

这种“一个核心,多种用途”的设计,极大地简化了密码系统的架构。在一个资源受限的环境中,你只需要实现和优化一个Keccak-f置换,就能获得一套完整的密码学工具箱。这也是SHA-3算法家族在设计哲学上的重大进步。

8.3 对认证加密设计的启发

现代认证加密模式(如AES-GCM)通常组合使用一个块密码和一个哈希函数(GHASH)。而基于海绵结构的认证加密方案(如Keccak团队提交的认证加密模式),则能在一个统一的框架内,用同一个置换同时完成加密和认证,无需模式组合。这减少了设计复杂性和潜在的组合风险。

“基于哈希函数的块密码”这一路径,促使密码学家思考:是否存在更本质的、更底层的密码学原语?能否用更少的、更通用的组件来满足更多的安全需求?海绵结构和可调密码正是这一思考下的杰出产物。它告诉我们,未来的密码学标准可能不再是一堆孤立的算法(一个加密、一个哈希、一个MAC),而是一个或几个高度优化、可证明安全的通用引擎,通过不同的调用模式来满足所有需求。

因此,学习和理解“基于哈希函数的块密码”,不仅仅是掌握一个冷门的算法选项,更是打开一扇窗,去洞察密码学组件复用、模式设计和原语统一化的深层逻辑。它锻炼的是一种将复杂系统拆解为基本构件,并思考构件之间如何转换和连接的能力,这种能力对于深入理解任何密码协议和安全系统都至关重要。