Python 加密与解密实战 18 招(一):散列哈希加密

Python 加密与解密实战 18 招(一):散列哈希加密

系列文章导航

  • 第一篇:散列哈希加密(招式1-4)
  • 第二篇:对称加密(招式5-9)
  • 第三篇:非对称加密·数字签名·密钥交换(招式10-13)
  • 第四篇:文件加密·扩展算法·安全随机数(招式14-18)

前言

在信息安全领域,加密与解密是核心技术。Python 生态提供了丰富的加密库,本系列文章将通过18 个实战招式,系统讲解 Python 加密解密的完整知识体系。

实验环境

项目信息
服务器华为云 FlexusX ECS
操作系统Ubuntu 24.04 server 64bit
CPU/内存8vCPUs / 16GiB
Python3.12.3
核心依赖cryptography 41.0.7、bcrypt 3.2.2、pycryptodome 3.23.0

加密解密知识体系总览

Python 加密解密 18 招 ├── 散列哈希 (招式1-4) │ ├── hashlib模块 (MD5/SHA系列) │ ├── 加盐处理 (Salt) │ ├── bcrypt模块 │ └── hmac模块 ├── 对称加密 (招式5-9) │ ├── AES加密 │ ├── AES进阶 (Fernet) │ ├── AES分组模式 (ECB/CBC/CTR/GCM/CFB/OFB) │ ├── DES/3DES加密 │ └── Blowfish加密 ├── 非对称加密 (招式10-13) │ ├── RSA加密 │ ├── DSA签名 │ ├── 数字签名 (PSS/PKCS1v15) │ └── DH/ECDH密钥交换 ├── 文件与扩展 (招式14-18) │ ├── hashlib文件签名 │ ├── cryptography文件签名 │ ├── 文件加密 (Fernet/AES-CBC/AES-GCM) │ ├── 扩展算法 (ARC4/CAST5/ChaCha20/Camellia/SM4/ECC) │ └── 密码学安全随机数

三大加密算法分类

类型特点典型算法适用场景
散列(哈希)单向不可逆、固定长度输出MD5、SHA系列、bcrypt密码存储、完整性校验
对称加密加解密同一密钥、速度快AES、DES、Blowfish大量数据加密
非对称加密公钥加密/私钥解密、速度慢RSA、DSA、ECC密钥交换、数字签名

招式1:hashlib 模块 —— MD5/SHA 系列散列加密

1.1 散列函数基础

散列(Hash)函数是将任意长度的输入映射为固定长度输出的单向函数。核心特性:

  • 确定性:相同输入永远产生相同输出
  • 雪崩效应:输入微小变化导致输出剧烈变化
  • 不可逆性:无法从哈希值反推原始数据
  • 抗碰撞性:很难找到两个不同输入产生相同输出

1.2 hashlib 支持的算法

importhashlib# 查看所有可用算法print(f"hashlib可用算法:")foralgoinsorted(hashlib.algorithms_available):print(f" -{algo}")

运行输出:

hashlib可用算法: - blake2b - blake2s - md5 - md5-sha1 - ripemd160 - sha1 - sha224 - sha256 - sha384 - sha3_224 - sha3_256 - sha3_384 - sha3_512 - sha512 - sha512_224 - sha512_256 - shake_128 - shake_256 - sm3

1.3 常用散列算法实战

