一、环境准备与依赖安装
在开始重构加密功能之前,我们需要搭建一个干净、高效的开发环境。本方案基于Python 3.8+环境,使用gmssl库实现国密SM4算法。相比于老旧的DES或不安全的AES-ECB模式,SM4-CBC模式在合规性和安全性上都有质的飞跃。
请在终端中执行以下命令,确保Python版本符合要求:
```bash
python3 --version
```
接着,安装核心依赖库gmssl。为了确保下载速度和稳定性,这里直接指定使用清华大学的PyPI镜像源:
```bash
pip3 install gmssl -i https://pypi.tuna.tsinghua.edu.cn/simple
```
如果安装过程中提示权限不足,请使用sudo或者在用户目录下使用--user参数。安装完成后,可以通过pip3 list | grep gmssl确认库是否已就位。
二、SM4国密算法工具类封装
为了保证代码的可复用性和逻辑的严密性,我们将SM4的加解密逻辑封装在一个独立的类中。这里我们采用SM4-CBC模式,该模式需要初始化向量(IV),能够有效防止相同的明文生成相同的密文,极大地提升了安全性。
请在项目目录下创建一个名为sm4_crypto.py的文件,并写入以下完整代码。这段代码包含了PKCS7填充补齐、密钥校验以及加解密的核心逻辑,直接复制即可使用,无需任何修改。
```python
import base64
import binascii
from gmssl import sm4, func
class SM4Crypto:
def __init__(self, key):
"""
初始化SM4加密类
:param key: 32位十六进制字符串的密钥,例如:0123456789abcdeffedcba9876543210
"""
if len(key) != 32:
raise ValueError("SM4密钥长度必须为32位十六进制字符串")
self.key = key
self.crypt_sm4 = sm4.CryptSM4(key=key, mode=sm4.SM4_CBC)
def pkcs7_padding(self, data):
"""
PKCS7填充补齐
"""
pks = 16 - (len(data) % 16)
pad_data = data + chr(pks) pks
return pad_data
def pkcs7_unpadding(self, data):
"""
PKCS7去除填充
"""
pad = ord(data[-1])
return data[:-pad]
def encrypt_data(self, plaintext):
"""
加密字符串,返回Base64编码的密文
"""
iv = b'0000000000000000' 初始化向量,实际生产中应随机生成并随密文存储
try:
对明文进行填充
pad_text = self.pkcs7_padding(plaintext)
将字符串转为bytes
input_bytes = pad_text.encode('utf-8')
将IV转为bytes
iv_bytes = iv.encode('utf-8')
执行加密
encrypt_bytes = self.crypt_sm4.crypt_cbc(iv_bytes, input_bytes, 1) 1代表加密
Base64编码
return base64.b64encode(encrypt_bytes).decode('utf-8')
except Exception as e:
print(f"加密异常: {e}")
return None
def decrypt_data(self, ciphertext_b64):
"""
解密Base64编码的密文,返回原始字符串
"""
iv = b'0000000000000000'
try:
Base64解码
encrypt_bytes = base64.b64decode(ciphertext_b64)
iv_bytes = iv.encode('utf-8')
执行解密
decrypt_bytes = self.crypt_sm4.crypt_cbc(iv_bytes, encrypt_bytes, 0) 0代表解密
去除填充
return self.pkcs7_unpadding(decrypt_bytes.decode('utf-8'))
except Exception as e:
print(f"解密异常: {e}")
return None
def encrypt_file(self, input_file, output_file):
"""
文件流加密(二进制模式)
"""
iv = b'0000000000000000'
iv_bytes = iv.encode('utf-8')
try:
with open(input_file, 'rb') as f_in:
data = f_in.read()
计算填充长度并处理二进制数据
pad_len = 16 - (len(data) % 16)
if pad_len == 16:
pad_len = 0
二进制PKCS7填充
if pad_len > 0:
data = data + bytes([pad_len] pad_len)
encrypted_data = self.crypt_sm4.crypt_cbc(iv_bytes, data, 1)
with open(output_file, 'wb') as f_out:
f_out.write(encrypted_data)
return True
except Exception as e:
print(f"文件加密失败: {e}")
return False
def decrypt_file(self, input_file, output_file):
"""
文件流解密(二进制模式)
"""
iv = b'0000000000000000'
iv_bytes = iv.encode('utf-8')
try:
with open(input_file, 'rb') as f_in:
data = f_in.read()
decrypted_data = self.crypt_sm4.crypt_cbc(iv_bytes, data, 0)
去除二进制PKCS7填充
pad_len = decrypted_data[-1]
if pad_len < 16:
decrypted_data = decrypted_data[:-pad_len]
with open(output_file, 'wb') as f_out:
f_out.write(decrypted_data)
return True
except Exception as e:
print(f"文件解密失败: {e}")
return False
```
三、档案文件加解密实操脚本
有了工具类,接下来我们编写一个可以直接运行的脚本,模拟档案软件的上传(加密)和下载(解密)过程。请在同级目录下创建run_demo.py。

