2015-05-11 2 views
0

Я пытаюсь преобразовать источник C++ в C#, который шифрует и дешифрует файл с использованием криптографии Rinjdael.Как преобразовать криптографию C++ Rijndael в C#, когда есть ошибка, говорящая, что «Заполнение недопустимо и не может быть удалено»?

Но источник C++ имеет немного отличное от нормального en/decryptions.

И я не очень хорош в C++, поэтому я сбиваюсь с толку.

Одно из приложений моих клиентов написано на VC++, и преобразовать его в C# является частью моей работы.

И предыдущий разработчик C++ использовал открытый код от http://www.codeproject.com/Articles/10657/A-Simple-Portable-Rinjdael-AES-Based-Stream-Cipher для управления en/decryption.

Вот исходные коды C++.

int DCipher::DecryptFile(LPCTSTR szSrcFile, LPCTSTR szDestFile, const char* pwd, int head[19]) 
{ 
    if(CheckMemSize() != 0) 
     return INSUFFICIENT_MEMORY; 

    FileSize=CurPosition=0; 

    _tcscpy(SrcFile, szSrcFile); 
    _tcscpy(OutFile, szDestFile); 
    //_tcscpy(OutFile, _T(".enc")); 

    strcpy(password, pwd); 

    for(int i=0; i<19; i++) 
    { 
     header[i] = head[i]; 
    } 

    FILE *r, *w; 
    GetFileLength(); 

    int nCheck = CheckIfEncrypted(); 

    if(nCheck != ENCRYPTED_FILE) 
     return nCheck; //either NORMAL_FILE or BAD_SIGNATURE 


    if((r = _tfopen(SrcFile, _T("rb"))) == NULL) 
     return ERROR_SRC_FILE; 


    if((w = _tfopen(OutFile, _T("wb"))) == NULL) 
    { 
     fclose(r); 
     return ERROR_DST_FILE; 
    } 

    char zzz[26]; //fixed invalid pointer - DKeesler 
    fread(zzz, 25, 1, r); // Skip first 25 bytes of the file. 

    int pad = header[19]; 
    pad *= 10; 
    pad += header[20]; 

    // convert password to Rijndael key 
    strcpy((char*)key, (const char*)CalcMD5FromString((const char*)password)); 

    /*************************************** 
    Decryption algorithm 
    ***************************************/ 
    int rval = NO_ERRORS_DONE; 
    FileSize -= 25; 

    unsigned int BUFF_SIZE = liChunkSize; 
    unsigned int WRITE_SIZE = liChunkSize; 

    int nRound = FileSize/liChunkSize; 
    unsigned int LAST_BLOCK = FileSize % liChunkSize; 

    if(LAST_BLOCK >= 1) 
     nRound++; 

    const unsigned char* intext; 
    unsigned char* output; 

    intext = (const unsigned char*)malloc(BUFF_SIZE); 
    output = (unsigned char*)malloc(BUFF_SIZE+16); 

    if(intext == NULL || output == NULL) 
    { 
     fclose(r); 
     fclose(w); 
     return ALLOC_ERROR; 
    } 

    Rijndael rj; 
    rj.init(Rijndael::CBC, Rijndael::Decrypt, key, Rijndael::Key32Bytes); 

    for(int loop=1; loop <= nRound; loop++) 
    { 

     if(loop == nRound && LAST_BLOCK >= 1) 
     { 
      BUFF_SIZE = LAST_BLOCK; 
      WRITE_SIZE = LAST_BLOCK - pad; 
     } 

     fread((void*)intext, sizeof(char), BUFF_SIZE, r); // read plaintext into intext[] buffer 

     int bsize = BUFF_SIZE*8; 
     int len = rj.blockDecrypt((const UINT8*)intext, bsize, (UINT8*)output); 

     if(len >= 0) 
     { 
      fwrite((const void*)output, sizeof(char), WRITE_SIZE, w); 
     } 
     else 
     { 
      rval = READ_WRITE_ERROR; 
      break; 
     } 
    } 

    fclose(r); //close input file 
    fclose(w); //close output file 

    free((void*)intext); 
    free((void*)output); 

    //change these two lines if you want to leave backups or unencrypted copies... 
    //that would sort of defeat the purpose of encryption in my mind, but it's your 
    // app so write it like you want it. 
    if(DECRYPTION_CANCEL == rval) { 
     _tremove(OutFile); 
    } 
    else { 
     //_tremove(SrcFile); //remove input file 
     //_trename(OutFile, SrcFile); //rename output file to input filename 
    } 

    return rval; //ZERO .. see defines for description of error codes. 
} 

И C# исходный код из https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndael(v=vs.110).aspx.

И я изменил несколько кодов.

Здесь приведены коды C#.

