001: /*
002: * $Header: /home/cvs/jakarta-commons/codec/src/java/org/apache/commons/codec/binary/Base64.java,v 1.1 2003/04/25 17:50:56 tobrien Exp $
003: * $Revision: 1.1 $
004: * $Date: 2003/04/25 17:50:56 $
005: *
006: * ====================================================================
007: *
008: * The Apache Software License, Version 1.1
009: *
010: * Copyright (c) 2003 The Apache Software Foundation. All rights
011: * reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions
015: * are met:
016: *
017: * 1. Redistributions of source code must retain the above copyright
018: * notice, this list of conditions and the following disclaimer.
019: *
020: * 2. Redistributions in binary form must reproduce the above copyright
021: * notice, this list of conditions and the following disclaimer in
022: * the documentation and/or other materials provided with the
023: * distribution.
024: *
025: * 3. The end-user documentation included with the redistribution, if
026: * any, must include the following acknowlegement:
027: * "This product includes software developed by the
028: * Apache Software Foundation (http://www.apache.org/)."
029: * Alternately, this acknowlegement may appear in the software itself,
030: * if and wherever such third-party acknowlegements normally appear.
031: *
032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
033: * Foundation" must not be used to endorse or promote products derived
034: * from this software without prior written permission. For written
035: * permission, please contact apache@apache.org.
036: *
037: * 5. Products derived from this software may not be called "Apache"
038: * nor may "Apache" appear in their names without prior written
039: * permission of the Apache Group.
040: *
041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
052: * SUCH DAMAGE.
053: * ====================================================================
054: *
055: * This software consists of voluntary contributions made by many
056: * individuals on behalf of the Apache Software Foundation. For more
057: * information on the Apache Software Foundation, please see
058: * <http://www.apache.org/>.
059: *
060: * [Additional notices, if required by prior licensing conditions]
061: *
062: */
063: package org.directwebremoting.util;
064:
065: /**
066: * This class provides encode/decode for RFC 2045 Base64 as
067: * defined by RFC 2045, N. Freed and N. Borenstein. <a
068: * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>:
069: * Multipurpose Internet Mail Extensions (MIME) Part One: Format of
070: * Internet Message Bodies. Reference 1996
071: *
072: * @author Jeffrey Rodriguez
073: * @author <a href="dlr@apache.org">Daniel Rall</a>
074: * @author <a href="m.redington@ucl.ac.uk">Martin Redington</a>
075: * @since 1.0-dev
076: *
077: */
078: @SuppressWarnings({"ALL"})
079: public class Base64 {
080:
081: // Create constants pertaining to the chunk requirement
082: static final int CHUNK_SIZE = 76;
083: static final byte[] CHUNK_SEPARATOR = "\n".getBytes();
084:
085: // Create numerical and byte constants
086: static final int BASELENGTH = 255;
087: static final int LOOKUPLENGTH = 64;
088: static final int TWENTYFOURBITGROUP = 24;
089: static final int EIGHTBIT = 8;
090: static final int SIXTEENBIT = 16;
091: static final int SIXBIT = 6;
092: static final int FOURBYTE = 4;
093: static final int SIGN = -128;
094: static final byte PAD = (byte) '=';
095:
096: // Create arrays to hold the base64 characters and a
097: // lookup for base64 chars
098: private static byte[] base64Alphabet = new byte[BASELENGTH];
099: private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
100:
101: // Populating the lookup and character arrays
102: static {
103: for (int i = 0; i < BASELENGTH; i++) {
104: base64Alphabet[i] = (byte) -1;
105: }
106: for (int i = 'Z'; i >= 'A'; i--) {
107: base64Alphabet[i] = (byte) (i - 'A');
108: }
109: for (int i = 'z'; i >= 'a'; i--) {
110: base64Alphabet[i] = (byte) (i - 'a' + 26);
111: }
112: for (int i = '9'; i >= '0'; i--) {
113: base64Alphabet[i] = (byte) (i - '0' + 52);
114: }
115:
116: base64Alphabet['+'] = 62;
117: base64Alphabet['/'] = 63;
118:
119: for (int i = 0; i <= 25; i++) {
120: lookUpBase64Alphabet[i] = (byte) ('A' + i);
121: }
122:
123: for (int i = 26, j = 0; i <= 51; i++, j++) {
124: lookUpBase64Alphabet[i] = (byte) ('a' + j);
125: }
126:
127: for (int i = 52, j = 0; i <= 61; i++, j++) {
128: lookUpBase64Alphabet[i] = (byte) ('0' + j);
129: }
130:
131: lookUpBase64Alphabet[62] = (byte) '+';
132: lookUpBase64Alphabet[63] = (byte) '/';
133: }
134:
135: private static boolean isBase64(byte octect) {
136: if (octect == PAD) {
137: return true;
138: } else if (base64Alphabet[octect] == -1) {
139: return false;
140: } else {
141: return true;
142: }
143: }
144:
145: public static boolean isArrayByteBase64(byte[] arrayOctect) {
146:
147: arrayOctect = discardWhitespace(arrayOctect);
148:
149: int length = arrayOctect.length;
150: if (length == 0) {
151: // shouldn't a 0 length array be valid base64 data?
152: // return false;
153: return true;
154: }
155: for (int i = 0; i < length; i++) {
156: if (!isBase64(arrayOctect[i])) {
157: return false;
158: }
159: }
160: return true;
161: }
162:
163: public static byte[] encodeBase64(byte[] binaryData) {
164: return (encodeBase64(binaryData, false));
165: }
166:
167: public static byte[] encodeBase64Chunked(byte[] binaryData) {
168: return (encodeBase64(binaryData, true));
169: }
170:
171: public Object decode(Object pObject)
172: throws IllegalArgumentException {
173:
174: Object result;
175:
176: if (!(pObject instanceof byte[])) {
177: throw new IllegalArgumentException("Parameter supplied to "
178: + "Base64 " + "decode is not a byte[]");
179: } else {
180: result = decode((byte[]) pObject);
181: }
182:
183: return result;
184:
185: }
186:
187: public byte[] decode(byte[] pArray) throws IllegalArgumentException {
188:
189: byte[] result;
190:
191: if (!isArrayByteBase64(pArray)) {
192: throw new IllegalArgumentException("Parameter supplied to "
193: + "Base64 " + "decode is not a valid base64 data.");
194: } else {
195: result = decodeBase64(pArray);
196: }
197:
198: return (result);
199: }
200:
201: /**
202: * Encodes hex octects into Base64.
203: *
204: * @param binaryData Array containing binary data to encode.
205: * @return Base64-encoded data.
206: */
207: public static byte[] encodeBase64(byte[] binaryData,
208: boolean isChunked) {
209: int lengthDataBits = binaryData.length * EIGHTBIT;
210: int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
211: int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
212: byte encodedData[] = null;
213: int encodedDataLength = 0;
214: int nbrChunks = 0;
215:
216: if (fewerThan24bits != 0) {
217: //data not divisible by 24 bit
218: encodedDataLength = (numberTriplets + 1) * 4;
219: } else {
220: // 16 or 8 bit
221: encodedDataLength = numberTriplets * 4;
222: }
223:
224: // If the output is to be "chunked" into 76 character sections,
225: // for compliance with RFC 2045 MIME, then it is important to
226: // allow for extra length to account for the separator(s)
227: if (isChunked) {
228:
229: nbrChunks = (CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math
230: .ceil((float) encodedDataLength / CHUNK_SIZE));
231: encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
232: }
233:
234: encodedData = new byte[encodedDataLength];
235:
236: byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
237:
238: int encodedIndex = 0;
239: int dataIndex = 0;
240: int i = 0;
241: int nextSeparatorIndex = CHUNK_SIZE;
242: int chunksSoFar = 0;
243:
244: //log.debug("number of triplets = " + numberTriplets);
245: for (i = 0; i < numberTriplets; i++) {
246: dataIndex = i * 3;
247: b1 = binaryData[dataIndex];
248: b2 = binaryData[dataIndex + 1];
249: b3 = binaryData[dataIndex + 2];
250:
251: //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
252:
253: l = (byte) (b2 & 0x0f);
254: k = (byte) (b1 & 0x03);
255:
256: byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
257: : (byte) ((b1) >> 2 ^ 0xc0);
258: byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
259: : (byte) ((b2) >> 4 ^ 0xf0);
260: byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6)
261: : (byte) ((b3) >> 6 ^ 0xfc);
262:
263: encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
264: //log.debug( "val2 = " + val2 );
265: //log.debug( "k4 = " + (k<<4) );
266: //log.debug( "vak = " + (val2 | (k<<4)) );
267: encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2
268: | (k << 4)];
269: encodedData[encodedIndex + 2] = lookUpBase64Alphabet[(l << 2)
270: | val3];
271: encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
272:
273: encodedIndex += 4;
274:
275: // If we are chunking, let's put a chunk separator down.
276: if (isChunked) {
277: // this assumes that CHUNK_SIZE % 4 == 0
278: if (encodedIndex == nextSeparatorIndex) {
279: System.arraycopy(CHUNK_SEPARATOR, 0, encodedData,
280: encodedIndex, CHUNK_SEPARATOR.length);
281: chunksSoFar++;
282: nextSeparatorIndex = (CHUNK_SIZE * (chunksSoFar + 1))
283: + (chunksSoFar * CHUNK_SEPARATOR.length);
284: encodedIndex += CHUNK_SEPARATOR.length;
285: }
286: }
287: }
288:
289: // form integral number of 6-bit groups
290: dataIndex = i * 3;
291:
292: if (fewerThan24bits == EIGHTBIT) {
293: b1 = binaryData[dataIndex];
294: k = (byte) (b1 & 0x03);
295: //log.debug("b1=" + b1);
296: //log.debug("b1<<2 = " + (b1>>2) );
297: byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
298: : (byte) ((b1) >> 2 ^ 0xc0);
299: encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
300: encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
301: encodedData[encodedIndex + 2] = PAD;
302: encodedData[encodedIndex + 3] = PAD;
303: } else if (fewerThan24bits == SIXTEENBIT) {
304:
305: b1 = binaryData[dataIndex];
306: b2 = binaryData[dataIndex + 1];
307: l = (byte) (b2 & 0x0f);
308: k = (byte) (b1 & 0x03);
309:
310: byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
311: : (byte) ((b1) >> 2 ^ 0xc0);
312: byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
313: : (byte) ((b2) >> 4 ^ 0xf0);
314:
315: encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
316: encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2
317: | (k << 4)];
318: encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
319: encodedData[encodedIndex + 3] = PAD;
320: }
321:
322: if (isChunked) {
323: // we also add a separator to the end of the final chunk.
324: if (chunksSoFar < nbrChunks) {
325: System.arraycopy(CHUNK_SEPARATOR, 0, encodedData,
326: encodedDataLength - CHUNK_SEPARATOR.length,
327: CHUNK_SEPARATOR.length);
328: }
329: }
330:
331: return encodedData;
332: }
333:
334: /**
335: * Decodes Base64 data into octects
336: *
337: * @param base64Data Byte array containing Base64 data
338: * @return Array containing decoded data.
339: */
340: public static byte[] decodeBase64(byte[] base64Data) {
341: // RFC 2045 suggests line wrapping at (no more than) 76
342: // characters -- we may have embedded whitespace.
343: base64Data = discardWhitespace(base64Data);
344:
345: // handle the edge case, so we don't have to worry about it later
346: if (base64Data.length == 0) {
347: return new byte[0];
348: }
349:
350: int numberQuadruple = base64Data.length / FOURBYTE;
351: byte decodedData[] = null;
352: byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
353:
354: // Throw away anything not in base64Data
355:
356: int encodedIndex = 0;
357: int dataIndex = 0;
358: {
359: // this sizes the output array properly - rlw
360: int lastData = base64Data.length;
361: // ignore the '=' padding
362: while (base64Data[lastData - 1] == PAD) {
363: if (--lastData == 0) {
364: return new byte[0];
365: }
366: }
367: decodedData = new byte[lastData - numberQuadruple];
368: }
369:
370: for (int i = 0; i < numberQuadruple; i++) {
371: dataIndex = i * 4;
372: marker0 = base64Data[dataIndex + 2];
373: marker1 = base64Data[dataIndex + 3];
374:
375: b1 = base64Alphabet[base64Data[dataIndex]];
376: b2 = base64Alphabet[base64Data[dataIndex + 1]];
377:
378: if (marker0 != PAD && marker1 != PAD) {
379: //No PAD e.g 3cQl
380: b3 = base64Alphabet[marker0];
381: b4 = base64Alphabet[marker1];
382:
383: decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
384: decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
385: decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
386: } else if (marker0 == PAD) {
387: //Two PAD e.g. 3c[Pad][Pad]
388: decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
389: } else if (marker1 == PAD) {
390: //One PAD e.g. 3cQ[Pad]
391: b3 = base64Alphabet[marker0];
392:
393: decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
394: decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
395: }
396: encodedIndex += 3;
397: }
398: return decodedData;
399: }
400:
401: /**
402: * Discards any whitespace from a base-64 encoded block.
403: *
404: * @param data The base-64 encoded data to discard the whitespace
405: * from.
406: * @return The data, less whitespace (see RFC 2045).
407: */
408: static byte[] discardWhitespace(byte[] data) {
409: byte groomedData[] = new byte[data.length];
410: int bytesCopied = 0;
411:
412: for (int i = 0; i < data.length; i++) {
413: switch (data[i]) {
414: case (byte) ' ':
415: case (byte) '\n':
416: case (byte) '\r':
417: case (byte) '\t':
418: break;
419: default:
420: groomedData[bytesCopied++] = data[i];
421: }
422: }
423:
424: byte packedData[] = new byte[bytesCopied];
425:
426: System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
427:
428: return packedData;
429: }
430:
431: // Implementation of the Encoder Interface
432:
433: /**
434: * encode an Object
435: */
436: public Object encode(Object pObject)
437: throws IllegalArgumentException {
438:
439: Object result;
440:
441: if (!(pObject instanceof byte[])) {
442: throw new IllegalArgumentException("Parameter supplied to "
443: + "Base64 " + "encode is not a byte[]");
444: } else {
445: result = encode((byte[]) pObject);
446: }
447:
448: return result;
449:
450: }
451:
452: public byte[] encode(byte[] pArray) throws IllegalArgumentException {
453: return (encodeBase64(pArray, false));
454: }
455:
456: }
|