importhashlib text="Hello, Python Crypto!"print(f"原始文本:{text}")# MD5 - 128位(16字节)哈希值md5_hash=hashlib.md5(text.encode()).hexdigest()print(f"\nMD5 ({len(md5_hash)}位十六进制 ={len(md5_hash)*4}位):")print(f"{md5_hash}")# SHA1 - 160位(20字节)哈希值sha1_hash=hashlib.sha1(text.encode()).hexdigest()print(f"\nSHA1 ({len(sha1_hash)}位十六进制 ={len(sha1_hash)*4}位):")print(f"{sha1_hash}")# SHA256 - 256位(32字节)哈希值sha256_hash=hashlib.sha256(text.encode()).hexdigest()print(f"\nSHA256 ({len(sha256_hash)}位十六进制 ={len(sha256_hash)*4}位):")print(f"{sha256_hash}")# SHA512 - 512位(64字节)哈希值sha512_hash=hashlib.sha512(text.encode()).hexdigest()print(f"\nSHA512 ({len(sha512_hash)}位十六进制 ={len(sha512_hash)*4}位):")print(f"{sha512_hash}")# SHA3-256 - 新一代SHA3标准sha3_hash=hashlib.sha3_256(text.encode()).hexdigest()print(f"\nSHA3-256 ({len(sha3_hash)}位十六进制 ={len(sha3_hash)*4}位):")print(f"{sha3_hash}")

运行输出:

原始文本: Hello, Python Crypto! MD5 (32位十六进制 = 128位): 6f3be9312107c24b560d1b6c4ea9ffe2 SHA1 (40位十六进制 = 160位): 71dbfc90be603c20a2e908451965502e9b0185a3 SHA256 (64位十六进制 = 256位): 17baf00e4e7fae0955ed1666e02c22f03bbfa7487227a767d529954b2c64f8de SHA512 (128位十六进制 = 512位): f152f6c5bcabbc25cb4aa03095be2d921efb2dbab89b326c5b4319d43719bbd35779cc4eb2615a3f163025723c89f6d4e329e5ab34ed034d3e0a30d4845e5367 SHA3-256 (64位十六进制 = 256位): fc58e962e260ae7a18914d296f28f83cdf84550b2e2f888231ebdad05d311446

1.4 大文件分块哈希

处理大文件时,应分块读取避免内存溢出:

importhashlib# 模拟10MB大文件数据large_data=b"X"*(10*1024*1024)# 10MB数据h=hashlib.sha256()foriinrange(0,len(large_data),4096):h.update(large_data[i:i+4096])print(f"10MB数据SHA256:{h.hexdigest()}")

运行输出:

10MB数据SHA256: 5449ecf9b06ad82bf75a0f1de67f1d8b40142c416dc93039c84a3ae6a98bb2cb

1.5 雪崩效应演示

importhashlibprint(f"'abc' MD5:{hashlib.md5(b'abc').hexdigest()}")print(f"'abd' MD5:{hashlib.md5(b'abd').hexdigest()}")print("(仅改变1个字符,哈希值完全不同)")

运行输出:

'abc' MD5: 900150983cd24fb0d6963f7d28e17f72 'abd' MD5: 4911e516e5aa21d327512e0c8b197616 (仅改变1个字符,哈希值完全不同)

小结:MD5 已被证明不安全(存在碰撞攻击),生产环境推荐使用 SHA-256 及以上。SHA-3 是新一代哈希标准,提供了与 SHA-2 不同的内部结构。


招式2:加盐处理(Salt)

2.1 为什么需要加盐

无盐哈希的致命弱点:相同密码始终产生相同哈希值,攻击者可以使用彩虹表预计算攻击。加盐通过为每个密码添加随机值,使相同密码产生不同哈希。

2.2 加盐实战

importhashlibimportosimporttime password="mypassword123"# 无盐哈希 - 容易被彩虹表破解plain_hash=hashlib.sha256(password.encode()).hexdigest()print(f"原始密码:{password}")print(f"无盐SHA256:{plain_hash}")# 随机盐值salt=os.urandom(16)print(f"\n随机盐值(16字节):{salt.hex()}")# 加盐哈希 - 方式1: salt + passwordsalted_hash1=hashlib.sha256(salt+password.encode()).hexdigest()print(f"加盐SHA256(salt+pwd):{salted_hash1}")# 加盐哈希 - 方式2: password + saltsalted_hash2=hashlib.sha256(password.encode()+salt).hexdigest()print(f"加盐SHA256(pwd+salt):{salted_hash2}")

运行输出:

