This class provides encoding of byte arrays into Base64-encoded strings, and decoding the other way. : Byte Array « File Input Output « Java






This class provides encoding of byte arrays into Base64-encoded strings, and decoding the other way.

 
package freenet.support;

/**
 * This class provides encoding of byte arrays into Base64-encoded strings,
 * and decoding the other way.
 *
 * <P>NOTE!  This is modified Base64 with slightly different characters than
 * usual, so it won't require escaping when used in URLs.
 *
 * <P>NOTE!  This class only does the padding that's normal in Base64
 * if the 'true' flag is given to the encode() method.  This is because
 * Base64 requires that the length of the encoded text be a multiple
 * of four characters, padded with '='.  Without the 'true' flag, we don't
 * add these '=' characters.
 *
 * @author Stephen Blackheath
 */
public class Base64
{
  private static char[] base64Alphabet = {
    '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', '0', '1', '2', '3',
    '4', '5', '6', '7', '8', '9', '~', '-'};
  
  private static char[] base64StandardAlphabet = {
      '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', '0', '1', '2', '3',
      '4', '5', '6', '7', '8', '9', '+', '/'};

  /**
   * A reverse lookup table to convert base64 letters back into the
   * a 6-bit sequence.
   */
  private static byte[] base64Reverse;
  private static byte[] base64StandardReverse;
  
   // Populate the base64Reverse lookup table from the base64Alphabet table.
  static {
    base64Reverse = new byte[128];
    base64StandardReverse = new byte[base64Reverse.length];
    
      // Set all entries to 0xFF, which means that that particular letter
      // is not a legal base64 letter.
    for (int i = 0; i < base64Reverse.length; i++) {
      base64Reverse[i] = (byte) 0xFF;
      base64StandardReverse[i] = (byte) 0xFF;
    }
    for (int i = 0; i < base64Alphabet.length; i++) {
      base64Reverse[base64Alphabet[i]] = (byte) i;
      base64StandardReverse[base64StandardAlphabet[i]] = (byte) i;
    }
  }

  /**
   * Encode to our shortened (non-standards-compliant) format.
   */
  public static String encode(byte[] in)
  {
    return encode(in, false);
  }

  /* FIXME: Figure out where this function is used and maybe remove it if its not
   * used. Its old javadoc which has been here for a while fools the user into believing
   * that the format is standard compliant */
  
  /**
   * Caller should specify equalsPad=true if they want a standards compliant padding,
   * but not standard compliant encoding.
   */
  public static String encode(byte[] in, boolean equalsPad) {
    return encode(in, equalsPad, base64Alphabet);
  }
  
  /**
   * Standard compliant encoding.
   */
  public static String encodeStandard(byte[] in) {
    return encode(in, true, base64StandardAlphabet);
  }
  
  /**
   * Caller should specify equalsPad=true if they want a standards compliant encoding.
   */
  private static String encode(byte[] in, boolean equalsPad, char[] alphabet)
  {
    char[] out = new char[((in.length+2)/3)*4];
    int rem = in.length%3;
    int o = 0;
    for (int i = 0; i < in.length;) {
      int val = (in[i++] & 0xFF) << 16;
      if (i < in.length)
        val |= (in[i++] & 0xFF) << 8;
      if (i < in.length)
        val |= (in[i++] & 0xFF);
      out[o++] = alphabet[(val>>18) & 0x3F];
      out[o++] = alphabet[(val>>12) & 0x3F];
      out[o++] = alphabet[(val>>6) & 0x3F];
      out[o++] = alphabet[val & 0x3F];
    }
    int outLen = out.length;
    switch (rem) {
      case 1: outLen -= 2; break;
      case 2: outLen -= 1; break;
    }
      // Pad with '=' signs up to a multiple of four if requested.
    if (equalsPad)
      while (outLen < out.length)
        out[outLen++] = '=';
    return new String(out, 0, outLen);
  }

  /**
   * Handles the standards-compliant padding (padded with '=' signs) as well as our
   * shortened form.
 * @throws IllegalBase64Exception 
   */
  public static byte[] decode(String inStr) throws Exception {
    return decode(inStr, base64Reverse);
  }
  
  /**
   * Handles the standards-compliant base64 encoding.
   */
  public static byte[] decodeStandard(String inStr) throws Exception {
    return decode(inStr, base64StandardReverse);
  }

