2013-01-23 25 views
3

我正在開發一個小程序,使用AES-256和HMAC來加密/解密二進制文件來檢查結果。多線程中的AES + HMAC加密 - Java

我的代碼是基於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): 公共類HiloCrypt繼承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的數據。 ¿這是個好時光?

+0

不要打擾。多線程會讓事情變得更慢。多次讀取和寫入會減慢I/O,因爲文件操作在順序時最快。在HMAC操作之前必須重新組裝數據會浪費時間。 (你使用什麼鏈接模式?) –

+0

這可能有很多原因。如果你關心表現,你應該試着弄清楚它們是什麼。 (數據是否已經在緩存中?您是否忘記按順序將數據提供給HMAC功能?您確定實際上處理了所有數據嗎?) –

+0

@DavidSchwartz是的,我在最後一塊數據時依次輸入數據得到處理。它實際上並不檢查所有線程是否完成,但是通過調試信息,我可以確保所有線程都在我的測試中結束。 – odarriba

回答

5

AESCrypt是not thread safe。你不能使用多線程。

一般來說,加密代碼很少是線程安全的,因爲它需要複雜的數學來生成安全輸出。 AES本身相對較快,如果您需要更高的速度,請考慮垂直縮放或硬件加速器作爲第一步。稍後,您可以添加更多服務器來同時加密不同的文件(水平縮放)。

+0

我知道它,但是我有不同的密碼對象實例,並且在所有數據都被加密時按順序執行HMAC,所以我不知道可能是什麼問題。對於AES來說,處理數據塊之前處理的數據很重要嗎?因爲問題在解密時只在文件中間大約16/32字節。其餘文件在二進制方面是相同的 – odarriba

+0

不同的對象是否在同一個文件上同時運行? – oleksii

+0

是的,購買我在「rw」模式下使用2個不同的RandomFileAccess對象,因爲它們具有不同的偏移量,所以不會阻止其他線程的操作 – odarriba

0

你基本上要多線程是本質順序的操作。

Stream cipher因爲每個塊依賴於先前塊的完成不能平行。所以你在並行可以加密多個文件獨立輕微的性能提升,尤其是當文件在內存中,而不是在磁盤上,但你不能加密使用多個核心的單個文件。

正如我所看到的,您使用的方法是update。我不是Java密碼術的專家,但即使方法的名稱告訴我,加密算法保留狀態:「多線程」和「狀態」不是朋友,你必須處理跨線程的狀態管理。

比賽條件解釋了爲什麼你會得到塊損壞。

+0

[ESSIV和XEX](http://en.wikipedia.org/wiki/XTS_mode #Encrypted_salt-sector_initialization_vector_.28ESSIV.29)解決這個問題。有完全可並行化的鏈接模式,內置消息完整性,所以甚至不需要HMAC。 –

+0

如果我生成原始塊的不同加密文件(對於2個線程,2個加密文件,原始文件中間的每個文件),並將它們解密並在解密中再次合併?它應該工作,不是? – odarriba

+1

@odarriba:你可以任意設計它。您可以通過使用每個文件IV來使其失敗。 –

1

它使絕對沒有任何意義使用多於1個線程的HMAC,因爲1)它必須被順序地計算,並且2)I/O訪問R/W比實際HMAC計算

對於AES慢得多在使用CNT模式或其他不需要了解以前數據塊的鏈接模式時使用多個線程是一個好主意。

將問題轉移到crypto-stackexchange如何?