2014-07-01 114 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