• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 508
  • Last Modified:

CryptoAPI - Encrypted string contains NULL character in middle of string.

I'm using the CryptoAPI to encrypt and decrypt data in our database. Everything seems to work fine with the exception that CryptEncrypt(...) sometimes returns an encrypted string that is terminated in the middle of the string.

The provider I'm using is PROV_RSA_FULL
For hashing I'm using CALG_MD5
For encryption I'm using CALG_RC2

Is there a known method of encryption (using the CryptoAPI) that will never put a terminator in the middle?

This question is extremely urgent, boss will pull the plug if I can't prove the encryption/decryption works.

Thanks,
Brian
0
BrianM_AZ
Asked:
BrianM_AZ
1 Solution
 
mrblueCommented:
Encryped data is binary so '\0' can happen. You can Base64 encode binary data to assure "normal" characters.

Check out CryptBinaryToString() & CryptStringToBinary()
0
 
mrblueCommented:
This is piece of code based on MSDN sample:

STDMETHODIMP CBase64EncoderDecoderObj::BASE64_Encode(VARIANT *InputData, VARIANT *OutputData)
{
      VARTYPE vt;
      LPBYTE pbtInputData, pbtOutputData;
      HRESULT hRes;
      TempBucket Raw;
      DWORD nInputSize, nOutputSize, nIndex = 0, nEDataLen = 0;

      if((InputData==NULL)||(OutputData==NULL)) return E_POINTER;
      nInputSize = GetSafeArraySize(*InputData, vt);
      if((nInputSize==0)||(vt!=VT_UI1)) return E_INVALIDARG;

      hRes = ::SafeArrayAccessData(InputData->parray, (void **)&pbtInputData);

      if(SUCCEEDED(hRes)) {
            nOutputSize = (nInputSize/3)*4+(((nInputSize%3)!=0)?4:0);

            if(AssureSafeArraySize(*OutputData, VT_UI1, nOutputSize, 0, 0)) {
                  hRes = ::SafeArrayAccessData(OutputData->parray, (void **)&pbtOutputData);

                  if(SUCCEEDED(hRes)) {
                        while((nIndex + 3) <= nInputSize) {
                              Raw.Clear();
                              ::CopyMemory(&Raw, pbtInputData + nIndex, 3);
                              Raw.nSize = 3;
                              _EncodeToBuffer(Raw, pbtOutputData + nEDataLen);
                              nIndex += 3;
                              nEDataLen += 4;
                        }

                        if(nInputSize > nIndex) {
                              Raw.Clear();
                              Raw.nSize = (BYTE)(nInputSize - nIndex);
                              ::CopyMemory(&Raw, pbtInputData + nIndex, nInputSize - nIndex);
                              _EncodeToBuffer(Raw, pbtOutputData + nEDataLen);
                              nEDataLen += 4;
                        };

                        ::SafeArrayUnaccessData(OutputData->parray);
                  };
            }
            else
                  hRes = E_OUTOFMEMORY;

            ::SafeArrayUnaccessData(InputData->parray);
      };

      return hRes;
}

STDMETHODIMP CBase64EncoderDecoderObj::BASE64_Decode(VARIANT *InputData, VARIANT *OutputData)
{
      VARTYPE vt;
      LPBYTE pbtInputData, pbtOutputData;
      HRESULT hRes;
      TempBucket Raw;
      DWORD nInputSize, nOutputSize, nIndex = 0, nEDataLen = 0, nDDataLen = 0, i, nRestSize;

      if((InputData==NULL)||(OutputData==NULL)) return E_POINTER;
      nInputSize = GetSafeArraySize(*InputData, vt);
      if((nInputSize==0)||(vt!=VT_UI1)) return E_INVALIDARG;

      hRes = ::SafeArrayAccessData(InputData->parray, (void **)&pbtInputData);

      if(SUCCEEDED(hRes)) {
            i = 0;

            while(i < nInputSize) {
                  if(!_IsBadMimeChar(pbtInputData[i])) {
                        if(pbtInputData[i]!=TEXT('=')) {  // not padding character
                              pbtInputData[nEDataLen] = pbtInputData[i];
                              nEDataLen++;
                        };
                  };

                  i++;
            }

            nRestSize = nEDataLen%4;
            if(nRestSize>0) nRestSize--;
            nOutputSize = (nEDataLen/4)*3 + nRestSize;

            if(AssureSafeArraySize(*OutputData, VT_UI1, nOutputSize, 0, 0)) {
                  hRes = ::SafeArrayAccessData(OutputData->parray, (void **)&pbtOutputData);

                  if(SUCCEEDED(hRes)) {
                        while((nIndex + 4) <= nEDataLen) {
                              Raw.Clear();
                              Raw.nData[0] = m_DecodeTable[pbtInputData[nIndex]];
                              Raw.nData[1] = m_DecodeTable[pbtInputData[nIndex + 1]];
                              Raw.nData[2] = m_DecodeTable[pbtInputData[nIndex + 2]];
                              Raw.nData[3] = m_DecodeTable[pbtInputData[nIndex + 3]];

                              if(Raw.nData[2] == 255) Raw.nData[2] = 0;
                              if(Raw.nData[3] == 255) Raw.nData[3] = 0;
            
                              Raw.nSize = 4;
                              _DecodeToBuffer(Raw, pbtOutputData + nDDataLen);
                              nIndex += 4;
                              nDDataLen += 3;
                        }
      
                        // If nIndex < nEDataLen, then we got a decode message without padding.
                        // We may want to throw some kind of warning here, but we are still required
                        // to handle the decoding as if it was properly padded.
                        if(nIndex < nEDataLen) {
                              Raw.Clear();

                              for(DWORD i = nIndex; i<nEDataLen; i++) {
                                    Raw.nData[i - nIndex] = m_DecodeTable[pbtInputData[i]];
                                    Raw.nSize++;

                                    if(Raw.nData[i - nIndex] == 255) Raw.nData[i - nIndex] = 0;
                              }

                              _DecodeToBuffer(Raw, pbtOutputData + nDDataLen);
                              nDDataLen += (nEDataLen - nIndex);
                        };

                        ::SafeArrayUnaccessData(OutputData->parray);
                  };
            };

            ::SafeArrayUnaccessData(InputData->parray);
      };

      return hRes;
}

