001: /*
002: * Copyright 2004-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.springframework.webflow.util;
017:
018: /**
019: * Encodes and decodes to and from Base64 notation.
020: * <p>
021: * Based on Base64 encoder and decoder version 2.2.1 written by Robert Harder
022: * (<a href="http://iharder.net/base64">http://iharder.net/base64</a>).
023: * Modified by Erwin Vervaet to use the '.' character as padding character
024: * when using URL safe encoding, like in the Bouncy Castle URLBase64 encoder
025: * (<a href="http://www.bouncycastle.org/java.html">http://www.bouncycastle.org/java.html</a>).
026: *
027: * @author Robert Harder
028: * @author Erwin Vervaet
029: */
030: public class Base64 {
031:
032: /* static data used by the encoding and decoding algorithm
033:
034: /* The equals sign (=) as a byte. */
035: private static final byte EQUALS_SIGN = (byte) '=';
036: /* The dot (.) as a byte. */
037: private static final byte DOT = (byte) '.';
038:
039: private static final byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
040: private static final byte PADDING_CHAR_ENC = -1; // Indicates padding char in encoding
041:
042: /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */
043:
044: /** The 64 valid Base64 values. */
045: /* Host platform may be something funny like EBCDIC, so we hardcode these values. */
046: private static final byte[] STANDARD_ALPHABET = { (byte) 'A',
047: (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
048: (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
049: (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
050: (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
051: (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
052: (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
053: (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
054: (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
055: (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
056: (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
057: (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
058: (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
059: (byte) '9', (byte) '+', (byte) '/' };
060:
061: /**
062: * Translates a Base64 value to either its 6-bit reconstruction value
063: * or a negative number indicating some other meaning.
064: **/
065: private static final byte[] STANDARD_DECODABET = { -9, -9, -9, -9,
066: -9, -9, -9, -9, -9, // Decimal 0 - 8
067: -5, -5, // Whitespace: Tab and Linefeed
068: -9, -9, // Decimal 11 - 12
069: -5, // Whitespace: Carriage Return
070: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
071: -9, -9, -9, -9, -9, // Decimal 27 - 31
072: -5, // Whitespace: Space
073: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
074: 62, // Plus sign at decimal 43
075: -9, -9, -9, // Decimal 44 - 46
076: 63, // Slash at decimal 47
077: 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
078: -9, -9, -9, // Decimal 58 - 60
079: -1, // Equals sign at decimal 61
080: -9, -9, -9, // Decimal 62 - 64
081: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
082: 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
083: -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
084: 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
085: 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
086: -9, -9, -9, -9 // Decimal 123 - 126
087: };
088:
089: /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */
090:
091: /**
092: * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548:
093: * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
094: * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."
095: */
096: private static final byte[] URL_SAFE_ALPHABET = { (byte) 'A',
097: (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
098: (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
099: (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
100: (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
101: (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
102: (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
103: (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
104: (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
105: (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
106: (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
107: (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
108: (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
109: (byte) '9', (byte) '-', (byte) '_' };
110:
111: /**
112: * Used in decoding URL- and Filename-safe dialects of Base64.
113: */
114: private static final byte[] URL_SAFE_DECODABET = { -9, -9, -9, -9,
115: -9, -9, -9, -9, -9, // Decimal 0 - 8
116: -5, -5, // Whitespace: Tab and Linefeed
117: -9, -9, // Decimal 11 - 12
118: -5, // Whitespace: Carriage Return
119: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
120: -9, -9, -9, -9, -9, // Decimal 27 - 31
121: -5, // Whitespace: Space
122: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
123: -9, // Plus sign at decimal 43
124: -9, // Decimal 44
125: 62, // Minus sign at decimal 45
126: -1, // Dot at decimal 46
127: -9, // Slash at decimal 47
128: 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
129: -9, -9, -9, // Decimal 58 - 60
130: -9, // Equals sign at decimal 61
131: -9, -9, -9, // Decimal 62 - 64
132: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
133: 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
134: -9, -9, -9, -9, // Decimal 91 - 94
135: 63, // Underscore at decimal 95
136: -9, // Decimal 96
137: 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
138: 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
139: -9, -9, -9, -9 // Decimal 123 - 126
140: };
141:
142: // instance members
143:
144: /**
145: * Encode using Base64-like encoding that is URL- and Filename-safe as described
146: * in Section 4 of RFC3548:
147: * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
148: */
149: private boolean urlSafe;
150:
151: private byte[] ALPHABET;
152: private byte[] DECODABET;
153: private byte PADDING_CHAR;
154:
155: /**
156: * Create a new Base64 encoder and decoder using the standard Base64 alphabet.
157: * Note that the resulting encoded strings are not <i>URL-safe</i>: they will
158: * can contain characters that are subject to URL encoding.
159: */
160: public Base64() {
161: this (false);
162: }
163:
164: /**
165: * Create a new Base64 encoder and decoder.
166: * <p>Allows Base64-like encoding that is URL- and Filename-safe as described
167: * in Section 4 of RFC3548:
168: * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
169: * When URL-safe encoding is used, the standard "=" Base64 padding character is replaced
170: * with the '.' character.
171: * <p>
172: * It is important to note that data encoded this way is <em>not</em> officially valid Base64,
173: * or at the very least should not be called Base64 without also specifying that is
174: * was encoded using the URL- and Filename-safe dialect
175: *
176: * @param urlSafe if true, URL safe encoding and decoding will be used
177: */
178: public Base64(boolean urlSafe) {
179: this .urlSafe = urlSafe;
180: if (urlSafe) {
181: ALPHABET = URL_SAFE_ALPHABET;
182: DECODABET = URL_SAFE_DECODABET;
183: PADDING_CHAR = DOT;
184: } else {
185: ALPHABET = STANDARD_ALPHABET;
186: DECODABET = STANDARD_DECODABET;
187: PADDING_CHAR = EQUALS_SIGN;
188: }
189: }
190:
191: /**
192: * Returns whether or not this coder is using Base64-like encoding that is URL- and Filename-safe
193: * as described in Section 4 of RFC3548:
194: * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
195: * When URL-safe encoding is used, the standard "=" Base64 padding character is replaced
196: * with the '.' character.
197: * <p>
198: * It is important to note that data encoded this way is <em>not</em> officially valid Base64,
199: * or at the very least should not be called Base64 without also specifying that is
200: * was encoded using the URL- and Filename-safe dialect.
201: * @return true or false
202: */
203: public boolean isUrlSafe() {
204: return urlSafe;
205: }
206:
207: /* ******** E N C O D I N G M E T H O D S ******** */
208:
209: /**
210: * Encodes up to three bytes of the array <var>source</var>
211: * and writes the resulting four Base64 bytes to <var>destination</var>.
212: * The source and destination arrays can be manipulated
213: * anywhere along their length by specifying
214: * <var>srcOffset</var> and <var>destOffset</var>.
215: * This method does not check to make sure your arrays
216: * are large enough to accomodate <var>srcOffset</var> + 3 for
217: * the <var>source</var> array or <var>destOffset</var> + 4 for
218: * the <var>destination</var> array.
219: * The actual number of significant bytes in your array is
220: * given by <var>numSigBytes</var>.</p>
221: * <p>This is the lowest level of the encoding methods with
222: * all possible parameters.</p>
223: * @param source the array to convert
224: * @param srcOffset the index where conversion begins
225: * @param numSigBytes the number of significant bytes in your array
226: * @param destination the array to hold the conversion
227: * @param destOffset the index where output will be put
228: * @return the <var>destination</var> array
229: */
230: private byte[] encode3to4(byte[] source, int srcOffset,
231: int numSigBytes, byte[] destination, int destOffset) {
232: // 1 2 3
233: // 01234567890123456789012345678901 Bit position
234: // --------000000001111111122222222 Array position from threeBytes
235: // --------| || || || | Six bit groups to index ALPHABET
236: // >>18 >>12 >> 6 >> 0 Right shift necessary
237: // 0x3f 0x3f 0x3f Additional AND
238:
239: // Create buffer with zero-padding if there are only one or two
240: // significant bytes passed in the array.
241: // We have to shift left 24 in order to flush out the 1's that appear
242: // when Java treats a value as negative that is cast from a byte to an int.
243: int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8)
244: : 0)
245: | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16)
246: : 0)
247: | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24)
248: : 0);
249:
250: switch (numSigBytes) {
251: case 3:
252: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
253: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
254: destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
255: destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
256: return destination;
257:
258: case 2:
259: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
260: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
261: destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
262: destination[destOffset + 3] = PADDING_CHAR;
263: return destination;
264:
265: case 1:
266: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
267: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
268: destination[destOffset + 2] = PADDING_CHAR;
269: destination[destOffset + 3] = PADDING_CHAR;
270: return destination;
271:
272: default:
273: return destination;
274: }
275: }
276:
277: /**
278: * Encodes a byte array into Base64 notation.
279: * @param source the data to convert
280: * @param off offset in array where conversion should begin
281: * @param len length of data to convert
282: * @return the encoded data
283: */
284: public final byte[] encode(byte[] source, int off, int len) {
285: int len43 = len * 4 / 3;
286: byte[] outBuff = new byte[(len43) // main 4:3
287: + ((len % 3) > 0 ? 4 : 0)]; // account for padding
288: int d = 0;
289: int e = 0;
290: int len2 = len - 2;
291: int lineLength = 0;
292: for (; d < len2; d += 3, e += 4) {
293: encode3to4(source, d + off, 3, outBuff, e);
294:
295: lineLength += 4;
296: } // end for: each piece of array
297:
298: if (d < len) {
299: encode3to4(source, d + off, len - d, outBuff, e);
300: e += 4;
301: } // end if: some padding needed
302:
303: byte[] out = new byte[e];
304: System.arraycopy(outBuff, 0, out, 0, e);
305: return out;
306: }
307:
308: /**
309: * Encodes a byte array into Base64 notation.
310: * @param source the data to encode
311: * @return the encoded data
312: */
313: public final byte[] encode(byte[] source) {
314: return encode(source, 0, source.length);
315: }
316:
317: /**
318: * Encodes a byte array into Base64 notation. The resulting string will
319: * be created using the platform default encoding.
320: * @param source the source data to encode
321: * @return the encoded data
322: */
323: public final String encodeToString(byte[] source) {
324: return new String(encode(source));
325: }
326:
327: /* ******** D E C O D I N G M E T H O D S ******** */
328:
329: /**
330: * Decodes four bytes from array <var>source</var>
331: * and writes the resulting bytes (up to three of them)
332: * to <var>destination</var>.
333: * The source and destination arrays can be manipulated
334: * anywhere along their length by specifying
335: * <var>srcOffset</var> and <var>destOffset</var>.
336: * This method does not check to make sure your arrays
337: * are large enough to accomodate <var>srcOffset</var> + 4 for
338: * the <var>source</var> array or <var>destOffset</var> + 3 for
339: * the <var>destination</var> array.
340: * This method returns the actual number of bytes that
341: * were converted from the Base64 encoding.
342: * <p>This is the lowest level of the decoding methods with
343: * all possible parameters.</p>
344: * @param source the array to convert
345: * @param srcOffset the index where conversion begins
346: * @param destination the array to hold the conversion
347: * @param destOffset the index where output will be put
348: * @return the number of decoded bytes converted
349: */
350: private final int decode4to3(byte[] source, int srcOffset,
351: byte[] destination, int destOffset) {
352: // Example: Dk== or Dk..
353: if (source[srcOffset + 2] == PADDING_CHAR) {
354: int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
355: | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
356:
357: destination[destOffset] = (byte) (outBuff >>> 16);
358: return 1;
359: }
360:
361: // Example: DkL= or DkL.
362: else if (source[srcOffset + 3] == PADDING_CHAR) {
363: int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
364: | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
365: | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
366:
367: destination[destOffset] = (byte) (outBuff >>> 16);
368: destination[destOffset + 1] = (byte) (outBuff >>> 8);
369: return 2;
370: }
371:
372: // Example: DkLE
373: else {
374: int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
375: | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
376: | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
377: | ((DECODABET[source[srcOffset + 3]] & 0xFF));
378:
379: destination[destOffset] = (byte) (outBuff >> 16);
380: destination[destOffset + 1] = (byte) (outBuff >> 8);
381: destination[destOffset + 2] = (byte) (outBuff);
382:
383: return 3;
384: }
385: }
386:
387: /**
388: * Very low-level access to decoding ASCII characters in
389: * the form of a byte array.
390: * @param source the Base64 encoded data
391: * @param off the offset of where to begin decoding
392: * @param len the length of characters to decode
393: * @return decoded data
394: */
395: public final byte[] decode(byte[] source, int off, int len) {
396: int len34 = len * 3 / 4;
397: byte[] outBuff = new byte[len34]; // upper limit on size of output
398: int outBuffPosn = 0;
399:
400: byte[] b4 = new byte[4];
401: int b4Posn = 0;
402: int i = 0;
403: byte sbiCrop = 0;
404: byte sbiDecode = 0;
405: for (i = off; i < off + len; i++) {
406: sbiCrop = (byte) (source[i] & 0x7f); // only the low seven bits
407: sbiDecode = DECODABET[sbiCrop];
408:
409: if (sbiDecode >= WHITE_SPACE_ENC) { // white space, equals sign or better
410: if (sbiDecode >= PADDING_CHAR_ENC) {
411: b4[b4Posn++] = sbiCrop;
412: if (b4Posn > 3) {
413: outBuffPosn += decode4to3(b4, 0, outBuff,
414: outBuffPosn);
415: b4Posn = 0;
416:
417: // if that was the padding char, break out of 'for' loop
418: if (sbiCrop == PADDING_CHAR) {
419: break;
420: }
421: } // end if: quartet built
422: } // end if: equals sign or better
423: } // end if: white space, equals sign or better
424: else {
425: //discard
426: }
427: } // each input character
428:
429: byte[] out = new byte[outBuffPosn];
430: System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
431: return out;
432: }
433:
434: /**
435: * Decodes data from Base64 notation.
436: * @param source the source data
437: * @return the decoded data
438: */
439: public final byte[] decode(byte[] source) {
440: return decode(source, 0, source.length);
441: }
442:
443: /**
444: * Decodes data from Base64 notation. Uses the platform default
445: * character set to obtain bytes from given string.
446: * @param s the string to decode
447: * @return the decoded data
448: */
449: public final byte[] decodeFromString(String s) {
450: return decode(s.getBytes());
451: }
452: }
|