该脚本会自动生成一个测试文件,对其进行加密存储,然后再解密还原,最后通过MD5校验确保数据完整性。这一步是验证方案可行性的关键。
```python
import os
import hashlib
from sm4_crypto import SM4Crypto
def get_file_md5(file_path):
"""计算文件MD5值用于校验"""
with open(file_path, 'rb') as f:
return hashlib.md5(f.read()).hexdigest()
def main():
1. 配置密钥 (请务必妥善保管此密钥,实际应用中建议从配置中心或环境变量读取)
这是一个测试密钥,生产环境请使用更复杂的随机生成值
SM4_KEY = '0123456789abcdeffedcba9876543210'
2. 初始化加密器
crypto = SM4Crypto(SM4_KEY)
3. 准备测试文件
original_file = 'archive_secret.pdf'
encrypted_file = 'archive_secret.enc'
decrypted_file = 'archive_restored.pdf'
创建一个模拟的档案文件内容
dummy_content = b"这是档案软件的核心机密数据内容。\n" 100
with open(original_file, 'wb') as f:
f.write(dummy_content)
print(f"1. 已生成测试档案文件: {original_file}")
4. 执行文件加密
print(f"2. 正在执行SM4加密...")
if crypto.encrypt_file(original_file, encrypted_file):
print(f" -> 加密成功,密文已保存为: {encrypted_file}")
观察密文大小,通常会比原文件稍大(因填充)
print(f" -> 原文件大小: {os.path.getsize(original_file)} bytes, 密文大小: {os.path.getsize(encrypted_file)} bytes")
else:
print(" -> 加密失败,请检查密钥配置")
return
5. 执行文件解密
print(f"3. 正在执行SM4解密...")
if crypto.decrypt_file(encrypted_file, decrypted_file):
print(f" -> 解密成功,还原文件已保存为: {decrypted_file}")
else:
print(" -> 解密失败")
return
6. 完整性校验
original_md5 = get_file_md5(original_file)
decrypted_md5 = get_file_md5(decrypted_file)
print(f"4. 数据完整性校验:")
print(f" -> 原文件MD5: {original_md5}")
print(f" -> 还原MD5: {decrypted_md5}")
if original_md5 == decrypted_md5:
print(" ->
校验通过!加密解密流程完美闭环,数据无丢失。")
else:
print(" -> 校验失败!数据存在异常,请检查填充逻辑。")
清理测试文件
os.remove(original_file)
os.remove(encrypted_file)
os.remove(decrypted_file)
if __name__ == '__main__':
main()
```
四、密钥安全存储方案
很多档案软件不安全的原因并非算法本身,而是密钥直接硬编码在代码中。为了彻底解决这个问题,我们需要将密钥剥离到环境变量中。
请在项目根目录下创建一个.env文件(注意该文件应被加入.gitignore防止上传到代码库),内容如下:
```bash
SM4加密密钥 (32位十六进制字符串)
ARCHIVE_SM4_KEY=0123456789abcdeffedcba9876543210
```
我们需要安装python-dotenv库来读取该文件:
```bash
pip3 install python-dotenv -i https://pypi.tuna.tsinghua.edu.cn/simple
```
修改run_demo.py的头部,引入环境变量加载逻辑,替换掉硬编码的密钥部分:
```python
import os
from dotenv import load_dotenv
加载.env文件
load_dotenv()
def main():
从环境变量获取密钥,如果不存在则抛出异常
sm4_key = os.getenv('ARCHIVE_SM4_KEY')
if not sm4_key:
raise ValueError("未在环境变量中找到ARCHIVE_SM4_KEY,请检查.env配置")
初始化加密器
crypto = SM4Crypto(sm4_key)
...后续代码保持不变
```
五、集成到现有系统的建议
对于正在维护的档案软件,建议按照以下步骤进行替换,以实现平滑迁移:
- 数据库字段迁移: 如果数据库中已存在使用旧算法(如Base64)加密的数据,不要直接修改。建议新增一个字段
content_secure,在用户下次访问或编辑档案时,后台自动调用新的SM4脚本加密并写入新字段,逐步完成数据清洗。
- 文件存储拦截: 在文件上传的Controller层,不要直接保存
file.stream。先将其保存到临时目录,调用crypto.encrypt_file生成加密文件,将加密文件移动到正式存储区,并删除临时明文文件。
- 下载流式处理: 用户下载时,先在内存或临时目录中解密文件,然后以流的形式响应给浏览器,确保服务器磁盘上不会残留解密后的明文文件。
通过以上步骤,你可以在不改变原有业务逻辑结构的前提下,将档案软件的安全等级提升至国密标准,彻底解决数据明文存储或弱加密带来的泄露风险。