001: /*
002: * Enhydra Java Application Server Project
003: *
004: * The contents of this file are subject to the Enhydra Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License on
007: * the Enhydra web site ( http://www.enhydra.org/ ).
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
011: * the License for the specific terms governing rights and limitations
012: * under the License.
013: *
014: * The Initial Developer of the Enhydra Application Server is Lutris
015: * Technologies, Inc. The Enhydra Application Server and portions created
016: * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
017: * All Rights Reserved.
018: *
019: * Contributor(s):
020: *
021: * $Id: Base64Encoder.java,v 1.2 2006-06-15 13:47:00 sinisa Exp $
022: */
023:
024: package com.lutris.util;
025:
026: import java.io.ByteArrayOutputStream;
027: import java.io.DataOutputStream;
028: import java.io.IOException;
029: import java.text.CharacterIterator;
030: import java.text.StringCharacterIterator;
031:
032: /**
033: * Various conversion methods.
034: * These methods are mostly used to convert internal java data
035: * fields into byte arrays or strings for use over the network.
036: *
037: * @author Mike Ward
038: *
039: */
040: public class Base64Encoder {
041:
042: /**
043: * The BASE64 encoding standard's 6-bit alphabet, from RFC 1521,
044: * plus the padding character at the end.
045: */
046: private static final char[] Base64Chars = { 'A', 'B', 'C', 'D',
047: 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
048: 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
049: 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
050: 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
051: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
052: '=' };
053:
054: /**
055: * Encoding alphabet for session keys. Contains only chars that
056: * are safe to use in cookies, URLs and file names. Same as BASE64
057: * except the last two chars and the padding char
058: */
059: private static final char[] SessionKeyChars = { 'A', 'B', 'C', 'D',
060: 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
061: 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
062: 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
063: 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
064: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_', '-',
065: '.' };
066:
067: /**
068: * Performs RFC1521 style Base64 encoding of arbitrary binary data.
069: * The output is a java String containing the Base64 characters
070: * representing the binary data. Be aware that this string is in
071: * Unicode form, and should be converted to UTF8 with the usual java
072: * conversion routines before it is sent over a network. The output
073: * string is guaranteed to only contain characters that are a single
074: * byte in UTF8 format. Also be aware that this routine leaves it to
075: * the caller to break the string into 70 byte lines as per RFC1521.
076: *
077: * @param bytes The array of bytes to convert to Base64 encoding.
078: * @return An string containing the specified bytes in Base64
079: * encoded form.
080: */
081: public static final String toBase64String(byte[] bytes) {
082: return toBase64String(bytes, Base64Chars);
083: }
084:
085: /**
086: * The encoding is more or less Base 64, but instead of '+'
087: * and '/' as defined in RFC1521, the characters '_' and
088: * '-' are used because they are safe in URLs and file names.
089: *
090: * @param bytes The array of bytes to convert to Base64SessionKey
091: * encoding.
092: * @return An string containing the specified bytes in Base64
093: * encoded form.
094: */
095: public static final String toBase64SessionKeyString(byte[] bytes) {
096: return toBase64String(bytes, SessionKeyChars);
097: }
098:
099: /**
100: * Performs encoding of arbitrary binary data based on a 6 bit
101: * alphabet. The output is a java String containing the encoded
102: * characters representing the binary data. Be aware that this
103: * string is in Unicode form, and should be converted to UTF8 with
104: * the usual java conversion routines before it is sent over a
105: * network. The alphabet passed in via <code>chars</code> is used
106: * without further checks, it's the callers responsibility to set
107: * it to something meaningful.
108: *
109: * @param bytes The array of bytes to convert to Base64 encoding.
110: * @param chars The alphabet used in encoding. Must contain
111: * exactly 65 characters: A 6 bit alphabet plus one
112: * padding char at position 65.
113: * @return An string containing the specified bytes in Base64
114: * encoded form.
115: */
116: private static final String toBase64String(byte[] bytes,
117: char[] chars) {
118: StringBuffer sb = new StringBuffer();
119: int len = bytes.length, i = 0, ival;
120: while (len >= 3) {
121: ival = ((int) bytes[i++] + 256) & 0xff;
122: ival <<= 8;
123: ival += ((int) bytes[i++] + 256) & 0xff;
124: ival <<= 8;
125: ival += ((int) bytes[i++] + 256) & 0xff;
126: len -= 3;
127: sb.append(chars[(ival >> 18) & 63]);
128: sb.append(chars[(ival >> 12) & 63]);
129: sb.append(chars[(ival >> 6) & 63]);
130: sb.append(chars[ival & 63]);
131: }
132: switch (len) {
133: case 0: // No pads needed.
134: break;
135: case 1: // Two more output bytes and two pads.
136: ival = ((int) bytes[i++] + 256) & 0xff;
137: ival <<= 16;
138: sb.append(chars[(ival >> 18) & 63]);
139: sb.append(chars[(ival >> 12) & 63]);
140: sb.append(chars[64]);
141: sb.append(chars[64]);
142: break;
143: case 2: // Three more output bytes and one pad.
144: ival = ((int) bytes[i++] + 256) & 0xff;
145: ival <<= 8;
146: ival += ((int) bytes[i] + 256) & 0xff;
147: ival <<= 8;
148: sb.append(chars[(ival >> 18) & 63]);
149: sb.append(chars[(ival >> 12) & 63]);
150: sb.append(chars[(ival >> 6) & 63]);
151: sb.append(chars[64]);
152: break;
153: }
154: return new String(sb);
155: }
156:
157: /**
158: * Performs RFC1521 style Base64 decoding of Base64 encoded data.
159: * The output is a byte array containing the decoded binary data.
160: * The input is expected to be a normal Unicode String object.
161: *
162: * @param s The Base64 encoded string to decode into binary data.
163: * @return An array of bytes containing the decoded data.
164: */
165: public static final byte[] fromBase64String(String s) {
166: try {
167: StringCharacterIterator iter = new StringCharacterIterator(
168: s);
169: ByteArrayOutputStream bytestr = new ByteArrayOutputStream();
170: DataOutputStream outstr = new DataOutputStream(bytestr);
171: char c;
172: int d, i, group;
173: int[] bgroup = new int[4];
174: decode: for (i = 0, group = 0, c = iter.first(); c != CharacterIterator.DONE; c = iter
175: .next()) {
176: switch (c) {
177: case 'A':
178: d = 0;
179: break;
180: case 'B':
181: d = 1;
182: break;
183: case 'C':
184: d = 2;
185: break;
186: case 'D':
187: d = 3;
188: break;
189: case 'E':
190: d = 4;
191: break;
192: case 'F':
193: d = 5;
194: break;
195: case 'G':
196: d = 6;
197: break;
198: case 'H':
199: d = 7;
200: break;
201: case 'I':
202: d = 8;
203: break;
204: case 'J':
205: d = 9;
206: break;
207: case 'K':
208: d = 10;
209: break;
210: case 'L':
211: d = 11;
212: break;
213: case 'M':
214: d = 12;
215: break;
216: case 'N':
217: d = 13;
218: break;
219: case 'O':
220: d = 14;
221: break;
222: case 'P':
223: d = 15;
224: break;
225: case 'Q':
226: d = 16;
227: break;
228: case 'R':
229: d = 17;
230: break;
231: case 'S':
232: d = 18;
233: break;
234: case 'T':
235: d = 19;
236: break;
237: case 'U':
238: d = 20;
239: break;
240: case 'V':
241: d = 21;
242: break;
243: case 'W':
244: d = 22;
245: break;
246: case 'X':
247: d = 23;
248: break;
249: case 'Y':
250: d = 24;
251: break;
252: case 'Z':
253: d = 25;
254: break;
255: case 'a':
256: d = 26;
257: break;
258: case 'b':
259: d = 27;
260: break;
261: case 'c':
262: d = 28;
263: break;
264: case 'd':
265: d = 29;
266: break;
267: case 'e':
268: d = 30;
269: break;
270: case 'f':
271: d = 31;
272: break;
273: case 'g':
274: d = 32;
275: break;
276: case 'h':
277: d = 33;
278: break;
279: case 'i':
280: d = 34;
281: break;
282: case 'j':
283: d = 35;
284: break;
285: case 'k':
286: d = 36;
287: break;
288: case 'l':
289: d = 37;
290: break;
291: case 'm':
292: d = 38;
293: break;
294: case 'n':
295: d = 39;
296: break;
297: case 'o':
298: d = 40;
299: break;
300: case 'p':
301: d = 41;
302: break;
303: case 'q':
304: d = 42;
305: break;
306: case 'r':
307: d = 43;
308: break;
309: case 's':
310: d = 44;
311: break;
312: case 't':
313: d = 45;
314: break;
315: case 'u':
316: d = 46;
317: break;
318: case 'v':
319: d = 47;
320: break;
321: case 'w':
322: d = 48;
323: break;
324: case 'x':
325: d = 49;
326: break;
327: case 'y':
328: d = 50;
329: break;
330: case 'z':
331: d = 51;
332: break;
333: case '0':
334: d = 52;
335: break;
336: case '1':
337: d = 53;
338: break;
339: case '2':
340: d = 54;
341: break;
342: case '3':
343: d = 55;
344: break;
345: case '4':
346: d = 56;
347: break;
348: case '5':
349: d = 57;
350: break;
351: case '6':
352: d = 58;
353: break;
354: case '7':
355: d = 59;
356: break;
357: case '8':
358: d = 60;
359: break;
360: case '9':
361: d = 61;
362: break;
363: case '+':
364: d = 62;
365: break;
366: case '/':
367: d = 63;
368: break;
369: case '_':
370: d = 62;
371: break;
372: case '-':
373: d = 63;
374: break;
375: default:
376: // Any character not in Base64 alphabet is treated
377: // as end of data. This includes the '=' (pad) char.
378: break decode; // Skip illegal characters.
379: }
380: bgroup[i++] = d;
381: if (i >= 4) {
382: i = 0;
383: group = ((bgroup[0] & 63) << 18)
384: + ((bgroup[1] & 63) << 12)
385: + ((bgroup[2] & 63) << 6)
386: + (bgroup[3] & 63);
387: outstr.writeByte(((group >> 16) & 255));
388: outstr.writeByte(((group >> 8) & 255));
389: outstr.writeByte(group & 255);
390: }
391: }
392: // Handle the case of remaining characters and
393: // pad handling. If input is not a multiple of 4
394: // in length, then '=' pads are assumed.
395: switch (i) {
396: case 2:
397: // One output byte from two input bytes.
398: group = ((bgroup[0] & 63) << 18)
399: + ((bgroup[1] & 63) << 12);
400: outstr.writeByte(((group >> 16) & 255));
401: break;
402: case 3:
403: // Two output bytes from three input bytes.
404: group = ((bgroup[0] & 63) << 18)
405: + ((bgroup[1] & 63) << 12)
406: + ((bgroup[2] & 63) << 6);
407: outstr.writeByte(((group >> 16) & 255));
408: outstr.writeByte(((group >> 8) & 255));
409: break;
410: default:
411: // Any other case, including correct 0, is treated as
412: // end of data.
413: break;
414: }
415: outstr.flush();
416: return bytestr.toByteArray();
417: } catch (IOException e) {
418: } // Won't happen. Return null if it does.
419: return null;
420: }
421: }
|