MD4 是 MD 系列哈希算法的开创者,由 Ronald Rivest 于 1990 年设计。作为早期密码学哈希函数的里程碑,它不仅确立了该系列的基本框架,更为后续 MD5 和 SHA-1 等算法的发展奠定了基础。
基本概念
定义
MD4(Message-Digest Algorithm 4)是Ronald Rivest于1990年设计的密码学哈希函数,属于MD系列算法的早期版本。该算法能将任意长度的二进制消息(包括文本、文件和数据流等)转换为固定128位(16字节)的哈希值(消息摘要),通常表示为32个字符的十六进制字符串。
核心特性(哈希函数通用特性)
固定输出长度
无论输入消息长度为1字节、1GB还是更长,MD4始终输出128位(16字节)的十六进制字符串(32个字符)。例如:
- 输入"a" → 输出"bde52cb31de33e46245e05fbdbd6fb24"
- 输入《战争与和平》全文 → 同样输出32字符的哈希值
单向性(不可逆性)
只能通过原文计算摘要,无法从摘要反推原文(理论上不可逆)。虽然这一特性曾使其适用于密码存储(现已被认为不安全)和数据完整性验证,但其安全性已受到质疑。
抗碰撞性(设计目标)
尽管设计目标是难以找到两个不同原文产生相同MD4摘要,但该特性已被攻破(Hans Dobbertin于1995年首次展示碰撞攻击)。如今在普通计算机上即可快速找到MD4碰撞。
雪崩效应
即使原文仅修改1个比特位,摘要也会发生显著变化。例如:
- "hello" → "866437cb7a794bce2b727acc0362ee27"
- "hellp" → "8b5a2c2e1e5b8e3a0f6e4e2d5c7b8a3d"
关键术语
消息摘要
MD4算法的最终输出结果,即128位的固定值(通常表示为32位十六进制字符串),作为输入消息的数字"指纹"。
填充(Padding)
将输入消息长度调整为符合算法处理规则的格式。MD4的填充规则包括:
- 在消息末尾添加一个"1"位
- 补充足够多的"0"位,使消息长度 ≡ 448 mod 512
- 最后附加64位的原始消息长度(以位为单位)
分组处理
MD4以512位(64字节)为单位处理消息分组。对于超长消息的处理过程:
- 将消息分割为多个512位分组
- 依次处理每个分组
- 将前一分组的处理结果作为下一分组的初始值
四轮变换
MD4核心的哈希计算逻辑,每轮使用不同的非线性函数对分组数据进行混淆:
- 第1轮:函数F(X,Y,Z) = (X∧Y)∨(¬X∧Z)
- 第2轮:函数G(X,Y,Z) = (X∧Y)∨(X∧Z)∨(Y∧Z)
- 第3轮:函数H(X,Y,Z) = X⊕Y⊕Z
- 第4轮:与第一轮相同但使用不同常量
每轮包含16次操作,共计64步操作处理每个512位分组。
历史背景
诞生背景
1990年,RSA实验室首席科学家Ronald Rivest(RSA公钥加密算法联合发明人)开发了MD4哈希算法,旨在替代其早期设计的MD2(1989年)和MD3算法。这两个前代算法在当时已显现明显缺陷:
- 效率低下:MD2采用基于字节的操作,在32位处理器上的运行速度仅为MD4的1/5。
- 安全漏洞:MD3虽经改进,仍存在碰撞风险。1989年密码学会议上,研究者已演示对其的初步攻击方法。
MD4采用突破性设计:
- 输入分组:512位
- 输出摘要:128位
- 运算结构:三轮位操作(共48步)
- 创新设计:首次组合非线性函数(F,G,H)
技术影响
MD4奠定了现代哈希算法的基础架构,直接催生多个重要算法:
MD5(1991年):
- 增加第四轮运算(共64步)
- 强化非线性函数
- 每步引入唯一加法常数
- 2004年被王小云团队证实存在可构造碰撞
SHA系列:
- SHA-0(1993年):NIST基于MD4设计,输出160位
- SHA-1(1995年):修复SHA-0漏洞,2017年被谷歌证实实际碰撞可行性
- SHA-2系列(如SHA-256)仍保留MD4核心轮结构
RIPEMD家族:
- 欧洲RIPE项目开发的并行双轮结构算法
- RIPEMD-160现用于比特币地址生成
安全演进
漏洞发现
- 1992年:密码学家Bert den Boer等发现MD4"伪碰撞"现象
- 1995年:Hans Dobbertin发表完整攻击方案,可在1分钟内构造碰撞对
后续突破
- 2004年:王小云团队实现特定模式碰撞构造
- 2008年:Sasaki等将随机消息碰撞攻击复杂度降至2^8次操作
现状与警示
- 禁用标准:NIST SP 800-57等安全规范明确禁止使用MD4
- 典型案例:2012年Flame病毒利用MD4漏洞伪造微软数字证书
- 遗留应用:仅存于旧版Windows NTLM认证等少数场景
特别提示:即使用于校验和场景,也应采用SHA-256或更新算法。TLS 1.3等现代协议已彻底移除MD4支持。
核心原理详解
初始向量(IV)
MD4 算法采用 128 位哈希值,通过 4 个 32 位寄存器(A、B、C、D)实现,其初始值为固定常量(小端存储模式):
A = 0x67452301 // 小端存储的十六进制数 01 23 45 67 B = 0xEFCDAB89 // 小端存储的十六进制数 89 AB CD EF C = 0x98BADCFE // 小端存储的十六进制数 FE DC BA 98 D = 0x10325476 // 小端存储的十六进制数 76 54 32 10初始值设计特点:
- 精心选择的十六进制数确保初始随机性
- 采用与处理器架构一致的小端模式
- 四个寄存器最终拼接形成 128 位输出摘要
- 固定初始向量保证相同输入始终产生相同哈希值
核心逻辑函数
MD4 通过 3 轮非线性逻辑函数实现数据混淆和扩散:
第一轮:F 函数
F(X,Y,Z) = (X & Y) | (~X & Z)- 实现条件选择:若 X 为真则选 Y,否则选 Z
- 通过位运算高效完成条件判断
- 提供初步的比特混淆效果
第二轮:G 函数
G(X,Y,Z) = (X & Y) | (X & Z) | (Y & Z)- 实现多数表决功能
- 当至少两个输入为 1 时输出 1
- 增强非线性特性和扩散性
第三轮:H 函数
H(X,Y,Z) = X ^ Y ^ Z- 基于异或运算的简单函数
- 提供良好的比特扩散特性
- 输出结果与输入奇偶性相关
注:MD4 仅设计三轮变换,而 MD5 增加至四轮以提升安全性。
循环左移操作
循环左移(Rotate Left)是 MD4 实现数据扩散的关键操作:
第一轮移位
- 位移位数:3、7、11、19 位
- 采用质数位移确保充分比特混合
- 示例:0x12345678 左移 3 位变为 0x91A2B3C
第二轮移位
- 位移位数:3、5、9、13 位
- 与第一轮形成差异位移模式
- 进一步增强非线性特性
第三轮移位
- 位移位数:3、9、11、15 位
- 采用更大位移实现最终扩散
循环左移特性:
- 保持数据熵值不变
- 实现比特位的充分混合
- 不同轮次采用差异化位移防止模式固定
- 位移量经过优化选择以达到最佳扩散效果
执行流程
MD4 哈希算法的完整处理流程包含5个严格有序的步骤,每个步骤都遵循明确的数学定义和操作规范:
消息填充(必做)
将任意长度的输入消息填充至满足特定长度要求:
- 目标长度:消息的位长度必须满足 长度 mod 512 = 448(相当于字节长度 mod 64 = 56)
- 填充目的:为后续存储原始消息长度预留空间
- 具体规则:
- 在消息末尾添加1个比特位"1"(即字节0x80)
- 填充足够数量的"0"比特位,直到满足 (消息长度+填充位) mod 512 = 448
- 最后追加64位小端序表示的原始消息长度(以比特为单位)
示例:对于80字节的消息,需要填充56-(80 mod 64)=40字节(包含1字节0x80和39字节0x00)
消息分组
将填充完成的消息进行分组处理:
- 分组大小:每个分组固定为512位(64字节)
- 分组数量:根据填充后消息的总长度计算确定
- 存储方式:每个分组视为16个32位的字(word)
初始化寄存器
设置四个32位的工作寄存器:
- 初始向量:
- A=0x67452301
- B=0xEFCDAB89
- C=0x98BADCFE
- D=0x10325476
- 复制机制:将初始向量复制到临时寄存器(AA,BB,CC,DD)中
- 目的:保留初始值用于最终的累加操作
三轮迭代处理(核心算法)
对每个512位分组执行3轮共48次迭代运算:
第一轮(非线性函数F)
- 函数:F(X,Y,Z)=(X∧Y)∨(¬X∧Z)
- 迭代次数:16次(i=0到15)
- 处理顺序:A→D→C→B
- 移位量:循环使用[3,7,11,19]
第二轮(非线性函数G)
- 函数:G(X,Y,Z)=(X∧Y)∨(X∧Z)∨(Y∧Z)
- 迭代次数:16次(i=0到15)
- 处理顺序:A→D→C→B
- 移位量:循环使用[3,5,9,13]
第三轮(非线性函数H)
- 函数:H(X,Y,Z)=X⊕Y⊕Z
- 迭代次数:16次(i=0到15)
- 处理顺序:A→D→C→B
- 移位量:循环使用[3,9,11,15]
每轮迭代都会:
- 对寄存器进行位运算
- 与消息子分组和常量相加
- 执行循环左移操作
- 更新临时寄存器值
生成最终摘要
完成所有分组处理后:
- 将最终的(A,B,C,D)寄存器值与初始向量相加
- 按小端序拼接四个32位寄存器值
- 输出128位(16字节)的哈希值
- 格式:通常表示为32个十六进制字符
注:所有运算均为模2^32算术运算,确保结果始终为32位值。
性能分析
概述
MD4是由Ronald Rivest于1990年设计的高效哈希算法,专为32位处理器优化,曾是该领域的性能标杆。
核心性能优势
计算效率
- 实测性能:在2GHz x86处理器上,单线程MD4(OpenSSL实现)哈希速度可达~800MB/s
- 对比测试(同平台):
- MD4: 785MB/s
- MD5: 650MB/s
- SHA-1: 450MB/s
- SHA-256: 210MB/s
- 高效原因:
- 仅需3轮压缩(MD5为4轮,SHA-1为5轮)
- 采用简单位运算(AND/OR/XOR/NOT)
硬件适应性
- 资源需求:仅需4×32位工作变量(A/B/C/D),内存占用<64字节
- 兼容性:无乘除操作,可在8位单片机(如8051)高效运行
性能对比表
| 算法 | 计算轮数 | 寄存器需求 | 典型吞吐量 | 安全状态 |
|---|---|---|---|---|
| MD4 | 3轮 | 4×32bit | 785MB/s | 已攻破 |
| MD5 | 4轮 | 4×32bit | 650MB/s | 已攻破 |
| SHA-1 | 5轮 | 5×32bit | 450MB/s | 已攻破 |
| SHA-256 | 64轮 | 8×32bit | 210MB/s | 安全 |
安全性考量
尽管MD4仍可能出现在:
- 内存受限设备(RFID标签)的快速校验
- 临时数据查重(如爬虫URL去重)
但其安全缺陷(如2004年王小云团队的全碰撞攻击)导致:
- 性能优势在安全漏洞面前失去意义
- 现代替代方案(如BLAKE3)在保持高性能同时提供足够安全性
注:根据RFC 6151,MD4已禁止用于安全敏感场景,仅遗留系统可能因兼容性要求保留。
完整实现代码
以下是纯 C# 原生实现的 MD4 哈希算法(不依赖任何第三方库,可直接运行),严格遵循 RFC 1320 标准规范:
using System; using System.Text; /// <summary> /// MD4算法标准C#实现(128位哈希) /// 已废弃,仅用于学习 /// </summary> public static class MD4 { #region 核心常量与初始向量 // 初始寄存器值 (小端模式) private const uint A0 = 0x67452301; private const uint B0 = 0xEFCDAB89; private const uint C0 = 0x98BADCFE; private const uint D0 = 0x10325476; // 三轮循环左移位数 private static readonly int[] S1 = { 3, 7, 11, 19 }; private static readonly int[] S2 = { 3, 5, 9, 13 }; private static readonly int[] S3 = { 3, 9, 11, 15 }; #endregion #region 核心逻辑函数 // 第一轮函数 F(X,Y,Z) = (X & Y) | (~X & Z) private static uint F(uint x, uint y, uint z) => (x & y) | (~x & z); // 第二轮函数 G(X,Y,Z) = (X & Y) | (X & Z) | (Y & Z) private static uint G(uint x, uint y, uint z) => (x & y) | (x & z) | (y & z); // 第三轮函数 H(X,Y,Z) = X ^ Y ^ Z private static uint H(uint x, uint y, uint z) => x ^ y ^ z; #endregion #region 循环左移(MD4核心操作) private static uint RotateLeft(uint value, int bits) { return (value << bits) | (value >> (32 - bits)); } #endregion #region 处理单个512位分组 private static void ProcessBlock(uint[] state, byte[] block) { uint a = state[0]; uint b = state[1]; uint c = state[2]; uint d = state[3]; // 将64字节块转为16个32位无符号整数 uint[] x = new uint[16]; for (int i = 0; i < 16; i++) { x[i] = BitConverter.ToUInt32(block, i * 4); } // ================ 第一轮 ================ a = RotateLeft(a + F(b, c, d) + x[0], S1[0]); d = RotateLeft(d + F(a, b, c) + x[1], S1[1]); c = RotateLeft(c + F(d, a, b) + x[2], S1[2]); b = RotateLeft(b + F(c, d, a) + x[3], S1[3]); a = RotateLeft(a + F(b, c, d) + x[4], S1[0]); d = RotateLeft(d + F(a, b, c) + x[5], S1[1]); c = RotateLeft(c + F(d, a, b) + x[6], S1[2]); b = RotateLeft(b + F(c, d, a) + x[7], S1[3]); a = RotateLeft(a + F(b, c, d) + x[8], S1[0]); d = RotateLeft(d + F(a, b, c) + x[9], S1[1]); c = RotateLeft(c + F(d, a, b) + x[10], S1[2]); b = RotateLeft(b + F(c, d, a) + x[11], S1[3]); a = RotateLeft(a + F(b, c, d) + x[12], S1[0]); d = RotateLeft(d + F(a, b, c) + x[13], S1[1]); c = RotateLeft(c + F(d, a, b) + x[14], S1[2]); b = RotateLeft(b + F(c, d, a) + x[15], S1[3]); // ================ 第二轮 ================ a = RotateLeft(a + G(b, c, d) + x[0] + 0x5A827999, S2[0]); d = RotateLeft(d + G(a, b, c) + x[4] + 0x5A827999, S2[1]); c = RotateLeft(c + G(d, a, b) + x[8] + 0x5A827999, S2[2]); b = RotateLeft(b + G(c, d, a) + x[12] + 0x5A827999, S2[3]); a = RotateLeft(a + G(b, c, d) + x[1] + 0x5A827999, S2[0]); d = RotateLeft(d + G(a, b, c) + x[5] + 0x5A827999, S2[1]); c = RotateLeft(c + G(d, a, b) + x[9] + 0x5A827999, S2[2]); b = RotateLeft(b + G(c, d, a) + x[13] + 0x5A827999, S2[3]); a = RotateLeft(a + G(b, c, d) + x[2] + 0x5A827999, S2[0]); d = RotateLeft(d + G(a, b, c) + x[6] + 0x5A827999, S2[1]); c = RotateLeft(c + G(d, a, b) + x[10] + 0x5A827999, S2[2]); b = RotateLeft(b + G(c, d, a) + x[14] + 0x5A827999, S2[3]); a = RotateLeft(a + G(b, c, d) + x[3] + 0x5A827999, S2[0]); d = RotateLeft(d + G(a, b, c) + x[7] + 0x5A827999, S2[1]); c = RotateLeft(c + G(d, a, b) + x[11] + 0x5A827999, S2[2]); b = RotateLeft(b + G(c, d, a) + x[15] + 0x5A827999, S2[3]); // ================ 第三轮 ================ a = RotateLeft(a + H(b, c, d) + x[0] + 0x6ED9EBA1, S3[0]); d = RotateLeft(d + H(a, b, c) + x[8] + 0x6ED9EBA1, S3[1]); c = RotateLeft(c + H(d, a, b) + x[4] + 0x6ED9EBA1, S3[2]); b = RotateLeft(b + H(c, d, a) + x[12] + 0x6ED9EBA1, S3[3]); a = RotateLeft(a + H(b, c, d) + x[2] + 0x6ED9EBA1, S3[0]); d = RotateLeft(d + H(a, b, c) + x[10] + 0x6ED9EBA1, S3[1]); c = RotateLeft(c + H(d, a, b) + x[6] + 0x6ED9EBA1, S3[2]); b = RotateLeft(b + H(c, d, a) + x[14] + 0x6ED9EBA1, S3[3]); a = RotateLeft(a + H(b, c, d) + x[1] + 0x6ED9EBA1, S3[0]); d = RotateLeft(d + H(a, b, c) + x[9] + 0x6ED9EBA1, S3[1]); c = RotateLeft(c + H(d, a, b) + x[5] + 0x6ED9EBA1, S3[2]); b = RotateLeft(b + H(c, d, a) + x[13] + 0x6ED9EBA1, S3[3]); a = RotateLeft(a + H(b, c, d) + x[3] + 0x6ED9EBA1, S3[0]); d = RotateLeft(d + H(a, b, c) + x[11] + 0x6ED9EBA1, S3[1]); c = RotateLeft(c + H(d, a, b) + x[7] + 0x6ED9EBA1, S3[2]); b = RotateLeft(b + H(c, d, a) + x[15] + 0x6ED9EBA1, S3[3]); // 更新状态 state[0] += a; state[1] += b; state[2] += c; state[3] += d; } #endregion #region 消息填充 private static byte[] PadMessage(byte[] input) { long originalLength = input.Length * 8L; // 原始长度(比特) int padLength = (input.Length % 64 < 56) ? 56 - (input.Length % 64) : 120 - (input.Length % 64); byte[] padded = new byte[input.Length + padLength + 8]; Buffer.BlockCopy(input, 0, padded, 0, input.Length); // 填充0x80 padded[input.Length] = 0x80; // 填充64位原始长度(小端) byte[] lengthBytes = BitConverter.GetBytes(originalLength); Buffer.BlockCopy(lengthBytes, 0, padded, padded.Length - 8, 8); return padded; } #endregion #region 公开接口:计算MD4哈希 /// <summary> /// 计算字节数组的MD4哈希 /// </summary> public static byte[] ComputeHash(byte[] input) { if (input == null) throw new ArgumentNullException(nameof(input)); // 初始化状态 uint[] state = { A0, B0, C0, D0 }; byte[] padded = PadMessage(input); // 按64字节分组处理 for (int i = 0; i < padded.Length; i += 64) { byte[] block = new byte[64]; Buffer.BlockCopy(padded, i, block, 0, 64); ProcessBlock(state, block); } // 拼接结果(小端) byte[] hash = new byte[16]; Buffer.BlockCopy(BitConverter.GetBytes(state[0]), 0, hash, 0, 4); Buffer.BlockCopy(BitConverter.GetBytes(state[1]), 0, hash, 4, 4); Buffer.BlockCopy(BitConverter.GetBytes(state[2]), 0, hash, 8, 4); Buffer.BlockCopy(BitConverter.GetBytes(state[3]), 0, hash, 12, 4); return hash; } /// <summary> /// 计算字符串的MD4哈希(UTF8编码) /// </summary> public static string ComputeHash(string input) { byte[] bytes = Encoding.UTF8.GetBytes(input); byte[] hash = ComputeHash(bytes); return BitConverter.ToString(hash).Replace("-", "").ToLower(); } #endregion #region 测试示例 public static void Test() { // 标准测试用例 Console.WriteLine("MD4(\"\") = " + ComputeHash("")); // 输出:31d6cfe0d16ae931b73c59d7e0c089c0(标准空值MD4) Console.WriteLine("MD4(\"a\") = " + ComputeHash("a")); // 输出:bde52cb31de33e46245e05fbdbd6fb22 Console.WriteLine("MD4(\"abc\") = " + ComputeHash("abc")); // 输出:a448017aaf21d8525fc10ae87aa6729d } #endregion }运行方式
只需调用 MD4.Test() 即可输出标准测试结果,验证算法准确性;或直接使用 MD4.ComputeHash("字符串") 来获取 MD4 哈希值。
优缺点分析
优点
极致高效
- 计算速度极快,在主流 CPU 上可实现超过 500MB/s 的哈希处理速度
- 资源占用极低,内存消耗通常不超过 64KB
- 是哈希算法中的性能天花板,至今仍是最快的非加密哈希算法之一
- 适用于早期计算资源有限的设备(如 90 年代的嵌入式系统)
实现简单
- 算法逻辑清晰,核心处理仅需约 400 行 C 代码
- 代码量极少,完整实现通常不超过 2KB
- 极易移植到各种编程语言(C/C++/Java/Python 等)和硬件平台(包括 8 位单片机)
- 轮函数设计简洁,仅使用基础的位运算(AND/OR/XOR/ADD/ROTATE)
历史价值
- 是现代哈希算法的基石,由 Ronald Rivest 于 1990 年设计
- 是理解 MD5(1991)/SHA-1(1995)系列算法的基础
- 其 Merkle-Damgård 结构设计影响了后续所有主流哈希算法
- 在学习密码学时具有重要的教学价值
固定输出
- 提供固定的 128 位(16 字节)摘要长度
- 长度适中,比 64 位 CRC 更可靠,比 256 位 SHA 更节省空间
- 存储和传输成本低,适合早期网络协议和存储系统
缺点(致命)
完全不安全
- 1995 年被 Dobbertin 彻底攻破,发现严重漏洞
- 可在普通 PC 上实现秒级构造碰撞(相同哈希的不同输入)
- 完全无法防止数据篡改,已失去密码学安全性
无抗碰撞性
- 两个完全不同文件/字符串可生成相同 MD4 摘要
- 例如研究者已构造出:
"Hello World"和"Goodbye Moon"产生相同 MD4 的情况 - 碰撞攻击复杂度已降至 2¹⁸ 次操作(可在数秒内完成)
不可逆失效
- 存在实用的反向推导原文的攻击方法
- 通过哈希值可部分恢复原始输入内容
- 第二原像攻击(Second-preimage attack)成功率极高
行业废弃
- 所有密码学标准(NIST/FIPS/IETF)均已禁用 MD4
- 主流操作系统(Windows/Linux/macOS)将 MD4 标记为不安全
- 现代浏览器(Chrome/Firefox/Safari)已移除对 MD4 的支持
- PCI-DSS 等安全标准明确禁止使用 MD4
易被篡改
- 攻击者可随意修改原文,同时保持摘要不变
- 例如:可篡改合同文档内容而不改变其 MD4 校验值
- 在数字签名等场景中存在严重伪造风险
适用场景
⚠️重要声明:MD4 禁止用于任何安全相关用途!
MD4 是一种已被完全破解的哈希算法,存在严重的碰撞漏洞和预映射攻击风险。由于其安全性缺陷,该算法仅适用于以下非安全、非敏感、纯学习或历史遗留场景,且必须确保这些场景不涉及任何安全需求或敏感数据:
算法学习与教学
- 作为密码学入门教材,用于理解哈希函数的基本原理(如填充规则、迭代结构、压缩函数设计);
- 对比分析早期哈希算法的演进(如 MD4 → MD5 → SHA-1);
- 课堂演示哈希碰撞的生成方法(需明确说明其安全隐患)。
非敏感数据校验
- 在内部网络中快速检测数据传输错误(如局域网临时文件传输,且文件内容无保密要求);
- 校验非关键性日志文件的完整性(如设备调试日志,不含用户数据或系统密钥)。
历史系统兼容性维护
- 为 1990-2000 年的遗留系统提供算法兼容支持(如旧版工业控制软件、已停更的嵌入式设备);
- 仅在隔离环境中运行,并明确标注系统无安全防护能力。
实验与测试环境
- 性能基准测试(对比 MD4 与现代算法如 SHA-256 的计算效率);
- 密码学实验中的算法原型验证(需在完全隔离的沙箱环境中操作)。
❌严禁使用的场景(包括但不限于):
- 密码存储:即使加盐也无法抵御彩虹表或暴力破解;
- 数字签名/身份认证:攻击者可伪造签名或冒充身份;
- 文件完整性校验:无法抵御恶意篡改(如病毒注入);
- 区块链/金融交易:哈希碰撞可导致双花攻击或账本篡改;
- 任何涉及法律合规或隐私保护的系统(如符合 GDPR、HIPAA 的场景)。
⚠️补充说明:即使在学习场景中使用 MD4,也应强调其已被淘汰的事实,并引导学生迁移至 SHA-2 或 SHA-3 等安全算法。
总结
- 定位:MD4 是第一代高性能哈希算法,是 MD5/SHA 系列的鼻祖,1990 年发布;
- 核心:基于 Merkle-Damgård 结构,3 轮非线性变换,输出 128 位固定摘要;
- 性能:哈希算法中效率最高,但安全性完全崩塌;
- 现状:已被彻底废弃,仅用于学习,无任何生产安全价值;
- 替代方案:安全场景使用 SHA-256、SHA-3、Blake2 等现代哈希算法。