1

現在、eBook リーダー アプリ用にサーバーから受信している PDF を復号化するための復号化アルゴリズムに取り組んでいます。完全に動作する iOS で動作する同等のコードがあります。現在、コードを Android で実行しようとしています。

詳細については、復号化は AES であり、ECB モードで実行されます。暗号化キーは、バイト配列に変換する 16 進文字列配列です (2 文字ごとに 1 バイトに変換します。たとえば、「FF」は 255 になります)。

私が経験していることは非常に興味深いものです。iOS と Android の両方から復号化した後の結果ファイルを比較していますが、一貫して、Android 復号化ファイルは iOS 復号化ファイルよりも 16 バイト短いことに気付きました。他のすべてのバイトは同じです (いくつかの例を以下に掲載します)。

この違いにより、私の eBook リーダーは 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 ファイルのサンプルをいくつか示します (これらの結果を得るために 16 進リーダーを使用しました)。

ブック 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 (Android):

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

私が気づいているのは、最後のバイトの最後に、それらが出現する回数と同じ数の同一のバイトがあることです。book 1 iOS では、最後のブロックでバイト 08 がちょうど 8 回出現します。book 2 iOS では、最後のブロックで、バイト 0e (14) がちょうど 14 回出現します。

それ以外は、どのようなパターンで発生しているのかわからないので、どうすれば解決できるのかわかりません。

私はすでに次の異なるパディングタイプを使用してみました:

ZeroBytePadding, NoPadding, PKCS5Padding, PKCS7Padding 

どんなアイデアでも大歓迎です。

4

2 に答える 2

4

OK、私は問題を理解しました...パディングタイプなどとは何の関係もありませんでした。どうやら、 Cipher.update() を使用すると、データの最後のブロックまで問題なく動作します。update() メソッドはこれを省略します。

これは、復号化プロセスを終了するときに doFinal() を呼び出す必要があることを意味します。そうしないと、最後のバイトが書き込まれません。

復号化を実行する大きな while ループの直後に追加したコードを次に示します。

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);

私はおそらくコードをより良くすることができなかったでしょうが、それはあります。問題が解決しました :)

于 2013-10-30T08:25:23.947 に答える