void CBase64EncoderDecoderObj::_EncodeToBuffer(const TempBucket &Decode, LPBYTE pbtBuffer)
{
      TempBucket      Data;

      _EncodeRaw(Data, Decode);

      for(INT i=0; i<4; i++) pbtBuffer[i] = Base64Digits[Data.nData[i]];

      switch(Decode.nSize) {
      case 1:
            pbtBuffer[2] = '=';
      case 2:
            pbtBuffer[3] = '=';
      }
}

void CBase64EncoderDecoderObj::_EncodeRaw(TempBucket &Data, const TempBucket &Decode)
{
      BYTE            nTemp;

      Data.nData[0] = Decode.nData[0];
      Data.nData[0] >>= 2;
      
      Data.nData[1] = Decode.nData[0];
      Data.nData[1] <<= 4;
      nTemp = Decode.nData[1];
      nTemp >>= 4;
      Data.nData[1] |= nTemp;
      Data.nData[1] &= 0x3F;

      Data.nData[2] = Decode.nData[1];
      Data.nData[2] <<= 2;

      nTemp = Decode.nData[2];
      nTemp >>= 6;

      Data.nData[2] |= nTemp;
      Data.nData[2] &= 0x3F;

      Data.nData[3] = Decode.nData[2];
      Data.nData[3] &= 0x3F;
}

DWORD CBase64EncoderDecoderObj::_DecodeToBuffer(const TempBucket &Decode, LPBYTE pbtBuffer)
{
      TempBucket Data;
      DWORD nCount = 0;
      INT nSizeM1 = Decode.nSize-1;

      _DecodeRaw(Data, Decode);

      for(INT i=0; i<nSizeM1; i++) {
            pbtBuffer[i] = Data.nData[i];
            if(pbtBuffer[i] != 255) nCount++;
      }

      return nCount;
}

void CBase64EncoderDecoderObj::_DecodeRaw(TempBucket &Data, const TempBucket &Decode)
{
      BYTE            nTemp;

      Data.nData[0] = Decode.nData[0];
      Data.nData[0] <<= 2;

      nTemp = Decode.nData[1];
      nTemp >>= 4;
      nTemp &= 0x03;
      Data.nData[0] |= nTemp;

      Data.nData[1] = Decode.nData[1];
      Data.nData[1] <<= 4;

      nTemp = Decode.nData[2];
      nTemp >>= 2;
      nTemp &= 0x0F;
      Data.nData[1] |= nTemp;

      Data.nData[2] = Decode.nData[2];
      Data.nData[2] <<= 6;
      nTemp = Decode.nData[3];
      nTemp &= 0x3F;
      Data.nData[2] |= nTemp;
}

BOOL CBase64EncoderDecoderObj::_IsBadMimeChar(BYTE nData)
{
      switch(nData) {
      case '\r':
      case '\n':
      case '\t':
      case ' ' :
      case '\b':
      case '\a':
      case '\f':
      case '\v':
            return TRUE;
      default:
            return FALSE;
      }
}

CBase64EncoderDecoderObj()
{  
      _Init();
}

// Initialize Decoding table.
void CBase64EncoderDecoderObj::_Init()
{
      INT      i;

      for(i=0; i<256; i++) m_DecodeTable[i] = -2;

      for(i=0; i<64; i++) {
            m_DecodeTable[Base64Digits[i]] = i;
            m_DecodeTable[Base64Digits[i]|0x80] = i;
      }

      m_DecodeTable['='] = -1;
      m_DecodeTable['='|0x80] = -1;
}

0
 
mrblueCommented:
And also

// Internal bucket class.
class TempBucket {
public:
    BYTE            nData[4];
    BYTE            nSize;
    void            Clear() { ::ZeroMemory(nData, 4); nSize = 0; };
};
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
mrblueCommented:
class CBase64EncoderDecoderObj : [...] {
[...]
static char      Base64Digits[];
static char m_DecodeTable[256];
[...]
};

// Digits...
char CBase64EncoderDecoderObj::Base64Digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char CBase64EncoderDecoderObj::m_DecodeTable[256];
0
 
mrblueCommented:
I you don't want it you can express binary as hexadecimal symbols - FF hex instead of 255 decimal , 80 hex instead of 128 decimal, and so on
0
 
jkrCommented:
RSA - as opposed to e.g. block cipher algorithms - will keep the original data size, your encrypted string will be of exactly the same length as the unencrypted string, so there should be no problem in maintaining that information. Where are you running into trouble with that?
0
 
WelkinMazeCommented:
Hi,
if you have zeros in the middle of the string but know its length you can manipulate it without using the standard string routines that treat strings as null terminated character arrays.
0
 
BrianM_AZAuthor Commented:
Thanks for all the help. I need to support w2k so I can't use CryptBinaryToString() & CryptStringToBinary(). Real bummer though, it works great.
Brian
0

Featured Post

Upgrade your Question Security!

Add Premium security features to your question to ensure its privacy or anonymity. Learn more about your ability to control Question Security today.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now