BASE 64 Decoding and Encoding Class 2003

Environment: VC6 SP4, NT4 SP3, Win95 and above, Win2k and above

Introduction

After publishing my POP3 class in 2001, lots of e-mails reached me. Most people were not asking for a problem or reporting bugs but they asked me how to de- or encode Base64 data. I think at the beginning of 2002 a new virus spread around the world. It was using a bug in MS Outlook that executed attached files automatically. I wanted to know how it works to protect my computer properly. That was the day when I decided to write my own Base64 class for decoding and encoding data out and into the Base64 format because I wanted to write a mail filter tool.

Sources

The Base64 format is decribed in RFC 2045 in part 6.8 called "Base64 Content-Transfer-Encoding." It explains all rules that must be followed to write a proper Base64 coder. This means "The encoded output stream must be represented in lines of no more than 76" and so on... Here is a link to this RFC. http://www.faqs.org/ftp/rfc/rfc2045.txt

Abilities

My Base64 class can decode and encode data in and out of the Base64 format. You can decode Base64 data in two ways. You can decode a whole file containing Base64 data or you can pass a buffer to the class that contains the Base64 data. Encoding works the same. Per file or per buffer as wanted and needed. The class works no matter how much data you pass or how huge the files are. The only thing you need to take care of is to reserve enough memory.

Sample

Below I will explain all four cases of de- and encoding Base64 data with my class. You can download the source of this class and use it. The only thing I would like to ask you is to reference at this class in the Info screen of your app, maybe, but that's up to you.

Contact Information

If you have problems using the class or want to tell me of improvements and so forth, feel free to mail me. I am still improving the class and optimizing it, so there may be updates in the near future.

Samples

Case 1: You want to decode a Base64-encoded application with the file name "test.b64" and store the decoded application in "test.exe".

CBase64 B64;
B64.DecodeFile ("test.b64", "test.exe");

Case 2: You want to decode a Base64-encoded buffer and store the decoded data in another buffer. This case is a bit more complicated. Base64-encoded data is around 30% larger in size than normal data. This means you do not need a buffer that is as large as the buffer containing the Base64 data. This is solved for you with the CreateMatchingDecodingBuffer function. The first Parameter of this function is the address of the buffer that holds the data that you want to decode. The second parameter is an address to a char* that will receive the adress of a matching decoding buffer. DO NOT FORGET to delete this buffer as soon as you do not need it any more. Here, pInput is the buffer that contains the Base64 data.

char* pDecodedDataBuffer;
B64.CreateMatchingDecodingBuffer (pInput, &pDecodedDataBuffer);
B64.DecodeBuffer (pInput, pDecodedDataBuffer);
//...use the decoded data write it to a file etc. ...
delete pDecodedDataBuffer;

Case 3: You want to encode a file into Base64 and store the decoded data in the file in "test.b64".

CBase64 B64;
B64.EncodeFile ("test.exe", "test.b64");

Case 4: You want to encode a buffer and store the encoded data in another buffer. This case is a bit more complicated than just encoding a file. Encoding a buffer means that your encoded data will be around 30% larger than your source data. To create a buffer that is capable of holding all the encoded data, you can call CreateMatchingEncodingBuffer. The function takes two parameters. The first parameter tells the function how many bytes there are in your buffer that need to be encoded. The second parameter is is the address to a char* that will be filled by the function with the address of a matching buffer for the encoded data. DO NOT FORGET to delete pInput as soon as you do not need it any more. In this sample, pInput contains 114 bytes that I want to be encoded. At first, I create a matching buffer by calling CreateMatchingEncodingBuffer. Then, I pass pInput and pB64Output to EncodeBuffer and tell it that there are 114 bytes to encode in pInput. The function now converts the bytes from pInput to Base64 and stores the result in pB64Output.

char* pB64Output;
B64.CreateMatchingEncodingBuffer (pInput, &pB64Output);
B64.EncodeBuffer (pInput, 114, pB64Output);
//... process the encoded data write it to a file, etc.
delete pInput;

Downloads

Download source - 4 Kb


Comments

  • Thanks for such a great code!

    Posted by ppkumar_74 on 09/13/2007 04:12am

    Dear Sir, I am a novice in programming and i need to use base64 program to encode and decode and i found your source pretty helpful.i am very thankful to you.sir kindly allow me to use your code into my project.i would be really greatful if you allowed me to do so. Thanking you, praveen

    Reply
  • Bug fix during decoding data with length no divisible by 4

    Posted by petro_nm on 03/15/2005 11:21am

    There was bugs in CBase64::CalculateRecquiredDecodeOutputBufferSize() and in CBase64::DecodeBuffer(). 
    Nobody fixed its yet. So my proposition is below.
    (you must add this in CBase64.cpp)
    ...
    #include 
    ...
    unsigned long CBase64::CalculateRecquiredDecodeOutputBufferSize (const char* p_pInputBufferString)
    {
    	unsigned long BufferLength = strlen (p_pInputBufferString);
    	
    	div_t input = div (BufferLength, 4);
    
    	const char* pLastQuarter = p_pInputBufferString + input.quot * 4;
    
    	char* pNewLastQuarter = NULL;
    	int numQuarters = numQuarters = input.quot;
    
    	if (input.rem != 0) {
    		pNewLastQuarter = new char[4];
    		for (int i = 0; i < input.rem; i++) {
    			pNewLastQuarter[i] = pLastQuarter[i];
    		}
    		for (; i < 4; i++) {
    			pNewLastQuarter[i] = '=';
    		}
    		pLastQuarter = pNewLastQuarter;
    		numQuarters = input.quot + 1;
    	} 
    
    	unsigned long res;
    	if (*(pLastQuarter + 3) != '=')
    	{
    		res = numQuarters * 3;
    	}
    	else
    	{
    		if (*(pLastQuarter + 2) == '=')
    		{
    			res = numQuarters * 3 - 2;
    		}
    		else
    		{
    			res = numQuarters * 3 - 1;
    		}
    	}
    
    	delete[] pNewLastQuarter;
    
    	return res;
    }
    
    ...
    
    unsigned long CBase64::DecodeBuffer (const char* p_pInputBufferString, char* p_pOutputBuffer)
    {
    	unsigned long InputBufferIndex  = 0;
    	unsigned long OutputBufferIndex = 0;
    	unsigned long InputBufferLength = strlen(p_pInputBufferString);
    
    	char ByteQuartet [4];
    	int i = 0;
    	while (InputBufferIndex < InputBufferLength)
    	{
    		// Ignore all characters except the ones in BASE64_ALPHABET
    		if ((p_pInputBufferString [InputBufferIndex] >= 48 && p_pInputBufferString [InputBufferIndex] <=  57) ||
    			(p_pInputBufferString [InputBufferIndex] >= 65 && p_pInputBufferString [InputBufferIndex] <=  90) ||
    			(p_pInputBufferString [InputBufferIndex] >= 97 && p_pInputBufferString [InputBufferIndex] <= 122) ||
    			p_pInputBufferString [InputBufferIndex] == '+' || 
    			p_pInputBufferString [InputBufferIndex] == '/' || 
    			p_pInputBufferString [InputBufferIndex] == '=')
    		{
    			ByteQuartet [i] = p_pInputBufferString [InputBufferIndex];
    			i++;
    		}
    		
    		InputBufferIndex++;
    		
    		if (i == 4) {
    			OutputBufferIndex += DecodeByteQuartet(ByteQuartet, p_pOutputBuffer + OutputBufferIndex);
    			i = 0;
    		}
    	}
    	// if i != 0 mean that there was some problem with input data
    	assert(i == 0);
    
    	// OutputBufferIndex gives us the next position of the next decoded character
    	// inside our output buffer and thus represents the number of decoded characters
    	// in our buffer.
    	return OutputBufferIndex;
    }

    • Is this fix correct?

      Posted by KaramChand03 on 02/17/2006 07:44am

      Is this the correct fix? I would like to use this class but just saw this message. If this is the correct fix then I will go ahead and do it.

      Reply
    Reply
  • Bug Fix

    Posted by Legacy on 02/12/2004 12:00am

    Originally posted by: Jan

    Hello,
    thanks for the hint.
    I have not yet tested it. I figured to have a problem with special file sizes a few days ago though.
    I am sure that will fix it.

    Sorry for not posting updates on this class
    but I have been busy with my rl programming job.

    Take care
    Jan

    Reply
  • Bug in CBase64::DecodeBuffer(...)

    Posted by Legacy on 02/09/2004 12:00am

    Originally posted by: Rhys Ziemer

    Well there is an error in my bug fix which I can't believe I didn't catch earlier than this (almost three months later).
    
    

    First off, the previous mentioned bug I declared occurred with files that contained a multiple of 19 bytes. This is incorrect, the actual multiple is that of 57 bytes ((57 * 4 / 3) = 76, which is when the program appends \r\n).

    My bug fix causes the last up to 3 bytes to be lopped off if the file is not a multiple of 57 bytes. The statement I posted:

    if (InputBufferIndex < InputBufferLength)
    OutputBufferIndex += DecodeByteQuartet(ByteQuartet,
    p_pOutputBuffer + OutputBufferIndex);

    It makes sense that the standard termination of the Base64 conversion should occurr when the InputBufferIndex reaches the InputBufferLength. Unfortunately, this causes the DecodeByteQuartet to never be called for the last 1 to 3 bytes. This should only be the case if the last characters read in happened to be \r\n, and the file is thus a multiple of 57 bytes. The simple fix to my previously posted code is as follows.

    First, bump the i decleration out of the for loop, from "for(int i=0 ..." to just preceding it. It should now look like:

    int i = 0;
    for (i = 0; ((i < 4)&&(Inpu ...

    And, change the if statement surrounding DecodeByteQuartet. Instead of (InputBufferIndex < InputBufferLength), it should state:

    if (i != 0)
    OutputBufferIndex += DecodeByteQuartet(...

    Sorry for posting wrong code. Hope I got it right this time.

    • Exception when call DecodeFile

      Posted by cklam on 10/13/2004 10:43pm

      //the test.txt contain the following text "I am Anthony Do you see me"
      
      CBase64 B64;
      B64.EncodeFile ("test.txt", "test.b64"); //store encode data to file "test.b64"
      B64.DecodeFile ("test.b64", "test.bat");
      
      But when it call the DecodeFile , the DecodeFile throw execption
      
      Please check it, thank you for your help!

      Reply
    Reply
  • Bug Fix

    Posted by Legacy on 12/17/2003 12:00am

    Originally posted by: Jan

    Hi Rhys,
    thanks for the good work.
    I think you are right.
    I haven't worked on the Base64 Class
    since posting the article.
    I will hopefully have the time to update the class
    and deliver some more efficient routines
    to encode and decode data.

    Merry Christmas
    Jan

    Reply
  • Bug in CBase64::DecodeBuffer(...)

    Posted by Legacy on 12/12/2003 12:00am

    Originally posted by: Rhys Ziemer

    There is a rather unfortunate bug in Decode Buffer.
    
    

    The problem is when the byte sequence being encoded is exactly divisible by 19, after the final bytes are encoded, the encoding sequence places a crlf (\r\n) after the last of the encoded characters. When attempting to decode this buffer, the program gets stuck in the "for (int i = 0; i < 4; i++)" loop until a valid 4 characters are read. This causes memory beyond what was allocated to be "decoded".

    There are two available solutions that I can think of. Either, when the encode algorithm decides to insert a crlf, first check to make sure that the final characters of the input byte array were not just encoded. (i.e. If you're done encoding, don't add a crlf to the end of the string).

    I would like to think that the encode function was written first, and the true bug lies in the decode function. Thus below I will address the bug fix (with modified source) in the decode function. Simply modify the for loop condition to include the while loop conditon. Then prefix the DecodeByteQuartet call with the same while condition.

    for (int i = 0; ((i < 4)&&
    (InputBufferIndex < InputBufferLength)); i++)
    {
    ByteQuartet [i] =
    p_pInputBufferString [InputBufferIndex];
    // Ignore all characters except the ones in
    // BASE64_ALPHABET
    if ((ByteQuartet [i] >= 48 && ByteQuartet [i] <= 57)||
    (ByteQuartet [i] >= 65 && ByteQuartet [i] <= 90)||
    (ByteQuartet [i] >= 97 && ByteQuartet [i] <= 122)||
    ByteQuartet [i] == '+' || ByteQuartet [i] == '/' ||
    ByteQuartet [i] == '=')
    {
    }
    else
    {
    // Invalid character
    i--;
    }
    }
    if (InputBufferIndex < InputBufferLength)
    OutputBufferIndex += DecodeByteQuartet(ByteQuartet,
    p_pOutputBuffer + OutputBufferIndex);


    I think that should do it. If I find any problems with these solutions, I'll repost a corrected version.

    Reply
  • Do you know...

    Posted by Legacy on 09/29/2003 12:00am

    Originally posted by: Manuel

    Hello Jan,

    Do you know how to compile your Cbase64 class in Borland C++ 6.0 or other free class on internet???

    Thanks for all!

    Reply
  • Does the job, but isn't very CPU efficient

    Posted by Legacy on 09/08/2003 12:00am

    Originally posted by: Code2Go

    Your solution performs allot of shifting about (<<s and >>s). The task can be accomplished with less shifts and smaller ones as well. I don't mean to be picky, but it could be crucial for performance critical applications. Other than this, good job Jan!

    Reply
  • You are create ;-))

    Posted by Legacy on 08/24/2003 12:00am

    Originally posted by: Marcel Beutner

    A class like this(soooo easy to use), I have ever searched.

    Thak you ;-))

    Reply
  • Perfect job

    Posted by Legacy on 07/16/2003 12:00am

    Originally posted by: Sohail Pakistani

    Works absolutely fine. Thanks for sharing the code with community.
    Regards,
    Sohail Pakistani

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: September 16, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you starting an on-premise-to-cloud data migration project? Have you thought about how much space you might need for your online platform or how to handle data that might be related to users who no longer exist? If these questions or any other concerns have been plaguing you about your migration project, check out this eSeminar. Join our speakers Betsy Bilhorn, VP, Product Management at Scribe, Mike Virnig, PowerSucess Manager and Michele …

  • This ESG study by Mark Peters evaluated a common industry-standard disk VTl deduplication system (with 15:1 reduction ratio) versus a tape library with LTO-5, drives with full nightly backups, over a five-year period.  The scenarios included replicated systems and offsite tape vaults.  In all circumstances, the TCO for VTL with deduplication ranged from about 2 to 4 times more expensive than the LTO-5 tape library TCO. The paper shares recent ESG research and lots more. 

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds