// base64 decoder // // Reference: ftp://ftp.isi.edu/in-notes/rfc2045.txt // // R. Perry, April 2000 // // updated Sept 2013 to reset before throwing an exception // package Base64; import java.io.*; // for testing, in main() /** * Decode base64 strings into a byte array. *

* This class is designed using internal buffers so that * the caller can decode strings, presumably read line-by-line * from a base64-encoded input file, then obtain the final * decoded result using the flush() method. *

* Example usage (from main(), for testing): *

 *   Base64.Decoder b64 = new Base64.Decoder();
 *
 *   BufferedReader f = new BufferedReader( new InputStreamReader( System.in));
 *   String line;
 * 
 *   while( (line = f.readLine()) != null)
 *     b64.decode( line);
 * 
 *   byte[] r = b64.flush();
 * 
 *   System.out.write( r);
 * 
 *   System.out.close();
 * 
* * @author Rick Perry * @version 29 September 2013 */ public class B64Decoder { /** * The mapping from ASCII chars to 6-bit integers. */ private static byte[] table = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 2 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 64, -1, -1, // 3 // 64 for '=' -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 4 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 5 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 6 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 7 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 8 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 9 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // B -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // D -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // F }; /** * Internal 4-byte buffer. */ private byte[] buf; /** * Internal buffer current length. */ private int buflen; /** * Flag for final buffer. */ private boolean end; /** * Decoded output array. */ private byte[] out; /** * Decoded output current length. */ private int outlen; /** * Size of decoded output array. */ private int maxlen; /** * Construct a new Decoder. */ public B64Decoder() { maxlen = 4096; out = new byte[maxlen]; buf = new byte[4]; reset(); } /** * reset the Decoder. */ public void reset() { outlen = buflen = 0; end = false; } /** * Append a byte to the output array. *

* The output array size is automatically increased as needed. * * @param b the byte to be appended. */ private void append( byte b) { if( outlen == maxlen) { // increase size maxlen *= 1.5; byte[] x = new byte[maxlen]; System.arraycopy( out, 0, x, 0, outlen); out = x; } out[outlen++] = b; } /** * Decode one character. *

* Characters outside of the base64 character set are ignored. * The character is appended to the internal 4-byte buffer; * whenever the internal buffer is full * it is decoded into 1 to 3 bytes (depending on trailing '=' * padding) which are appended to the output array. * * @param b the character to be decoded. * @exception IllegalArgumentException * if bad '=' characters are detected. */ private void decodeChar( char b) throws IllegalArgumentException { if( end) // previously saw trailing '=' { reset(); throw new IllegalArgumentException( "bad '='"); } byte i = table[ b]; if( i < 0) return; buf[buflen++] = i; if( buflen == 4) { buflen = 0; if( buf[0] == 64 || buf[1] == 64 || (buf[2] == 64 && buf[3] != 64) ) { reset(); throw new IllegalArgumentException( "bad '='"); } append( (byte) ((buf[0] << 2) | (buf[1] >>> 4)) ); if( buf[2] == 64) { end = true; return; } append( (byte) ((buf[1] << 4) | (buf[2] >>> 2)) ); if( buf[3] == 64) { end = true; return; } append( (byte) ((buf[2] << 6) | buf[3]) ); } } /** * Decode a string. *

* The string is decoded character-by-character into * an internal buffer. * * @param s the string to be decoded. * @exception IllegalArgumentException * if bad '=' characters are detected. */ public void decode( String s) throws IllegalArgumentException { for( int i = 0; i < s.length(); ++i) decodeChar( s.charAt(i)); } /** * Return decoded output. *

* The decoder internal buffer is reset after this call is made. * * @return the decoded output byte array. * @exception IllegalArgumentException * if the cumulative input was not a multiple of 4 characters. */ public byte[] flush() throws IllegalArgumentException { if( buflen != 0) { reset(); throw new IllegalArgumentException( "not multiple of 4 chars"); } byte[] r = new byte[outlen]; System.arraycopy( out, 0, r, 0, outlen); reset(); return r; } /** * Just for testing. */ public static void main( String args[]) throws Exception { byte[] b = new byte[4096]; int n; // Base64.Decoder b64 = new Base64.Decoder(); B64Decoder b64 = new B64Decoder(); BufferedReader f = new BufferedReader( new InputStreamReader( System.in)); String line; while( (line = f.readLine()) != null) b64.decode( line); byte[] r = b64.flush(); System.out.write( r); System.out.close(); } }