MD5 算法详解:哈希原理、标准向量、输入编码与安全边界

MD5 是哈希函数,不是"不能解密的加密"。它把输入映射成 128 位摘要,没有一把解密钥匙可以把摘要还原成原文。本篇把算法流程、输入字节、在线反查、碰撞和使用边界连起来,并用 Python 复现标准测试向量。

同为密码学工具,职责不同

RSA、SM2 具有公钥和私钥,并分别定义加密、签名等方案;MD5 本身没有密钥,只计算摘要。Base64、十六进制又属于编码,能还原原始字节。不能看到一串"像乱码的字符"就认定它是加密结果。

MD5 输出 128 位,也就是 16 字节。每个字节写成两位十六进制,所以常见字符串长度是 32。所谓"16 位 MD5"常指应用截取了 32 字符输出的一部分,不是标准另外定义了一种 16 字节之外的新算法。截断不会增强安全性。

算法如何从任意字节走到 16 字节

按 RFC 1321 的算法描述,消息先补一个 1 位和若干 0,使长度达到模 512 为 448;随后追加原始比特长度的低 64 位,按小端表示。于是最终长度是 512 位,也就是 64 字节的整数倍。即使原消息长度已经合适,也仍然要填充。

内部维护四个 32 位状态字 A、B、C、D;每个 64 字节分组拆成十六个小端 32 位字。每个分组经历 64 步运算,分为四轮,混合布尔函数、常数、模 2^32 加法和循环左移;再把结果累计回状态。全部分组处理完后,以规定字节序输出四个状态字。

例如三字节 abc 的原始长度是 24 比特,填充后占一个分组。长文件则逐块推进状态,所以流式 update 可以得到与一次性输入完全相同的摘要。注意:这是同一个哈希对象接收连续字节,绝不是把各块 MD5 文本拼起来再算一次。

这里讲流程是为了理解分组、字节序和流式行为,不建议为了业务需要手写压缩函数。正确实现算法,也不会修复 MD5 已知的密码学弱点。

"不可逆"与"能猜中"不矛盾

固定长度输出无法唯一容纳任意长度原文,必然存在不同输入得到相同摘要。因而不存在一个能从所有 MD5 值唯一恢复所有原文的通用解码函数。不过,这个多对一事实本身并不能证明某个哈希难以攻击。

工程上还要分清三类问题:碰撞是在可选择的消息中寻找不同的 x、y 使摘要相同;原像是给定一个摘要寻找匹配输入;第二原像是给定一个消息,寻找另一个同摘要消息。三者不是同一个任务。"MD5 碰撞已被攻破"不等于"任意目标文件现在都能从摘要恢复"。

所谓在线解密可能采用已有数据库、字典或候选枚举。比如候选只有 red、green、blue,分别算一次即可找出匹配 green 的项;这依靠范围很小,不是把哈希运算反向执行。匹配只能证明候选摘要相同,无法由摘要单独证明它一定是历史原文。

完整 Python 实验:标准向量、换行和流式输入

2026-09-28 在 macOS 27.2 arm64、Python 3.13.13 实际运行。只使用标准库 hashlib。保存为 md5_demo.py,执行 python3 md5_demo.py;候选反查只用三个虚构颜色词,不涉及任何账户数据。

python 复制代码
import hashlib
vectors = {
    b'': 'd41d8cd98f00b204e9800998ecf8427e',
    b'a': '0cc175b9c0f1b6a831c399e269772661',
    b'abc': '900150983cd24fb0d6963f7d28e17f72',
    b'message digest': 'f96b697d7cb7938d525a2f31aaf161d0',
    b'abcdefghijklmnopqrstuvwxyz': 'c3fcd3d76192e4007dfb496cca67e13b',
    b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789': 'd174ab98d277d9f5a5611c2c9f419d9f',
    b'1234567890'*8: '57edf4a22be3c955ac49da2e2107b67a',
}
for raw, expected in vectors.items():
    assert hashlib.md5(raw).hexdigest() == expected
print('RFC1321_vectors=7/7')
for raw in (b'abc', b'abc\n', b'abc\r\n', '中文'.encode('utf-8')):
    digest = hashlib.md5(raw)
    print(raw.hex(), len(raw), digest.hexdigest())
    assert len(digest.digest()) == 16 and len(digest.hexdigest()) == 32
parts = [b'ab', b'c']
h = hashlib.md5()
for part in parts:
    h.update(part)
assert h.hexdigest() == hashlib.md5(b'abc').hexdigest()
assert h.hexdigest() != hashlib.md5(b'abc\n').hexdigest()
print('streaming=equal newline=different')
# A finite toy lookup, not inversion and not a password attack.
candidates = ['red', 'green', 'blue']
target = hashlib.md5(b'green').hexdigest()
found = [s for s in candidates if hashlib.md5(s.encode()).hexdigest() == target]
assert found == ['green']
print('toy_lookup=', found)

本次实际输出:

text 复制代码
RFC1321_vectors=7/7
616263 3 900150983cd24fb0d6963f7d28e17f72
6162630a 4 0bee89b07a248e27c83fc3d5951213c1
6162630d0a 5 c13b6afecf97ea6b38d21a8f5167fa1e
e4b8ade69687 6 a7bac2239fcdcb3a067903d8077c4a07
streaming=equal newline=different
toy_lookup= ['green']

abc、abc 加 LF、abc 加 CRLF 分别是 3、4、5 字节;中文两字 UTF-8 编码为 6 字节。输出不同不是哈希不稳定,而是输入根本不同。7 个 RFC 向量通过,只说明当前函数结果符合这些已知样本,不代表 MD5 安全。

Python hashlib 文档 说明 update 接收字节对象,并提示 MD5 的碰撞弱点及部分受限构建的可用性差异。若运行环境禁用 MD5,应遵从系统策略或更换学习环境,不把关闭安全策略当作排障步骤。

接口签名不一致,先检查这五件事

第一,参与摘要的是字符串的 UTF-8、GBK,还是已经解码的二进制。第二,末尾是否有换行、空格或 BOM。第三,JSON 数字、字段顺序及分隔符是否遵循同一协议。第四,对端计算的是原始字节还是十六进制文本。第五,输出是否又做了大小写变化、截断或 Base64。

这里不应武断地给所有请求排序字段或删除空白。业务协议要求签原始正文时,任何"帮忙清洗"都改变输入。可在本地记录非敏感最小样本的长度和十六进制对照,不要把真实请求密钥、Cookie 或用户数据粘到在线工具。

即使参数对齐,直接 MD5(message) 也不能证明消息是谁发的:任何人都能重新计算。自己拼 secret+message 不是标准 HMAC,MD5 这类结构还涉及长度扩展等问题。新系统应采用协议约定的成熟 MAC 或签名方案。

还能在哪里使用,哪里必须换思路

RFC 6151 明确不再接受 MD5 用于需要抗碰撞的场景,包括数字签名。只检查非恶意传输差错与面对主动攻击者,是不同的安全目标。新设计不必为了"大家都用过"继续选择 MD5。

下载文件时,摘要只有来自可信渠道才有意义。即使用 SHA-256,若攻击者同时能替换文件和摘要,裸哈希也不能提供来源认证。去重时不要把一个有碰撞可能的摘要直接当永不冲突的全局主键;可按要求使用更合适的摘要并保留字节比较等判定机制。

密码存储也不是把 MD5 换成一次 SHA-256 就完成。快速哈希有利于离线批量猜测;随机盐避免相同密码有相同存储值,却不会把快速哈希变慢。OWASP 的密码存储指南 推荐使用专门的密码哈希方案,例如 Argon2id,并按环境选择参数和迁移策略。

学习 MD5 的价值,是读懂旧协议、定位输入错误,并知道什么时候该升级方案。判断一个接口时,先问它需要保密、来源认证、密码存储,还是普通内容指纹,再选择合适的工具。

本文使用 AI 辅助整理与写作,代码和列出的输出已在文中环境实际运行;未进行生产环境、跨厂商或安全认证测试。

相关推荐
小许同学记录成长1 小时前
几何体编辑算法
qt·算法
土星云SaturnCloud1 小时前
HRNet-pose 算法全解析:从高分辨率网络原理到土星云 SE110S 边缘部署实战
服务器·人工智能·算法·ai·边缘计算
bksczm1 小时前
零基础面试题1
jvm·数据结构·算法
m0_739312871 小时前
【自动驾驶与机器人技术】之惯性导航与组合导航
算法·机器人·自动驾驶
用户4320579311291 小时前
Jev 初筛实测:准确率没变,漏报多了 25 条
安全
驭渊的小故事1 小时前
牛客网算法刷题笔记:哈希表、贪心与动态规划实战
笔记·算法·散列表
lupai2 小时前
SSL 证书落地应用与网站安全加固指南
网络协议·安全·ssl
傲世仙尊2 小时前
线程池收尾-回调单例与可重入线程安全
linux·安全
careathers2 小时前
【数据结构】二叉树
数据结构·算法