001 //////////////////////license & copyright header///////////////////////// 002 // // 003 // Base64 - encode/decode data using the Base64 encoding scheme // 004 // // 005 // Copyright (c) 1998 by Kevin Kelley // 006 // // 007 // This library is free software; you can redistribute it and/or // 008 // modify it under the terms of the GNU Lesser General Public // 009 // License as published by the Free Software Foundation; either // 010 // version 2.1 of the License, or (at your option) any later version. // 011 // // 012 // This library is distributed in the hope that it will be useful, // 013 // but WITHOUT ANY WARRANTY; without even the implied warranty of // 014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 015 // GNU Lesser General Public License for more details. // 016 // // 017 // You should have received a copy of the GNU Lesser General Public // 018 // License along with this library; if not, write to the Free Software // 019 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA // 020 // 02111-1307, USA, or contact the author: // 021 // // 022 // Kevin Kelley <kelley@ruralnet.net> - 30718 Rd. 28, La Junta, CO, // 023 // 81050 USA. // 024 // // 025 ////////////////////end license & copyright header/////////////////////// 026 027 package org.objectweb.jac.util; 028 029 import java.io.*; // needed only for main() method. 030 031 032 /** 033 * Provides encoding of raw bytes to base64-encoded characters, and 034 * decoding of base64 characters to raw bytes. 035 * 036 * @author Kevin Kelley (kelley@ruralnet.net) 037 * @version 1.3 038 */ 039 public class Base64 { 040 041 /** 042 * returns an array of base64-encoded characters to represent the 043 * passed data array. 044 * 045 * @param data the array of bytes to encode 046 * @return base64-coded character array. 047 */ 048 static public char[] encode(byte[] data) 049 { 050 char[] out = new char[((data.length + 2) / 3) * 4]; 051 052 // 053 // 3 bytes encode to 4 chars. Output is always an even 054 // multiple of 4 characters. 055 // 056 for (int i=0, index=0; i<data.length; i+=3, index+=4) { 057 boolean quad = false; 058 boolean trip = false; 059 060 int val = (0xFF & (int) data[i]); 061 val <<= 8; 062 if ((i+1) < data.length) { 063 val |= (0xFF & (int) data[i+1]); 064 trip = true; 065 } 066 val <<= 8; 067 if ((i+2) < data.length) { 068 val |= (0xFF & (int) data[i+2]); 069 quad = true; 070 } 071 out[index+3] = alphabet[(quad? (val & 0x3F): 64)]; 072 val >>= 6; 073 out[index+2] = alphabet[(trip? (val & 0x3F): 64)]; 074 val >>= 6; 075 out[index+1] = alphabet[val & 0x3F]; 076 val >>= 6; 077 out[index+0] = alphabet[val & 0x3F]; 078 } 079 return out; 080 } 081 082 public static String encodeToString(byte[] data) { 083 return new String(encode(data)); 084 } 085 086 /** 087 * Decodes a BASE-64 encoded stream to recover the original 088 * data. White space before and after will be trimmed away, 089 * but no other manipulation of the input will be performed. 090 * 091 * As of version 1.2 this method will properly handle input 092 * containing junk characters (newlines and the like) rather 093 * than throwing an error. It does this by pre-parsing the 094 * input and generating from that a count of VALID input 095 * characters. 096 **/ 097 static public byte[] decode(char[] data) 098 { 099 // as our input could contain non-BASE64 data (newlines, 100 // whitespace of any sort, whatever) we must first adjust 101 // our count of USABLE data so that... 102 // (a) we don't misallocate the output array, and 103 // (b) think that we miscalculated our data length 104 // just because of extraneous throw-away junk 105 106 int tempLen = data.length; 107 for( int ix=0; ix<data.length; ix++ ) 108 { 109 if( (data[ix] > 255) || codes[ data[ix] ] < 0 ) 110 --tempLen; // ignore non-valid chars and padding 111 } 112 // calculate required length: 113 // -- 3 bytes for every 4 valid base64 chars 114 // -- plus 2 bytes if there are 3 extra base64 chars, 115 // or plus 1 byte if there are 2 extra. 116 117 int len = (tempLen / 4) * 3; 118 if ((tempLen % 4) == 3) len += 2; 119 if ((tempLen % 4) == 2) len += 1; 120 121 byte[] out = new byte[len]; 122 123 124 125 int shift = 0; // # of excess bits stored in accum 126 int accum = 0; // excess bits 127 int index = 0; 128 129 // we now go through the entire array (NOT using the 'tempLen' value) 130 for (int ix=0; ix<data.length; ix++) 131 { 132 int value = (data[ix]>255)? -1: codes[ data[ix] ]; 133 134 if ( value >= 0 ) // skip over non-code 135 { 136 accum <<= 6; // bits shift up by 6 each time thru 137 shift += 6; // loop, with new bits being put in 138 accum |= value; // at the bottom. 139 if ( shift >= 8 ) // whenever there are 8 or more shifted in, 140 { 141 shift -= 8; // write them out (from the top, leaving any 142 out[index++] = // excess at the bottom for next iteration. 143 (byte) ((accum >> shift) & 0xff); 144 } 145 } 146 // we will also have skipped processing a padding null byte ('=') here; 147 // these are used ONLY for padding to an even length and do not legally 148 // occur as encoded data. for this reason we can ignore the fact that 149 // no index++ operation occurs in that special case: the out[] array is 150 // initialized to all-zero bytes to start with and that works to our 151 // advantage in this combination. 152 } 153 154 // if there is STILL something wrong we just have to throw up now! 155 if( index != out.length) 156 { 157 throw new Error("Miscalculated data length (wrote " + index + " instead of " + out.length + ")"); 158 } 159 160 return out; 161 } 162 163 public static byte[] decode(String data) { 164 return decode(data.toCharArray()); 165 } 166 167 // 168 // code characters for values 0..63 169 // 170 static private char[] alphabet = 171 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" 172 .toCharArray(); 173 174 // 175 // lookup table for converting base64 characters to value in range 0..63 176 // 177 static private byte[] codes = new byte[256]; 178 static { 179 for (int i=0; i<256; i++) codes[i] = -1; 180 for (int i = 'A'; i <= 'Z'; i++) codes[i] = (byte)( i - 'A'); 181 for (int i = 'a'; i <= 'z'; i++) codes[i] = (byte)(26 + i - 'a'); 182 for (int i = '0'; i <= '9'; i++) codes[i] = (byte)(52 + i - '0'); 183 codes['+'] = 62; 184 codes['/'] = 63; 185 } 186 187 188 189 190 /////////////////////////////////////////////////// 191 // remainder (main method and helper functions) is 192 // for testing purposes only, feel free to clip it. 193 /////////////////////////////////////////////////// 194 195 public static void main(String[] args) 196 { 197 boolean decode = false; 198 199 if (args.length == 0) { 200 System.out.println("usage: java Base64 [-d[ecode]] filename"); 201 System.exit(0); 202 } 203 for (int i=0; i<args.length; i++) { 204 if ("-decode".equalsIgnoreCase(args[i])) decode = true; 205 else if ("-d".equalsIgnoreCase(args[i])) decode = true; 206 } 207 208 String filename = args[args.length-1]; 209 File file = new File(filename); 210 if (!file.exists()) { 211 System.out.println("Error: file '" + filename + "' doesn't exist!"); 212 System.exit(0); 213 } 214 215 if (decode) 216 { 217 char[] encoded = readChars(file); 218 byte[] decoded = decode(encoded); 219 writeBytes(file, decoded); 220 } 221 else 222 { 223 byte[] decoded = readBytes(file); 224 char[] encoded = encode(decoded); 225 writeChars(file, encoded); 226 } 227 } 228 229 private static byte[] readBytes(File file) 230 { 231 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 232 try 233 { 234 InputStream fis = new FileInputStream(file); 235 InputStream is = new BufferedInputStream(fis); 236 int count = 0; 237 byte[] buf = new byte[16384]; 238 while ((count=is.read(buf)) != -1) { 239 if (count > 0) baos.write(buf, 0, count); 240 } 241 is.close(); 242 } 243 catch (Exception e) { e.printStackTrace(); } 244 245 return baos.toByteArray(); 246 } 247 248 private static char[] readChars(File file) 249 { 250 CharArrayWriter caw = new CharArrayWriter(); 251 try 252 { 253 Reader fr = new FileReader(file); 254 Reader in = new BufferedReader(fr); 255 int count = 0; 256 char[] buf = new char[16384]; 257 while ((count=in.read(buf)) != -1) { 258 if (count > 0) caw.write(buf, 0, count); 259 } 260 in.close(); 261 } 262 catch (Exception e) { e.printStackTrace(); } 263 264 return caw.toCharArray(); 265 } 266 267 private static void writeBytes(File file, byte[] data) { 268 try { 269 OutputStream fos = new FileOutputStream(file); 270 OutputStream os = new BufferedOutputStream(fos); 271 os.write(data); 272 os.close(); 273 } 274 catch (Exception e) { e.printStackTrace(); } 275 } 276 277 private static void writeChars(File file, char[] data) { 278 try { 279 Writer fos = new FileWriter(file); 280 Writer os = new BufferedWriter(fos); 281 os.write(data); 282 os.close(); 283 } 284 catch (Exception e) { e.printStackTrace(); } 285 } 286 /////////////////////////////////////////////////// 287 // end of test code. 288 /////////////////////////////////////////////////// 289 290 }