2

次のコードを使用して、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;
  }
}

このコードは Android では問題なく動作しますが、Java デスクトップ アプリケーションでは動作がおかしくなります。私が観察したことは、CipherInputStream 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;
}

oldHash を正しく計算しますが、他の長さ (32 の倍数ではない) のテキストを変更すると、oldHash の最後のいくつかの値がゼロになります。

私の観察:

  1. テキスト サイズ 6 文字 - oldHash の末尾のゼロ - 6
  2. テキスト サイズ 13 文字 - oldHash の末尾のゼロ - 13
  3. テキスト サイズ 20 文字 - oldHash の末尾のゼロ - 4
  4. テキスト サイズ 32 文字 - oldHash の末尾のゼロ - 0 // 正しい結果
  5. テキスト サイズ 31 文字 - oldHash の末尾のゼロ - 1
  6. テキスト サイズ 64 文字 - oldHash の末尾のゼロ - 0 // 正しい結果

この動作を理解するのを手伝ってください。

4

1 に答える 1

4

DuncanJonesに同意します。あなたのループは混乱しています。read() メソッドの戻り値を適切にチェックしても、ループの反復では、すべての read() が最後の読み取りに対して BUFFER_SIZE バイトまたは「slack」バイトを返すと想定しています。

を適切に使用すると、コードが大幅に改善されますDataInputStream。たとえば、FileInputStream finを a でDataInputStream囲みますが、次の 2 行で間違ったメソッドを使用します。

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

代わりに、次のようにreadFullyメソッドを使用する必要があります。

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

CipherInputStream cIn同様に、次のような別の DataInputStream でラップするとメリットがあります。

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

DataInputStream には既にgetLongメソッドがあるため、次の行を置き換えるだけで済みます。

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

cIn.read(FC_TAGBuffer, 0, 8);

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

独自の方法を捨てなければなりませんgetLonglSizeこれで、正確に BUFFER_SIZE チャンクで次のバイトを読み取り、dcIn.readFully(bytes)コードをよりクリーンで短く、読みやすく、修正することができます。

于 2013-04-27T15:42:20.670 に答える