CryptBox






4.45/5 (9投票s)
CryptBox 帮助使用基于密码的加密 (PBE) 来加密和解密文件
引言
使用基于密码的加密 (PBE) 进行文件加密和解密。
基于密码的加密 (PBE) 从密码派生加密密钥。为了使攻击者从密码到密钥的转换过程非常耗时,大多数 PBE 实现(如下所示)会混合一个称为“盐”的随机数来创建密钥。
j2se1.4 支持 PBE 算法(SunJCE 和其他提供商的后续版本支持其他算法)。
有时,我们希望有效地选择加密密钥。我们可能想根据自己输入的密码短语来加密文件,以便我们能够记住它。在这种情况下,我们希望唯一保密的信息就是密码短语。
从用户生成的密码短语生成密钥的技术通常称为基于密码的加密 (PBE)。正如您可能想象的那样,这充满了挑战。特别是:
- 我们的要求和安全要求通常会发生冲突:我们需要一个易于记忆的密码短语,或者至少是由可识别字符组成且足够短以便写下来的密码短语;但根据当今的标准进行安全加密,我们需要至少 128 位强随机位(理想情况下更多);
- 基于密码的加密通常用于攻击者可以反复尝试猜测密码而不被发现,并且超出合法发送者/接收者控制的应用程序(如果密码用于登录服务器,则可以检测到如此多的无效尝试,在最坏的情况下可以关闭服务器以阻止进一步的尝试;但如果窃听者复制了我们使用的加密 ZIP 文件,我们将永远不知道他们拥有一个拥有 10 万个处理器的僵尸网络正在尝试暴力破解密码,并且他们可以长时间地这样做)。
PBE (基于密码的加密) = 哈希 + 对称加密
将一个 64 位随机数(盐)添加到密码中,并使用消息摘要算法(例如 MD5)进行哈希。
密码哈希的次数由迭代计数决定。添加随机数并多次哈希会扩大密钥空间。
在设置密码或哈希迭代次数以加密文件时要小心。
如果丢失了密码或哈希迭代次数,则可能无法找回任何内容。
这将导致加密文件解密失败,因此加密文件可能永远无法使用。
背景
我想保护我的文件不被他人打开。那时我才想到加密我的文件并将其存储在硬盘上。即使有人想打开它,他/她也必须先解密文件,然后才能打开。由于我使用密码进行文件加密,并且还添加了密码哈希迭代,因此任何人都不容易解密。
我的工作思路来自 http://cs.saddleback.edu/rwatkins/CS4B/Crypto/FileEncryptor.html,所有的功劳都归功于它在加密和解密方面的贡献。
屏幕截图
- 可以通过两种方式将文件添加到 CryptBox 中进行加密或解密。
- 要么单击“选择”按钮。选择“加密”单选按钮将帮助用户选择要加密的任何文件。选择“解密”单选按钮将帮助用户选择任何扩展名为“enc”的加密文件。
- 通过从 Windows 资源管理器拖放一个或多个文件。CryptBox 将根据所选的单选按钮执行加密或解密。
- 选定的文件将在“文件”列中显示完整路径。
- 状态将设置为“排队中”,当加密或解密开始时,它将根据进程状态而变化。
- “子状态”列将显示正在进行的中间工作。
- 用户选择文件后,单击“开始”按钮。将弹出一个窗口。
- CryptBox 使用 PBEWithMD5AndDES 作为消息摘要算法。
- 输入密码哈希迭代次数,默认为 100。密码哈希的次数由迭代次数决定。
- 输入用于保护文件的密码,该密码也将用于加密。
- “重复密码”应与“密码”完全相同,以检查您想要的密码是否一致。
- 解密加密文件时,用户需要与加密该文件时使用的消息摘要算法、哈希迭代次数和密码相同。

- 在加密或解密文件时,用户可以通过“详细信息”按钮查看操作的详细状态。它还会显示完成百分比的状态进度条。
- 如果在解密文件时用户提供了错误的消息摘要算法、密码哈希迭代次数或密码(与加密文件不匹配),则 CryptBox 会要求重新输入这些详细信息,以便能够成功解密。
- 在加密时,为文件提供的任何消息摘要算法、密码哈希迭代次数或密码,在解密时都必须与加密时相同。

