2013-10-30 51 views
1

我目前工作的一個解密算法來解密一個PDF我從服務器接收的電子書閱讀器應用程序。 在iOS上運行的等效代碼非常完美,我現在試圖讓代碼在Android上運行。安卓解密:最後塊(16個字節)中缺少結果文件

只是一些細節,解密是AES和ECB模式下運行。加密密鑰是一個十六進制字符串數組,我將其轉換爲一個字節數組(通過將每兩個字符轉換爲一個字節,例如:「FF」變爲255等)。

我正在經歷非常有趣。我比較iOS和Android解密後的結果文件,我注意到Android解密文件比iOS解密文件短16個字節,特別是最後。所有其他字節是相同的(我會在下面的一些例子)。

這種差異是造成我的電子書閱讀器拒絕打開PDF文件,而它成功打開iOS圖書。

這裏是我的解密代碼:

private void performDecryption(DocumentModel document) 
    {        
     byte[] keyBytes = generateByteArray(document.getEncryptionKey()); 


     SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "AES"); 

     File encryptedDocument = new File(getBookFolderDocumentName(document, document.getFileSuffix())); 
     File decryptedDocument = new File(BOOK_FOLDER + document.getGeneratedAssetName() + "_decrypted" + "." + document.getFileSuffix()); 

     decryptedDocument.mkdirs(); 
     if (decryptedDocument.exists()) 
      decryptedDocument.delete(); 


     Cipher cipher = null;  

     try 
     { 

      cipher = Cipher.getInstance("AES/ECB/ZeroBytePadding"); 
      cipher.init(Cipher.DECRYPT_MODE, skeySpec);   
     } 
     catch (NoSuchAlgorithmException noSuchAlgorithmEx) 
     { 
      Log.e("Decryption", "NoSuchAlgorithmException: " + noSuchAlgorithmEx.getMessage()); 
     } 
     catch (NoSuchPaddingException noSuchPaddingEx) 
     { 
      Log.e("Decryption", "NoSuchPaddingException: " + noSuchPaddingEx.getMessage()); 
     } 
     catch (InvalidKeyException invalidKeyEx) 
     { 
      Log.e("Decryption", "InvalidKeyException: " + invalidKeyEx.getMessage()); 
     } 

     FileInputStream encryptedFileStream = null; 
     FileOutputStream decryptedFileStream = null; 


     try 
     { 

      encryptedFileStream = new FileInputStream(encryptedDocument); 
      decryptedFileStream = new FileOutputStream(decryptedDocument); 



      long totalFileSize = encryptedDocument.length(); 
      long totalDecrypted = 0; 
      int lastPercentage = -1; 
      int currentPercentage = 0; 

      byte[] encryptedBuffer = new byte[4096]; 
      byte[] decryptedBuffer = new byte[4096]; 
      int encryptedLength = 0; 
      int decryptedLength = 0; 

      while((encryptedLength = encryptedFileStream.read(encryptedBuffer)) > 0) 
      { 
       while (encryptedLength % 16 != 0) // the code never lands in this loop 
       {     
        encryptedBuffer[encryptedLength] = 0; 
        encryptedLength++; 
       } 

       decryptedLength = cipher.update(encryptedBuffer, 0, encryptedLength, decryptedBuffer); 


       while (decryptedLength % 16 != 0) // the code never lands in this loop 
       { 
        decryptedBuffer[decryptedLength] = 0; 
        decryptedLength++; 
       } 

       decryptedFileStream.write(decryptedBuffer, 0, decryptedLength); 


       totalDecrypted += encryptedLength; 

       currentPercentage = (int)(((float)totalDecrypted/(float)totalFileSize) * 100f); 

       if (currentPercentage != lastPercentage) 
       { 
        lastPercentage = currentPercentage; 
        Log.i("Decryption", "Decrypting... " + currentPercentage + "%"); 
       } 
      } 




      Log.i("Decryption", "Finished decrypting!"); 
     } 
     catch (FileNotFoundException fileNotFoundEx) 
     { 
      Log.e("Decryption", "FileNotFoundException: " + fileNotFoundEx.getMessage()); 
     } 
     catch (IOException ioEx) 
     { 
      Log.e("Decryption", "IOException: " + ioEx.getMessage()); 
     } 
     catch (ShortBufferException e) 
     {  
      e.printStackTrace(); 
     } 
     finally 
     { 

     } 

     try 
     {     
      encryptedFileStream.close(); 
      decryptedFileStream.close(); 
      cipherOutputStream.close();   
     } 
     catch (IOException e1) 
     { 

     } 


     document.setDecryptedFilePath(decryptedDocument.getAbsolutePath()); 



     Log.i("Decryption", "Finished!"); 
    } 

