加密算法这块,算是计算机基础里"人人都听过、少有人讲透"的内容。我经常看到有朋友把MD5当加密用,也有人以为RSA就是万能的安全方案,其实这俩在现代密码学里分属完全不同的赛道——一个是哈希摘要,一个是公钥加密。这篇就把常见的加密算法理一遍,再重点把MD5和RSA从数学原理到工程落地掰开讲清楚,看完你至少能明白"什么场景该用哪一个、为什么不能乱用"。
先说好,这篇不涉及底层数学证明推导,只讲原理、应用场景和实际编码中容易踩的坑,适合做后端、做客户端、做安全审计的开发者,也适合想系统了解密码学基础的产品和运维同学。
1. 常见加密算法全景认知
1.1 加密算法的三大家族划分
平时我们说的"加密算法",其实混了三种不同的东西:对称加密、非对称加密、哈希算法。很多人分不清,是因为它们都被叫成"加密",但解决的问题和适用场景差别极大。
对称加密的特点是"一把钥匙走天下",加密和解密用同一个密钥。典型代表是AES、DES、3DES,还有国密的SM1/SM4。这种方案的问题是密钥怎么安全传给对方——你加密数据发过去,密钥也得发过去,传输过程中一旦密钥泄露,加密就完全失效。所以对称加密适合解决"数据静置"的安全,比如本地文件加密、数据库字段加密,密钥不用跨网络传递的场景。
非对称加密设计更巧妙,它有一对钥匙:公钥和私钥。公钥可以公开分发,私钥自己藏好。用公钥加密的数据只有私钥能解,反过来用私钥签名的数据大家用公钥验证。RSA、ECC、国密SM2都属于这一类。它的核心价值在于解决了密钥分发问题,但性能比对称加密慢好几个数量级,所以实际系统中几乎都是混合方案——用非对称加密协商出一个临时对称密钥,再用对称密钥加密实际业务数据。
哈希算法严格说不算"加密",它是单向的。你把任意长度的数据丢进去,它吐出一个固定长度的摘要,而且从摘要反推原始数据在计算上是不可行的。MD5、SHA-1、SHA-256、SM3都是哈希算法。哈希算法的核心用途不是保护数据机密性,而是做完整性校验、密码存储、数字签名里的摘要提取。
1.2 为什么说哈希算法不是加密算法
这是最容易被误解的一点,值得单独拎出来讲。MD5、SHA系列生成的是摘要,但这个过程没有"解密"的概念,它是不可逆的。很多文档和博客把"MD5加密"挂在嘴边,严格来说这个叫法是有问题的——哈希是单向散列,不是加密。
那为什么网上铺天盖地的"MD5加密密码"教程?因为把密码转成MD5存进数据库,数据库泄露后攻击者也拿不到明文密码。这个场景里,哈希确实起到了"保护"作用,但它的安全模型和加密完全不同。加密是可逆的,拿到密钥就能还原明文;哈希是不可逆的,攻击者只能靠暴力碰撞或者查彩虹表去猜原文。
搞清楚这个区别,后面理解MD5为什么被淘汰、RSA为什么还在服役,就容易多了。哈希和加密是两个维度的事,不存在谁替代谁的关系——它们是配合使用的。真实世界的HTTPS握手、数字签名、文件校验,都是加密和哈希混合工作的结果。
1.3 选型思维:先想清楚你要防谁
聊算法选型之前,先养成一个习惯:问自己"我要防的威胁是什么"。是防外部窃听?防数据篡改?防数据库泄露后密码被还原?还是防通信内容被伪造?
防窃听,用对称加密+AES;防篡改和伪造,用哈希摘要+RSA签名;防密码泄露后被还原,用加盐哈希甚至专门设计的慢哈希算法(比如bcrypt、scrypt、Argon2)。没有万能的算法,只有匹配场景的方案。
这也是为什么这篇要花大篇幅讲MD5和RSA——它们分别代表了两大类算法中的典型,把这两个搞明白了,其他的基本就是变体和升级。
2. MD5算法深度拆解
2.1 MD5的前世今生与设计初衷
MD5全称Message-Digest Algorithm 5,是MIT的Ronald Rivest在1991年设计的,用于产出128位(16字节)的消息摘要。设计初衷是给数字签名做预处理——把任意长度的消息压缩成一个固定长度的摘要,再对这个摘要做签名,效率会高很多。
1990年代初期,MD5确实站在技术前沿。同时期流行的还有MD2、MD4,MD5是对前面几个版本的改良,性能更好,碰撞性也比MD4有改善。当时广泛应用于文件校验、软件发布完整性检查,后来被大规模用于密码存储,一度是主流方案。
但请注意,MD5在2004年就已经被中国学者王小云团队攻破了——他们实现了构造碰撞的实用方法。所谓碰撞,就是找到两个不同的输入,它们的MD5哈希值相同。这意味着MD5不能再用在需要防篡改和防伪造的场景里,比如数字签名、证书校验、软件发布验证。但在某些低安全等级场景(比如缓存key、分布式一致性哈希、文件去重),MD5仍然可以用,因为它快、省空间,而且这些场景对碰撞攻击不敏感。
2.2 MD5算法流程逐步解析
MD5的处理流程可以拆成四个阶段,理解它的核心逻辑,你就明白哈希函数为什么能把任意长度的数据变成固定长度的摘要。
第一步是填充。MD5要求输入数据的长度对512取模等于448。也就是说,填充后数据长度 L 满足 L ≡ 448 (mod 512)。填充规则是:先在消息末尾补一个1,然后补若干0,直到满足上述长度条件。最后再用64位存储原始消息的比特长度,拼在填充结果的末尾。这样总长度正好是512的整数倍,而且原消息长度信息被保留下来,能增强密码学安全性。
第二步是分块。填充后的数据会被切成N个512位的分组,每个分组又分成16个32位的字,记为 M[0] 到 M[15]。
第三步是初始化链接变量。MD5用四个32位寄存器的初始值,这些值是精心挑选的魔数:A=0x67452301,B=0xEFCDAB89,C=0x98BADCFE,D=0x10325476。如果注意看,这些数字其实就是标准MD5初始值的十六进制表示,后面所有运算都是基于这四个值的迭代。
第四步是压缩循环。对于每一个512位的分组,MD5执行四轮运算,每轮16步,共64步。每步做一次非线性函数、一次加法、一次左循环移位,操作对象是四个链接变量和一个32位的子分组。四轮用的非线性函数分别是 F、G、H、I:
F(X,Y,Z) = (X & Y) | (~X & Z) G(X,Y,Z) = (X & Z) | (Y & ~Z) H(X,Y,Z) = X ^ Y ^ Z I(X,Y,Z) = Y ^ (X | ~Z)
每轮还使用64个常量表K中的一组,K[i] = floor(abs(sin(i+1)) × 2^32),这些常量让MD5看起来更"随机",避免输入过于规律时输出也能有足够扩散。
64步运算都围绕一个核心公式:
a = b + ((a + F(b,c,d) + M[i] + K[i]) <<< s)
其中 <<< s 表示循环左移s位,s在64步中各不相同。每完成一个512位分组的压缩,就把计算结果累加到A、B、C、D上(这就是所谓"链接")。所有分组处理完,把A、B、C、D按小端序拼接成16字节,就得到最终的128位MD5摘要。
整个过程一句话总结:填充、分块、初始化、64轮迭代压缩。就这么简单,但每一步都经过精心设计,让输出的每一位都受输入每一位的影响,这就是雪崩效应——输入哪怕只改一个比特,输出也会大变样。
2.3 MD5在中国互联网场景的"剩余价值"
既然MD5已不安全,为什么代码里还在大量出现?我得替它说句公道话——不是所有场景都需要抵抗碰撞攻击。
- 缓存Key生成:把一组查询参数序列化成字符串,取MD5作为Redis的key,碰撞概率极低,而且碰撞的后果只是缓存互串,不涉及安全问题。
- 文件去重:上传文件先算MD5,判断云端是否已存在相同文件。即便有人恶意构造碰撞文件,影响的也只是存储系统里的去重误判,不构成严重漏洞。
- 分布式负载均衡的一致性哈希:一致性哈希环上节点映射用MD5或SHA-1,主要要求分布均匀和稳定,安全性要求不高。
- 数据库唯一索引的冗余字段:用MD5哈希做等值查询加速,或者做短链接映射。
注意,这些场景的共同点是不存在"攻击者能通过碰撞获利"的可能。如果数据涉及机密性、完整性验证或防伪,一律不要用MD5。
2.4 MD5的实际用法与常见误区
很多人在代码里用MD5,方式其实是有问题的。最常见的错误是直接用现成库拼字符串,要特别注意编码一致性——同一个字符串,UTF-8编码和GBK编码算出的MD5完全不同。跨语言调用时(比如Java端算MD5、Python端验签),如果字符编码不一致,校验必挂。
还要注意大小写问题。MD5摘要一般是16进制字符串,有些库输出大写,有些输出小写。做字符串比较时,一不小心就会踩坑。我建议统一转成大写或小写再做比较,把规范化逻辑放到公共工具类里。
加盐处理是另一个高频话题。如果你的系统里还在用MD5存密码,至少得加盐。盐就是随机字符串,每个用户用不同的盐,这样攻击者无法用预计算的彩虹表直接反查。盐要够长,至少16字节以上,而且要随机生成。曾经有真实的浏览器密码泄露事件,就是因为网站拿MD5直接哈希密码,连盐都没加,结果用户密码被彩虹表批量还原。加盐的逻辑非常简单:
stored_hash = MD5(salt + password)存储时将 salt 和 hash 一并存入数据库。验证时取回 salt,重新拼接再哈希比对。但这只是"最不坏"的方案——真要存密码,还是优先用bcrypt或Argon2这类慢哈希,它们从设计上就考虑了抗GPU暴力破解,比MD5+盐安全得多。
3. RSA算法深度拆解
3.1 RSA数学原理与密钥生成过程
RSA是1977年由Ron Rivest、Adi Shamir、Leonard Adleman三人提出的公钥加密算法,也是目前实际应用最广泛的非对称加密算法之一。它的安全性基于一个数学难题:大整数因子分解非常困难。你随便给两个大素数相乘,计算乘积只需瞬间;但给你一个很大的合数,把它分解回两个素因子,现代计算机也只能望着它叹气。
RSA的密钥生成过程如下:
第一步,随机选择两个不等的大素数 p 和 q。实际工程中,p 和 q 通常是1024位甚至2048位级别的素数,生成时用概率性素性检测(比如Miller-Rabin算法)来快速筛选。这一步非常耗时,尤其在高强度安全要求下,生成一对2048位密钥可能需要几秒甚至更久。
第二步,计算 n = p × q。n 的二进制长度就是RSA密钥长度。n 公开,作为公钥和私钥都需要的公共模数。安全强度的核心就在这里——n 越大,因子分解越难。当前业界普遍认为1024位RSA已不安全,2048位可保护到2030年左右,金融和政务场景推荐3072位或4096位。
第三步,计算欧拉函数 φ(n) = (p-1)(q-1)。
第四步,选择公钥指数 e,通常取65537。为什么是65537?因为它是费马素数,二进制是10000000000000001,只有两个1,模幂运算时可以有效加速,同时安全性也经过充分验证。早期有人用3作为公钥指数,但容易受到低指数攻击,后来被业界弃用。
第五步,计算私钥指数 d,满足 e × d ≡ 1 (mod φ(n))。也就是 d 是 e 在模 φ(n) 下的乘法逆元,用扩展欧几里得算法计算。
最终,公钥是 (n, e),私钥是 (n, d)。p、q、φ(n) 都必须严格保密,最好销毁或安全存储——因为一旦泄露,攻击者可以通过它们算出 d,私钥形同虚设。
3.2 RSA加密与解密、签名与验签
RSA的加密公式非常简洁。发送方拿到接收方的公钥 (n, e),对明文消息 m(视为整数,且 m < n)计算:
密文 c = m^e mod n
接收方持有私钥 (n, d),恢复明文: m = c^d mod n
这一来一回依赖的就是欧拉定理的推论:m^(e×d) ≡ m (mod n)。只要 e×d ≡ 1 (mod φ(n)),上面的等式就一定成立。
签名和验签的逻辑正好反过来。签名者用自己的私钥对摘要做运算: 签名 s = hash(m)^d mod n
验证者用签名者的公钥验证: hash(m) ≡ s^e (mod n)
能逆过来用,是RSA最巧妙的地方。但要注意,工程上签名时绝不会直接对原始消息做RSA运算,而是先对消息做哈希摘要,再对摘要签名。原因有两个:其一,RSA运算很慢,消息可能很长,直接对整条消息做幂运算性能不可接受;其二,直接对原始消息做RSA运算,攻击者可以通过数学构造伪造合法签名,而加哈希能破坏这种结构。
3.3 从数学公式到工程实现的要点
理论公式看着简单,真到代码层面,坑可不少。第一个坑是填充方案。教科书式的RSA——直接 m^e mod n——在实际中必须摒弃,因为它是确定性算法,同一明文永远得到同一密文,攻击者可以通过选择明文攻击来破解。现实中必须使用带随机性的填充方案,比如PKCS#1 v1.5填充或OAEP填充。
具体来说,PKCS#1 v1.5填充是在明文前加随机字节,让相同明文每次加密产生不同密文。但它被证明存在Bleichenbacher攻击(填充预言攻击),所以现代推荐使用OAEP(Optimal Asymmetric Encryption Padding)。OAEP加入随机种子和两轮哈希掩码,在语义安全性上更稳健。
第二个坑是明文长度限制。RSA加密每个分组的明文长度是固定的:密钥字节数减去填充开销。对2048位RSA + OAEP填充,最大明文大概244字节(256 - 2 - 2×32)。如果业务消息超过这个长度,必须分段加密,或者干脆改用混合加密方案——首先生成随机的AES会话密钥,用AES加密业务数据,再用RSA加密这个会话密钥。这是TLS握手里真实使用的模式,能兼顾性能和安全。
第三个坑是性能差异。RSA加密比AES慢大概100到1000倍,公钥操作比私钥操作快很多(因为e选得小),但这不意味着公钥加密可以滥用。在实际业务系统中,RSA一般只用在握手阶段、签名验签和小量敏感数据加密上,海量数据的机密性保护全部交给对称加密。理解这个性能差异,你在设计接口时就知道哪里该用RSA,哪里该用AES。
3.4 RSA现代应用中的典型场景
- HTTPS证书体系:服务器证书里含RSA公钥,客户端用证书公钥验签,再用RSA或ECC协商出会话密钥。这是大多数人接触RSA最多的场景。
- 数字签名:代码签名的JAR包、APK安装包的签名块、电子合同中的签名,大量使用RSA私钥签名,公钥验证。
- 支付网关的敏感信息加密:银行卡号、身份证号在传输前用平台公钥加密,平台侧私钥解密,防止链路窃听。
- SSH密钥登录:SSH支持RSA密钥对,私钥留在本地,公钥放在服务器,登录时通过签名机制验证身份。
- 软件授权与License校验:软件厂商用私钥签发授权文件,客户端用内置公钥验签,防止授权文件被篡改。
说白了,RSA在现实系统里干两件事:加密少量关键数据、签名做身份和完整性验证。你只要认准这两件事,选型就不会跑偏。
4. MD5与RSA的实战对比与代码级应用
4.1 一张表看懂MD5和RSA的本质差异
| 维度 | MD5 | RSA |
|---|---|---|
| 算法类别 | 哈希算法(单向散列) | 非对称加密算法 |
| 输出长度 | 固定128位(16字节) | 取决于密钥长度,通常512~4096位 |
| 是否可逆 | 不可逆 | 可逆(私钥解密) |
| 密钥 | 无密钥 | 公钥 + 私钥 |
| 安全性现状 | 已被碰撞攻击攻破 | 2048位目前安全 |
| 典型用途 | 完整性校验、缓存Key、密码存储(不建议) | 数据加密、签名/验签、密钥协商 |
| 性能 | 极快,纳秒级处理小数据 | 慢,毫秒级,且数据长度受限 |
| 填充需求 | 无需填充,但需处理长度对齐 | 必须使用PSS/OAEP填充 |
很多人问我"MD5和RSA能不能互相替代",答案是不能。MD5做的是"不可逆摘要",RSA做的是"可逆加密+不可伪造签名",两者解决的问题不同。在同一个安全体系里,它们常常是配合出现的:先用哈希算法提取摘要,再用RSA私钥对摘要签名——先哈希后签名,是对RSA性能劣势的规避,也是安全性的必要设计。
4.2 代码级实现示例:MD5做缓存Key
以Java为例,用JDK自带的MessageDigest类就可以安全地生成MD5摘要,不需要引入任何第三方依赖:
public static String md5(String input) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder sb = new StringBuilder(); for (byte b : digest) { sb.append(String.format("%02x", b)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("MD5 algorithm not available", e); } }这里有两个关键点:一是指定UTF-8编码,避免跨平台时中文字符编码不一致;二是使用 StringBuilder 格式化输出小写十六进制字符串。如果多个服务间共享同一个缓存Key生成规则,整个团队必须统一编码和大小写规范,否则同一数据在不同语言环境下生成的Key不一致,缓存命中率会直线下降。
4.3 代码级实现示例:RSA分段加密与解密实战
实际业务中,RSA加密的最大痛点就是明文长度限制。下面这段代码展示了一个稳妥的分段加密方案:
public static byte[] rsaEncrypt(byte[] data, PublicKey publicKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); int keyLength = ((RSAPublicKey) publicKey).getModulus().bitLength() / 8; int maxBlock = keyLength - 11; // PKCS1Padding开销为11字节 ByteArrayOutputStream out = new ByteArrayOutputStream(); int offset = 0; while (offset < data.length) { int inputLen = Math.min(data.length - offset, maxBlock); byte[] cache = cipher.doFinal(data, offset, inputLen); out.write(cache); offset += inputLen; } return out.toByteArray(); }注意几个细节:
- 算法声明是"RSA/ECB/PKCS1Padding",其中ECB在这里表示将数据分块处理,并不代表使用了CBC这类分组模式。
- 每次加密前要重新获取待处理数据长度,防止数组越界。
- 如果使用OAEP填充,maxBlock需要减去2倍哈希长度加2,具体计算为 keyLength - 2 - 2 * hashLength(例如SHA-256时hashLength为32字节),所以是 keyLength - 66。
- 在实际项目中,我建议优先用OAEP替换掉PKCS1Padding,如果兼容的第三方系统不支持OAEP,再回退到PKCS1Padding。
4.4 混合加密模式:RSA + AES 的标准组合套路
真正的生产级系统从来不会只用一种算法。我推荐一个经过验证的混合加密流程:
- 客户端随机生成一个256位的AES会话密钥。
- 客户端用AES-GCM加密业务数据(带认证标签,能同时防篡改)。
- 客户端用服务端的RSA公钥加密这个AES会话密钥。
- 客户端将密文数据、加密后的会话密钥、算法标识一起发送给服务端。
- 服务端用RSA私钥解密出AES会话密钥。
- 服务端用AES-GCM解密业务数据并校验认证标签。
这个方案结合了对称加密的高性能和非对称加密的密钥安全分发特性,是目前TLS、消息加密等场景的通用思路。它避免了RSA处理大数据时的性能和长度问题,也避免了AES密钥直接明文传输的安全隐患。
5. 常见问题与避坑指南
5.1 这些问题我踩过,你们别再踩了
第一个坑:把MD5的高强度哈希用在密码存储上。早年间很多系统都是这样做的,结果数据库一泄露,攻击者用彩虹表秒还原。后来大家学会加盐,但加盐的MD5依然防不住GPU暴力破解——普通显卡每秒能算数十亿次MD5,一个6位纯数字密码几秒钟就完事。真正安全的做法是改用bcrypt、scrypt或Argon2,它们用大量内存和CPU算力刻意拖慢每次哈希的耗时,让暴力破解的成本高到不划算。
第二个坑:RSA私钥泄露到代码仓库。我见过不止一个项目的私钥被提交到Git仓库里,美其名曰"方便测试"。一旦仓库变为公开,私钥就废了,所有用它签发的证书、加密的数据全都完蛋。私钥必须放在专用的密钥管理系统或硬件安全模块里,代码里只能引用别名,不能出现私钥明文。
第三个坑:跨语言RSA加密结果不一致。Java和OpenSSL的RSA实现很多细节不同,密钥格式也不一样。Java默认使用PKCS#8格式的私钥,OpenSSL常用PKCS#1格式,直接读取会报错。处理方式是转换密钥格式,或者使用统一的编码工具库。另外,RSA加解密结果在不同语言间只要密钥相同、填充方案相同、原始数据相同,是可以在线互通的——如果不互通,优先检查这三项是否一致。
第四个坑:不区分"加密"和"签名"的用途。很多人用RSA加密代替签名,或者用签名代替加密。这是两种安全属性完全不同的操作:加密是为了保密,签名是为了防伪。实际业务里,私钥签名、公钥验签常用于身份认证;公钥加密、私钥解密常用于机密信息传输。角色搞反了,轻则签名的数据所有人都能验证,重则私钥逆用的场景直接让认证体系崩溃。
5.2 快速排查表
| 现象 | 可能原因 | 处理建议 |
|---|---|---|
| 同一字符串MD5结果不同 | 字符编码不一致、输入含不可见字符 | 统一UTF-8,先trim再哈希 |
| MD5碰撞 | 输入数据量极大,概率极低 | 换用SHA-256或SHA-3 |
| RSA解密报错"Data must not be longer" | 明文超过单次加密上限 | 分段加密或改用混合加密 |
| RSA加解密互通失败 | 填充方案不一致、密钥格式不同 | 统一PKCS1/OAEP,检查密钥配置 |
| RSA签名验证失败 | 哈希算法不一致、摘要编码不同 | 统一签名算法标识(如SHA256withRSA) |
| 私钥读取报错 | 密钥格式不匹配 | 检查PKCS#1/PKCS#8格式,做格式转换 |
| AES解密后乱码 | GCM认证标签校验失败或AES模式不匹配 | 改用GCM并统一Nonce长度 |
5.3 关于密钥长度的建议
最后说说密钥长度。MD5没有密钥概念,所以不存在长度选择问题。但RSA有。1024位已经不安全,真实攻击实验已经证明其可被破解。2048位是当前商业系统的最低保险线,也是大多数证书颁发机构和支付平台的标准配置。涉及高价值数据的系统,建议直接上3072位或4096位,代价是密钥生成和加解密耗时增加,但换来的是安全边际。
另外还有一个小建议:新系统不要只支持RSA。ECC曲线(比如P-256)在同安全强度下密钥更短、性能更好,TLS 1.3已经把基于ECC的密钥交换作为默认选项。RSA在未来几年内不会消失,但新项目如果条件允许,优先用ECC做密钥交换,用Ed25519做签名,会是不错的方向。
6. 最近几年国密算法的一些补充认知
这块近几年在国内系统里越来越重要,值得单独说一嘴。国密算法是指中国商用密码算法,核心是SM2、SM3、SM4。SM2是非对称算法,对应RSA/ECC;SM3是哈希算法,对应SHA-256;SM4是对称分组算法,对应AES。
政策层面,等保2.0、密评都要求关键信息基础设施和政务系统优先使用国密算法。技术层面,SM2和SM3/SM4的实现和开源的OpenSSL库已经能平滑对接,很多国密加密机、SSL VPN、浏览器都支持国密套件。如果你在做政府项目、金融系统的对接,提前了解国密算法是必须的,否则接口测试时对方拿出一套SM2证书,你手头全是RSA的算法,对接会很被动。
国密算法和通用算法在原理上并无代差,SM2是椭圆曲线公钥密码,SM3是256位输出的哈希算法,SM4是128位分组密码,设计思路和AES/SHA-256对齐。工程上,SM2的密钥交换流程和签名流程都基于椭圆曲线运算,国密SSL双证书体系(签名证书和加密证书分离)也与国际主流的单证书体系略有差异。做技术方案时,既要会调国际算法,也要会调国密算法,很多加密机设备同时支持两类,切换起来并不麻烦。
有一点必须提醒:国密算法不是换掉算法就完事,密钥管理体系、证书体系、随机源质量、协议流程都得配套改造。有些项目只是把AES换成SM4,证书还是自签的RSA证书,协议流程完全没变,这种"假国密"在密评时是过不了关的。
7. 写给不同角色的落地建议
7.1 如果你在做Web后端
后端接触加密算法的频率最高。我的建议是:
- 密码存储:一律用bcrypt或Argon2,不要再碰MD5/SHA1做密码摘要。
- 接口签名:对外提供OpenAPI时,用非对称签名而不是简单的MD5+盐拼接。用私钥签名、公钥验证,避免密钥被逆向破解。
- 敏感字段传输:能走HTTPS就走HTTPS,需要做应用层加密时,用RSA或SM2做密钥协商,用对称密钥加密实际数据。
- 日志脱敏:身份证、手机号、银行卡号写入日志前要做脱敏处理,单纯MD5并不可靠,最好采用可配置的脱敏策略,需要反查时用HMAC。
7.2 如果你在做客户端
客户端最缺的是密钥保护能力。MD5和RSA的算法本身都一样,但密钥藏不藏得住,直接决定系统安全下限:
- 客户端内置公钥是常规操作,公钥不怕泄露。
- 但如果客户端内置私钥,那就等于把安全边界击穿了。任何能拿到APK/IPA包的人都能导出私钥。
- 推荐的方案是把签名私钥放到后端服务或硬件安全模块中,客户端只负责携带公钥。
- 如果确实需要在客户端做解密,建议把密钥放到系统级的安全存储区域(比如Android的Keystore、iOS的Keychain),并且配合设备绑定和混淆加固手段,尽可能抬高逆向门槛。
7.3 如果你在做安全架构
架构层面,我一般按"数据分类分级"来选型:
- 最高级别数据(比如支付密码、生物特征):必须硬件加密机+国密算法+专属通道,同时配合密钥轮换。
- 高敏数据(身份证号、银行卡号):传输用RSA/SM2加密或TLS,存储用AES/SM4加密,访问有完整审计。
- 中敏数据(用户注册信息):至少保证传输层加密,存储层按合规要求即可。
- 低敏数据(公开资料、运营数据):做好完整性校验即可,不需要上加密。
7.4 如果你在写工具脚本
快速原型和本地脚本,用哈希校验就够了。下载文件用SHA-256校验完整性,本地密码本用SQLCipher(SQLite的AES加密扩展)加密,签名和验签用Python的cryptography库就能搞定。但记住:任何便捷手段都只适合在受控环境使用,生产环境的加密方案永远是"宁可多一层,不要少一层"。
8. 说几个经验里总结的要点
写了这么长,最后送几条大白话经验:
第一,算法选型不要追新,也不要过于守旧。SHA-256和AES-256在当前足够,2048位RSA还能用很多年。真正容易被攻击的不是算法本身,而是实现方式和密钥管理——用错了填充方式、私钥乱放、随机数生成弱,算法再强也是白搭。
第二,随机数质量是安全方案的底座。RSA密钥生成、OAEP填充、AES密钥生成、Nonce生成,全都依赖高质量随机源。在Java里用SecureRandom,在Python里用secrets模块,在Node.js里用crypto.randomBytes,这些才是符合密码学要求的随机源。不要用Math.random或random()生成密钥,它们的随机性和安全性完全不够。
第三,加密整个链路不是"调用一遍算法"就完事。密钥从产生、传输、存储、轮换、注销,每个环节都有泄露风险。生产上我建议给每个密钥设置有效期,到期强制轮换,旧密钥留一个宽限期用于解密存量数据。密码学里有个概念叫前向安全——即使当前密钥泄露,历史通信内容也不能被还原。要实现这一点,就得周期性更换密钥,每次更换后旧密钥立即失效。
第四,能用开源的成熟库,就不要自己造轮子。密码学是唯一一个"自己实现一定比轮子差"的领域。AES、RSA、哈希的成熟实现经过无数攻击者检验,轮子直接调用就好。真正需要自己设计的是业务层的密钥编排、权限控制、审计日志,这些才是安全架构师的价值所在。
我自己的体会是,做安全相关的项目,最怕的不是攻击者太强,而是开发者太自信。MD5和RSA纠结谁强谁弱,不如先搞清楚自己的需求。你要是能把"哈希做摘要、对称加密保数据、非对称加密传密钥"这个组合逻辑吃透,再把密钥管好,大部分安全需求都能稳稳拿下。