使用代码
加密方法
public int EncryptFile(String strFileName, String strMessageDigestAlgorithm, int intPasswordHashIteration, String strPassword, String strExtension) throws Exception
{
FileInputStream objFileInputStream;
FileOutputStream objFileOutputStream;
try
{
// File to encrypt. It does not have to be a text file!
// Password must be at least 8 characters (bytes) long
objFileInputStream = new FileInputStream(strFileName);
objFileOutputStream = new FileOutputStream(strFileName + "." + strExtension);
// Use PBEKeySpec to create a key based on a password.
// The password is passed as a character array
PBEKeySpec objPBEKeySpec = new PBEKeySpec(strPassword.toCharArray());
//SecretKeyFactory objSecretKeyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKeyFactory objSecretKeyFactory = SecretKeyFactory.getInstance(strMessageDigestAlgorithm);
SecretKey objSecretKey = objSecretKeyFactory.generateSecret(objPBEKeySpec);
// PBE = hashing + symmetric encryption. A 64 bit random
// number (the salt) is added to the password and hashed
// using a Message Digest Algorithm (MD5 in this example.).
// The number of times the password is hashed is determined
// by the interation count. Adding a random number and
// hashing multiple times enlarges the key space.
byte[] byteSalt = new byte[8];
Random objRandom = new Random();
objRandom.nextBytes(byteSalt);
//int intIteration = 100;
int intIteration = intPasswordHashIteration;
//Create the parameter spec for this salt and interation count
PBEParameterSpec objPBEParameterSpec = new PBEParameterSpec(byteSalt, intIteration);
// Create the cipher and initialize it for encryption.
Cipher objCipher = Cipher.getInstance("PBEWithMD5AndDES");
objCipher.init(Cipher.ENCRYPT_MODE, objSecretKey, objPBEParameterSpec);
// Need to write the salt to the (encrypted) file. The
// salt is needed when reconstructing the key for decryption.
objFileOutputStream.write(byteSalt);
// Read the file and encrypt its bytes.
byte[] byteInput = new byte[64];
int intRead;
while ((intRead = objFileInputStream.read(byteInput)) != -1)
{
byte[] byteOutput = objCipher.update(byteInput, 0, intRead);
if (byteOutput != null)
{
objFileOutputStream.write(byteOutput);
}
}
byte[] byteOutput = objCipher.doFinal();
if (byteOutput != null)
{
objFileOutputStream.write(byteOutput);
}
objFileInputStream.close();
objFileOutputStream.flush();
objFileOutputStream.close();
return 0;
}
catch(Exception ex)
{
JOptionPane.showMessageDialog(null, ex, "CryptBox Error", JOptionPane.ERROR_MESSAGE);
return 1;
}
}
解密方法
public int DecryptFile(String strFileName, String strMessageDigestAlgorithm, int intPasswordHashIteration, String strPassword, String strExtension) throws Exception
{
FileInputStream objFileInputStream;
FileOutputStream objFileOutputStream;
try
{
// File to decrypt.
// Password must be at least 8 characters (bytes) long
objFileInputStream = new FileInputStream(strFileName);
objFileOutputStream = new FileOutputStream(strFileName.substring(0, strFileName.length() - strExtension.length() - 1));
PBEKeySpec objPBEKeySpec = new PBEKeySpec(strPassword.toCharArray());
//SecretKeyFactory objSecretKeyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKeyFactory objSecretKeyFactory = SecretKeyFactory.getInstance(strMessageDigestAlgorithm);
SecretKey objSecretKey = objSecretKeyFactory.generateSecret(objPBEKeySpec);
// Read in the previouly stored salt and set the iteration count.
byte[] byteSalt = new byte[8];
objFileInputStream.read(byteSalt);
//int intIteration = 100;
int intIteration = intPasswordHashIteration;
PBEParameterSpec objPBEParameterSpec = new PBEParameterSpec(byteSalt, intIteration);
// Create the cipher and initialize it for decryption.
Cipher objCipher = Cipher.getInstance("PBEWithMD5AndDES");
objCipher.init(Cipher.DECRYPT_MODE, objSecretKey, objPBEParameterSpec);
byte[] byteInput = new byte[64];
int intRead;
while ((intRead = objFileInputStream.read(byteInput)) != -1)
{
byte[] byteOutput = objCipher.update(byteInput, 0, intRead);
if (byteOutput != null)
{
objFileOutputStream.write(byteOutput);
}
}
byte[] byteOutput = objCipher.doFinal();
if (byteOutput != null)
{
objFileOutputStream.write(byteOutput);
}
objFileInputStream.close();
objFileOutputStream.flush();
objFileOutputStream.close();
return 0;
}
catch(Exception ex)
{
JOptionPane.showMessageDialog(null, ex, "CryptBox Error", JOptionPane.ERROR_MESSAGE);
return 1;
}
}
- 这 2 个(即加密和解密)是 CryptBox 中的主要方法。还有一种方法可以在加密或解密后删除前一个文件。
- 加密或解密所需的时间取决于文件大小。例如,如果一个文件是 1 GB,加密或解密大约需要 8 分钟。
- CryptBox 使用 SwingWorker 实现,这是一个抽象类。SwingWorker 专为需要在后台线程中运行长时间运行的任务,并在完成时或处理过程中提供 UI 更新的情况而设计。
兴趣点
如果用户提供了错误的信息来解密加密文件,它将永远不会这样做,除非提供了正确的信息。
历史
这是第一个版本,提交于 2013 年 2 月 18 日。