密码算法分类全解析:对称、非对称、哈希与密钥派生选型指南

发布时间:2026/10/16 5:26:56
密码算法分类全解析:对称、非对称、哈希与密钥派生选型指南
1. 密码算法到底在分什么从一次踩坑说起刚入行那会儿我对密码算法的理解基本停留在“加密就是加个密”的水平。有一次做一个本地数据存储的小工具需要把用户配置里的敏感字段保护起来我随手选了个看起来名字很唬人的算法结果被组里一位老前辈看了一眼就笑了“你拿一个非对称算法去加密几KB的配置文件是嫌CPU太闲吗”那次之后我才认真去梳理密码算法的分类体系才发现这里面门道比我想象的深得多。密码算法分类这件事表面上看像是教科书里的目录实际上它直接决定了你在工程里怎么选型、怎么用、怎么避坑。你打开任何一个主流编程语言的加密库里面几十个算法名字摆在那里AES、RSA、ECC、SHA、HMAC、PBKDF2……如果脑子里没有一张清晰的分类地图选型基本靠猜猜错了轻则性能拉胯重则安全形同虚设。这篇笔记我打算按我自己理解的方式把密码算法的分类体系重新捋一遍。不是照搬教材目录而是从“你拿到一个需求该怎么定位到合适的算法类别”这个角度切入。内容会覆盖对称加密、非对称加密、哈希函数、消息认证码、密钥派生函数这几大块每一块我都会说清楚它解决什么问题、典型算法有哪些、参数怎么选、实际用的时候容易在哪里翻车。适合刚接触密码学的开发者也适合像我这样用过一些算法但没系统梳理过的朋友。2. 对称加密一把钥匙开一把锁2.1 对称加密的核心逻辑与适用边界对称加密是整个密码学体系里最古老也最基础的一类。它的核心特征就一句话加密和解密用同一把密钥。你拿这把钥匙锁上箱子也得拿同一把钥匙才能打开。这个特性决定了它的最大优势——快。无论是AES还是ChaCha20在现代CPU上跑起来都是GB/s级别的吞吐量拿来加密大量数据完全不心疼。但对称加密有个绕不开的死结密钥怎么安全地送到对方手里你总不能把钥匙和锁一起寄过去吧。这个问题在密码学里叫“密钥分发问题”正是它催生了后面要讲的非对称加密。所以在实际工程里对称加密几乎从来不单独出现它总是和非对称加密或者密钥协商协议配合使用——用非对称的方式把对称密钥安全地传过去然后用对称密钥来加密真正的数据。这个组合模式叫混合加密TLS就是最典型的例子。对称加密内部还可以再分两类分组密码和流密码。分组密码把数据切成固定大小的块比如AES是128位一块逐块加密流密码则是把密钥扩展成一条密钥流然后和明文逐字节异或。分组密码的典型代表是AES和DES已淘汰流密码的典型代表是ChaCha20和RC4已淘汰。现在工程里主流的选择基本就是AES和ChaCha20这两个前者在有硬件加速的平台上更快后者在纯软件实现和移动端上表现更好。2.2 AES的参数选择别再用ECB了AES是对称加密里当之无愧的王者但它的工作模式mode of operation选择比算法本身更容易出问题。我见过太多代码里直接写AES/ECB的每次看到都替他们的数据捏把汗。ECB模式的问题在于相同的明文块会产生相同的密文块。这意味着如果你加密的是一张图片加密后的图片依然能看出原始轮廓如果你加密的是结构化数据攻击者可以通过观察密文块的重复模式推断出一些信息。这个弱点在密码学里有个形象的叫法叫“ECB企鹅图”——加密后的企鹅图片轮廓依然清晰可辨。目前工程上推荐的工作模式主要是CBC、CTR和GCM。CBC需要一个随机初始化向量IV每个IV只能用一次CTR把分组密码变成流密码同样需要唯一的nonceGCM是在CTR基础上加了认证功能属于AEAD带关联数据的认证加密模式能同时保证机密性和完整性。我的建议是如果没有特殊理由直接用AES-GCM它帮你把加密和认证一起解决了省得你还要单独加一个HMAC。密钥长度方面AES-128目前仍然安全但AES-256在性能损失很小的情况下提供了更大的安全边际。如果你的系统需要长期保护数据比如归档存储选AES-256更稳妥。IV/nonce的长度GCM模式下推荐12字节这个长度在性能和安全性之间取得了最好的平衡。注意GCM模式下同一个密钥下nonce绝对不能重复使用重复使用会直接导致密钥流复用攻击者可以恢复出明文甚至伪造认证标签。这不是“最好避免”的问题是“必须保证”的问题。2.3 ChaCha20-Poly1305移动端的好朋友ChaCha20是Daniel J. Bernstein设计的一种流密码Poly1305是配套的消息认证码。这两个组合在一起形成ChaCha20-Poly1305也是一个AEAD方案。它的最大优势是不依赖硬件加速就能跑得很快在手机、嵌入式设备这些没有AES-NI指令集的平台上性能往往超过AES-GCM。另外一个容易被忽略的好处是ChaCha20-Poly1305在软件实现上天然抵抗时序侧信道攻击。AES的软件实现如果没做好查表操作可能会泄露密钥相关的时序信息而ChaCha20的设计使得这种攻击很难奏效。所以现在很多安全协议比如TLS 1.3把ChaCha20-Poly1305和AES-GCM并列作为推荐选项让客户端根据硬件情况自己选。实际选型的时候我的经验是服务端有AES硬件加速就用AES-GCM移动端或者不确定硬件情况就用ChaCha20-Poly1305。两者安全性在同一水平线上不存在谁比谁更安全的问题纯粹是性能适配的考量。3. 非对称加密解决钥匙怎么给的问题3.1 非对称加密的原理与代价非对称加密的核心思想是加密和解密用不同的密钥。一把叫公钥可以随便公开一把叫私钥必须自己藏好。别人用你的公钥加密的数据只有你的私钥能解开。反过来你用私钥签名的东西别人用你的公钥能验证——这就引出了数字签名的概念。这个机制漂亮地解决了密钥分发问题但代价是性能。非对称加密的运算速度比对称加密慢好几个数量级RSA加密一个块的时间够AES加密几MB数据了。所以非对称加密在实际使用中几乎只用来做两件事加密对称密钥密钥传输和做数字签名。真正的大量数据加密还是交给对称算法。非对称加密主要分两大数学难题阵营大整数分解问题RSA和椭圆曲线离散对数问题ECC。RSA出现得早生态成熟但密钥长度越来越长ECC用更短的密钥就能达到同等安全强度在移动端和物联网设备上更受欢迎。3.2 RSA经典但要注意填充方式RSA的安全性建立在大整数分解的困难性上。给你两个大质数相乘得到一个大数很容易但给你这个大数让你分解回两个质数在经典计算机上几乎不可能。目前推荐的最小密钥长度是2048位安全要求高的场景用3072位或4096位。1024位的RSA已经不安全了不要再用了。RSA使用中最容易出问题的地方是填充方式。教科书式的RSA直接对明文做模幂运算是确定性的同样的明文永远得到同样的密文而且还有各种数学攻击方式。PKCS#1 v1.5填充曾经广泛使用但它在某些场景下容易受到Bleichenbacher攻击。现在推荐使用OAEP填充用于加密和PSS填充用于签名。我踩过的一个坑是早期用某语言的加密库时默认填充方式就是PKCS#1 v1.5我没注意就直接用了。后来做安全审计时被指出来虽然实际风险取决于具体使用场景但换成OAEP之后心里踏实多了。所以选RSA的时候一定要显式指定填充方式不要依赖库的默认值。3.3 ECC短密钥高强度的选择椭圆曲线密码学ECC基于椭圆曲线上的离散对数问题。它的最大卖点是密钥短。256位的ECC密钥提供的安全强度大致相当于3072位的RSA密钥这意味着更少的存储、更快的运算、更低的带宽消耗。对于证书链、移动设备、物联网这些对资源敏感的场景ECC优势明显。但ECC的曲线选择有讲究。不是所有椭圆曲线都安全有些曲线可能被植入了后门比如某些参数选择不透明的曲线。目前工程上推荐使用Curve25519和Curve448这两条曲线的参数选择过程完全透明设计上也有各种防御侧信道攻击的考量。NIST的P-256曲线虽然广泛使用但参数来源的透明度一直有些争议如果可以选择的话Curve25519是更稳妥的选项。Ed25519是Curve25519上的签名算法性能好、签名短、实现简单现在很多新系统都在用它替代RSA签名。X25519是Curve25519上的密钥交换算法TLS 1.3里就用它做密钥协商。这两个算法我在最近几个项目里都用过API简单不容易用错推荐。4. 哈希函数不是加密但比加密更容易用错4.1 哈希函数的本质与安全属性哈希函数严格来说不属于加密算法因为它不可逆——你没法从哈希值反推出原始数据。它的作用是把任意长度的输入压缩成固定长度的输出而且要做到同样的输入永远得到同样的输出确定性、不同的输入尽量得到不同的输出抗碰撞性、从输出反推输入在计算上不可行单向性。密码学哈希函数需要满足三个核心安全属性抗前像性给定哈希值找不到原始输入、抗第二前像性给定一个输入和它的哈希值找不到另一个输入得到同样的哈希值、抗碰撞性找不到任意两个不同的输入得到同样的哈希值。这三个属性强度依次递增抗碰撞性是最难满足的。常见的密码学哈希函数有SHA-256、SHA-512、SHA-3系列。MD5和SHA-1已经被证明不安全MD5的碰撞攻击早就能在普通电脑上跑出来了SHA-1的碰撞攻击也在2017年被实际演示过。这两个不要再用于任何安全相关的场景拿来做个文件校验和还行但也要知道它可能被恶意构造碰撞。4.2 哈希函数的典型应用场景哈希函数在工程里的使用场景非常多我列几个最常见的数据完整性校验下载文件后比对哈希值确认文件没被篡改。但要注意如果哈希值本身也是从同一个渠道获取的攻击者可以同时替换文件和哈希值所以这种校验只防传输错误不防恶意攻击。密码存储这个后面单独讲因为直接用SHA-256存密码是不对的。数字签名签名的时候不是对原始消息签名而是对消息的哈希值签名。因为非对称加密运算慢对固定长度的哈希值签名比对大文件签名高效得多。消息认证码HMAC就是基于哈希函数构造的消息认证码用来验证消息的完整性和来源。区块链和默克尔树用哈希函数构建数据结构实现高效的存在性证明。4.3 密码存储为什么不能用SHA-256这是我在代码审查里见到最多的问题之一。很多开发者知道不能明文存密码于是用SHA-256哈希一下存起来觉得这样就安全了。问题是SHA-256设计的目标是快而快的哈希函数对密码存储来说恰恰是缺点。攻击者拿到哈希值后可以用GPU每秒计算几十亿次SHA-256把常见密码的哈希值全部算出来做成彩虹表。即使用盐salt来防御彩虹表攻击者还是可以用暴力枚举的方式逐个尝试。密码通常熵很低很多人用的密码就在几万个常见密码列表里快速哈希函数根本挡不住。正确的做法是使用专门为密码存储设计的慢哈希函数bcrypt、scrypt或Argon2。这些函数的设计目标就是慢而且可以通过参数调节计算成本。bcrypt有工作因子cost factor每增加1计算时间翻倍Argon2可以调节内存消耗、迭代次数和并行度能有效抵抗GPU和ASIC攻击。目前Argon2id是密码存储的首选bcrypt在兼容性要求高的场景下也仍然可用。提示密码存储的盐必须是每个用户独立的随机值长度至少16字节和哈希值一起存储。不要用全局固定的盐那样等于没加盐。5. 消息认证码与密钥派生容易被忽视的两块拼图5.1 HMAC验证消息没被改过消息认证码MAC解决的是完整性和认证问题这条消息是不是你发的有没有被中途篡改HMAC基于哈希的消息认证码是最常用的一种构造方式它用密钥和哈希函数一起计算出一个认证标签接收方用同样的密钥和算法验证标签是否匹配。HMAC的安全性依赖于底层哈希函数和密钥的保密性。HMAC-SHA256是目前最常用的组合安全性和性能都很均衡。使用HMAC的时候有几个注意点密钥长度不能太短至少要和哈希输出长度一样SHA-256的话就是32字节验证标签的时候要用恒定时间比较不能用普通的字符串比较否则可能被时序攻击逐字节猜出正确的标签。CMAC是另一种MAC构造基于分组密码比如AES。在有些场景下CMAC比HMAC更合适比如你已经有AES硬件加速或者需要处理的场景天然适合分组密码。但HMAC的通用性更强实现也更简单大多数情况下HMAC是默认选择。5.2 密钥派生函数从一个密钥生出多个密钥密钥派生函数KDF的作用是从一个主密钥或密码派生出多个用途不同的子密钥。比如你有一个主密钥需要派生出加密密钥、认证密钥、初始化向量等直接用主密钥的不同部分是不安全的应该用KDF来派生。KDF分两类基于密码的和基于密钥的。基于密码的KDF如PBKDF2、bcrypt、scrypt、Argon2主要用于从用户密码派生出加密密钥设计目标是抵抗暴力枚举所以故意做得慢。基于密钥的KDF如HKDF用于从高熵的密钥材料派生子密钥不需要故意慢重点是保证派生出的密钥之间没有关联性。HKDF在TLS 1.3里被大量使用它的结构分两步提取extract和扩展expand。提取阶段把输入的密钥材料“浓缩”成一个固定长度的伪随机密钥扩展阶段从这个伪随机密钥派生出任意长度的输出。这个两阶段设计使得即使输入密钥材料的分布不够均匀输出的密钥也是均匀随机的。PBKDF2是较早的基于密码的KDF现在仍然广泛使用但它的主要缺点是只消耗CPU时间不消耗内存所以对GPU攻击的抵抗能力有限。如果你的系统需要从用户密码派生密钥优先考虑Argon2id其次scryptPBKDF2作为兼容性兜底。6. 算法选型速查与常见翻车现场6.1 按场景选算法的速查表我把常见场景和推荐算法整理成一张表方便你直接对照场景推荐算法关键参数避免使用大量数据加密AES-256-GCM 或 ChaCha20-Poly1305GCM nonce 12字节唯一AES-ECB、RC4密钥交换X25519 或 ECDH P-256临时密钥对固定密钥交换数字签名Ed25519 或 RSA-PSSRSA至少2048位RSA-PKCS#1 v1.5密码存储Argon2id 或 bcrypt内存≥64MB迭代≥3SHA-256、MD5消息认证HMAC-SHA256密钥≥32字节裸哈希密钥派生从密码Argon2id 或 scrypt按硬件调参直接哈希密钥派生从密钥HKDF-SHA256指定info和salt直接截断文件完整性SHA-256 或 BLAKE3无MD5、SHA-1这张表不是绝对的具体场景还要结合性能要求、合规要求、已有系统兼容性来调整。但如果你不确定选什么照着这张表走基本不会出大错。6.2 我踩过的坑和见过的翻车第一个坑是IV/nonce复用。有一次做一个文件加密功能我图省事把IV固定成了全零测试的时候一切正常因为每次加密的文件内容不同。后来有个场景是加密同一份文件的多个版本密文的前几个块完全一样被安全扫描工具直接标红了。IV/nonce的唯一性是AEAD模式的命根子每次加密都必须用新的随机值这个没有商量余地。第二个坑是密钥长度不够。早期用AES-128觉得够用了后来做合规审计时被要求至少AES-256。虽然AES-128在可预见的未来仍然安全但合规要求有时候不跟你讲道理直接上256省事。第三个坑是错误处理泄露信息。解密失败时直接返回“填充错误”和“认证失败”两种不同的错误信息攻击者可以通过错误类型区分出是填充问题还是认证问题进而实施填充预言攻击。正确的做法是统一返回“解密失败”不泄露具体原因。第四个坑是随机数生成器用错。加密用的随机数必须来自密码学安全的随机数生成器CSPRNG不能用普通的rand()。普通rand()的种子空间可能很小输出可预测用它生成密钥或IV等于白给。大多数语言里都有专门的CSPRNG接口比如Python的secrets模块、Java的SecureRandom、Go的crypto/rand认准这些用。6.3 常见问题速查问AES-GCM和ChaCha20-Poly1305选哪个答有AES硬件加速选AES-GCM没有或者不确定选ChaCha20-Poly1305。两者安全性同级。问RSA密钥长度选多少答最低2048位推荐3072位长期归档用4096位。1024位已不安全。问SHA-256能用来存密码吗答不能。用Argon2id或bcrypt。SHA-256太快挡不住暴力枚举。问HMAC的密钥可以和加密密钥用同一个吗答最好不要。用KDF从主密钥派生出不同用途的子密钥避免密钥复用带来的交叉风险。问ECC和RSA选哪个答新系统优先ECCCurve25519/Ed25519密钥短、性能好。需要兼容老系统或合规要求RSA时用RSA。问哈希函数的输出长度怎么选答SHA-256输出32字节安全强度128位目前足够。需要更高安全边际用SHA-512或SHA-3-512。6.4 一个容易被忽略的实践原则最后分享一个我在实际项目中体会很深的原则不要自己发明密码学方案。密码学经过几十年的发展每一个推荐算法和参数都是经过大量密码分析检验的。你觉得“把两个算法组合一下会更安全”的想法大概率会引入意想不到的弱点。用标准算法、标准参数、标准组合方式把精力放在密钥管理、访问控制、日志审计这些工程问题上才是更有效的安全投入。密钥管理的重要性怎么强调都不过分。算法选得再好密钥泄露了等于零。密钥要存在安全的地方硬件安全模块、密钥管理服务、至少也是加密的密钥库要有轮换机制要有访问审计。我见过太多系统算法用得没问题但密钥硬编码在代码里或者明文存在配置文件里这种情况下算法分类学得再清楚也没用。密码算法分类这张地图最终是为了让你在工程决策时能快速定位到正确的方向。分类本身不是目的选对、用对、管好才是。希望这篇笔记能帮你在下次面对一堆算法名字的时候心里有底手上不慌。