3

AES-256 と HMAC を使用してバイナリ ファイルを暗号化/復号化して結果を確認する小さなプログラムを開発しています。

私のコードは Java での AESCrypt 実装に基づいていますが、複数のスレッドが同時にジョブを実行できるように変更したいと考えていました。

元のバイトのサイズを取得し、スレッドごとの 16 バイト ブロックの数を計算してから、読み取りと書き込みに適用するオフセットに関する情報を使用してスレッドを開始します (暗号化されたファイルのヘッダーがあるため、offset_write = offset_read +header_length)。

暗号化が終了したら、HMAC を介して出力コンテンツ (ヘッダーなし) を渡し、チェックサムを生成しました。

問題は、2 つのスレッド間のバイトで一部のバイトが破損することです。

メインのコード:

//..
// Initialization and creation of iv, aesKey
//..

in = new FileInputStream(fromPath);
out = new FileOutputStream(toPath);

//..
// Some code for generate the header and write it to out
//..
double totalBytes = new Long(archivo.length()).doubleValue();
int bloquesHilo = new Double(Math.ceil(totalBytes/(AESCrypt.NUM_THREADS*AESCrypt.BLOCK_SIZE))).intValue();
int offset_write = new Long((out.getChannel()).position()).intValue();

for (int i = 0; i < AESCrypt.NUM_THREADS; i++)
{
    int offset = bloquesHilo*AESCrypt.BLOCK_SIZE*i;
    HiloCrypt hilo = new HiloCrypt(fromPath, toPath, ivSpec, aesKey, offset, offsetInicio, bloquesHilo, this);
    hilo.start();
}

スレッドのコード (クラス HiloCrypt): public class HiloCrypt extends Thread {

    private RandomAccessFile in;
    private RandomAccessFile out;

    private Cipher cipher;
    private Mac hmac;
    private IvParameterSpec ivSpec2;
    private SecretKeySpec aesKey2;

    private Integer num_blocks;
    private Integer offset_read;
    private Integer offset_write;

    private AESCrypt parent;

    public HiloCrypt(String input, String output, IvParameterSpec ivSpec, SecretKeySpec aesKey, Integer offset_thread, Integer offset_write, Integer blocks, AESCrypt parent2) 
    {
        try
        {
                        // If i don't use RandomAccessFile there is a problem copying data
            this.in = new RandomAccessFile(input, "r");
            this.out = new RandomAccessFile(output, "rw");

            int total_offset_write = offset_write + offset_thread;

                        // Adjust the offset for reading and writing 
            this.out.seek(total_offset_write);
            this.in.seek(offset_thread);

            this.ivSpec2 = ivSpec;
            this.aesKey2 = aesKey;

            this.cipher = Cipher.getInstance(AESCrypt.CRYPT_TRANS);
            this.hmac = Mac.getInstance(AESCrypt.HMAC_ALG);

            this.num_blocks = blocks;
            this.offset_read = offset_thread;
            this.offset_write = total_offset_write;
            this.parent = parent2;

        } catch (Exception e)
        {
            System.err.println(e);
            return;
        }
    }


    public void run()
        {
        int len, last,block_counter,total = 0;
        byte[] text = new byte[AESCrypt.BLOCK_SIZE];

        try{
            // Start encryption objects
            this.cipher.init(Cipher.ENCRYPT_MODE, this.aesKey2, this.ivSpec2);
            this.hmac.init(new SecretKeySpec(this.aesKey2.getEncoded(), AESCrypt.HMAC_ALG));

            while ((len = this.in.read(text)) > 0 && block_counter < this.num_blocks) 
            {
                this.cipher.update(text, 0, AESCrypt.BLOCK_SIZE, text);
                this.hmac.update(text);

                // Write the block
                this.out.write(text);

                last = len;
                total+=len;

                block_counter++;
            }

            if (len < 0) // If it's the last block, calculate the HMAC
            {
                last &= 0x0f;
                this.out.write(last);

                this.out.seek(this.offset_write-this.offset_read);

                while ((len = this.out.read(text)) > 0) 
                {
                    this.hmac.update(text);
                }

                // write last block of HMAC
                text=this.hmac.doFinal();
                this.out.write(text);
            }

                        // Close streams
            this.in.close();
            this.out.close();

                        // Code to notify the end of the thread
        }
        catch(Exception e)
        {
            System.err.println("Hola!");
            System.err.println(e);
        }
    }
}

このコードでは、1 つのスレッドのみを実行すると、暗号化/復号化は完璧に行われますが、2 つ以上のスレッドでは、スレッド ジョブ間のゾーンのバイトに問題があり、そこでデータが破損し、チェックサムも失敗します。

1 つのスレッドよりも 2 倍近く高速になるため、スレッドでこれを実行しようとしています。ファイルへのアクセスではなく、処理が原因であると思います。

無関係なデータとして、MB Air で 43 秒で 250Mb のデータを圧縮します。いい時間ですよね?

4

3 に答える 3

5

AESCrypt はスレッドセーフではありません。複数のスレッドを使用することはできません。

一般的に言えば、暗号化コードは安全な出力を生成するために複雑な数学を必要とするため、めったにスレッドセーフではありません。AES 自体は比較的高速です。より高速な速度が必要な場合は、最初のステップとして垂直スケーリングまたはハードウェア アクセラレータを検討してください。後でサーバーを追加して、さまざまなファイルを同時に暗号化できます (水平スケーリング)。

于 2013-01-23T11:00:29.533 に答える
1

基本的に、本質的にシーケンシャルな操作をマルチスレッド化する必要があります。

Stream cipher各ブロックは前のブロックの完了に依存するため、並列化することはできません。したがって、特にファイルがディスク上ではなくメモリ内にある場合は、パフォーマンスをわずかに向上させながら、複数のファイルを個別に並列に暗号化できますが、複数のコアを使用して単一のファイルを暗号化することはできません。

ご覧のとおり、updateメソッドを使用します。私はJava暗号化の専門家ではありませんが、メソッドの名前でさえ、暗号化アルゴリズムが状態を保持していることを教えてくれます。「マルチスレッド」と「状態」は友達ではないため、スレッド間の状態管理を処理する必要があります。

競合状態は、ブロックが損傷する理由を説明しています。

于 2013-01-23T11:06:36.153 に答える
1

HMAC に複数のスレッドを使用することはまったく意味がありません。1) 順次計算する必要があり、2) I/O アクセス R/W は実際の HMAC 計算よりもはるかに遅いためです。

AES の場合、以前のデータ ブロックの知識を必要としない CNT モードまたはその他のチェーン モードを使用する場合は、複数のスレッドを使用することをお勧めします。

質問を crypto-stackexchange に移動するのはどうですか?

于 2013-01-23T11:32:29.670 に答える