如何将C++ Rijndael 加密转换为 C#,当出现错误说"Padding is invalid and cannot be removed"?



我正在尝试使用rinjdael cryptography将c 源转换为c#c#c#cymypt和解密文件。

但是C 源与普通EN/解密有点差异。

我并不是很擅长C ,所以我感到困惑。

我客户的应用程序之一是用VC 编写的,将其转换为C#是我工作的一部分。

和以前的C 开发人员从http://www.codeproject.com/articles/10657/a-simple-portable-portable-rinjdael-aes基于stream-cipher进行操作。P>

这是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.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的C 源代码的作者)的说法,前25个字节充满了用户数据(签名,填充和文件状态)。因此,我跳过了第一个25个字节,然后将其余字节保存到_restfilebytes varialbes(字节数组)。

和Keesler具有一个称为块尺寸的变量,将文件字节分为块大小(只要我理解)。

无论如何,我认为我几乎转换为c#,但我仍然收到此错误消息"填充是无效的,并且在C#中处置CryptoStream时无法删除"。

谁能给我一些指南来解决此错误?

None应用作填充模式。似乎您的同事和原始文章的作者构成了自己的填充计划。

此外,应该从文件中流式传输(确保您读取所有字节)。当前,您正在使用IV重新启动每个块的IV加密,这不好,IV仅应在Ciphertext的开始时使用。

在开始之前,打印出C 和C#的十六进制键,并在开始之前进行比较。

请注意,Read方法与C 中的fread方法略有不同。

相关内容