public int DecryptFile(string SourceFilePath, string DestFilePath, string Password, string Signature) 
{ 
    try 
    { 
     FileSize  = CurPosition = 0; 

     FileInfo _fi = new FileInfo(SourceFilePath); 
     FileSize  = _fi.Length; 

     // copy the signature to _header 
     for(int i = 0; i < 19; i++) 
     { 
      _header[i] = (byte)Signature[i]; 
     } 

     /* 
      * check if the file is valid encrypted file. 
      */ 
     int nCheck = this.CheckIfEncrypted(SourceFilePath); 
     switch (nCheck) 
     { 
      case ENCRYPTED_FILE: 
       // The file is an encrypted file. 
       break; 
      case NORMAL_FILE: 
       throw new ArgumentException("The file is a normal file."); 
      case BAD_SIGNATURE: 
       throw new ArgumentException("User signature doesn't match."); 
     } 

     int pad = _header[19]; 
     pad  *= 10; 
     pad  += _header[20]; 

     // Rijndael session key 
     byte[] session_key = this.CalcMD5FromString(Password); 

     byte[] _restFileBytes = new byte[_fi.Length - 25]; 
     using (FileStream _fs = new FileStream(SourceFilePath, FileMode.Open, FileAccess.Read)) 
     { 
      _fs.Read(_restFileBytes, 0, _restFileBytes.Length); 
     } 

     int rval  = NO_ERRORS_DONE; 
     FileSize  -= 25; 

     int BUFF_SIZE = liChunkSize; 
     int WRITE_SIZE = liChunkSize; 

     int nRound  = (int)FileSize/liChunkSize; 
     int LAST_BLOCK = (int)FileSize % liChunkSize; 

     if(LAST_BLOCK >= 1) 
      nRound++; 

     byte[] intext = new byte[BUFF_SIZE]; 
     byte[] output = new byte[BUFF_SIZE + 16]; 

     if (intext.Length == 0 || output.Length == 0) 
     { 
      return ALLOC_ERROR; 
     } 

     for (int loop = 1; loop <= nRound; loop++) 
     { 
      if (loop == nRound && LAST_BLOCK >= 1) 
      { 
       BUFF_SIZE = LAST_BLOCK; 
       WRITE_SIZE = LAST_BLOCK - pad; 
      } 

      intext = new byte[BUFF_SIZE]; 

      System.Buffer.BlockCopy(_restFileBytes, (loop - 1) * this.liChunkSize, intext, 0, BUFF_SIZE); 

      int bsize = BUFF_SIZE * 8; // -> I still couldn't figure out what this bsize does on Rijndael decryption. 
      using (RijndaelManaged myRijndael = new RijndaelManaged()) 
      { 
       myRijndael.Key   = session_key; 
       //myRijndael.BlockSize = bsize; 
       //myRijndael.Padding  = PaddingMode.None; 
       myRijndael.GenerateIV(); 

       using (Rijndael rijAlg = Rijndael.Create()) 
       { 
        rijAlg.Key = myRijndael.Key; 
        rijAlg.IV = myRijndael.IV; 

        // Create a decrytor to perform the stream transform. 
        ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); 

        // Create the streams used for decryption. 
        using (MemoryStream msDecrypt = new MemoryStream(intext)) 
        { 
         using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) 
         { 
          //using (StreamReader srDecrypt = new StreamReader(csDecrypt)) 
          //{ 
          // // Read the decrypted bytes from the decrypting stream and place them in a string. 
          // //string s = srDecrypt.ReadToEnd(); 
          //} 
          byte[] rettt = msDecrypt.ToArray(); 
         } // --> Padding is invalid and cannot be removed error occurs here and msDecrypt byte array is just same as intext. So, it's not decrypted at all. 
        } 

       } 
      } 
     } 


     return rval; 
    } 
    catch 
    { 
     throw; 
    } 
} 

Согласно Keesler (который является автором из с исходниками ++ от codeproject.com), первые 25 байтов заполняются данными пользователя (подпись, отступов и статус файла). Итак, я пропустил первые 25 байтов и сохранил оставшиеся байты в _restFileBytes varialbes (массив байтов).

И у Keesler есть переменная с названием chunk size, которая разбивает байты файлов на размер блока (если я понимаю).

В любом случае, я думаю, что почти конвертирован в C#, но я все еще получаю это сообщение об ошибке «Заполнение недопустимо и не может быть удалено», когда CryptoStream удаляет C#.

Может ли кто-нибудь дать мне руководство по исправлению этой ошибки?

+0

Можете ли вы также опубликовать свой шифрованный код, возможно, у вас есть такая же проблема, о которой сообщалось здесь: http://stackoverflow.com/questions/8583112/padding-is-invalid-and-cannot-be-removed?rq=1 –

+0

@ RodrigoLópez Вы имеете в виду мой код шифрования в C#? Я еще не начал его. У меня есть только код C++. –

+0

@deviantfan Что вы имеете в виду, я не должен публиковать этот код. Это уже открытый исходный код, поскольку я упомянул исходный URL-адрес источника. Конечно, я бы с удовольствием использовал встроенные Rijndael calss, но есть так много файлов, зашифрованных предыдущим проектом C++, и мне все еще нужно, чтобы мое приложение было в состоянии en/decrypt этих файлов. –

ответ

1

None следует использовать в качестве режима заполнения. Кажется, что ваш коллега и автор оригинальной статьи составили свою собственную схему заполнения.

Кроме того, все зашифрованный текст должен быть передан из файла (убедитесь, что вы прочитали все байты). В настоящее время вы перезапускаете шифрование с IV для каждого фрагмента, что не очень хорошо, IV следует использовать только в начале зашифрованного текста.

Распечатайте ключ в шестнадцатеричном формате как для C++, так и для C# и сравните перед началом работы.

Обратите внимание, что метод Read немного отличается от метода fread в C++.

+0

И, наконец, не используйте * случайное дерьмо * для шифрования/дешифрования. Мои глаза все еще поливают, если только для того, чтобы неправильно писать Rijndael * в названии *. Тьфу. –

+0

Спасибо за ваш ответ. Это было очень полезно, но я решил использовать VC++ dll из C# с помощью DllImport. И IV вещь, я посмотрел rijndael.cpp, который из codeproject, и он ограничивает значение IV каждый раз шифровать и расшифровывать. Спасибо большое. –

+0

@JoshuaSon Вы должны действительно использовать четко определенный формат контейнера вместо этого и отойти от него как можно скорее. Попробуйте CMS или откройте PGP. –