Search code examples
c++encryptioncryptoapi

CryptoAPI RSA: CryptDecrypt decrypts only at the first time, other calls return NTE_BAD_DATA


I have write the programm which encrypts/decrypts memory buffer of custom length. Ecryption ends well; but my decryption code decrypts data only one time at any buffer position, wich corresponds to the block entries. Decryption of the other blocks ends up with NTE_BAD_DATA.

Do you have any suggestions why is this happening?

Here is my encryption code:

void CWinRSA::FinishEncrypt(const char* pcbRawData, const size_t nDataSize, char** ppcbEcrData, size_t& rnEcrSize) const
{
    if (m_hProvider == NULL)
    {
        throw ("Cannot encrypt data with wrong provider!!");
    }

    if (m_hKey == NULL)
    {
        throw ("Cannot encrypt data with a wrong key!!");
    }

    size_t nBlockLength = GetBlockLength();
    size_t nPaddingSize = nBlockLength - 11;

    size_t nRemain = nDataSize % nBlockLength;
    size_t nBlockProcess = (nDataSize / nPaddingSize + (nRemain != 0 ? 1 : 0));

    size_t nResultSize = nBlockProcess * nBlockLength;

    (*ppcbEcrData) = new char[nResultSize];

    DWORD dwBufferLength = nBlockLength;
    DWORD dwDataLength;

    for (int iBlock = 0; iBlock < nBlockProcess - 1; iBlock++)
    {
        memcpy((*ppcbEcrData) + (iBlock * nBlockLength),
            pcbRawData + (iBlock * nPaddingSize), nPaddingSize);

        dwDataLength = nPaddingSize;

        if (!CryptEncrypt(m_hKey, NULL, FALSE, 0,
            (BYTE*)((*ppcbEcrData) + (iBlock * nBlockLength)),
            &dwDataLength, dwBufferLength))
        {
            throw ("Cannot encrypt data!!");
        }
    }

    memcpy((*ppcbEcrData) + ((nBlockProcess - 1) * nBlockLength),
        pcbRawData + ((nBlockProcess - 1) * nPaddingSize), (nRemain ? nRemain : nPaddingSize));

    dwDataLength = (nRemain ? nRemain : nPaddingSize);

    if (!CryptEncrypt(m_hKey, NULL, TRUE, 0,
        (BYTE*)((*ppcbEcrData) + ((nBlockProcess - 1) * nBlockLength)),
        &dwDataLength, dwBufferLength))
    {
        throw ("Cannot encrypt data!!");
    }

    rnEcrSize = nResultSize;
}

the decryption:

void CWinRSA::FinishDecrypt(const char* pcbRawData, const size_t nDataSize, char** ppcbDecData, size_t& rnDecSize) const
{
    if (m_hProvider == NULL)
    {
        throw ("Cannot decrypt data with wrong provider!!");
    }

    if (m_hKey == NULL)
    {
        throw ("Cannot decrypt data with a wrong key!!");
    }

    size_t nBlockLength = GetBlockLength();

    if ((nDataSize % nBlockLength) != 0)
    {
        throw ("Cannot decrypt data!! Probably data is corrupted!!");
    }

    size_t nPaddingSize = nBlockLength - 11;
    size_t nBlockProcess = nDataSize / nBlockLength;

    size_t nResultSize = nBlockProcess * nPaddingSize;

    (*ppcbDecData) = new char[nResultSize];

    DWORD dwDataLength;

    char* pcbComputeResult = new char[nBlockLength];

    for (int iBlock = 0; iBlock < nBlockProcess - 1; iBlock++)
    {
        memcpy(pcbComputeResult, pcbRawData + (iBlock * nBlockLength), nBlockLength);

        if (!CryptDecrypt(m_hKey, NULL, FALSE, 0, (BYTE*)pcbComputeResult, &dwDataLength))
        {
            throw ("Cannot decrypt data!!");
        }

        memcpy((*ppcbDecData) + (iBlock * nPaddingSize), pcbComputeResult, nPaddingSize);
    }

    memcpy(pcbComputeResult, pcbRawData + ((nBlockProcess - 1) * nBlockLength), nBlockLength);

    if (!CryptDecrypt(m_hKey, NULL, TRUE, 0, (BYTE*)pcbComputeResult, &dwDataLength))
    {
        DWORD dwError = GetLastError();

        throw ("Cannot decrypt data!!");
    }

    memcpy((*ppcbDecData) + ((nBlockProcess - 1) * nPaddingSize), pcbComputeResult, nPaddingSize);

    rnDecSize = ((nBlockProcess - 1) * nPaddingSize) + dwDataLength;

    delete[] pcbComputeResult;
    pcbComputeResult = NULL;
}

Solution

  • I have found an answer. I must initialize dwDataLength with block length in bytes before all decryption calls.

    dwDataLength = nBlockLength;
    
    if (!CryptDecrypt(m_hKey, NULL, TRUE, 0, (BYTE*)pcbComputeResult, &dwDataLength))
    {
         DWORD dwError = GetLastError();
    
         throw ("Cannot decrypt data!!");
    }
    

    Entire decryption method

    void CWinRSA::FinishDecrypt(const char* pcbRawData, const size_t nDataSize, char** ppcbDecData, size_t& rnDecSize) const
    {
        if (m_hProvider == NULL)
        {
            throw ("Cannot decrypt data with wrong provider!!");
        }
    
        if (m_hKey == NULL)
        {
            throw ("Cannot decrypt data with a wrong key!!");
        }
    
        size_t nBlockLength = GetBlockLength();
    
        if ((nDataSize % nBlockLength) != 0)
        {
            throw ("Cannot decrypt data!! Probably data is corrupted!!");
        }
    
        size_t nPaddingSize = nBlockLength - 11;
        size_t nBlockProcess = nDataSize / nBlockLength;
    
        size_t nResultSize = nBlockProcess * nPaddingSize;
    
        (*ppcbDecData) = new char[nResultSize];
    
        DWORD dwDataLength;
    
        char* pcbComputeResult = new char[nBlockLength];
    
        for (int iBlock = 0; iBlock < nBlockProcess - 1; iBlock++)
        {
            memcpy(pcbComputeResult, pcbRawData + (iBlock * nBlockLength), nBlockLength);
    
            dwDataLength = nBlockLength;
    
            if (!CryptDecrypt(m_hKey, NULL, FALSE, 0, (BYTE*)pcbComputeResult, &dwDataLength))
            {
                throw ("Cannot decrypt data!!");
            }
    
            memcpy((*ppcbDecData) + (iBlock * nPaddingSize), pcbComputeResult, nPaddingSize);
        }
    
        memcpy(pcbComputeResult, pcbRawData + ((nBlockProcess - 1) * nBlockLength), nBlockLength);
    
        dwDataLength = nBlockLength;
    
        if (!CryptDecrypt(m_hKey, NULL, TRUE, 0, (BYTE*)pcbComputeResult, &dwDataLength))
        {
            throw ("Cannot decrypt data!!");
        }
    
        memcpy((*ppcbDecData) + ((nBlockProcess - 1) * nPaddingSize), pcbComputeResult, nPaddingSize);
    
        rnDecSize = ((nBlockProcess - 1) * nPaddingSize) + dwDataLength;
    
        delete[] pcbComputeResult;
        pcbComputeResult = NULL;
    }