1/* 2 * @(#)Base64.java 1.5 03/12/19 3 * 4 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 5 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 6 */ 7 8package org.ahmadsoft.foprocessor.core; 9 10/** 11 * Static methods for translating Base64 encoded strings to byte arrays 12 * and vice-versa. 13 * 14 * @author Josh Bloch 15 * @version 1.5, 12/19/03 16 * @see Preferences 17 * @since 1.4 18 */ 19public class Base64 { 20 /** 21 * Translates the specified byte array into a Base64 string as per 22 * Preferences.put(byte[]). 23 */ 24 public static String byteArrayToBase64(byte[] a) { 25 return byteArrayToBase64(a, false); 26 } 27 28 /** 29 * Translates the specified byte array into an "aternate representation" 30 * Base64 string. This non-standard variant uses an alphabet that does 31 * not contain the uppercase alphabetic characters, which makes it 32 * suitable for use in situations where case-folding occurs. 33 */ 34 public static String byteArrayToAltBase64(byte[] a) { 35 return byteArrayToBase64(a, true); 36 } 37 38 private static String byteArrayToBase64(byte[] a, boolean alternate) { 39 int aLen = a.length; 40 int numFullGroups = aLen/3; 41 int numBytesInPartialGroup = aLen - 3*numFullGroups; 42 int resultLen = 4*((aLen + 2)/3); 43 StringBuffer result = new StringBuffer(resultLen); 44 char[] intToAlpha = (alternate ? intToAltBase64 : intToBase64); 45 46 // Translate all full groups from byte array elements to Base64 47 int inCursor = 0; 48 for (int i=0; i<numFullGroups; i++) { 49 int byte0 = a[inCursor++] & 0xff; 50 int byte1 = a[inCursor++] & 0xff; 51 int byte2 = a[inCursor++] & 0xff; 52 result.append(intToAlpha[byte0 >> 2]); 53 result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]); 54 result.append(intToAlpha[(byte1 << 2)&0x3f | (byte2 >> 6)]); 55 result.append(intToAlpha[byte2 & 0x3f]); 56 } 57 58 // Translate partial group if present 59 if (numBytesInPartialGroup != 0) { 60 int byte0 = a[inCursor++] & 0xff; 61 result.append(intToAlpha[byte0 >> 2]); 62 if (numBytesInPartialGroup == 1) { 63 result.append(intToAlpha[(byte0 << 4) & 0x3f]); 64 result.append("=="); 65 } else { 66 // assert numBytesInPartialGroup == 2; 67 int byte1 = a[inCursor++] & 0xff; 68 result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]); 69 result.append(intToAlpha[(byte1 << 2)&0x3f]); 70 result.append('='); 71 } 72 } 73 // assert inCursor == a.length; 74 // assert result.length() == resultLen; 75 return result.toString(); 76 } 77 78 /** 79 * This array is a lookup table that translates 6-bit positive integer 80 * index values into their "Base64 Alphabet" equivalents as specified 81 * in Table 1 of RFC 2045. 82 */ 83 private static final char intToBase64[] = { 84 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 85 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 86 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 87 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 88 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' 89 }; 90 91 /** 92 * This array is a lookup table that translates 6-bit positive integer 93 * index values into their "Alternate Base64 Alphabet" equivalents. 94 * This is NOT the real Base64 Alphabet as per in Table 1 of RFC 2045. 95 * This alternate alphabet does not use the capital letters. It is 96 * designed for use in environments where "case folding" occurs. 97 */ 98 private static final char intToAltBase64[] = { 99 '!', '"', '#', '$', '%', '&', '\'', '(', ')', ',', '-', '.', ':', 100 ';', '<', '>', '@', '[', ']', '^', '`', '_', '{', '|', '}', '~', 101 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 102 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 103 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '?' 104 }; 105 106 /** 107 * Translates the specified Base64 string (as per Preferences.get(byte[])) 108 * into a byte array. 109 * 110 * @throw IllegalArgumentException if <tt>s</tt> is not a valid Base64 111 * string. 112 */ 113 public static byte[] base64ToByteArray(String s) { 114 return base64ToByteArray(s, false); 115 } 116 117 /** 118 * Translates the specified "aternate representation" Base64 string 119 * into a byte array. 120 * 121 * @throw IllegalArgumentException or ArrayOutOfBoundsException 122 * if <tt>s</tt> is not a valid alternate representation 123 * Base64 string. 124 */ 125 public static byte[] altBase64ToByteArray(String s) { 126 return base64ToByteArray(s, true); 127 } 128 129 private static byte[] base64ToByteArray(String s, boolean alternate) { 130 byte[] alphaToInt = (alternate ? altBase64ToInt : base64ToInt); 131 int sLen = s.length(); 132 int numGroups = sLen/4; 133 if (4*numGroups != sLen) 134 throw new IllegalArgumentException( 135 "String length must be a multiple of four."); 136 int missingBytesInLastGroup = 0; 137 int numFullGroups = numGroups; 138 if (sLen != 0) { 139 if (s.charAt(sLen-1) == '=') { 140 missingBytesInLastGroup++; 141 numFullGroups--; 142 } 143 if (s.charAt(sLen-2) == '=') 144 missingBytesInLastGroup++; 145 } 146 byte[] result = new byte[3*numGroups - missingBytesInLastGroup]; 147 148 // Translate all full groups from base64 to byte array elements 149 int inCursor = 0, outCursor = 0; 150 for (int i=0; i<numFullGroups; i++) { 151 int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt); 152 int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt); 153 int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt); 154 int ch3 = base64toInt(s.charAt(inCursor++), alphaToInt); 155 result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4)); 156 result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); 157 result[outCursor++] = (byte) ((ch2 << 6) | ch3); 158 } 159 160 // Translate partial group, if present 161 if (missingBytesInLastGroup != 0) { 162 int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt); 163 int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt); 164 result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4)); 165 166 if (missingBytesInLastGroup == 1) { 167 int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt); 168 result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); 169 } 170 } 171 // assert inCursor == s.length()-missingBytesInLastGroup; 172 // assert outCursor == result.length; 173 return result; 174 } 175 176 /** 177 * Translates the specified character, which is assumed to be in the 178 * "Base 64 Alphabet" into its equivalent 6-bit positive integer. 179 * 180 * @throw IllegalArgumentException or ArrayOutOfBoundsException if 181 * c is not in the Base64 Alphabet. 182 */ 183 private static int base64toInt(char c, byte[] alphaToInt) { 184 int result = alphaToInt[c]; 185 if (result < 0) 186 throw new IllegalArgumentException("Illegal character " + c); 187 return result; 188 } 189 190 /** 191 * This array is a lookup table that translates unicode characters 192 * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045) 193 * into their 6-bit positive integer equivalents. Characters that 194 * are not in the Base64 alphabet but fall within the bounds of the 195 * array are translated to -1. 196 */ 197 private static final byte base64ToInt[] = { 198 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 199 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 200 -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 201 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 202 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 203 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 204 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 205 }; 206 207 /** 208 * This array is the analogue of base64ToInt, but for the nonstandard 209 * variant that avoids the use of uppercase alphabetic characters. 210 */ 211 private static final byte altBase64ToInt[] = { 212 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 213 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 214 2, 3, 4, 5, 6, 7, 8, -1, 62, 9, 10, 11, -1 , 52, 53, 54, 55, 56, 57, 215 58, 59, 60, 61, 12, 13, 14, -1, 15, 63, 16, -1, -1, -1, -1, -1, -1, 216 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 217 -1, -1, -1, 17, -1, 18, 19, 21, 20, 26, 27, 28, 29, 30, 31, 32, 33, 218 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 219 51, 22, 23, 24, 25 220 }; 221 222 public static void main(String args[]) { 223 int numRuns = Integer.parseInt(args[0]); 224 int numBytes = Integer.parseInt(args[1]); 225 java.util.Random rnd = new java.util.Random(); 226 for (int i=0; i<numRuns; i++) { 227 for (int j=0; j<numBytes; j++) { 228 byte[] arr = new byte[j]; 229 for (int k=0; k<j; k++) 230 arr[k] = (byte)rnd.nextInt(); 231 232 String s = byteArrayToBase64(arr); 233 byte [] b = base64ToByteArray(s); 234 if (!java.util.Arrays.equals(arr, b)) 235 System.out.println("Dismal failure!"); 236 237 s = byteArrayToAltBase64(arr); 238 b = altBase64ToByteArray(s); 239 if (!java.util.Arrays.equals(arr, b)) 240 System.out.println("Alternate dismal failure!"); 241 } 242 } 243 } 244} 245