Java PBE:在尝试解密之前验证密码

Java PBE:在尝试解密之前验证密码,java,passwords,encryption,Java,Passwords,Encryption,我正在用Java制作一个应用程序,我希望允许用户使用自己选择的密码加密文件(或文件夹-我会压缩目录)。我目前有以下方法: static Cipher createCipher(int模式,字符串密码)引发异常{ PBEKeySpec keySpec=新的PBEKeySpec(password.toCharArray()); SecretKeyFactory keyFactory=SecretKeyFactory.getInstance(“PBEWithMD5AndDES”); SecretKey

我正在用Java制作一个应用程序,我希望允许用户使用自己选择的密码加密文件(或文件夹-我会压缩目录)。我目前有以下方法:

static Cipher createCipher(int模式,字符串密码)引发异常{
PBEKeySpec keySpec=新的PBEKeySpec(password.toCharArray());
SecretKeyFactory keyFactory=SecretKeyFactory.getInstance(“PBEWithMD5AndDES”);
SecretKey key=keyFactory.generateSecret(keySpec);
MessageDigest md=MessageDigest.getInstance(“MD5”);
md.update(“input.getBytes());
字节[]摘要=md.digest();
字节[]salt=新字节[8];
对于(int i=0;i<8;++i)
盐[我]=消化[我];
PBEParameterSpec paramSpec=新PBEParameterSpec(salt,20);
Cipher Cipher=Cipher.getInstance(“PBEWithMD5AndDES”);
cipher.init(模式、密钥、参数规范);
返回密码;
}
静态void applyCipher(字符串填充、字符串输出文件、密码)引发异常{
字符串解密=”;
CipherInputStream in=新的CipherInputStream(新文件输入流(infle),cipher);
BufferedOutputStream out=新的BufferedOutputStream(新文件输出流(输出文件));
int BUFFER_SIZE=8;
字节[]缓冲区=新字节[缓冲区大小];
int numRead=0;
做{
numRead=in.read(缓冲区);
System.out.println(缓冲区+”,0,“+numRead);
如果(numRead>0){
out.write(缓冲区,0,numRead);
System.out.println(toHexString(buffer,0,numRead));
}
}而(numRead==8);
in.close();
out.flush();
out.close();
}
私有静态字符[]十六进制表={
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
‘a’、‘b’、‘c’、‘d’、‘e’、‘f’};
公共静态字符串到十六进制字符串(字节[]数据,整数偏移量,整数长度)
{
StringBuffer s=新的StringBuffer(长度*2);
int end=偏移量+长度;
for(int i=偏移;i>>4;
int low_nibble=(数据[i]&0x0f);
s、 追加(十六进制表[高位半字节]);
s、 追加(十六进制表[低半字节]);
}
返回s.toString();
}
然而,为了使程序更加用户友好,我希望能够在生成文件之前检查用户是否输入了正确的密码。我不想“把钥匙留在门垫下”或完全撤销安全保护等-我只想防止在用户输入错误密码时产生错误的文件

任何想法都将不胜感激。如果您还需要更多详细信息,请随时询问


提前感谢

您可以将加密密码与文件一起保存。当用户输入密码时,您对其进行加密并检查文件中是否存在相同的加密密码。如果没有,则不加载文件。

使用pbkdf2和hmacsha1,而不是pbewithmd5和des。后来的用户使用了两种不同的过时原语。前者是当前的标准

你有两个选择

  • 快速但不太安全: 在加密文件的开头输入一个已知的短值,或在同一密码下加密一个完全不同的短文件。解密此文件时,请检查已知值

    显然,这很快就能奏效。它的安全性稍差,因为这意味着试图暴力破解密码的攻击者可以更快地放弃猜测的密码:他们不必查看整个文件,只需检查该值。这并不是什么大问题,因为您的键派生函数应该足够硬,而且它们仍然必须运行它

  • 存储加密文件的散列,并在解密时验证散列。 更安全的是,攻击者必须解密整个文件并读取它,但由于同样的原因,它的速度很慢


  • 我会使用一种模式,比如CCM或EAX。这将在解密文件时检查文件每个块的完整性,如果密钥不正确或文件被篡改,则会失败。Bouncy Castle提供了这些模式。

    如果我要实现类似的功能,我会压缩用户选择的文件,但我也会添加一个非常小的文件(1字节),用于验证密码,然后验证文件的内容。谢谢你的回答,但我不太明白你的意思。你是说我应该将密码以明文形式存储在加密文件旁边,还是使用固定密码/算法将密码存储在加密文件中?请详细说明/解释…当用户向文件提供密码时,您将对其进行加密并将其保存在文件中(该文件以相同方式加密)。当用户加载文件时,提示输入密码,对其进行加密,并将其与文件中的加密密码进行比较。因此,密码存储在文件中,但经过加密。我认为这与密码存储在unix中是一样的。再次感谢,我最终使用了这种方法,但现在我将密码存储在文件的顶部(正如您阅读接受的答案所看到的)。我知道您不应该这样做,但我认为“不太不安全”是指“不太安全”!?此外,在这些想法的基础上,您认为我能够使用以下链接上的方法在文件顶部存储哈希版本的密码以进行验证,然后如果密码正确,继续解密文件的其余部分吗?如果是这样的话,那会有多安全?顺便说一句,谢谢你的回答,特别是对于原语/标准!此外,我刚刚尝试使用HMACSHA1实现PBKDF2,并创建一个密码,与使用PBEWithMD5和DES时相同,但每次这样做时,我都会收到一个
    InvalidKeySpecException:Salt not found
    
    static Cipher createCipher(int mode, String password) throws Exception {
                PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
                SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
                SecretKey key = keyFactory.generateSecret(keySpec);
                MessageDigest md = MessageDigest.getInstance("MD5");
                md.update("input".getBytes());
                byte[] digest = md.digest();
                byte[] salt = new byte[8];
                for (int i = 0; i < 8; ++i)
                  salt[i] = digest[i];
                PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 20);
                Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
                cipher.init(mode, key, paramSpec);
                return cipher;
        }
    
         static void applyCipher(String inFile, String outFile, Cipher cipher) throws Exception {
                String decryption = "";
                CipherInputStream in = new CipherInputStream(new FileInputStream(inFile), cipher);
                BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outFile));
                int BUFFER_SIZE = 8;
                byte[] buffer = new byte[BUFFER_SIZE];
                int numRead = 0;
                do {
                  numRead = in.read(buffer);
                  System.out.println(buffer + ", 0, " + numRead);
                  if (numRead > 0){
                    out.write(buffer, 0, numRead);
                    System.out.println(toHexString(buffer, 0, numRead));
                  }
                 } while (numRead == 8);
                in.close();
                out.flush();
                out.close();
              }
         private static char[] hex_table = {
                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
                'a', 'b', 'c', 'd', 'e', 'f'};
    
         public static String toHexString(byte[] data, int offset, int length)
         {
           StringBuffer s = new StringBuffer(length*2);
           int end = offset+length;
    
           for (int i = offset; i < end; i++)
           {
             int high_nibble = (data[i] & 0xf0) >>> 4;
             int low_nibble = (data[i] & 0x0f);
             s.append(hex_table[high_nibble]);
             s.append(hex_table[low_nibble]);
           }
    
           return s.toString();
         }