Abantes 文件加密工具 - Python复刻C#重制版

查看 5|回复 0
作者:hdxzd12   
Abantes 文件加密工具 - Python复刻C#重制版
一个基于Python的文件加密解密工具,使用AES-256-CFB加密算法,兼容原有的C#版本加密格式。
回顾
我们先来回顾一下C#版本的代码  
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;
namespace Abantes.Utils
{
        // Token: 0x02000008 RID: 8
        internal class Encryption
        {
                // Token: 0x0600002B RID: 43 RVA: 0x000045C0 File Offset: 0x000027C0
                public static byte[] GenerateRandomSalt()
                {
                        byte[] array = new byte[32];
                        using (RNGCryptoServiceProvider rngcryptoServiceProvider = new RNGCryptoServiceProvider())
                        {
                                for (int i = 0; i  0)
                                {
                                        Application.DoEvents();
                                        cryptoStream.Write(array2, 0, count);
                                }
                                fileStream2.Close();
                        }
                        catch (Exception ex)
                        {
                                Console.WriteLine("Error: " + ex.Message);
                        }
                        finally
                        {
                                cryptoStream.Close();
                                fileStream.Close();
                        }
                }
                // Token: 0x0600002D RID: 45 RVA: 0x00004740 File Offset: 0x00002940
                public static void FileDecrypt(string inputFile, string outputFile, string password)
                {
                        byte[] bytes = Encoding.UTF8.GetBytes(password);
                        byte[] array = new byte[32];
                        FileStream fileStream = new FileStream(inputFile, FileMode.Open);
                        fileStream.Read(array, 0, array.Length);
                        RijndaelManaged rijndaelManaged = new RijndaelManaged();
                        rijndaelManaged.KeySize = 256;
                        rijndaelManaged.BlockSize = 128;
                        Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(bytes, array, 50000);
                        rijndaelManaged.Key = rfc2898DeriveBytes.GetBytes(rijndaelManaged.KeySize / 8);
                        rijndaelManaged.IV = rfc2898DeriveBytes.GetBytes(rijndaelManaged.BlockSize / 8);
                        rijndaelManaged.Padding = PaddingMode.PKCS7;
                        rijndaelManaged.Mode = CipherMode.CFB;
                        CryptoStream cryptoStream = new CryptoStream(fileStream, rijndaelManaged.CreateDecryptor(), CryptoStreamMode.Read);
                        FileStream fileStream2 = new FileStream(outputFile, FileMode.Create);
                        byte[] array2 = new byte[1048576];
                        try
                        {
                                int count;
                                while ((count = cryptoStream.Read(array2, 0, array2.Length)) > 0)
                                {
                                        Application.DoEvents();
                                        fileStream2.Write(array2, 0, count);
                                }
                        }
                        catch (CryptographicException ex)
                        {
                                Console.WriteLine("CryptographicException error: " + ex.Message);
                        }
                        catch (Exception ex2)
                        {
                                Console.WriteLine("Error: " + ex2.Message);
                        }
                        try
                        {
                                cryptoStream.Close();
                        }
                        catch (Exception ex3)
                        {
                                Console.WriteLine("Error by closing CryptoStream: " + ex3.Message);
                        }
                        finally
                        {
                                fileStream2.Close();
                                fileStream.Close();
                        }
                }
        }
}
文件加密解密流程详解
加密流程 (FileEncrypt 方法)
1. 生成随机盐
  • 使用 GenerateRandomSalt() 方法生成一个长度为 32 字节的随机盐
  • 盐在密码学中用于增加每个密码的唯一性,从而增强安全性
  • 即使相同的密码,使用不同的盐也会产生完全不同的加密结果

    2. 打开输出文件流
  • 使用 FileStream 打开一个新文件
  • 文件名为原始输入文件名加上 .Abantes 后缀
  • 这个文件将用来存储加密后的数据

    3. 生成密钥和 IV
  • 使用 Rfc2898DeriveBytes 类基于用户提供的密码和生成的随机盐来派生密钥和初始化向量 (IV)
  • 使用 50000 次迭代来增加密码派生的复杂性和安全性
  • 这种密钥派生方式可以有效抵抗暴力破解攻击

    4. 配置加密算法
  • 创建 RijndaelManaged 对象
  • 设置密钥大小为 256 位
  • 设置块大小为 128 位
  • 设置填充模式为 PaddingMode.PKCS7
  • 设置加密模式为 CipherMode.CFB
  • 这些参数定义了 AES 加密算法的使用方式

    5. 写入盐
  • 将生成的随机盐写入到输出文件流的开头
  • 盐的存在使得每个文件的加密结果唯一
  • 解密时需要从文件开头读取这个盐值

    6. 创建加密流
  • 使用 CryptoStream 创建一个流
  • 该流连接到输出文件流
  • 使用 RijndaelManaged 创建的加密器进行加密操作

    7. 读取和加密文件
  • 打开原始输入文件的 FileStream
  • 读取文件内容(使用 1MB 的缓冲区进行分块读取)
  • 使用 CryptoStream 将读取的数据进行加密
  • 加密后的数据会直接写入到输出文件流中
  • 分块处理可以避免一次性加载大文件到内存中

    8. 关闭流
  • 关闭 CryptoStream 和文件流
  • 释放相关资源
  • 确保所有数据都已正确写入磁盘

    解密流程 (FileDecrypt 方法)
    1. 读取盐
  • 打开加密后的输入文件(文件名由用户提供)
  • 读取开头的 32 字节作为盐
  • 这个盐与加密时写入的盐必须完全对应

    2. 生成密钥和 IV
  • 使用与加密过程中相同的方法
  • 使用 Rfc2898DeriveBytes 类基于用户提供的密码和读取的盐来派生密钥和 IV
  • 使用相同的 50000 次迭代次数

    3. 配置解密算法
  • 创建 RijndaelManaged 对象
  • 设置与加密时相同的参数:
  • 密钥大小:256 位
  • 块大小:128 位
  • 填充模式:PaddingMode.PKCS7
  • 加密模式:CipherMode.CFB


    4. 创建解密流
  • 使用 CryptoStream 创建一个流
  • 连接到加密后的输入文件流
  • 使用 RijndaelManaged 创建的解密器进行解密操作

    5. 写入解密后的文件
  • 创建一个新的 FileStream,用于写入解密后的数据
  • 文件名由用户提供(通常移除 .Abantes 后缀)
  • 通过 CryptoStream 读取加密后的输入文件流的数据
  • 将解密后的明文写入到新文件中
  • 同样使用分块处理方式

    6. 关闭流
  • 关闭 CryptoStream 和文件流
  • 释放相关资源
  • 确保解密后的文件已完整写入磁盘

    关键技术点说明
    密钥和 IV 的派生
  • 使用 Rfc2898DeriveBytes 类基于密码和盐来生成固定长度的密钥和 IV
  • 这种方式确保了加密和解密时所使用的密钥和 IV 是一致的
  • 高迭代次数(50000)增强了对密码的保护,增加暴力破解的难度

    加密算法设置
  • AES-256:使用 256 位密钥,提供高强度的加密保护
  • CFB 模式:密码反馈模式,支持流式加密,不需要填充到完整的块大小
  • PKCS7 填充:确保数据块符合加密算法的块大小要求

    流式处理设计
  • 使用 CryptoStream 将加密和解密操作集成到文件处理流程中
  • 避免一次性将整个文件内容加载到内存中
  • 支持处理大文件,内存占用稳定
  • 1MB 的缓冲区大小在性能和内存使用之间取得良好平衡

    异常处理机制
  • 使用 try-catch 块捕获可能发生的异常
  • 处理密码错误、文件读写问题等各种异常情况
  • 提供友好的错误信息反馈给用户
  • 确保在异常情况下也能正确关闭流和释放资源

    安全性保障
  • 每个文件使用唯一的随机盐值
  • 强密钥派生函数抵抗暴力破解
  • 行业标准的 AES 加密算法
  • 完整的文件完整性保护

    这些步骤构成了一个安全可靠的文件加密和解密流程,适用于需要保护敏感数据的应用场景,同时提供了良好的性能和用户体验。
    功能特性
  • 1 AES-256-CFB 强加密算法
  • 2 PBKDF2密钥派生(50,000次迭代)
  • 3 大文件支持(分块处理)
  • 4 与C#版本完全兼容
  • 5 安全的随机盐值生成

    安装依赖
    pip install pycryptodome
    使用方法
    加密文件
    # 使用默认密钥加密
    python Abantes.py -encrypt 文件名
    # 使用自定义密钥加密
    python Abantes.py -encrypt 文件名 自定义密钥
    解密文件
    # 使用默认密钥解密
    python Abantes.py -decrypt 文件名.Abantes
    # 使用自定义密钥解密
    python Abantes.py -decrypt 文件名.Abantes 自定义密钥
    参数说明
  • -encrypt : 加密指定文件
  • -decrypt : 解密指定文件
  • key (可选): 加密密钥,默认为 WR8h2GIbf9FGz6VVlSzJ

    文件格式
  • 加密后的文件会自动添加 .Abantes 后缀
  • 解密时会自动移除 .Abantes 后缀
  • 如果输出文件已存在,程序会提示是否覆盖

    技术细节
  • 加密算法: AES-256-CFB
  • 密钥派生: PBKDF2 with 50,000 iterations
  • 盐值长度: 32字节
  • 块大小: 128位
  • 密钥大小: 256位
  • 缓冲区: 1MB分块处理

    示例
    # 加密一个MP3文件
    python Abantes.py -encrypt music.mp3
    # 解密加密后的文件
    python Abantes.py -decrypt music.mp3.Abantes
    # 使用自定义密钥加密重要文档
    python Abantes.py -encrypt document.pdf MyStrongPassword123
    注意事项
    [ol]
  • 请妥善保管加密密钥,丢失密钥将无法恢复文件
  • 加密大文件可能需要较长时间,请耐心等待
  • 建议在加密前备份重要文件
  • 本工具生成的加密文件与C#版本的Abantes工具完全兼容
    [/ol]
    故障排除
    如果遇到解密问题:
    [ol]
  • 确认使用的密钥与加密时一致
  • 检查加密文件是否完整
  • 确保安装了正确版本的依赖库
    [/ol]
    如需技术支持,请提供详细的错误信息和操作步骤。
    Python代码
    import os
    import sys
    import argparse
    from Crypto.Cipher import AES
    from Crypto.Protocol.KDF import PBKDF2
    from Crypto.Random import get_random_bytes
    class Encryption:
        @staticmethod
        def generate_random_salt():
            """生成32字节的随机盐值"""
            return get_random_bytes(32)
        @staticmethod
        def file_encrypt(input_file, password):
            """加密文件"""
            try:
                # 生成盐值
                salt = Encryption.generate_random_salt()
                # 创建输出文件
                output_file = input_file + ".Abantes"
                # 准备AES加密参数
                key_size = 256
                block_size = 128
                # 使用PBKDF2生成密钥和IV
                key_iv = PBKDF2(password.encode('utf-8'), salt, dkLen=(key_size + block_size) // 8, count=50000)
                key = key_iv[:key_size // 8]
                iv = key_iv[key_size // 8:key_size // 8 + block_size // 8]
                # 创建AES加密器
                cipher = AES.new(key, AES.MODE_CFB, iv=iv, segment_size=128)
                with open(input_file, 'rb') as file_in:
                    with open(output_file, 'wb') as file_out:
                        # 写入盐值
                        file_out.write(salt)
                        # 加密并写入数据
                        while True:
                            chunk = file_in.read(1048576)  # 1MB chunks
                            if not chunk:
                                break
                            encrypted_chunk = cipher.encrypt(chunk)
                            file_out.write(encrypted_chunk)
                print(f"文件加密成功: {output_file}")
                return True
            except Exception as e:
                print(f"加密错误: {e}")
                return False
        @staticmethod
        def file_decrypt(input_file, output_file, password):
            """解密文件"""
            try:
                with open(input_file, 'rb') as file_in:
                    # 读取盐值
                    salt = file_in.read(32)
                    # 准备AES解密参数
                    key_size = 256
                    block_size = 128
                    # 使用PBKDF2生成密钥和IV
                    key_iv = PBKDF2(password.encode('utf-8'), salt, dkLen=(key_size + block_size) // 8, count=50000)
                    key = key_iv[:key_size // 8]
                    iv = key_iv[key_size // 8:key_size // 8 + block_size // 8]
                    # 创建AES解密器
                    cipher = AES.new(key, AES.MODE_CFB, iv=iv, segment_size=128)
                    with open(output_file, 'wb') as file_out:
                        # 解密并写入数据
                        while True:
                            chunk = file_in.read(1048576)  # 1MB chunks
                            if not chunk:
                                break
                            decrypted_chunk = cipher.decrypt(chunk)
                            file_out.write(decrypted_chunk)
                print(f"文件解密成功: {output_file}")
                #Debug: 验证解密后的文件
                #if output_file.lower().endswith('.mp3'):
                #    with open(output_file, 'rb') as f:
                #        header = f.read(3)
                #        if header == b'ID3':
                #            print("MP3文件头验证成功")
                #        else:
                #            print(f"警告: MP3文件头异常,前3字节: {header.hex()}")
                #
                #return True
            except Exception as e:
                print(f"解密错误: {e}")
                return False
    def main():
        parser = argparse.ArgumentParser(description='文件加密解密工具')
        parser.add_argument('-encrypt', metavar='文件路径', help='加密文件')
        parser.add_argument('-decrypt', metavar='文件路径', help='解密文件')
        parser.add_argument('key', nargs='?', default='WR8h2GIbf9FGz6VVlSzJ', help='加密密钥(可选,默认为WR8h2GIbf9FGz6VVlSzJ)')
        args = parser.parse_args()
        # 安装依赖检查
        try:
            from Crypto.Cipher import AES
        except ImportError:
            print("请安装pycryptodome库: pip install pycryptodome")
            return
        if args.encrypt:
            if not os.path.exists(args.encrypt):
                print(f"文件不存在: {args.encrypt}")
                return
            Encryption.file_encrypt(args.encrypt, args.key)
        elif args.decrypt:
            if not os.path.exists(args.decrypt):
                print(f"文件不存在: {args.decrypt}")
                return
            # 生成解密后的文件名
            if args.decrypt.endswith('.Abantes'):
                output_file = args.decrypt[:-8]  # 移除.Abantes后缀
            else:
                output_file = args.decrypt + '.decrypted'
            # 检查输出文件是否已存在
            if os.path.exists(output_file):
                response = input(f"文件 {output_file} 已存在,是否覆盖?(y/n): ")
                if response.lower() != 'y':
                    print("操作取消")
                    return
            Encryption.file_decrypt(args.decrypt, output_file, args.key)
        else:
            print("请指定 -encrypt 或 -decrypt 参数")
            parser.print_help()
    if __name__ == "__main__":
        main()

    文件, 密钥

  • 您需要登录后才可以回帖 登录 | 立即注册

    返回顶部