  /**
   * Handles the standards-compliant (padded with '=' signs) as well as our
   * shortened form.
   */
  private static byte[] decode(String inStr, byte[] reverseAlphabet)
    throws Exception
  {
    try {
      char[] in = inStr.toCharArray();
      int inLength = in.length;

        // Strip trailing equals signs.
      while ((inLength > 0) && (in[inLength-1] == '='))
        inLength--;

      int blocks = inLength/4;
      int remainder = inLength & 3;
        // wholeInLen and wholeOutLen are the the length of the input and output
        // sequences respectively, not including any partial block at the end.
      int wholeInLen  = blocks*4;
      int wholeOutLen = blocks*3;
      int outLen = wholeOutLen;
      switch (remainder) {
        case 1: throw new Exception("illegal Base64 length");
        case 2:  outLen = wholeOutLen+1; break;
        case 3:  outLen = wholeOutLen+2; break;
        default: outLen = wholeOutLen;
      }
      byte[] out = new byte[outLen];
      int o = 0;
      int i;
      for (i = 0; i < wholeInLen;) {
        int in1 = reverseAlphabet[in[i]];
        int in2 = reverseAlphabet[in[i+1]];
        int in3 = reverseAlphabet[in[i+2]];
        int in4 = reverseAlphabet[in[i+3]];
        int orValue = in1|in2|in3|in4;
        if ((orValue & 0x80) != 0)
          throw new Exception("illegal Base64 character");
        int outVal = (in1 << 18) | (in2 << 12) | (in3 << 6) | in4;
        out[o] = (byte) (outVal>>16);
        out[o+1] = (byte) (outVal>>8);
        out[o+2] = (byte) outVal; 
        i += 4;
        o += 3;
      }
      int orValue;
      switch (remainder) {
        case 2:
          {
            int in1 = reverseAlphabet[in[i]];
            int in2 = reverseAlphabet[in[i+1]];
            orValue = in1|in2;
            int outVal = (in1 << 18) | (in2 << 12);
            out[o] = (byte) (outVal>>16);
          }
          break;
        case 3:
          {
            int in1 = reverseAlphabet[in[i]];
            int in2 = reverseAlphabet[in[i+1]];
            int in3 = reverseAlphabet[in[i+2]];
            orValue = in1|in2|in3;
            int outVal = (in1 << 18) | (in2 << 12) | (in3 << 6);
            out[o] = (byte) (outVal>>16);
            out[o+1] = (byte) (outVal>>8);
          }
          break;
        default:
            // Keep compiler happy
          orValue = 0;
      }
      if ((orValue & 0x80) != 0)
        throw new Exception("illegal Base64 character");
      return out;
    }
      // Illegal characters can cause an ArrayIndexOutOfBoundsException when
      // looking up reverseAlphabet.
    catch (ArrayIndexOutOfBoundsException e) {
      throw new Exception("illegal Base64 character");
    }
  }
}


/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package freenet.support;

import java.util.Arrays;
import java.util.Random;

import junit.framework.TestCase;

/**
 * Test case for {@link freenet.support.Base64} class.
 * 
 * @author Alberto Bacchelli &lt;sback@freenetproject.org&gt;
 */
public class Base64Test extends TestCase {
  
  /**
   * Test the encode(byte[]) method
   * against a well-known example
   * (see http://en.wikipedia.org/wiki/Base_64 as reference)
   * to verify if it encode works correctly.
   */
  public void testEncode() {
    String toEncode = "Man is distinguished, not only by his reason, but by this singular " +
        "passion from other animals, which is a lust of the mind, that by a perseverance " +
        "of delight in the continued and indefatigable generation of knowledge, exceeds " +
        "the short vehemence of any carnal pleasure.";
    String expectedResult = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ" +
        "1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG" +
        "x1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY" +
        "29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRz" +
        "IHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4";
    byte[] aByteArrayToEncode = toEncode.getBytes();
    assertEquals(Base64.encode(aByteArrayToEncode),expectedResult);
  }
  
  /**
   * Test the decode(String) method
   * against a well-known example
   * (see http://en.wikipedia.org/wiki/Base_64 as reference)
   * to verify if it decode an already encoded string correctly.
   */  
  public void testDecode() {
    String toDecode = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ" +
        "1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG" +
        "x1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY" +
        "29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRz" +
        "IHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=";
    String expectedResult = "Man is distinguished, not only by his reason, but by this singular " +
        "passion from other animals, which is a lust of the mind, that by a perseverance " +
        "of delight in the continued and indefatigable generation of knowledge, exceeds " +
        "the short vehemence of any carnal pleasure.";
    try {
      String decodedString = new String(Base64.decode(toDecode));
      assertEquals(decodedString,expectedResult);
    } catch (IllegalBase64Exception aException) {
      fail("Not expected exception thrown : " + aException.getMessage()); }
  }
  
  /**
   * Test encode(byte[] in)
   * and decode(String inStr) methods,
   * to verify if they work correctly together.
   * It compares the string before encoding
   * and with the one after decoding.
   */
  public void testEncodeDecode() {
    byte[] bytesDecoded;
    byte[] bytesToEncode = new byte[5];
    
    //byte upper bound
    bytesToEncode[0] = 127;
    bytesToEncode[1] = 64;
    bytesToEncode[2] = 0;
    bytesToEncode[3] = -64;
    //byte lower bound
    bytesToEncode[4] = -128;  
    
    String aBase64EncodedString = Base64.encode(bytesToEncode);
    
    try {
      bytesDecoded = Base64.decode(aBase64EncodedString);
      assertTrue(Arrays.equals(bytesToEncode,bytesDecoded)); } 
    catch (IllegalBase64Exception aException) {
      fail("Not expected exception thrown : " + aException.getMessage()); }
  }
  
