2015-01-16 117 views
0

我正在實現文件的加密/解密,只有一定數量的字節應該加密。 例如:我有500 MB的大文件,我想加密(和當然解密)只有2 MB的文件。如何只加密/解密一定量的字節(文件)?

我已經實現了一切,加密工作正常(沒有錯誤),但是當我運行解密它總是拋出這個錯誤:

System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed. 
at System.Security.Cryptography.CapiSymmetricAlgorithm.DepadBlock(Byte[] block, Int32 offset, Int32 count) at System.Security.Cryptography.CapiSymmetricAlgorithm.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) 
at System.Security.Cryptography.CryptoStream.FlushFinalBlock() 
at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing) 
at System.IO.Stream.Close() 
at System.IO.Stream.Dispose() 
at ... 

然後我試圖填充設爲encryptor.Padding = PaddingMode.None;(並嘗試PaddingMode.Zeros等)。在使用此填充模式運行加密和解密之後,解密的結果沒有錯誤/異常,但文件的加密部分仍然是加密的,但有點不同。我也檢查了密碼是否正確

現在我不再有解決方案如何處理文件的「部分」加密。有任何想法嗎?如何處理這個填充或字節?

這是我的加密和一定量的字節的解密過程的代碼(遺憾的長度,但只有你會知道:)):

static byte[] FILE_HEADER = Encoding.Default.GetBytes("header_of_file"); //this is written to the first line of encrypted file 
static long limitBytes = 4096 * 8; //limit encryption to this amount of bytes 

public static bool Encrypt(string inputFilePath, string outputfilePath, string EncryptionKey) 
{ 
int bytesRead = 1; 
long byteWriteCounter = 0; 
long encryptedByteCounter = 0; 
byte[] blength = null; 
byte[] intBytes = null; 
int bufferLen = 4096; 
byte[] buffer = new byte[bufferLen]; 
blength = new byte[FILE_HEADER.Length]; 
long sumBytesRead = 0; 

try 
{ 
    using (FileStream fsInput = new FileStream(inputFilePath, FileMode.Open)) 
    {      
     fsInput.Read(blength, 0, blength.Length); 

     if (!blength.SequenceEqual(FILE_HEADER)) //read the FILE_HEADER - if not equal that we can encrypt otherwise is already encrypted 
     { 
      fsInput.Position = 0; 

      using (FileStream fsOutput = new FileStream(outputfilePath, FileMode.Create)) 
      {        
       using (Aes encryptor = Aes.Create()) 
       { 

        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); 
        encryptor.Key = pdb.GetBytes(32); 
        encryptor.IV = pdb.GetBytes(16); 

        using (CryptoStream cs = new CryptoStream(fsOutput, encryptor.CreateEncryptor(), CryptoStreamMode.Write)) 
        { 
         try 
         { 

          while (bytesRead != 0) 
          { 
           bytesRead = fsInput.Read(buffer, 0, bufferLen); 
           sumBytesRead += bytesRead; 

           if (sumBytesRead <= limitBytes) //limit encryption 
           {         

            if (bytesRead < bufferLen) 
            {             

             cs.Write(buffer, 0, bytesRead); 
             encryptedByteCounter += bytesRead; 
             byteWriteCounter += bytesRead; 

             intBytes = Encoding.Default.GetBytes(encryptedByteCounter.ToString("D7")); 

             fsOutput.Position = 0; 
             fsOutput.Write(FILE_HEADER, 0, FILE_HEADER.Length); //we write our header to file, to know which is encrypted 
             fsOutput.Write(intBytes, 0, intBytes.Length); //we write number of encrypted bytes next to header, for decryption to know 
             bytesRead = 0; 
            } 
            else 
            { 
             cs.Write(buffer, 0, bytesRead); 
             encryptedByteCounter += bytesRead; 
             byteWriteCounter += bytesRead; 

            } 
           } 
           else //if the file is bigger than limit, we continue to write normal data (unencrypted) 
           { 
            if (bytesRead < bufferLen) 
            { 
             fsOutput.Write(buffer, 0, bytesRead); 
             byteWriteCounter += bytesRead; 
             intBytes = Encoding.Default.GetBytes(encryptedByteCounter.ToString("D7")); 

             fsOutput.Position = 0; 
             fsOutput.Write(FILE_HEADER, 0, FILE_HEADER.Length); 
             fsOutput.Write(intBytes, 0, intBytes.Length); 
             bytesRead = 0; 
            } 
            else 
            { 
             fsOutput.Write(buffer, 0, bytesRead); 
             byteWriteCounter += bytesRead; 
            } 
           } 

          } 

         } 
         catch (SystemException se) 
         { 
          Console.WriteLine("Exception ENC: " + se); 
         } 
        } 
       } 
      } 
      return true; 

     } 
     else 
     { 
      //file is already encrypted 
      return false; 
     } 
    } 


} 
catch (SystemException se) 
{ 

    Console.WriteLine("Main ENC exception: "+se); 

    return false; 
} 

} 

......和解密功能:

public static bool Decrypt(string inputFilePath, string outputfilePath, string EncryptionKey) 
{ 
byte[] blength = null; 
blength = new byte[FILE_HEADER.Length]; 
long byteWriteCounter = 0; 
int bufferLen = 4096; 
byte[] buffer = new byte[bufferLen]; 
int bytesRead = 1; 
long decryptedByteCounter = 0; 
long sumBytesRead = 0; 
byte[] bufferEncBytes = new byte[7]; 

try 
{ 

    using (FileStream fsInput = new FileStream(inputFilePath, FileMode.Open)) 
    { 

     fsInput.Read(blength, 0, blength.Length); 

     if (blength.SequenceEqual(FILE_HEADER))//check if is our encrypted file 
     { 


      fsInput.Read(bufferEncBytes, 0, bufferEncBytes.Length); 

      int numOfDecBytes = Convert.ToInt32(Encoding.Default.GetString(bufferEncBytes)); //get number of encrypted bytes 

      using (FileStream fsOutput = new FileStream(outputfilePath, FileMode.Create)) 
      {     
       using (Aes encryptor = Aes.Create()) 
       { 
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); 
        encryptor.Key = pdb.GetBytes(32); 
        encryptor.IV = pdb.GetBytes(16);       

        using (CryptoStream cs = new CryptoStream(fsOutput, encryptor.CreateDecryptor(), CryptoStreamMode.Write)) 
        { 
         try 
         { 

          while (bytesRead != 0) 
          { 
           bytesRead = fsInput.Read(buffer, 0, bufferLen); 
           sumBytesRead += bytesRead; 

           if (sumBytesRead <= numOfDecBytes) //decrypt until limit 
           { 

            if (bytesRead < bufferLen) 
            { 
             cs.Write(buffer, 0, bytesRead); 
             decryptedByteCounter += bytesRead; 
             byteWriteCounter += bytesRead; 

             bytesRead = 0; //we are at the end of file, end everything 
            } 
            else 
            { 
             cs.Write(buffer, 0, bytesRead); 
             decryptedByteCounter += bytesRead; 
             byteWriteCounter += bytesRead;           
            } 
           } 
           else //if we have decrypted encrypted bytes, continue with rest of the (normal) data 
           { 
            if (bytesRead < bufferLen) 
            { 
             fsOutput.Write(buffer, 0, bytesRead); 
             byteWriteCounter += bytesRead; 

             bytesRead = 0; 
            } 
            else 
            { 
             fsOutput.Write(buffer, 0, bytesRead); 
             byteWriteCounter += bytesRead; 
            } 
           } 

          } 

         } 
         catch (SystemException se) 
         { 
          Console.WriteLine("Exception DECR: " + se); 
         } 

        } 

       } 

      } 
      return true; 
     } 
     else 
     { 
      //not right file for decryption 
      return false; 
     } 
    } 

} 
catch (SystemException eks) 
{ 
    Console.WriteLine("Error: " + eks); //here the exception of Invalid Padding is thrown 

    return false; 
} 
} 
+0

這基本上相當於調試文件處理。假設你的密文與純文本一樣大是錯誤的。假設你完全閱讀你想要加密的明文的結尾似乎也是錯誤的。密文也可能比明文大。總而言之,許多錯誤和加密/解密方法試圖做一切,而不是做出好的設計。 –

+0

爲什麼你只想加密部分文件?這顯然不安全。 – CodesInChaos

+0

@ MaartenBodewes-owlstead我知道,有很多錯誤,我正在開發階段測試,如果它符合我的邏輯。但是,你能幫助並提供解決方案如何實現加密字節的數量,在解密中可以計數和解密?或者我應該使用其他加密算法? – OnlyKing

回答

2

當您使用塊密碼進行加密時,大多數模式都會添加填充。這意味着加密數據將會是較長的,即原來的明文,因爲有額外的填充。你需要保留加密數據的填充,否則你會得到錯誤的填充錯誤。

您可以確保仔細處理填充或切換到CTR模式,該模式不使用填充,給出與明文長度相同的密文。

使用Stream Cypher,如Rabbit或Salsa20會產生同樣的效果。

+0

在這種情況下,我該如何仔細處理Padding?此算法不支持CTR模式。 – OnlyKing

+0

如果您將明文大小存儲在密文前面,那麼您可以嘗試並實施填充到塊大小的填充(即AES的0..15個字節)。在這種情況下,請確保您的緩衝區和要加密的字節數都是塊大小的倍數。然後你可以使用'NoPadding'來加密/解密所需的字節。如果你已經加密到最大,那麼你不需要填充。在另一種情況下,你可以通過填充零的緩衝區的最後部分來填充。 PS在你的情況下,我當然會考慮存儲64位大小的明文。 –

+0

加密,並記錄* encrypted *字節的大小。將這些字節添加到文件的未加密部分的前面。保留一個固定大小的記錄,將您預先添加到密文中的密文大小。在解密端,首先讀取密文大小。然後解密那麼多字節(填充應該自動消失)。將解密的明文前加到文件未加密的剩餘部分。確保固定大小的長度變量足夠大,以保持您需要的最長可能的密文大小。 – rossum