3

我正在开发一个小程序来加密/解密二进制文件,使用 AES-256 和 HMAC 来检查结果。

我的代码基于 Java 中的 AESCrypt 实现,但我想修改它以允许多个线程同时完成这项工作。

我得到原始字节的大小并计算每个线程16字节块的数量,然后我用有关偏移量的信息启动线程以申请读写(因为加密文件有一个头,所以offset_write = offset_read +header_length)。

当它完成加密时,我通过 HMAC 传递了输出内容(没有标题)以生成校验和。

问题是两个线程之间的字节中的某些字节被破坏了。

主要代码:

//..
// 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 个以上的线程,线程作业之间的区域中的字节就会出现问题,数据在那里被损坏并且校验和也失败了。

我正在尝试使用线程来执行此操作,因为它比使用一个线程快 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 个线程绝对没有意义,因为 1)它必须按顺序计算,并且 2)I/O 访问 R/W 比实际的 HMAC 计算慢得多

对于 AES,在使用 CNT 模式或其他不需要了解先前数据块的链接模式时使用多个线程可能是一个好主意。

将问题转移到crypto-stackexchange怎么样?

于 2013-01-23T11:32:29.670 回答