  /**
   * Test the encode(String,boolean)
   * method to verify if the padding
   * character '=' is correctly placed.
   */
  public void testEncodePadding() {
    byte[][] methodBytesArray = {
        //three byte Array -> no padding char expected
        {4,4,4},    
        //two byte Array -> one padding char expected
        {4,4},    
        //one byte Array -> two padding-chars expected  
        {4}};    
    String encoded;
    
    for (int i = 0; i<methodBytesArray.length; i++) {
      encoded = Base64.encode(methodBytesArray[i],true);
      if (i == 0)
        //no occurrences expected
        assertEquals(encoded.indexOf('='),-1);
      else
        assertEquals(encoded.indexOf('='),encoded.length()-i);
    }
  }
  
  /**
   * Test if the decode(String) method
   * raise correctly an exception when
   * providing a string with non-Base64
   * characters.
   */
  public void testIllegalBaseCharacter() {
//    TODO: check many other possibile cases!
    String illegalCharString = "abcd=fghilmn";
    try {
      Base64.decode(illegalCharString);
      fail("Expected IllegalBase64Exception not thrown"); }
    catch (IllegalBase64Exception exception) {
      assertSame("illegal Base64 character",exception.getMessage()); }
  }
  
  /**
   * Test if the decode(String) method
   * raise correctly an exception when
   * providing a string with a 
   * wrong Base64 length.
   * (as we can consider not-padded strings too,
   *  the only wrong lengths are the ones
   *  where -> number MOD 4 = 1).
   */
  public void testIllegalBaseLength() {
    //most interesting case
    String illegalLengthString = "a";
    try {
      Base64.decode(illegalLengthString);
      fail("Expected IllegalBase64Exception not thrown"); }
    catch (IllegalBase64Exception exception) {
      assertSame("illegal Base64 length",exception.getMessage()); }
  }
  
  /**
   * Random test
   * 
   * @throws IllegalBase64Exception
   */
  public void testRandom() throws IllegalBase64Exception {
    int iter;
    Random r = new Random(1234);
    for (iter = 0; iter < 1000; iter++) {
      byte[] b = new byte[r.nextInt(64)];
      for (int i = 0; i < b.length; i++)
        b[i] = (byte) (r.nextInt(256));
      String encoded = Base64.encode(b);
      byte[] decoded = Base64.decode(encoded);
      assertEquals("length mismatch", decoded.length, b.length);

      for (int i = 0; i < b.length; i++)
        assertEquals("data mismatch: index " + i + " of " + b.length + " should be 0x"
                + Integer.toHexString(b[i] & 0xFF) + " was 0x" + Integer.toHexString(decoded[i] & 0xFF), b[i],
                decoded[i]);
    }
  }
}

   
  








Related examples in the same category

1.Load file to byte array
2.Manages fixed-length byte arrays
3.Byte-Array Conversion Utility Functions
4.ByteArray wraps java byte arrays (byte[]) to allow byte arrays to be used as keys in hashtables.
5.Returns a object from the given byte array.
6.Load File as byte array
7.Gets an array of bytes corresponding to the given object
8.An implementation of a virtual file, whose contents are kept in memory
9.Given a hexstring this will return the byte array corresponding to string
10.Decode byte array
11.Compare two byte[] for differences, either may be null
12.Convert the bytes within the specified range of the given byte array into a String
13.Convert the bytes within the specified range of the given byte array into a signed integer
14.Convert the bytes within the specified range of the given byte array into a signed integer in the given radix
15.Convert the bytes within the specified range of the given byte array into a signed long
16.Converts a byte array into a hexadecimal string
17.Converts a byte array into hexadecimal characters which are written as ASCII to the given output stream.
18.Convert byte array into a printable format: a String of hexadecimal digit characters (two per byte).
19.Convert hexadecimal digits into byte array by encoding each two hexadecimal digits as a byte.
20.Get Hex from byte arrayGet Hex from byte array
21.Read file to byte array
22.Invert the endianness of words (4 bytes) in the given byte array starting at the given offset and repeating length/4 times.
23.Returns a hexadecimal representation of the given byte array.
24.Converts a hex string representation to a byte array.
25.extracts floats from byte array
26.Extracts ints from byte array
27.extracts longs from byte array
28.Extracts short ints from byte array
29.writes doubles to byte array
30.writes floats to byte array
31.writes ints to byte array
32.writes longs to byte array
33.writes short ints to byte array
34.Convert a string of hex digits to a byte array, with the first byte in the array being the MSB.
35.Byte Array
36.Byte Array for Android
37.Convert byte array to Hex string
38.implements an output stream in which the data is written into a byte array. The buffer automatically grows as data is written to it.
39.Convert a TCP/IP address string into a byte array
40.Read file to byte array and save byte array to file
41.Convert object to byte array and convert byte array to object
42.Byte ArrayList
43.converts an int integer array to a byte array.
44.Compress byte array
45.Utility method to convert byte array to hex-encoded string.
46.InputStream to String and Byte array
47.Fast Byte Array InputStream
48.Fast Byte Array OutputStream