2014-07-01 38 views
0

我正在寫一個Windows應用程序在C#中必須與Mac應用程序(用Cocoa編寫)進行交互。此應用程序使用CBC(IV,密鑰,鹽,HMAC)以AES加密文件。我對加密不太瞭解,但我認爲這就是它的作用。我們使用的可可庫是RNCryptor。他們有一個C# version,這是我在Windows端使用的(有一些修改,主要是使用byte[]而不是字符串)。AES解密非文本文件最終損壞數據

現在文本文件被正確解密,但其他文件(例如,PNG文件)最終被損壞(正確的文件在右邊,損壞在左邊,使用UTF8編碼,你可以看到有一個大量的鑽石與S):

differences

我相信這是由於文件的編碼,但我想UTF8,預設的Unicode,ASCII ......和輸出文件總是與損壞不同的文件大小,是ASCII和默認編碼(UTF16我相信)大小最接近。

這是RNCryptor修改後的代碼我使用:

public byte[] Decrypt (byte[] encryptedBase64, string password) 
    { 
     PayloadComponents components = this.unpackEncryptedBase64Data (encryptedBase64); 

     if (!this.hmacIsValid (components, password)) { 
      return null; 
     } 

     byte[] key = this.generateKey (components.salt, password); 

     byte[] plaintextBytes = new byte[0]; 

     switch (this.aesMode) { 
      case AesMode.CTR: 
       // Yes, we are "encrypting" here. CTR uses the same code in both directions. 
       plaintextBytes = this.encryptAesCtrLittleEndianNoPadding(components.ciphertext, key, components.iv); 
       break; 

      case AesMode.CBC: 
       plaintextBytes = this.decryptAesCbcPkcs7(components.ciphertext, key, components.iv); 
       break; 
     } 

     return plaintextBytes; 
    } 

    private byte[] decryptAesCbcPkcs7 (byte[] encrypted, byte[] key, byte[] iv) 
    { 
     var aes = Aes.Create(); 
     aes.Mode = CipherMode.CBC; 
     aes.Padding = PaddingMode.PKCS7; 
     var decryptor = aes.CreateDecryptor(key, iv); 

     string plaintext; 
     using (MemoryStream msDecrypt = new MemoryStream(encrypted)) 
     { 
      using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) 
      { 
       using (StreamReader srDecrypt = new StreamReader(csDecrypt)) 
       { 
        plaintext = srDecrypt.ReadToEnd(); 
       } 
      } 
     } 

     return Encoding.UTF8.GetBytes(plaintext); 
    } 

    private PayloadComponents unpackEncryptedBase64Data (byte[] encryptedBase64) 
    { 
     List<byte> binaryBytes = new List<byte>(); 
     binaryBytes.AddRange (encryptedBase64); 

     PayloadComponents components; 
     int offset = 0; 

     components.schema = binaryBytes.GetRange(0, 1).ToArray(); 
     offset++; 

     this.configureSettings ((Schema)binaryBytes [0]); 

     components.options = binaryBytes.GetRange (1, 1).ToArray(); 
     offset++; 

     components.salt = binaryBytes.GetRange (offset, Cryptor.saltLength).ToArray(); 
     offset += components.salt.Length; 

     components.hmacSalt = binaryBytes.GetRange(offset, Cryptor.saltLength).ToArray(); 
     offset += components.hmacSalt.Length; 

     components.iv = binaryBytes.GetRange(offset, Cryptor.ivLength).ToArray(); 
     offset += components.iv.Length; 

     components.headerLength = offset; 

     components.ciphertext = binaryBytes.GetRange (offset, binaryBytes.Count - Cryptor.hmac_length - components.headerLength).ToArray(); 
     offset += components.ciphertext.Length; 

     components.hmac = binaryBytes.GetRange (offset, Cryptor.hmac_length).ToArray(); 

     return components; 

    } 

    private bool hmacIsValid (PayloadComponents components, string password) 
    { 
     byte[] generatedHmac = this.generateHmac (components, password); 

     if (generatedHmac.Length != components.hmac.Length) { 
      return false; 
     } 

     for (int i = 0; i < components.hmac.Length; i++) { 
      if (generatedHmac[i] != components.hmac[i]) { 
       return false; 
      } 
     } 
     return true; 
    } 

,這是我的代碼解密和寫入文件:

byte[] decryptedFile = this.decryptor.Decrypt(File.ReadAllBytes(filePath), password); 
File.WriteAllBytes(filePath, decryptedFile); 

什麼可以錯在這裏?提前致謝。

回答

1

問題出在您解密時使用StreamReaderStreamReader讀取文本(UTF-8 here),不是任意二進制數據。一種解決方案是將數據讀入MemoryStream,並使用其ToArray()方法得到結果byte[]

+0

嘿,謝謝你的回覆。不幸的是,除非我做錯了什麼,這是行不通的。這是修改後的方法:http://pastebin.com/DkXFndbC。該文件最終像很多中文字符一樣。 – pmerino

+0

@pmerino看起來你錯誤地從'msDecrypt'讀取()包含* encrypted *字節,而不是從'csDecrypt'這將是* decrypted *字節。 – Iridium

+0

謝謝!嘗試重寫代碼,你有什麼想法,現在它工作! – pmerino