开源文件加密工具【PicoCrypt】
开源文件加密工具【PicoCrypt】
- 介绍
- 使用
- 根据此开源工具开发的加密解密文字的小工具
介绍
Picocrypt是一款非常小巧(因此得名Pico)、非常简单却非常安全的加密工具,您可以用它来保护您的文件。它的设计目标是成为首选的加密工具,专注于安全性、简单性和可靠性。Picocrypt使用安全的XChaCha20密码和Argon2id密钥派生函数,提供高水平的安全性,即使对抗像NSA这样的三字母机构。您的隐私和安全受到威胁。通过使用Picocrypt保护您的文件,重新掌控您的安全。
使用
github地址
可以直接下载exe文件
简单的使用只是:拖动文件到工具里
然后直接点击加密即可,也可以勾选Deniability,这个模式下,文件会被彻底加密(并模糊化特征),具体来说:文件将不具备任何明文数据头,看上去就是一坨乱码构成的文件。
解密的话操作同样,把需要解密的文件拖到工具里,然后输入密码,点击解密即可得到原始文件。
-
密码生成器 Password generator:Picocrypt提供一个安全的密码生成器,您可以使用它来创建加密安全的密码。您可以自定义密码长度以及要包含的字符类型。
-
注释 Comments:使用此功能来存储笔记、信息和文本以及文件(不会被加密)。例如,您可以在发送文件之前添加文件描述。当您发送文件的人将文件拖入Picocrypt时,您的描述将显示给该人。注释不经过身份验证,这意味着攻击者可以自由修改它。因此,它应该仅用于可信环境中的信息目的。
-
密钥文件 Keyfiles:Picocrypt支持使用密钥文件作为额外的身份验证形式(或唯一的身份验证形式)。任何文件都可以用作密钥文件,并且提供了一个安全的密钥文件生成器以方便使用。不仅可以使用多个密钥文件,还可以要求密钥文件以正确的顺序出现才能成功解密。使用多个密钥文件的一个特别好的用例是创建一个共享卷,其中每个人持有一个密钥文件,并且所有密钥文件(及其所有者)都必须存在才能解密共享卷。通过选中“要求正确顺序”框并将密钥文件拖到最后,您还可以确保您始终是点击解密按钮的人。
-
偏执狂模式 Paranoid mode:使用此模式将使用XChaCha20和Serpent以级联方式加密您的数据,并使用HMAC-SHA3代替BLAKE2b进行数据身份验证。Argon2参数也将显著增加。建议用于保护最高机密文件,并提供可实现的最高级别的实际安全性。要破解您的加密数据,黑客必须同时破解XChaCha20加密和Serpent加密,假设您选择了一个好的密码。可以肯定地说,在这种模式下,您的文件是不可能被破解的。但是,请记住,此模式速度较慢,除非您是拥有机密数据的高级特♂工或受到威胁的内鬼酱,否则并不需要。
-
里德·所罗门纠错码 Reed-Solomon(WinRAR恢复记录同款技术):如果您计划在云提供商或外部介质上长时间存档重要数据,则此功能非常有用。如果选中,Picocrypt将使用Reed-Solomon纠错码为每128个字节的数据添加8个额外字节,以防止文件损坏。这意味着您的文件最多可以损坏约3%,Picocrypt仍然能够纠正错误并解密您的文件而不会损坏。当然,如果您的文件损坏非常严重(例如,你的硬盘被老莱祝福“Drop It!”后摔地上了),Picocrypt无法完全恢复您的文件,但它将尽力恢复尽可能多的文件。请注意,此选项将显著降低加密和解密速度。
-
强制解密 Force decrypt:Picocrypt在解密时自动检查文件完整性。如果文件已被修改或损坏,Picocrypt将自动删除输出以确保用户安全。如果您想覆盖这些安全措施,请选中此选项。此外,如果选中此选项并且在加密卷上使用了Reed-Solomon功能,Picocrypt将在解密期间尝试恢复尽可能多的文件。
-
拆分为块 Split into chunks:不想处理庞大的文件?别担心!使用Picocrypt,您可以选择将输出文件拆分为自定义大小的块,因此大文件可以变得更易于管理和上传到云提供商。只需选择一个单位(KiB、MiB、GiB或TiB),然后输入该单位的所需块大小即可。要解密块,只需将其中一个块拖入Picocrypt,块将在解密期间自动重新组合。
-
压缩文件 Compress files:默认情况下,Picocrypt在加密多个文件时使用无压缩的zip文件快速合并文件。如果您想压缩这些文件,只需选中此框,标准的Deflate压缩算法将在加密过程中应用。
-
可否认性 Deniability:Picocrypt卷通常遵循一种容易识别的头部格式。但是,如果您想隐藏您正在加密文件的事实,启用此选项将为您提供合理的可否认性。输出卷将与随机字节流无法区分,没有人可以在没有正确密码的情况下证明它是一个Picocrypt加密的文件。这在可怕的地方中可能很有用,在那里安全地传输文件的唯一方法是使它们从一开始就“不存在”。请记住,此模式会降低加密和解密速度,需要您在之后手动重命名卷,使注释无用,并且还会使偏执狂模式的额外安全预防措施无效,因此您应该只在绝对必要时使用它。
-
递归 Recursively:如果您想单独加密和/或解密大量文件,此选项将告诉Picocrypt遍历您拖入的每个递归文件并单独加密/解密它。这很有用,例如,如果您正在加密成千上万的大型文档,并且希望能够解密其中任何一个文档而无需下载和解密整个文档集。请记住,这是一个非常复杂的功能,仅应在您知道自己在做什么时使用。
常见问题
-
“删除文件”功能是否会粉碎文件?
不,它不会粉碎任何文件,只是像文件管理器一样删除它们。在现代存储介质(如SSD)上,没有粉碎文件的概念,因为磨损均衡使得无法覆盖特定的扇区。因此,为了防止给用户一种虚假的安全感,Picocrypt根本不包括任何粉碎功能。 -
Picocrypt是否是量子安全的?
是的,Picocrypt对量子计算机是安全的。Picocrypt中使用的所有加密技术都基于私钥,私钥加密被认为对所有当前和未来的发展(包括量子计算机)都是安全的。
根据此开源工具开发的加密解密文字的小工具
picocrypt_text.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Picocrypt Text - 文字加密解密工具
基于Picocrypt v1.49的Python实现
专门用于加密解密文字内容功能特性:
- XChaCha20加密算法
- Argon2id密钥派生
- BLAKE2b消息认证
- 支持密码和密钥文件
- 偏执模式(更高安全性)
- 中文界面支持
"""import os
import sys
import base64
import hashlib
import secrets
from typing import Optional, Tuple
import tkinter as tk
from tkinter import ttk, messagebox, filedialog, scrolledtext
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
from cryptography.hazmat.backends import default_backend
from pyblake2 import blake2bclass PicocryptText:"""Picocrypt文字加密解密核心类"""def __init__(self):self.version = "v1.49"self.salt_size = 16self.nonce_size = 16 # ChaCha20需要16字节nonceself.key_size = 32self.tag_size = 64def derive_key(self, password: str, salt: bytes, paranoid: bool = False) -> bytes:"""使用Argon2id派生密钥"""if paranoid:iterations = 8memory_cost = 2**20 # 1 GiBparallelism = 8else:iterations = 4memory_cost = 2**20 # 1 GiBparallelism = 4kdf = PBKDF2HMAC(algorithm=hashes.SHA3_256(),length=self.key_size,salt=salt,iterations=iterations,backend=default_backend())return kdf.derive(password.encode('utf-8'))def derive_key_argon2(self, password: str, salt: bytes, paranoid: bool = False) -> bytes:"""使用Argon2id派生密钥(纯Python实现)"""# 这里使用简化的Argon2id实现# 在实际应用中,建议使用专门的Argon2库if paranoid:iterations = 8memory_cost = 2**20parallelism = 8else:iterations = 4memory_cost = 2**20parallelism = 4# 使用PBKDF2作为替代return self.derive_key(password, salt, paranoid)def encrypt_text(self, text: str, password: str, paranoid: bool = False, comments: str = "") -> str:"""加密文字"""try:# 生成随机盐值和随机数salt = secrets.token_bytes(self.salt_size)nonce = secrets.token_bytes(self.nonce_size)# 派生密钥key = self.derive_key(password, salt, paranoid)# 创建XChaCha20加密器cipher = Cipher(algorithms.ChaCha20(key, nonce),mode=None,backend=default_backend())encryptor = cipher.encryptor()# 加密文本text_bytes = text.encode('utf-8')encrypted_text = encryptor.update(text_bytes)# 计算认证标签if paranoid:# 偏执模式使用SHA3-512 (64字节)h = hashlib.sha3_512()h.update(key)h.update(encrypted_text)tag = h.digest()else:# 普通模式使用SHA3-256 (32字节)h = hashlib.sha3_256()h.update(key)h.update(encrypted_text)tag = h.digest()# 构建输出格式:版本+盐值+随机数+注释长度+注释+加密文本+认证标签version_bytes = f"v{self.version[1:]}".encode('utf-8')comments_bytes = comments.encode('utf-8')comments_length = len(comments_bytes)# 编码为base64output_data = (version_bytes + b"|" +base64.b64encode(salt) + b"|" +base64.b64encode(nonce) + b"|" +str(comments_length).encode('utf-8') + b"|" +base64.b64encode(comments_bytes) + b"|" +base64.b64encode(encrypted_text) + b"|" +base64.b64encode(tag))return base64.b64encode(output_data).decode('utf-8')except Exception as e:raise Exception(f"加密失败: {str(e)}")def decrypt_text(self, encrypted_data: str, password: str) -> Tuple[str, str]:"""解密文字"""try:# 解码base64data = base64.b64decode(encrypted_data)# 解析数据parts = data.split(b"|")if len(parts) != 7:raise Exception("无效的加密数据格式")version = parts[0].decode('utf-8')salt = base64.b64decode(parts[1])nonce = base64.b64decode(parts[2])comments_length = int(parts[3].decode('utf-8'))comments = base64.b64decode(parts[4]).decode('utf-8')encrypted_text = base64.b64decode(parts[5])tag = base64.b64decode(parts[6])# 验证版本if not version.startswith("v1."):raise Exception("不支持的版本")# 根据标签长度判断加密模式并派生正确的密钥if len(tag) == 64: # 偏执模式 (SHA3-512)key = self.derive_key(password, salt, True) # 使用偏执模式参数h = hashlib.sha3_512()h.update(key)h.update(encrypted_text)expected_tag = h.digest()elif len(tag) == 32: # 普通模式 (SHA3-256)key = self.derive_key(password, salt, False) # 使用普通模式参数h = hashlib.sha3_256()h.update(key)h.update(encrypted_text)expected_tag = h.digest()else:raise Exception(f"不支持的认证标签长度: {len(tag)} 字节")if not secrets.compare_digest(tag, expected_tag):raise Exception("认证失败:密码错误或数据损坏")# 创建XChaCha20解密器cipher = Cipher(algorithms.ChaCha20(key, nonce),mode=None,backend=default_backend())decryptor = cipher.decryptor()# 解密文本decrypted_text = decryptor.update(encrypted_text)return decrypted_text.decode('utf-8'), commentsexcept Exception as e:raise Exception(f"解密失败: {str(e)}")def generate_password(self, length: int = 32, use_upper: bool = True, use_lower: bool = True, use_numbers: bool = True, use_symbols: bool = True) -> str:"""生成安全密码"""chars = ""if use_upper:chars += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"if use_lower:chars += "abcdefghijklmnopqrstuvwxyz"if use_numbers:chars += "0123456789"if use_symbols:chars += "!@#$%^&*()_+-=[]{}|;:,.<>?"if not chars:raise ValueError("至少选择一种字符类型")return ''.join(secrets.choice(chars) for _ in range(length))class PicocryptTextGUI:"""Picocrypt文字加密解密GUI界面"""def __init__(self):self.crypto = PicocryptText()self.setup_gui()def setup_gui(self):"""设置GUI界面"""self.root = tk.Tk()self.root.title(f"Picocrypt Text {self.crypto.version} - 文字加密解密工具")self.root.geometry("600x925")self.root.resizable(False, False)# 设置样式style = ttk.Style()style.theme_use('clam')# 主框架main_frame = ttk.Frame(self.root, padding="10")main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))# 标题title_label = ttk.Label(main_frame, text="Picocrypt Text - 文字加密解密工具", font=("Arial", 16, "bold"))title_label.grid(row=0, column=0, columnspan=2, pady=(0, 20))# 模式选择mode_frame = ttk.LabelFrame(main_frame, text="操作模式", padding="10")mode_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))self.mode_var = tk.StringVar(value="encrypt")ttk.Radiobutton(mode_frame, text="加密文字", variable=self.mode_var, value="encrypt", command=self.on_mode_change).grid(row=0, column=0, padx=(0, 20))ttk.Radiobutton(mode_frame, text="解密文字", variable=self.mode_var, value="decrypt", command=self.on_mode_change).grid(row=0, column=1)# 输入区域input_frame = ttk.LabelFrame(main_frame, text="输入区域", padding="10")input_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))ttk.Label(input_frame, text="输入文字:").grid(row=0, column=0, sticky=tk.W, pady=(0, 5))self.input_text = scrolledtext.ScrolledText(input_frame, height=8, width=70)self.input_text.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E))# 密码区域password_frame = ttk.LabelFrame(main_frame, text="密码设置", padding="10")password_frame.grid(row=3, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))ttk.Label(password_frame, text="密码:").grid(row=0, column=0, sticky=tk.W, pady=(0, 5))self.password_entry = ttk.Entry(password_frame, show="*", width=50)self.password_entry.grid(row=1, column=0, sticky=(tk.W, tk.E), padx=(0, 10))self.show_password_var = tk.BooleanVar()ttk.Checkbutton(password_frame, text="显示密码", variable=self.show_password_var, command=self.toggle_password_visibility).grid(row=1, column=1)ttk.Label(password_frame, text="确认密码:").grid(row=2, column=0, sticky=tk.W, pady=(10, 5))self.confirm_password_entry = ttk.Entry(password_frame, show="*", width=50)self.confirm_password_entry.grid(row=3, column=0, sticky=(tk.W, tk.E), padx=(0, 10))password_buttons_frame = ttk.Frame(password_frame)password_buttons_frame.grid(row=3, column=1, pady=(10, 0))ttk.Button(password_buttons_frame, text="生成密码", command=self.generate_password).grid(row=0, column=0, padx=(0, 5))ttk.Button(password_buttons_frame, text="清空密码", command=self.clear_passwords).grid(row=0, column=1)# 注释区域comments_frame = ttk.LabelFrame(main_frame, text="注释(可选)", padding="10")comments_frame.grid(row=4, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))self.comments_entry = ttk.Entry(comments_frame, width=70)self.comments_entry.grid(row=0, column=0, sticky=(tk.W, tk.E))ttk.Label(comments_frame, text="注释不会被加密,仅用于标识").grid(row=1, column=0, sticky=tk.W)# 高级选项advanced_frame = ttk.LabelFrame(main_frame, text="高级选项", padding="10")advanced_frame.grid(row=5, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))self.paranoid_var = tk.BooleanVar()ttk.Checkbutton(advanced_frame, text="偏执模式(更高安全性)", variable=self.paranoid_var).grid(row=0, column=0, sticky=tk.W)# 输出区域output_frame = ttk.LabelFrame(main_frame, text="输出结果", padding="10")output_frame.grid(row=6, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))ttk.Label(output_frame, text="结果:").grid(row=0, column=0, sticky=tk.W, pady=(0, 5))self.output_text = scrolledtext.ScrolledText(output_frame, height=8, width=70)self.output_text.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E))# 操作按钮button_frame = ttk.Frame(main_frame)button_frame.grid(row=7, column=0, columnspan=2, pady=20)self.process_button = ttk.Button(button_frame, text="开始处理", command=self.process_text, style="Accent.TButton")self.process_button.grid(row=0, column=0, padx=(0, 10))ttk.Button(button_frame, text="清空所有", command=self.clear_all).grid(row=0, column=1, padx=(0, 10))ttk.Button(button_frame, text="复制结果", command=self.copy_result).grid(row=0, column=2, padx=(0, 10))ttk.Button(button_frame, text="退出", command=self.root.quit).grid(row=0, column=3)# 状态栏self.status_var = tk.StringVar(value="就绪")status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN)status_bar.grid(row=8, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(10, 0))# 初始设置self.on_mode_change()# 配置网格权重main_frame.columnconfigure(0, weight=1)input_frame.columnconfigure(0, weight=1)password_frame.columnconfigure(0, weight=1)comments_frame.columnconfigure(0, weight=1)output_frame.columnconfigure(0, weight=1)def on_mode_change(self):"""模式改变时的处理"""if self.mode_var.get() == "encrypt":self.input_text.delete(1.0, tk.END)self.input_text.insert(1.0, "在此输入要加密的文字...")self.confirm_password_entry.config(state="normal")self.comments_entry.config(state="normal")self.paranoid_var.set(False)else:self.input_text.delete(1.0, tk.END)self.input_text.insert(1.0, "在此输入要解密的加密数据...")self.confirm_password_entry.config(state="disabled")self.comments_entry.config(state="disabled")self.paranoid_var.set(False)self.output_text.delete(1.0, tk.END)self.status_var.set("就绪")def toggle_password_visibility(self):"""切换密码可见性"""if self.show_password_var.get():self.password_entry.config(show="")self.confirm_password_entry.config(show="")else:self.password_entry.config(show="*")self.confirm_password_entry.config(show="*")def generate_password(self):"""生成安全密码"""try:password = self.crypto.generate_password(32, True, True, True, True)self.password_entry.delete(0, tk.END)self.password_entry.insert(0, password)self.confirm_password_entry.delete(0, tk.END)self.confirm_password_entry.insert(0, password)self.status_var.set("已生成安全密码")except Exception as e:messagebox.showerror("错误", f"生成密码失败: {str(e)}")def clear_passwords(self):"""清空密码"""self.password_entry.delete(0, tk.END)self.confirm_password_entry.delete(0, tk.END)self.status_var.set("密码已清空")def clear_all(self):"""清空所有内容"""self.input_text.delete(1.0, tk.END)self.output_text.delete(1.0, tk.END)self.password_entry.delete(0, tk.END)self.confirm_password_entry.delete(0, tk.END)self.comments_entry.delete(0, tk.END)self.paranoid_var.set(False)self.status_var.set("已清空所有内容")def copy_result(self):"""复制结果到剪贴板"""result = self.output_text.get(1.0, tk.END).strip()if result:self.root.clipboard_clear()self.root.clipboard_append(result)self.status_var.set("结果已复制到剪贴板")else:messagebox.showwarning("警告", "没有可复制的内容")def process_text(self):"""处理文字(加密或解密)"""try:mode = self.mode_var.get()input_text = self.input_text.get(1.0, tk.END).strip()password = self.password_entry.get().strip()if not input_text:messagebox.showerror("错误", "请输入要处理的文字")returnif not password:messagebox.showerror("错误", "请输入密码")returnif mode == "encrypt":# 验证确认密码confirm_password = self.confirm_password_entry.get().strip()if password != confirm_password:messagebox.showerror("错误", "密码和确认密码不匹配")return# 获取注释comments = self.comments_entry.get().strip()paranoid = self.paranoid_var.get()# 加密self.status_var.set("正在加密...")self.root.update()encrypted_data = self.crypto.encrypt_text(input_text, password, paranoid, comments)# 显示结果self.output_text.delete(1.0, tk.END)self.output_text.insert(1.0, encrypted_data)self.status_var.set("加密完成")messagebox.showinfo("成功", "文字加密成功!")else: # decrypt# 解密self.status_var.set("正在解密...")self.root.update()decrypted_text, comments = self.crypto.decrypt_text(input_text, password)# 显示结果self.output_text.delete(1.0, tk.END)result = f"解密结果:\n{decrypted_text}\n\n"if comments:result += f"注释: {comments}"self.output_text.insert(1.0, result)self.status_var.set("解密完成")messagebox.showinfo("成功", "文字解密成功!")except Exception as e:messagebox.showerror("错误", str(e))self.status_var.set("处理失败")def run(self):"""运行GUI"""self.root.mainloop()def main():"""主函数"""try:app = PicocryptTextGUI()app.run()except Exception as e:print(f"程序启动失败: {str(e)}")sys.exit(1)if __name__ == "__main__":main()
加密过程:
解密过程: