Note to readers: The first half of this post is a simple introduction to Base64 intended mainly for beginners. The second half discusses implementing Base64 along with sample code, and is intended for intermediate- to advanced-level programmers.
What is Base64?
Base64 is used to transform raw binary data into printable text data. If you’re totally new to Base64, let’s break down this definition a bit further. Maybe you’re wondering, “what’s the difference between text vs. binary data. An example of text data is this paragraph. Remember that from a computer’s perspective, each letter is just a number. For example, to a computer, the letter “W” is just 87. With this in mind, “text data” is simply a string of numbers that all correspond to letters, numbers, symbols, etc.
Binary data often contains numbers that don’t match up to any letter or number. For example, picture, song, and movie files all contain binary data. Have you ever accidentally opened a picture or song in a word processor, and ended up with a screen full of junk that looks like “0:09PrintIM0300öÇ äùÇí”à’à@ê02?? 20êöêÆëë¬í”? That’s an example of binary data. Base64 gives us a way to convert this binary data to text data.
Why would we want to convert binary data into text data?
Here’s a scenario where Base64 would be useful: You’re talking with a friend via instant message (IM), and you want to send a picture. Unfortunately, for whatever reason (corporate firewalls, bad/different IM apps, etc.), you can’t send the file directly. However, you could simply convert the picture to Base64, and copy/paste the resulting text into the IM window. Your friend can take the Base64 text and convert it back into binary data, then just double click and open the picture file.
Also, when you visit a webpage, the images on that page are sent in Base64. Your web browser decodes them back into binary data and displays the images for you.
If you’re interested in other uses of Base64, check out the Wikipedia article.
The Base64 Encoding Algorithm
Alright, now it’s time to discuss implementing a Base64 encoder/decoder. I’m using C++ for this example, but I suspect that it would be easy to translate the code to other languages.
Base64 is simply a base-64 number system, hence the name. It’s an extension of the Decimal and Hexadecimal number systems. To review:
- In Decimal we use the digits: 0 1 2 3 4 5 6 7 8 9.
- In Hexadecimal we use the digits: 0 1 2 3 4 5 6 7 8 9 A B C D E F.
- In Base-64 we use the digits: 0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z + /
At its root, encoding a message in Base64 is just a simple substitution of binary data to Base64 digits. Because bytes are composed of 8 bits, and a base64 character can only represent 6 bits, it prevents us from just performing a 1:1 substitution. Encoding is done three bytes at a time, producing four base64 digits. Note that three bytes is 3 * 8 = 24 bits, and three Base64 digits is 4 * 6 = 24 bits. Refer to the Wikipedia article for a more complete example of this encoding. They have an excellent table demonstrating this conversion.
Before we get too far into the details, note that there are several different variants of Base64. My code supports MIME-Base64 and FileName-Base64. MIME is used when encoding images and data for web pages, and FileName always produces output that is a valid file name. To be specific, the only change is that FileName-Base64 uses the ‘-’ character instead of the ‘/’ character.
At this point we can outline a basic algorithm for conversion:
- Load up a block of binary data and store it into an array called ‘original’.
- Load 3 bytes of data from the ‘original’ array and combine into one 24-bit number.
- Take the 24-bit number and split it into four 6-bit numbers.
- Translate each 6-bit number into a base64 digit.
- This can be done with a series of ‘if’ statements.
- The same can be accomplished using a look-up table (i.e. a dictionary of sorts) that translates each 6-bit number into a Base64 digit. This is the approach I have used in my code.
- Go back to step 2 and repeat until there are < 3 bytes of data left.
- If there are 1 or 2 bytes remaining, encoding them must be handled as a special case.
- You cannot just insert 1 or 2 zeros and convert the last block, because upon decoding the data, there would be no way to tell whether those zeros were just padding or actual data.
- Wikipedia does a good job of explaining what to do in this case. Refer to the “Padding” section of the Base64 article.
- Celebrate because you’re done!
Sample Code
Below I’ve provided links to the source and header file. I’ve decided not to go through the code line-by-line in this post, because I believe that if the code is elegantly written, the code itself is its best documentation. I’ve included some extra comments that identify different parts of the encoding/decoding functions, as well as additional details in the comment blocks at the top of both the source and header file.
HTML files: (Good for viewing in a web browser, but doesn’t compile)
Source files: (These are the actual files you want to add to your project)
Questions?
Feel free to comment or ask questions if anything is unclear. If you have suggestions on how to improve the code, I’d be happy to hear them as well.