How does Base64 work

Base64 is a data encoding scheme used in safe data transfer such as HTTP and its extensions. Base64 encoding can conver arbitrary group of bytes into a sequence of readable ASCII characters. These converted characters can safely put in a HTTP header without causing any problem while the peers process the HTTP header.

Base64 encoding was invented as part of the MIME content transfer encoding. It is similar to other encoding schemes such as Uuencode and BinHex but with higher efficiency.

8 bit to 6 bit

Base64 will divide the bytes into groups of 6 bits. Every 6 bits will map to a character. This character will be one of the 64 characters in the Base64 character table. These 64 characters are common and can be safely put in a HTTP header. These characters include a-z, A-Z, 0-9, +, / and a special purpose character =(The 65th character).

Since Base64 will use a character to represent 6 bits of data, so the size of Base64 encoded data will be 33% larger than the original data. 

Below is a simple example of how Base64 works. Assume there are three characters "Ow!" which are to be Base64 encoded.

  1. The characters "Ow!" will be converted to three 8 bit byte(0x4F, 0x77, 0x21)
  2. These 3 bytes will be transformed to 24 bits(01001111 01110111 00100001)
  3. These 24 bits will be divided into groups of 6 bits(010011、110111、011100、1000001). 
  4. Each 6 bits will be a value between 0 and 63, it will map to one of 64 characters above. The end result after encoding is "T3ch".

Base64 Padding

Base64 will take byte sequence and divide them to groups of 6 bits. Sometimes it's impossible to divide the sequence into exact number of groups of 6 bits. In this case, 0 will be padded into the sequence so that it will be exact number of groups of 24 bits(least common multiple of 6 and 8). This process is called padding.

When encoding the padded data, if there are 6 bits group fully padded(each bit in the group is padded without containing any bit from the original data), it will be mapped to "=". 

Below are a few examples of how padding works :

  • a:a -- 011000 010011 101001 100001 -- YTph

  • a:aa -- 011000 010011 101001 100001 011000 01xxxx xxxxxx xxxxxx -- YTphYQ==

  • a:aaa -- 011000 010011 101001 100001 011000 010110 0001xx xxxxxx -- YTphYWE=

  • a:aaaa -- 011000 010011 101001 100001 011000 010110 000101 1000001 -- YTphYWFh

The reason why need to pad to 24 bits but not 6 bits is because it ensures the decoded data is the same as original data if two Based64 encoded sequences are concatenated and then decoded.

For example, if we encode two string "a.aa" and "a.aa", if we just pad to 6 bits, then the encoded string for "a.aa" would be 011000 010011 101001 100001 011000 010000. Now if these two encoded strings are concatenated, it will become 010011 101001 100001 011000 010000 010011 101001 100001 011000 010000. If this string is being decoded, the result will be "01001110 10011000 01011000 01000001 00111010 01100001 01100001 0000 and it cannot be decoded correctly.

Reference :