简介这是一款用Python编写的文件加密解密工具面向需要保护敏感数据、对文件安全有特殊需求的企业或个人开发者。其设计思路与勒索病毒相似加密后会自动修改文件扩展名使原始数据无法被直接访问但用途是安全地加密与解密文件而非破坏数据。资源包共11个文件包含exe可执行程序、html说明页、bat脚本、msi安装包、url快捷方式及jpg图片等压缩包约22MB其中加密工具与解密工具分别独立运行并附带msodbcsql安装程序用于数据库连接排错。密钥信息保存在SQL Server数据库中由encryption_key字段记录使用前需自行创建空库工具运行后会在当前目录生成db_config配置文件。目前已有113人学习关注。通过该资源读者可获得一套完整的加解密脚本与可执行程序理解密钥入库、扩展名修改与数据库配置的落地方式并掌握连接失败时的诊断思路适合作为文件安全方向的实践参考。1. 拿到这个 Python 文件加密解密工具先想清楚它和勒索病毒的边界在哪第一次看到这个工具的文件列表时我脑子里蹦出来的词就是勒索病毒。加密工具.exe、解密工具.exe、自动改扩展名、密钥存数据库——这套组合拳和真实勒索样本的行为链几乎一模一样。区别只在于勒索病毒把密钥发到攻击者服务器然后问你要赎金而这个 Python 写的工具把密钥老老实实存进你自己建的 SQL Server 库里解密权始终在你手上。它解决的是一个很具体的需求批量加密本地敏感文件让文件在离开你的管控范围后无法被直接打开需要时再用密钥还原。适合手里有大量合同、图纸、客户数据需要归档或外发的从业者也适合想研究文件加密流程的 Python 学习者。但前提是——你得先接受一个事实密钥丢了文件就真没了没有后悔药。这个工具用 Python 编写核心逻辑是遍历目录、对文件内容做加密、修改文件扩展名、把密钥写入数据库的 encryption_key 字段。数据库用 SQL Server连接不上就用 msodbcsql.msi 装驱动。运行后当前目录会生成 db_config 文件里面是数据库连接配置。下面我从环境搭建开始一步步拆这个工具怎么跑起来、参数怎么调、哪里容易翻车。2. 环境搭建与数据库准备从零把运行底座搭起来2.1 Python 环境与依赖确认这个工具是 Python 写的但压缩包里给的是 exe说明作者已经打包过。如果你想改代码或者排查问题得先把 Python 环境配好。我一般用 3.8 到 3.10 之间的版本太新的版本有些老库会出兼容问题。先确认 Python 是否可用python --version pip --version如果版本低于 3.7建议去 python下载安装教程 里找 3.9 的安装包重装。安装时记得勾选 Add Python to PATH否则后面命令行调用会报 python 不是内部或外部命令。依赖方面这个工具核心用到的是数据库连接库和加密库。常见做法是pip install pyodbc cryptographypyodbc负责连 SQL Servercryptography负责实际的加解密运算。如果你用 vscode python环境配置 或者 pycharm配置python环境直接在终端里跑上面这行就行。装完之后用下面这段代码验证一下import pyodbc from cryptography.fernet import Fernet # 生成一个测试密钥确认加密库可用 key Fernet.generate_key() f Fernet(key) token f.encrypt(btest) print(解密结果:, f.decrypt(token)) print(pyodbc 驱动列表:, [d for d in pyodbc.drivers()])这段代码做两件事一是用 Fernet 做一次完整的加密解密闭环确认 cryptography 装好了二是列出系统里可用的 ODBC 驱动后面连数据库要用到。如果驱动列表是空的说明 msodbcsql 还没装。2.2 SQL Server 建库与 encryption_key 表设计工具要求你先自行创建一个空库。我用 SSMS 或者 sqlcmd 都行建库语句很简单CREATE DATABASE FileCryptoDB; GO USE FileCryptoDB; GO CREATE TABLE encryption_key ( id INT IDENTITY(1,1) PRIMARY KEY, file_name NVARCHAR(500) NOT NULL, encryption_key NVARCHAR(MAX) NOT NULL, created_at DATETIME DEFAULT GETDATE() ); GO这里有几个点要注意。encryption_key字段用NVARCHAR(MAX)是因为 Fernet 生成的密钥是 base64 编码的字符串长度虽然固定但保险起见给足空间。file_name记录被加密的文件名方便解密时按文件名检索对应密钥。created_at是给自己留的时间戳后面排查哪个文件什么时候加的密全靠它。建完表之后用一条插入语句测试数据库是否可写INSERT INTO encryption_key (file_name, encryption_key) VALUES (Ntest.txt, NtestKey123); SELECT * FROM encryption_key;能查到刚插入的记录说明库和表都没问题。查不到就检查是不是连到了错误的实例或者权限不够。2.3 db_config 文件与连接串配置工具运行后会在当前目录生成 db_config 文件。这个文件我见过两种格式一种是 JSON一种是 ini。不管哪种核心就几个参数服务器地址、数据库名、用户名、密码、驱动名。如果是 JSON 格式大概长这样{ server: localhost, database: FileCryptoDB, username: sa, password: YourPassword, driver: ODBC Driver 17 for SQL Server }驱动名必须和pyodbc.drivers()里列出来的一致。常见的有 ODBC Driver 17 for SQL Server 和 SQL Server Native Client 11.0。写错了会报 Data source name not found。连接串的拼接方式import json import pyodbc with open(db_config, r, encodingutf-8) as f: cfg json.load(f) conn_str ( fDRIVER{{{cfg[driver]}}}; fSERVER{cfg[server]}; fDATABASE{cfg[database]}; fUID{cfg[username]}; fPWD{cfg[password]}; TrustServerCertificateyes; ) conn pyodbc.connect(conn_str) print(数据库连接成功)TrustServerCertificateyes是本地开发环境常用的参数避免自签名证书导致的连接失败。生产环境建议配好证书后去掉这个参数。连接成功后工具就能往 encryption_key 表里读写密钥了。3. 加密与解密全流程拆解文件遍历、密钥写入与扩展名修改3.1 加密流程从遍历目录到写库加密工具的核心逻辑分四步遍历目标目录、读取文件内容、加密并写入新文件、把密钥存进数据库。我用一段简化代码把流程串起来import os import pyodbc from cryptography.fernet import Fernet def encrypt_file(filepath, conn): # 生成该文件专属的密钥 key Fernet.generate_key() f Fernet(key) # 读取原始内容 with open(filepath, rb) as fp: data fp.read() # 加密后写回同时修改扩展名 encrypted f.encrypt(data) new_path filepath .enc with open(new_path, wb) as fp: fp.write(encrypted) # 删除原文件可选建议先备份 os.remove(filepath) # 密钥写入数据库 cursor conn.cursor() cursor.execute( INSERT INTO encryption_key (file_name, encryption_key) VALUES (?, ?), (os.path.basename(filepath), key.decode()) ) conn.commit() print(f已加密: {filepath} - {new_path}) def walk_and_encrypt(root_dir, conn): for dirpath, dirnames, filenames in os.walk(root_dir): for name in filenames: full_path os.path.join(dirpath, name) # 跳过已加密文件和配置文件 if full_path.endswith(.enc) or name db_config: continue encrypt_file(full_path, conn)Fernet.generate_key()每次调用生成一个独立的密钥这意味着每个文件有自己专属的密钥。这样做的好处是单个密钥泄露不会影响其他文件坏处是密钥管理量随文件数线性增长。os.walk递归遍历所有子目录.enc后缀用来标记已加密文件避免重复加密。参数方面root_dir建议指向一个测试目录先跑通流程别一上来就对着整个 D 盘跑。conn是前面建好的数据库连接对象每次插入后commit()确保密钥落库。3.2 解密流程按文件名检索密钥并还原解密是加密的逆操作关键是从数据库里按文件名找到对应的密钥def decrypt_file(enc_path, conn): # 去掉 .enc 后缀得到原始文件名 original_name os.path.basename(enc_path).replace(.enc, ) cursor conn.cursor() cursor.execute( SELECT encryption_key FROM encryption_key WHERE file_name ?, (original_name,) ) row cursor.fetchone() if not row: print(f未找到密钥: {original_name}) return key row[0].encode() f Fernet(key) with open(enc_path, rb) as fp: encrypted fp.read() decrypted f.decrypt(encrypted) original_path enc_path.replace(.enc, ) with open(original_path, wb) as fp: fp.write(decrypted) os.remove(enc_path) print(f已解密: {enc_path} - {original_path})这里有个血泪经验如果同一个文件名在数据库里有多条记录比如加密了两次fetchone()只会取第一条可能取到旧密钥导致解密失败。解决办法是在插入时加唯一约束或者查询时按created_at DESC排序取最新一条。3.3 扩展名修改与文件识别工具会自动修改文件扩展名这是它像勒索病毒的地方。常见做法是加.enc、.locked或者随机后缀。改扩展名的目的不是加密本身而是让操作系统和常见软件无法直接关联打开方式起到一层心理隔离作用。但改扩展名有个坑如果原文件本身就有扩展名比如report.docx加密后变成report.docx.enc解密时用replace(.enc, )能正确还原。但如果原文件名里本身就含.enc字符串比如data.enc.backup替换就会出错。更稳妥的做法是用os.path.splitext或者记录原始文件名到数据库。我一般会在数据库里多加一列original_ext记录原始扩展名解密时拼回去。这样即使文件名里有奇怪字符也不会翻车。4. 避坑与排查连接失败、密钥丢失、重复加密的常见问题4.1 数据库连接报错 Data source name not found现象运行工具后弹窗或日志提示驱动找不到连接直接失败。原因系统里没装 msodbcsql 驱动或者 db_config 里写的驱动名和实际安装的不一致。解决先跑pyodbc.drivers()看列表里有什么。如果为空双击压缩包里的 msodbcsql.msi 安装。安装完重启命令行再查一次。驱动名要一字不差地填进 db_config大小写和空格都算。4.2 加密后解密失败提示 InvalidToken现象解密时报错文件无法还原。原因密钥不对。常见情况是数据库里存了多条同名文件的密钥解密时取了旧的那条或者加密时用的密钥根本没写进数据库比如事务没提交。解决先查SELECT * FROM encryption_key WHERE file_name xxx看有几条记录。多条的话按created_at取最新。如果一条都没有说明加密时写库失败了检查conn.commit()有没有执行。这种情况基本没救只能从备份恢复。4.3 重复加密导致文件损坏现象对同一个目录跑了两次加密文件变成.enc.enc解密一次后还是打不开。原因工具没有跳过已加密文件的逻辑或者跳过判断写错了。解决加密前先判断文件是否以.enc结尾是就跳过。更稳妥的做法是在数据库里记录已加密文件的路径每次加密前查一下。如果已经重复加密了手动把.enc.enc改回.enc再解密一次。4.4 db_config 文件被误加密现象加密整个目录时把 db_config 也加密了下次运行工具连数据库配置都读不到。原因遍历时没有排除配置文件。解决在walk_and_encrypt里加白名单跳过db_config、*.exe、*.msi这些工具自身文件。我一般会把工具放在独立目录加密目标指向另一个目录物理隔离最省心。4.5 SQL Server 连接超时现象连接串没问题但每次连接都要等很久然后超时。原因SQL Server 的 TCP/IP 协议没启用或者防火墙拦了 1433 端口。解决打开 SQL Server 配置管理器确认 TCP/IP 已启用IP 地址里的 TCP 端口是 1433。防火墙入站规则加一条允许 1433。本地测试可以用127.0.0.1代替localhost有时候能绕过名称解析的问题。5. 进阶技巧密钥轮换、批量解密与自动化校验5.1 密钥轮换与备份策略这个工具最大的风险点全在密钥上。我的习惯是每次批量加密后立刻把encryption_key表导出成 CSV存到另一个物理位置。SQL Server 自带的导出功能或者一条bcp命令就能搞定bcp FileCryptoDB.dbo.encryption_key out key_backup.csv -c -T -S localhost-c表示字符模式-T表示信任连接-S指定服务器。导出的 CSV 里就是文件名和密钥的对应关系哪天数据库崩了拿着这个文件还能手动解密。密钥轮换方面如果某个密钥怀疑泄露可以只对该文件重新加密先用旧密钥解密再用新密钥加密更新数据库里的记录。不要一次性换掉所有密钥除非你有把握全部重新处理一遍。5.2 批量解密的脚本化处理当需要解密整个目录时手动一个个点不现实。我一般写个批处理脚本import os import pyodbc from cryptography.fernet import Fernet conn pyodbc.connect(conn_str) # 复用前面的连接串 cursor conn.cursor() for dirpath, _, filenames in os.walk(target_dir): for name in filenames: if not name.endswith(.enc): continue enc_path os.path.join(dirpath, name) original_name name.replace(.enc, ) cursor.execute( SELECT TOP 1 encryption_key FROM encryption_key WHERE file_name ? ORDER BY created_at DESC, (original_name,) ) row cursor.fetchone() if row: f Fernet(row[0].encode()) with open(enc_path, rb) as fp: data fp.read() with open(enc_path.replace(.enc, ), wb) as fp: fp.write(f.decrypt(data)) os.remove(enc_path) print(f解密成功: {original_name}) else: print(f跳过无密钥: {original_name})TOP 1 ... ORDER BY created_at DESC确保取最新密钥。os.remove删除加密文件避免残留。跑之前先在测试目录验证确认无误再对正式数据操作。5.3 加密完整性校验加密完成后怎么确认文件没损坏我一般会做一次抽样解密验证随机挑几个文件解密到临时目录用hashlib比对 MD5import hashlib def file_md5(path): h hashlib.md5() with open(path, rb) as f: for chunk in iter(lambda: f.read(8192), b): h.update(chunk) return h.hexdigest() # 加密前记录原始 MD5解密后比对 original_md5 file_md5(original.txt) # ... 加密再解密 ... decrypted_md5 file_md5(decrypted.txt) assert original_md5 decrypted_md5, 文件损坏这个校验步骤我每次批量操作后都会跑一遍。从那以后我每次加密前都强制走一遍 MD5 记录解密后比对确认无误才删原始备份。希望帮到你。本文还有配套的精品资源点击获取
