简体   繁体   English

Java CipherInputStream在解密时的行为

[英]Java CipherInputStream behavior on decryption

I am using the following code to decrypt files encrypted from android device. 我正在使用以下代码解密从android设备加密的文件。

private void mDecrypt_File(FileInputStream fin, String outFile) throws Exception {
  FileOutputStream fout = new FileOutputStream(outFile);

  byte[] iv = new byte[16];
  byte[] salt = new byte[16];
  byte[] len = new byte[8];
  byte[] FC_TAGBuffer = new byte[8];

  Cipher cipher = Cipher.getInstance(CIPHER_INSTANCE);

  DataInputStream dis = new DataInputStream(fin);

  dis.read(iv, 0, 16);
  dis.read(salt, 0, 16);

  Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(DEFAULT_PASSWORD, salt, F_ITERATIONS);
  SecretKey key = new SecretKeySpec(rfc.getBytes(32), "AES");

  //decryption code
  cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
  CipherInputStream cIn = new CipherInputStream(dis, cipher);

  cIn.read(len, 0, 8);
  long lSize = getLong(len, 0);

  cIn.read(FC_TAGBuffer, 0, 8);

  byte[] tempFC_TAGBuffer = changeByteArray(FC_TAGBuffer, 0);//new byte[8];                           

  BigInteger ulong = new BigInteger(1, tempFC_TAGBuffer);

  if (!ulong.equals(FC_TAG)) {
    Exception ex = new Exception("Tags are not equal");
    throw ex;
  }

  byte[] bytes = new byte[BUFFER_SIZE];
  //determine number of reads to process on the file                          
  long numReads = lSize / BUFFER_SIZE;
  // determine what is left of the file, after numReads                   
  long slack = (long) lSize % BUFFER_SIZE;

  int read = -1;
  int value = 0;
  int outValue = 0;

  MessageDigest md = MessageDigest.getInstance("SHA-256");
  md.reset();
  // read the buffer_sized chunks         
  for (int i = 0; i < numReads; ++i) {
    read = cIn.read(bytes, 0, bytes.length);
    fout.write(bytes, 0, read);
    md.update(bytes, 0, read);
    value += read;
    outValue += read;
  }
  // now read the slack                   
  if (slack > 0) {
    read = cIn.read(bytes, 0, (int) slack);
    fout.write(bytes, 0, read);
    md.update(bytes, 0, read);
    value += read;
    outValue += read;
  }
  fout.flush();
  fout.close();
  byte[] curHash = md.digest();

  byte[] oldHash = new byte[md.getDigestLength()];
  read = cIn.read(oldHash, 0, oldHash.length);
  if (oldHash.length != read || (!CheckByteArrays(oldHash, curHash))) {
    Exception ex = new Exception("File Corrupted!");
    throw ex;
  }
  if (outValue != lSize) {
    Exception ex = new Exception("File Sizes don't match!");
    throw ex;
  }
}

This code is working fine on android but behaving strange on Java desktop application. 这段代码在android上运行良好,但在Java桌面应用程序上却表现不佳。 What I have observed is, on reading old hash from CipherInputStream cIn returns correct hash value only if the size of data to be decrypted is multiples of 32. For example, if I encrypt a text file which have a text of length 32 chars(or 64/128/...), then the following code 我观察到的是,从CipherInputStream读取旧哈希时,仅当要解密的数据大小为32的倍数时,cIn才返回正确的哈希值。例如,如果我加密长度为32个字符的文本文件(或64/128 / ...),然后输入以下代码

byte[] oldHash = new byte[md.getDigestLength()];
read = cIn.read(oldHash, 0, oldHash.length);
if (oldHash.length != read || (!CheckByteArrays(oldHash, curHash))) {
    Exception ex = new Exception("File Corrupted!");
    throw ex;
}

calculates oldHash correctly, but if I change the text of any other length(not multiple of 32) then the oldHash's last few values becomes zeros. 可以正确计算oldHash,但是如果我更改任何其他长度的文本(不是32的倍数),则oldHash的最后几个值将变为零。

My Observations : 我的观察:

  1. Text Size 6 char - Trailing zeros in oldHash - 6 文字大小6个字符-oldHash中的尾随零-6
  2. Text Size 13 char - Trailing zeros in oldHash - 13 文字大小13个字符-oldHash中的尾随零-13
  3. Text Size 20 char - Trailing zeros in oldHash - 4 文字大小20个字符-oldHash中的尾随零-4
  4. Text Size 32 char - Trailing zeros in oldHash - 0 // Correct Result 文字大小32个字符-oldHash中的尾随零-0 //正确结果
  5. Text Size 31 char - Trailing zeros in oldHash - 1 文字大小31个字符-oldHash中的尾随零-1
  6. Text Size 64 char - Trailing zeros in oldHash - 0 // Correct Result 文字大小64个字符-oldHash中的尾随零-0 //正确结果

Please help me understanding this behavior. 请帮助我了解这种行为。

Agree with DuncanJones, your loop is a mess. 同意DuncanJones,您的循环很烂。 Although you properly check the return value of the read() method your loop iterations assume that every read() will return BUFFER_SIZE bytes or 'slack' bytes for the last read. 尽管您正确检查了read()方法的返回值,但是循环迭代假定每个read()将为最后一次读取返回BUFFER_SIZE字节或“松弛”字节。

You code would be hugely better if you make proper use of DataInputStream . 如果正确使用DataInputStream则代码会更好。 For example, you wrap the FileInputStream fin in a DataInputStream but then use the wrong methods in these two lines: 例如,将FileInputStream fin包装在DataInputStream ,然后在这两行中使用错误的方法:

dis.read(iv, 0, 16);
dis.read(salt, 0, 16);

instead, you should use the readFully method, as in: 相反,您应该使用readFully方法,如下所示:

dis.readFully(iv);
dis.readFully(salt);

Similarly, you would benefit from wrapping your CipherInputStream cIn with another DataInputStream, something like: 同样,您可以通过将CipherInputStream cIn与另一个DataInputStream包装在一起而受益,例如:

CipherInputStream cIn = new CipherInputStream(dis, cipher);
DataInputStream dcIn = new DataInputStream(cIn);

DataInputStream already has a getLong method, so you could just replace these lines: DataInputStream已经具有getLong方法,因此您只需替换以下行即可:

cIn.read(len, 0, 8);
long lSize = getLong(len, 0);

cIn.read(FC_TAGBuffer, 0, 8);

with

long lSize = dcIn.getLong()
dcIn.readFully(FC_TAGBuffer);

and you get to throw out your homegrown getLong method. 然后您将抛出自己的getLong方法。 Now you can go on and read the next lSize bytes in exactly BUFFER_SIZE chunks using dcIn.readFully(bytes) and make your code, cleaner, shorter, easier to read, and correct. 现在,您可以继续使用dcIn.readFully(bytes)准确读取BUFFER_SIZE块中的下一个lSize字节,并使您的代码更dcIn.readFully(bytes) ,更短,更易于阅读和更正。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM