Blog Post‎ > ‎

Base64: what it is and why it is useful

posted Aug 4, 2013, 5:37 AM by Julian Zhu   [ updated Aug 4, 2013, 5:43 AM ]

Base64 is an algorithm to encode binary data to ASCII text (and decode from ASCII text back to binary). It is typically used in the following scenarios:

  • HTTP header: WWW-Authentication uses base64 encoding to encode "user: password". This is deemed as absolutely insecure for transmitting user id and password in HTTP header. The practice is as bad as clear text because base64 coded data can be easily decoded. This is not popular any longer nowadays.
  • Mime-Type data encryption for transmitting. It is common in email protocol (transmitting binary data in email as attachment), XML (embedding binary data in ASCII text format so that it is easily managed in text based XML message).
  • Combined with other security encoding algorithm to encode one-way or reversible encryption.
Here is a Java program that I wrote to implement Base64 encoding/decoding algorithm. Enjoy!

Source Code

/** @(#)Base64.java    1.00 07/03/2003
 *
 * Copyright 1996-2003. All Rights Reserved.
 *
 * @author: Qizhi Zhu (Julian6866@gmail.com)
 *
 * Change History:
 * 
 */
package com.sinotar.algorithm;

/**
 * Base64 - implements Base64 encoding algorithm.
 * <P>
 * Base64 encoding schema defines a mechanism for encoding arbitary binary
 * information for transmission by electronic mail or HTTP headers. Triplets
 * of 8-bit octets are encoded as groups of four characters, each representing
 * 6 bits of the source 24 bits. Only characters present in all variants of
 * ASCII and EBCDIC are used, avoiding incompatibilities in other forms of
 * encoding.
 * <P>
 * <PRE>
 *  ...
 *    // To encode byte array to be String object
 *    byte[] bytes = ...;        // bytes to be encoded
 *    String encodedString = Base64.encode(bytes);
 *
 *    // To decode String to be byte array
 *    byte[] decodedBytes = Base64.decode(encodedString);
 *    ...
 * </PRE>
 *
 * @version %I%, %G%
 * @since 1.2
 *
 */
public class Base64 {

    final static String baseTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /**
     * Encode a byte array.
     *
     * @param bytes a byte array to be encoded.
     * @return encoded object as a String object.
     */
    public static String encode(byte[] bytes) {

        StringBuffer tmp = new StringBuffer();
        int i = 0;
        byte pos;

        for(i=0; i < (bytes.length - bytes.length%3); i+=3) {

            pos = (byte) ((bytes[i] >> 2) & 63);
            tmp.append(baseTable.charAt(pos));

            pos = (byte) (((bytes[i] & 3) << 4) + ((bytes[i+1] >> 4) & 15));
            tmp.append(baseTable.charAt( pos ));
                   
            pos = (byte) (((bytes[i+1] & 15) << 2) + ((bytes[i+2]  >> 6) & 3));
            tmp.append(baseTable.charAt(pos));
       
            pos = (byte) (((bytes[i+2]) & 63));
            tmp.append(baseTable.charAt(pos));
       
            // Add a new line for each 76 chars.
            // 76*3/4 = 57
            if(((i+2)%56) == 0) {
                tmp.append("\r\n");
            }
        }

        if(bytes.length % 3 != 0) {

            if(bytes.length % 3 == 2) {

                pos = (byte) ((bytes[i] >> 2) & 63);
                tmp.append(baseTable.charAt(pos));

                pos = (byte) (((bytes[i] & 3) << 4) + ((bytes[i+1] >> 4) & 15));
                tmp.append(baseTable.charAt( pos ));
                       
                pos = (byte) ((bytes[i+1] & 15) << 2);
                tmp.append(baseTable.charAt(pos));
           
                tmp.append("=");

            } else if(bytes.length % 3 == 1) {
               
                pos = (byte) ((bytes[i] >> 2) & 63);
                tmp.append(baseTable.charAt(pos));

                pos = (byte) ((bytes[i] & 3) << 4);
                tmp.append(baseTable.charAt( pos ));
                       
                tmp.append("==");
            }
        }
        return tmp.toString();

    }

    /**
     * Encode a String object.
     *
     * @param src a String object to be encoded with Base64 schema.
     * @return encoded String object.
     */
    public static String encode(String src) {
       
        return encode(src.getBytes());   
    }

    public static byte[] decode(String src) throws AlgorithmException {

        byte[] bytes = null;

        StringBuffer buf = new StringBuffer(src.trim());

        // First, Remove white spaces (\r\n, \t, " ");
        int i = 0;
        char c = ' ';
        char oc = ' ';
        while( i < buf.length()) {           
            oc = c;
            c = buf.charAt(i);
            //if( oc == '\r' && c == '\n') {
            //    buf.deleteCharAt(i);
            //    buf.deleteCharAt(i-1);
            //    i -= 2;
            //} else
            if( Character.isWhitespace(c)) { // == '\t' || c == '\n' || c == '\r' || c == ' ') {
                buf.deleteCharAt(i);
                i --;
            }
            i++;
        }

        // The source should consists groups with length of 4 chars.        
        if(buf.length() % 4 != 0) {
            throw new AlgorithmException("Base64 decoding invalid length");
        }

        // pre-set byte array size.
        bytes = new byte[3 * (buf.length() / 4)];
        //int len = 3 * (buf.length() % 4);
        //System.out.println("Size of Bytes array: " + len);
        int index = 0;
       
        // Now decode each group
        for(i = 0; i < buf.length(); i+=4) {

            byte data = 0;
            int nGroup = 0;

            for(int j = 0; j < 4; j++) {

                char theChar = buf.charAt(i + j);

                if(theChar == '=') {
                    data = 0;
                } else {
                    data = getBaseTableIndex(theChar);
                }

                if(data == -1) {
                    throw new AlgorithmException("Base64 decoding bad character");
                }

                nGroup = 64*nGroup + data;
            }

            bytes[index] = (byte) (255 & (nGroup >> 16));
            index ++;

            bytes[index] = (byte) (255 & (nGroup >> 8));
            index ++;

            bytes[index] = (byte) (255 & (nGroup));
            index ++;
        }
       
        byte[] newBytes = new byte[index];
        for(i = 0; i < index; i++) {
            newBytes[i] = bytes[i];
        }

        return newBytes;
    }

    /**
     * Find index number in base table for a given character.
     *
     */
    protected static byte getBaseTableIndex(char c) {
       
        byte index = -1;

        for(byte i = 0; i < baseTable.length(); i ++) {
       
            if(baseTable.charAt(i) == c) {
                index = i;
                break;
            }
        }

        return index;
    }
}



Comments