下面是從PDF文件的一些樣本(我用十六進制閱讀器來獲得這些結果):

1冊(IOS):

0D 0A 3C 3C 2F 53 69 7A 65 20 35 38 31 3E 3E 0D 
0A 73 74 61 72 74 78 72 65 66 0D 0A 31 31 36 0D 
0A 25 25 45 4F 46 0D 0A 08 08 08 08 08 08 08 08 <-- this block is missing in android. 

書1(的Android):

0D 0A 3C 3C 2F 53 69 7A 65 20 35 38 31 3E 3E 0D 
0A 73 74 61 72 74 78 72 65 66 0D 0A 31 31 36 0D 

書2(iOS版):

65 6E 64 6F 62 6A 0D 73 74 61 72 74 78 72 65 66 
0D 0A 34 30 36 32 35 33 36 0D 0A 25 25 45 4F 46 
0D 0A 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E <-- this block is missing in android. 

書2(安卓):

65 6E 64 6F 62 6A 0D 73 74 61 72 74 78 72 65 66 
0D 0A 34 30 36 32 35 33 36 0D 0A 25 25 45 4F 46 

什麼我注意到的是,最後一個字節都在最後幾個相同的字節數,等於量他們出現的時間。在第1冊iOS中,在最後一個塊中,字節08正好出現8次。在書2的iOS,在最後的塊,字節0E(14)出現整整14倍,等等

除此之外,我不知道什麼花樣的現象發生,所以我不知道我怎麼能解決此問題。

我使用以下不同的填充類型已經嘗試過:

ZeroBytePadding, NoPadding, PKCS5Padding, PKCS7Padding 

任何想法,將不勝感激。

回答

4

好吧,我想通了這個問題...它與填充類型或類似的東西沒有任何關係。顯然,使用Cipher.update()將會正常工作,直到最後一塊數據。 update()方法將省略這個。

這意味着,當完成解密過程時,您必須調用doFinal(),否則最終的字節將不會被寫入。

這是我立即加入後,我的大循環的同時執行解密代碼:

byte[] finalBytes = cipher.doFinal(); 

byte[] finalBytesPadded = new byte[16]; 

for (int i = 0; i < finalBytes.length; i++) 
{ 
    finalBytesPadded[i] = finalBytes[i]; 
} 

for (int i = finalBytes.length; i < 16; i++) 
{ 
    finalBytesPadded[i] = (byte)(16 - finalBytes.length); 
} 

decryptedFileStream.write(finalBytesPadded); 

我couldn'v可能使代碼更好,但它是。問題解決了:)

+0

其實我會懷疑你也有一個錯誤的iPhone實現,因爲你似乎在輸出中得到填充。你有沒有填充選項的iPhone解密?使用填充選項時,解密方法無法知道解密數據是實際數據還是最終填充,直到調用最終方法爲止。它將保持緩衝區中的最後一塊數據,直到它知道它的填充或真實數據。 –

+0

解密由第三方圖書供應商完成。我們從他們那裏收到了解密指南,以及樣本解密書籍與之比較。根據他們,最後的額外字節應該在那裏。 –

+0

那麼,巧合(!),看起來像一個[PKCS#7填充](http://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7)看起來... –

0

一些快速google搜索後,似乎你應該使用的

cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 

代替

cipher = Cipher.getInstance("AES/ECB/ZeroBytePadding"); 

看起來正在使用PKCS#7填充。 http://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7

Android似乎沒有內置PKCS#7,但this guy表示PKCS#7填充與PKCS#5填充兼容。

+0

嘿,tnx的答案,我可能should'v指定這一點,但我已經嘗試了所有不同的填充類型。 –

+0

嗯,那很不幸,那是我的猜測。我對這件事並不十分了解,但我認爲我會盡力提供幫助。祝你好運! –

+0

我發現問題是如果你想看看:) –

相關問題