原始密码: mypassword123 无盐SHA256: 6e659deaa85842cdabb5c6305fcc40033ba43772ec00d45c2a3c921741a5e377 随机盐值(16字节): 190d7cc9d3249da4a11dffeb487be6d2 加盐SHA256(salt+pwd): 5494981c3d48c57899ed943ee9031fd3abf602dcb169573a6ffb6cc0b160034b 加盐SHA256(pwd+salt): 591a880269b123845e00c0eada7165926a5a62e207ec09a1b254eb0e29f321c3

2.3 PBKDF2 多轮迭代

iterations=100000start=time.time()dk=hashlib.pbkdf2_hmac('sha256',password.encode(),salt,iterations)elapsed=time.time()-startprint(f"PBKDF2-HMAC-SHA256 (迭代{iterations}次):")print(f" 密钥:{dk.hex()}")print(f" 耗时:{elapsed:.3f}秒")

运行输出:

PBKDF2-HMAC-SHA256 (迭代100000次): 密钥: 684eab10994f67df36739f44f95b86491f257899a154b4fed20f0612820c1b18 耗时: 0.024秒

2.4 同密码不同盐对比

salt2=os.urandom(16)salted_hash3=hashlib.sha256(salt2+password.encode()).hexdigest()print(f"同密码不同盐的哈希对比:")print(f" 盐1哈希:{salted_hash1}")print(f" 盐2哈希:{salted_hash3}")print(f" 是否相同:{salted_hash1==salted_hash3}")

运行输出:

同密码不同盐的哈希对比: 盐1哈希: 5494981c3d48c57899ed943ee9031fd3abf602dcb169573a6ffb6cc0b160034b 盐2哈希: 31562836a306aca540a09324a87925076a575a24ecdd8f3d99f324988aebd89b 是否相同: False

小结:加盐使彩虹表攻击失效。PBKDF2 通过多轮迭代增加计算成本,有效抵御暴力破解。推荐迭代次数不低于 100,000 次。


招式3:bcrypt 模块 —— 专业密码哈希

3.1 bcrypt 优势

bcrypt 是专为密码存储设计的哈希函数,相比 PBKDF2 具有以下优势:

  • 自动加盐:盐值嵌入在哈希结果中,无需单独存储
  • 自适应成本:通过rounds参数控制计算复杂度
  • 抗 GPU/ASIC 攻击:内存访问模式使其难以并行加速

3.2 bcrypt 实战

importbcryptimporttime password="mypassword123"pwd_bytes=password.encode('utf-8')# 生成盐并哈希salt_bcrypt=bcrypt.gensalt(rounds=12)print(f"bcrypt生成的盐:{salt_bcrypt.decode()}")hashed=bcrypt.hashpw(pwd_bytes,salt_bcrypt)print(f"bcrypt哈希结果:{hashed.decode()}")# 验证密码is_valid=bcrypt.checkpw(pwd_bytes,hashed)print(f"\n密码验证(正确密码):{'通过'ifis_validelse'失败'}")is_valid_wrong=bcrypt.checkpw(b'wrongpassword',hashed)print(f"密码验证(错误密码):{'通过'ifis_valid_wrongelse'失败'}")

运行输出:

bcrypt生成的盐: $2b$12$5LGzB.jIXVT1TVOQzmbARu bcrypt哈希结果: $2b$12$5LGzB.jIXVT1TVOQzmbARuFb.jRBnBwYyd0urjLMQE8yBPdaxQNHK 密码验证(正确密码): 通过 密码验证(错误密码): 失败

3.3 计算轮数性能对比

print("不同rounds(计算轮数)性能对比:")forroundsin[4,8,12,14]:start=time.time()salt_test=bcrypt.gensalt(rounds=rounds)bcrypt.hashpw(pwd_bytes,salt_test)elapsed=time.time()-startprint(f" rounds={rounds:2d}耗时:{elapsed:.4f}秒")

运行输出:

不同rounds(计算轮数)性能对比: rounds= 4 耗时: 0.0009秒 rounds= 8 耗时: 0.0129秒 rounds=12 耗时: 0.2053秒 rounds=14 耗时: 0.8214秒

小结rounds每增加 1,计算时间翻倍。推荐生产环境使用rounds=12(约 0.2 秒),在安全性和用户体验之间取得平衡。bcrypt 哈希结果格式$2b$rounds$salt+hash中自带了盐和轮数信息,验证时自动提取。


招式4:hmac 模块 —— 消息认证码

4.1 HMAC 原理

HMAC(Hash-based Message Authentication Code)是一种使用密钥的消息认证码。与普通哈希的区别:

特性普通哈希HMAC
需要密钥
防篡改
防伪造否(任何人可计算)是(需要密钥)
身份认证

4.2 HMAC 实战

importhmacimporthashlib secret_key=b"my_secret_key_2024"message=b"Important message to verify"# HMAC-MD5hmac_md5=hmac.new(secret_key,message,hashlib.md5).hexdigest()print(f"HMAC-MD5:{hmac_md5}")# HMAC-SHA256hmac_sha256=hmac.new(secret_key,message,hashlib.sha256).hexdigest()print(f"HMAC-SHA256:{hmac_sha256}")# HMAC-SHA512hmac_sha512=hmac.new(secret_key,message,hashlib.sha512).hexdigest()print(f"HMAC-SHA512:{hmac_sha512}")

运行输出:

HMAC-MD5: e7cfa5bee68e1ceb90358a40e72bcd4c HMAC-SHA256: 1a674b34376f5c172a0c365b4bd0b5ce165f64e70db53751b775c5303d7e5b94 HMAC-SHA512: f0711edd74edb61a351778eb71a1f9c6f0c2b509a43aabd3154553b26dc754b0b0dce52ed73b9e3919787c7f13e16a153776d3f5e24ddedb5487b4d76dda10ad

4.3 HMAC 验证与防时序攻击

# HMAC验证 - 安全比较received_hmac=hmac.new(secret_key,message,hashlib.sha256).hexdigest()is_match=hmac.compare_digest(hmac_sha256,received_hmac)print(f"\nHMAC验证(正确消息):{'匹配'ifis_matchelse'不匹配'}")# 篡改消息后的HMACtampered_msg=b"Important message to VERIFIED"tampered_hmac=hmac.new(secret_key,tampered_msg,hashlib.sha256).hexdigest()is_match_tampered=hmac.compare_digest(hmac_sha256,tampered_hmac)print(f"HMAC验证(篡改消息):{'匹配'ifis_match_tamperedelse'不匹配'}")# 使用不同密钥wrong_key_hmac=hmac.new(b"wrong_key",message,hashlib.sha256).hexdigest()is_match_wrong=hmac.compare_digest(hmac_sha256,wrong_key_hmac)print(f"HMAC验证(错误密钥):{'匹配'ifis_match_wrongelse'不匹配'}")

运行输出:

HMAC验证(正确消息): 匹配 HMAC验证(篡改消息): 不匹配 HMAC验证(错误密钥): 不匹配

小结hmac.compare_digest()是常量时间比较函数,防止时序攻击。永远不要用==比较 HMAC 值,因为==会在第一个不匹配字节处短路返回,攻击者可通过测量响应时间逐字节猜测正确值。


本篇总结

招式模块核心用途安全等级
1hashlib数据指纹/完整性校验SHA-256+ 推荐
2hashlib + os密码加盐哈希PBKDF2 推荐
3bcrypt密码存储(专业)最高(自适应成本)
4hmac消息认证/防篡改SHA-256 推荐

安全实践建议

  1. 密码存储:优先 bcrypt,其次 PBKDF2(迭代 ≥ 100,000)
  2. 完整性校验:使用 SHA-256 及以上,避免 MD5/SHA-1
  3. 消息认证:始终使用hmac.compare_digest()进行比较
  4. 盐值管理:每个密码使用独立随机盐,盐值长度 ≥ 16 字节

下一篇:Python 加密与解密实战 18 招(二):对称加密 AES/DES/Blowfish