617 lines
22 KiB
Python
Executable File
617 lines
22 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""
|
||
AES-CBC/GCM 命令行加解密工具 - 支持文本和文件的加密解密,支持批量处理
|
||
"""
|
||
|
||
import base64
|
||
import hashlib
|
||
from Crypto.Cipher import AES
|
||
from Crypto.Util.Padding import pad, unpad
|
||
from Crypto.Random import get_random_bytes
|
||
import os
|
||
import sys
|
||
import json
|
||
import argparse
|
||
from pathlib import Path
|
||
|
||
# 定义分块大小(64MB)
|
||
CHUNK_SIZE = 64 * 1024 * 1024
|
||
|
||
|
||
class AESCli:
|
||
"""AES命令行加解密工具"""
|
||
|
||
def __init__(self, mode='CBC'):
|
||
"""
|
||
初始化AES工具
|
||
:param mode: 加密模式 'CBC' 或 'GCM'
|
||
"""
|
||
self.mode = mode
|
||
self.key = None
|
||
self.iv = None
|
||
|
||
def set_key_iv(self, key_hex, iv_hex):
|
||
"""
|
||
设置密钥和IV/Nonce
|
||
:param key_hex: 十六进制密钥字符串
|
||
:param iv_hex: 十六进制IV/Nonce字符串
|
||
"""
|
||
try:
|
||
if not key_hex:
|
||
raise ValueError("密钥不能为空")
|
||
if not iv_hex:
|
||
raise ValueError("IV/Nonce不能为空")
|
||
|
||
self.key = bytes.fromhex(key_hex)
|
||
self.iv = bytes.fromhex(iv_hex)
|
||
|
||
if len(self.key) not in [16, 24, 32]:
|
||
raise ValueError(f"密钥长度必须为16、24或32字节,当前为{len(self.key)}字节")
|
||
|
||
if self.mode == "CBC" and len(self.iv) != 16:
|
||
raise ValueError(f"CBC模式IV长度必须为16字节,当前为{len(self.iv)}字节")
|
||
elif self.mode == "GCM" and len(self.iv) != 12:
|
||
raise ValueError(f"GCM模式Nonce长度必须为12字节,当前为{len(self.iv)}字节")
|
||
|
||
return True
|
||
except ValueError as e:
|
||
print(f"错误: {e}")
|
||
return False
|
||
|
||
def generate_random_key(self, key_size=16):
|
||
"""生成随机密钥"""
|
||
if key_size not in [16, 24, 32]:
|
||
print(f"错误: 密钥大小必须为16、24或32,当前为{key_size}")
|
||
return None
|
||
|
||
self.key = get_random_bytes(key_size)
|
||
key_hex = self.key.hex()
|
||
print(f"已生成 {key_size} 字节随机密钥: {key_hex}")
|
||
return key_hex
|
||
|
||
def generate_random_iv(self):
|
||
"""生成随机IV或Nonce"""
|
||
if self.mode == "CBC":
|
||
iv_size = 16
|
||
label = "IV"
|
||
else: # GCM
|
||
iv_size = 12
|
||
label = "Nonce"
|
||
|
||
self.iv = get_random_bytes(iv_size)
|
||
iv_hex = self.iv.hex()
|
||
print(f"已生成 {iv_size} 字节随机{label}: {iv_hex}")
|
||
return iv_hex
|
||
|
||
def encrypt_text(self, plaintext):
|
||
"""加密文本"""
|
||
if not self.key or not self.iv:
|
||
raise ValueError("请先设置密钥和IV/Nonce")
|
||
|
||
if self.mode == "CBC":
|
||
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
|
||
ciphertext = cipher.encrypt(pad(plaintext.encode('utf-8'), AES.block_size))
|
||
encrypted = base64.b64encode(ciphertext).decode('utf-8')
|
||
else: # GCM
|
||
cipher = AES.new(self.key, AES.MODE_GCM, nonce=self.iv)
|
||
ciphertext, tag = cipher.encrypt_and_digest(plaintext.encode('utf-8'))
|
||
# 存储格式: ciphertext + tag
|
||
combined = ciphertext + tag
|
||
encrypted = base64.b64encode(combined).decode('utf-8')
|
||
|
||
return encrypted
|
||
|
||
def decrypt_text(self, encrypted_text):
|
||
"""解密文本"""
|
||
if not self.key or not self.iv:
|
||
raise ValueError("请先设置密钥和IV/Nonce")
|
||
|
||
combined = base64.b64decode(encrypted_text)
|
||
|
||
if self.mode == "CBC":
|
||
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
|
||
decrypted = unpad(cipher.decrypt(combined), AES.block_size)
|
||
else: # GCM
|
||
# 分离ciphertext和tag (最后16字节是tag)
|
||
if len(combined) < 16:
|
||
raise ValueError("密文格式无效:缺少认证标签")
|
||
tag = combined[-16:]
|
||
ciphertext = combined[:-16]
|
||
cipher = AES.new(self.key, AES.MODE_GCM, nonce=self.iv)
|
||
decrypted = cipher.decrypt_and_verify(ciphertext, tag)
|
||
|
||
return decrypted.decode('utf-8')
|
||
|
||
def encrypt_file(self, input_path, output_path, delete_source=False):
|
||
"""加密文件"""
|
||
if not self.key or not self.iv:
|
||
raise ValueError("请先设置密钥和IV/Nonce")
|
||
|
||
if not os.path.exists(input_path):
|
||
raise FileNotFoundError(f"输入文件不存在: {input_path}")
|
||
|
||
file_size = os.path.getsize(input_path)
|
||
print(f"开始加密文件: {input_path} ({self._format_size(file_size)})")
|
||
|
||
if self.mode == "CBC":
|
||
self._encrypt_file_cbc(input_path, output_path)
|
||
else: # GCM
|
||
self._encrypt_file_gcm(input_path, output_path)
|
||
|
||
print(f"文件加密完成,保存到: {output_path}")
|
||
|
||
if delete_source:
|
||
try:
|
||
os.remove(input_path)
|
||
print(f"已删除源文件: {input_path}")
|
||
except Exception as e:
|
||
print(f"警告: 删除源文件失败: {e}")
|
||
|
||
def _encrypt_file_cbc(self, input_path, output_path):
|
||
"""CBC模式加密文件"""
|
||
with open(input_path, 'rb') as infile:
|
||
file_data = infile.read()
|
||
|
||
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
|
||
ciphertext = cipher.encrypt(pad(file_data, AES.block_size))
|
||
|
||
with open(output_path, 'wb') as outfile:
|
||
# 写入IV + 密文
|
||
outfile.write(self.iv + ciphertext)
|
||
|
||
def _encrypt_file_gcm(self, input_path, output_path):
|
||
"""GCM模式加密文件"""
|
||
with open(input_path, 'rb') as infile:
|
||
file_data = infile.read()
|
||
|
||
cipher = AES.new(self.key, AES.MODE_GCM, nonce=self.iv)
|
||
ciphertext, tag = cipher.encrypt_and_digest(file_data)
|
||
|
||
with open(output_path, 'wb') as outfile:
|
||
# 写入IV + 密文 + tag
|
||
outfile.write(self.iv + ciphertext + tag)
|
||
|
||
def decrypt_file(self, input_path, output_path, delete_source=False):
|
||
"""解密文件"""
|
||
if not self.key or not self.iv:
|
||
raise ValueError("请先设置密钥和IV/Nonce")
|
||
|
||
if not os.path.exists(input_path):
|
||
raise FileNotFoundError(f"输入文件不存在: {input_path}")
|
||
|
||
file_size = os.path.getsize(input_path)
|
||
print(f"开始解密文件: {input_path} ({self._format_size(file_size)})")
|
||
|
||
if self.mode == "CBC":
|
||
self._decrypt_file_cbc(input_path, output_path)
|
||
else: # GCM
|
||
self._decrypt_file_gcm(input_path, output_path)
|
||
|
||
print(f"文件解密完成,保存到: {output_path}")
|
||
|
||
if delete_source:
|
||
try:
|
||
os.remove(input_path)
|
||
print(f"已删除加密文件: {input_path}")
|
||
except Exception as e:
|
||
print(f"警告: 删除加密文件失败: {e}")
|
||
|
||
def _decrypt_file_cbc(self, input_path, output_path):
|
||
"""CBC模式解密文件"""
|
||
with open(input_path, 'rb') as infile:
|
||
file_iv = infile.read(16)
|
||
ciphertext = infile.read()
|
||
|
||
cipher = AES.new(self.key, AES.MODE_CBC, file_iv)
|
||
decrypted_data = unpad(cipher.decrypt(ciphertext), AES.block_size)
|
||
|
||
with open(output_path, 'wb') as outfile:
|
||
outfile.write(decrypted_data)
|
||
|
||
def _decrypt_file_gcm(self, input_path, output_path):
|
||
"""GCM模式解密文件"""
|
||
with open(input_path, 'rb') as infile:
|
||
file_iv = infile.read(12)
|
||
remaining = infile.read()
|
||
|
||
if len(remaining) < 16:
|
||
raise ValueError("加密文件格式无效:缺少认证标签")
|
||
|
||
tag = remaining[-16:]
|
||
ciphertext = remaining[:-16]
|
||
|
||
cipher = AES.new(self.key, AES.MODE_GCM, nonce=file_iv)
|
||
decrypted_data = cipher.decrypt_and_verify(ciphertext, tag)
|
||
|
||
with open(output_path, 'wb') as outfile:
|
||
outfile.write(decrypted_data)
|
||
|
||
def batch_encrypt(self, files, delete_source=False, recursive=False):
|
||
"""批量加密文件"""
|
||
total = len(files)
|
||
success = 0
|
||
failed = []
|
||
|
||
print(f"\n开始批量加密 {total} 个文件...")
|
||
|
||
for i, input_file in enumerate(files, 1):
|
||
try:
|
||
if not os.path.exists(input_file):
|
||
failed.append(f"{input_file} (文件不存在)")
|
||
continue
|
||
|
||
output_file = self._generate_encrypt_filename(input_file)
|
||
print(f"[{i}/{total}] 加密: {input_file}")
|
||
|
||
self.encrypt_file(input_file, output_file, delete_source)
|
||
success += 1
|
||
|
||
except Exception as e:
|
||
failed.append(f"{input_file} ({str(e)})")
|
||
print(f" ✗ 失败: {e}")
|
||
|
||
self._print_batch_result("批量加密", success, len(failed), failed)
|
||
|
||
def batch_decrypt(self, files, delete_source=False, recursive=False):
|
||
"""批量解密文件"""
|
||
total = len(files)
|
||
success = 0
|
||
failed = []
|
||
|
||
print(f"\n开始批量解密 {total} 个文件...")
|
||
|
||
for i, input_file in enumerate(files, 1):
|
||
try:
|
||
if not os.path.exists(input_file):
|
||
failed.append(f"{input_file} (文件不存在)")
|
||
continue
|
||
|
||
output_file = self._generate_decrypt_filename(input_file)
|
||
print(f"[{i}/{total}] 解密: {input_file}")
|
||
|
||
self.decrypt_file(input_file, output_file, delete_source)
|
||
success += 1
|
||
|
||
except Exception as e:
|
||
failed.append(f"{input_file} ({str(e)})")
|
||
print(f" ✗ 失败: {e}")
|
||
|
||
self._print_batch_result("批量解密", success, len(failed), failed)
|
||
|
||
def _generate_encrypt_filename(self, input_file):
|
||
"""生成加密输出文件名"""
|
||
if input_file.endswith('.encrypted'):
|
||
return input_file
|
||
else:
|
||
return f"{input_file}.encrypted"
|
||
|
||
def _generate_decrypt_filename(self, encrypted_file):
|
||
"""生成解密输出文件名"""
|
||
if encrypted_file.endswith('.encrypted'):
|
||
return encrypted_file[:-10]
|
||
else:
|
||
return f"{encrypted_file}.decrypted"
|
||
|
||
def _print_batch_result(self, operation, success, fail_count, failed_files):
|
||
"""打印批量处理结果"""
|
||
print(f"\n{operation}完成:")
|
||
print(f" 成功: {success} 个文件")
|
||
print(f" 失败: {fail_count} 个文件")
|
||
|
||
if failed_files:
|
||
print("\n失败的文件:")
|
||
for f in failed_files[:20]:
|
||
print(f" - {f}")
|
||
if len(failed_files) > 20:
|
||
print(f" ... 还有 {len(failed_files) - 20} 个文件")
|
||
|
||
@staticmethod
|
||
def _format_size(size_bytes):
|
||
"""格式化文件大小"""
|
||
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
|
||
if size_bytes < 1024.0:
|
||
return f"{size_bytes:.2f} {unit}"
|
||
size_bytes /= 1024.0
|
||
return f"{size_bytes:.2f} PB"
|
||
|
||
def save_key_iv(self, filepath):
|
||
"""保存密钥和IV到JSON文件"""
|
||
if not self.key and not self.iv:
|
||
print("警告: 没有可保存的密钥或IV")
|
||
return False
|
||
|
||
data = {
|
||
"mode": self.mode,
|
||
"key": self.key.hex() if self.key else "",
|
||
"iv": self.iv.hex() if self.iv else "",
|
||
"key_size": len(self.key) if self.key else 0,
|
||
"description": f"AES-{self.mode}密钥和{'IV' if self.mode == 'CBC' else 'Nonce'}"
|
||
}
|
||
|
||
with open(filepath, 'w', encoding='utf-8') as f:
|
||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||
|
||
print(f"密钥和IV已保存到: {filepath}")
|
||
return True
|
||
|
||
def load_key_iv(self, filepath):
|
||
"""从JSON文件加载密钥和IV"""
|
||
if not os.path.exists(filepath):
|
||
print(f"错误: 文件不存在: {filepath}")
|
||
return False
|
||
|
||
with open(filepath, 'r', encoding='utf-8') as f:
|
||
data = json.load(f)
|
||
|
||
# 加载加密模式
|
||
if 'mode' in data and data['mode'] in ['CBC', 'GCM']:
|
||
self.mode = data['mode']
|
||
print(f"加载加密模式: {self.mode}")
|
||
|
||
# 加载密钥
|
||
if 'key' in data and data['key']:
|
||
key_hex = data['key']
|
||
self.key = bytes.fromhex(key_hex)
|
||
print(f"加载密钥: {len(self.key)} 字节")
|
||
|
||
# 加载IV
|
||
if 'iv' in data and data['iv']:
|
||
iv_hex = data['iv']
|
||
self.iv = bytes.fromhex(iv_hex)
|
||
label = 'IV' if self.mode == 'CBC' else 'Nonce'
|
||
print(f"加载{label}: {len(self.iv)} 字节")
|
||
|
||
print(f"密钥和IV已从 {filepath} 加载")
|
||
return True
|
||
|
||
|
||
def collect_files(paths, pattern=None, recursive=False):
|
||
"""收集文件列表"""
|
||
files = []
|
||
|
||
for path in paths:
|
||
if os.path.isfile(path):
|
||
files.append(os.path.abspath(path))
|
||
elif os.path.isdir(path):
|
||
if recursive:
|
||
for root, dirs, filenames in os.walk(path):
|
||
for filename in filenames:
|
||
filepath = os.path.join(root, filename)
|
||
if pattern is None or filename.endswith(pattern):
|
||
files.append(os.path.abspath(filepath))
|
||
else:
|
||
for filename in os.listdir(path):
|
||
filepath = os.path.join(path, filename)
|
||
if os.path.isfile(filepath):
|
||
if pattern is None or filename.endswith(pattern):
|
||
files.append(os.path.abspath(filepath))
|
||
|
||
return files
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(
|
||
description='AES-CBC/GCM 命令行加解密工具 - 支持文本和文件的加密解密,支持批量处理',
|
||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
epilog="""
|
||
使用示例:
|
||
# 生成随机密钥和IV
|
||
aes-crypto --gen-key
|
||
aes-crypto --gen-iv
|
||
|
||
# 文本加解密
|
||
aes-crypto --key <密钥hex> --iv <IV/Nonce hex> --encrypt-text "Hello World"
|
||
aes-crypto --key <密钥hex> --iv <IV/Nonce hex> --decrypt-text "<密文base64>"
|
||
|
||
# 文件加解密
|
||
aes-crypto --key <密钥hex> --iv <IV/Nonce hex> --encrypt-file input.txt
|
||
aes-crypto --key <密钥hex> --iv <IV/Nonce hex> --decrypt-file input.txt.encrypted
|
||
|
||
# 批量加解密
|
||
aes-crypto --key <密钥hex> --iv <IV/Nonce hex> --batch-encrypt file1.txt file2.txt
|
||
aes-crypto --key <密钥hex> --iv <IV/Nonce hex> --batch-decrypt *.encrypted
|
||
|
||
# 保存和加载密钥
|
||
aes-crypto --key <密钥hex> --iv <IV/Nonce hex> --save-key keys.json
|
||
aes-crypto --load-key keys.json --encrypt-file input.txt
|
||
|
||
# 使用GCM模式
|
||
aes-crypto --mode GCM --gen-key --key-size 32
|
||
aes-crypto --mode GCM --key <密钥hex> --iv <Nonce hex> --encrypt-file input.txt
|
||
|
||
# 从文件读取密钥
|
||
aes-crypto --key-file key.txt --iv <IV/Nonce hex> --encrypt-file input.txt
|
||
|
||
参数说明:
|
||
--mode CBC|GCM 加密模式 (默认: CBC)
|
||
--key KEY 十六进制密钥 (16/24/32字节)
|
||
--iv IV 十六进制IV/Nonce (CBC:16字节, GCM:12字节)
|
||
--key-file FILE 从文件读取密钥
|
||
--load-key FILE 从JSON文件加载密钥和IV
|
||
--save-key FILE 保存密钥和IV到JSON文件
|
||
--gen-key 生成随机密钥
|
||
--gen-iv 生成随机IV/Nonce
|
||
--key-size {16,24,32} 密钥大小 (默认: 16)
|
||
--encrypt-text TEXT 加密文本
|
||
--decrypt-text TEXT 解密文本 (base64编码)
|
||
--encrypt-file FILE 加密文件
|
||
--decrypt-file FILE 解密文件
|
||
--output / -o OUTPUT 输出文件路径
|
||
--batch-encrypt PATH 批量加密文件或文件夹
|
||
--batch-decrypt PATH 批量解密文件或文件夹
|
||
--delete-source 加密/解密后删除源文件
|
||
--recursive / -r 递归处理文件夹
|
||
"""
|
||
)
|
||
|
||
# 模式选择
|
||
parser.add_argument('--mode', choices=['CBC', 'GCM'], default='CBC',
|
||
help='加密模式 (默认: CBC)')
|
||
|
||
# 密钥和IV设置
|
||
parser.add_argument('--key', help='十六进制密钥 (16/24/32字节)')
|
||
parser.add_argument('--iv', help='十六进制IV/Nonce (CBC:16字节, GCM:12字节)')
|
||
parser.add_argument('--key-file', help='从文件读取密钥')
|
||
parser.add_argument('--load-key', help='从JSON文件加载密钥和IV')
|
||
parser.add_argument('--save-key', help='保存密钥和IV到JSON文件')
|
||
|
||
# 密钥生成
|
||
parser.add_argument('--gen-key', action='store_true', help='生成随机密钥')
|
||
parser.add_argument('--gen-iv', action='store_true', help='生成随机IV/Nonce')
|
||
parser.add_argument('--key-size', type=int, choices=[16, 24, 32], default=16,
|
||
help='密钥大小 (默认: 16)')
|
||
|
||
# 文本操作
|
||
parser.add_argument('--encrypt-text', help='加密文本')
|
||
parser.add_argument('--decrypt-text', help='解密文本 (base64编码)')
|
||
|
||
# 文件操作
|
||
parser.add_argument('--encrypt-file', help='加密文件')
|
||
parser.add_argument('--decrypt-file', help='解密文件')
|
||
parser.add_argument('--output', '-o', help='输出文件路径')
|
||
|
||
# 批量操作
|
||
parser.add_argument('--batch-encrypt', nargs='+', metavar='PATH',
|
||
help='批量加密文件或文件夹')
|
||
parser.add_argument('--batch-decrypt', nargs='+', metavar='PATH',
|
||
help='批量解密文件或文件夹')
|
||
parser.add_argument('--delete-source', action='store_true',
|
||
help='加密/解密后删除源文件')
|
||
parser.add_argument('--recursive', '-r', action='store_true',
|
||
help='递归处理文件夹')
|
||
|
||
args = parser.parse_args()
|
||
|
||
# 创建AES工具实例
|
||
aes = AESCli(mode=args.mode)
|
||
|
||
# 处理密钥加载
|
||
if args.load_key:
|
||
if not aes.load_key_iv(args.load_key):
|
||
sys.exit(1)
|
||
|
||
# 处理密钥文件
|
||
if args.key_file:
|
||
try:
|
||
with open(args.key_file, 'r') as f:
|
||
args.key = f.read().strip()
|
||
except Exception as e:
|
||
print(f"错误: 读取密钥文件失败: {e}")
|
||
sys.exit(1)
|
||
|
||
# 设置密钥和IV
|
||
if args.key:
|
||
if args.iv is None and not aes.iv:
|
||
print("错误: 需要同时提供密钥和IV/Nonce")
|
||
sys.exit(1)
|
||
|
||
if args.iv:
|
||
if not aes.set_key_iv(args.key, args.iv):
|
||
sys.exit(1)
|
||
elif aes.iv:
|
||
# 从加载的密钥文件中获取IV
|
||
try:
|
||
aes.key = bytes.fromhex(args.key)
|
||
if len(aes.key) not in [16, 24, 32]:
|
||
print(f"错误: 密钥长度必须为16、24或32字节,当前为{len(aes.key)}字节")
|
||
sys.exit(1)
|
||
except ValueError:
|
||
print("错误: 密钥格式无效,需要十六进制字符串")
|
||
sys.exit(1)
|
||
|
||
# 生成密钥
|
||
if args.gen_key:
|
||
key_hex = aes.generate_random_key(args.key_size)
|
||
if key_hex is None:
|
||
sys.exit(1)
|
||
|
||
# 生成IV
|
||
if args.gen_iv:
|
||
iv_hex = aes.generate_random_iv()
|
||
if iv_hex is None:
|
||
sys.exit(1)
|
||
|
||
# 保存密钥
|
||
if args.save_key:
|
||
if not aes.save_key_iv(args.save_key):
|
||
sys.exit(1)
|
||
|
||
# 如果只是生成密钥,到这里就结束了
|
||
if args.gen_key or args.gen_iv:
|
||
if not (args.encrypt_text or args.decrypt_text or
|
||
args.encrypt_file or args.decrypt_file or
|
||
args.batch_encrypt or args.batch_decrypt or args.save_key):
|
||
return
|
||
|
||
# 检查密钥和IV是否已设置
|
||
if not aes.key or not aes.iv:
|
||
if args.encrypt_text or args.decrypt_text or \
|
||
args.encrypt_file or args.decrypt_file or \
|
||
args.batch_encrypt or args.batch_decrypt:
|
||
print("错误: 请先设置密钥和IV/Nonce (使用 --key 和 --iv 或 --load-key)")
|
||
sys.exit(1)
|
||
|
||
try:
|
||
# 文本加密
|
||
if args.encrypt_text:
|
||
encrypted = aes.encrypt_text(args.encrypt_text)
|
||
print(encrypted)
|
||
|
||
# 文本解密
|
||
elif args.decrypt_text:
|
||
decrypted = aes.decrypt_text(args.decrypt_text)
|
||
print(decrypted)
|
||
|
||
# 文件加密
|
||
elif args.encrypt_file:
|
||
input_file = args.encrypt_file
|
||
output_file = args.output or aes._generate_encrypt_filename(input_file)
|
||
aes.encrypt_file(input_file, output_file, args.delete_source)
|
||
|
||
# 文件解密
|
||
elif args.decrypt_file:
|
||
input_file = args.decrypt_file
|
||
output_file = args.output or aes._generate_decrypt_filename(input_file)
|
||
aes.decrypt_file(input_file, output_file, args.delete_source)
|
||
|
||
# 批量加密
|
||
elif args.batch_encrypt:
|
||
files = collect_files(args.batch_encrypt, recursive=args.recursive)
|
||
if not files:
|
||
print("错误: 没有找到要加密的文件")
|
||
sys.exit(1)
|
||
|
||
# 过滤掉已加密的文件
|
||
files = [f for f in files if not f.endswith('.encrypted')]
|
||
if not files:
|
||
print("错误: 所有文件都已加密 (.encrypted)")
|
||
sys.exit(1)
|
||
|
||
print(f"找到 {len(files)} 个文件需要加密")
|
||
aes.batch_encrypt(files, args.delete_source, args.recursive)
|
||
|
||
# 批量解密
|
||
elif args.batch_decrypt:
|
||
files = collect_files(args.batch_decrypt, pattern='.encrypted', recursive=args.recursive)
|
||
if not files:
|
||
print("错误: 没有找到 .encrypted 文件")
|
||
sys.exit(1)
|
||
|
||
print(f"找到 {len(files)} 个文件需要解密")
|
||
aes.batch_decrypt(files, args.delete_source, args.recursive)
|
||
|
||
# 没有指定操作
|
||
else:
|
||
parser.print_help()
|
||
|
||
except KeyboardInterrupt:
|
||
print("\n操作已取消")
|
||
sys.exit(1)
|
||
except Exception as e:
|
||
print(f"错误: {e}")
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|