001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064:
065: package com.jcorporate.expresso.core.misc;
066:
067: /**
068: * Base 64 encoding class.
069: * Many thanks goes to Wei Dai's
070: * <A HREF="http://www.eskimo.com/~weidai/cryptlib.html">Crypto++</A> for
071: * providing public domain C++ code
072: * that this could be derived from.
073: * </p>
074: *
075: * @since Expresso 3.0
076: */
077: public class Base64 {
078: static final private int bytesPerLine = 80; // number of bytes per line
079:
080: /* This array maps the characters to their 6 bit values */
081: private final static char[] vec = { 'A', 'B', 'C', 'D', 'E', 'F',
082: 'G', 'H', // 0
083: 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
084: 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
085: 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
086: 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
087: 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
088: 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
089: '4', '5', '6', '7', '8', '9', '+', '/' // 7
090: };
091: private final static byte padding = (byte) '=';
092:
093: public Base64() {
094: }
095:
096: /**
097: * @param inByte
098: * @return
099: */
100: static private byte convertToNumber(byte inByte) {
101: if (inByte >= 'A' && inByte <= 'Z') {
102: return (byte) (inByte - 'A');
103: }
104: if (inByte >= 'a' && inByte <= 'z') {
105: return (byte) (inByte - 'a' + 26);
106: }
107: if (inByte >= '0' && inByte <= '9') {
108: return (byte) (inByte - '0' + 52);
109: }
110: if (inByte == '+') {
111: return (62);
112: }
113: if (inByte == '/') {
114: return (63);
115: }
116:
117: return (-1);
118: } /* convertToNumber(byte) */
119:
120: /**
121: * decode - Decodes a Base64 encoded string back into its original
122: * binary format. If the data is not a proper base64 encoded string,
123: * it might throw an ArrayIndexOutOfBoundsException.
124: *
125: * @param data the data to decode
126: * @return
127: */
128: static public byte[] decode(String data)
129: throws IllegalArgumentException {
130:
131: //Check for NULL
132: if (data == null) {
133: throw new IllegalArgumentException(
134: "Base64.decode: data must not be null");
135: }
136: //Check to see if data is proper length
137: if (data.length() % 4 != 0) {
138: throw new IllegalArgumentException(
139: "Base64.decode: data is not of proper length");
140: }
141:
142: byte[] inputBuffer = data.getBytes();
143: int validInputCount = 0;
144: byte[] inBuffer = new byte[inputBuffer.length];
145: int dataLength = inputBuffer.length;
146:
147: //We use the same input and output buffer since
148: // the value will be the same
149: //
150: //Preprocess -- Strip the LF's et al
151: //
152: int i;
153:
154: for (i = 0; i < dataLength; i++) {
155: byte temp = convertToNumber(inputBuffer[i]);
156:
157: if (temp >= 0) {
158: inBuffer[i] = temp;
159: validInputCount++;
160: }
161: }
162:
163: byte[] outBuffer = new byte[dataLength];
164: int startOut = 0;
165: int startIn = 0;
166:
167: while (validInputCount > startIn) {
168: int dataLen = validInputCount;
169:
170: if (dataLen - startIn > 3) {
171: outBuffer[startOut] = (byte) ((inBuffer[startIn] << 2) + (inBuffer[startIn + 1] >>> 4));
172: outBuffer[startOut + 1] = (byte) ((inBuffer[startIn + 1] << 4) + (inBuffer[startIn + 2] >>> 2));
173: outBuffer[startOut + 2] = (byte) ((inBuffer[startIn + 2] << 6) + (inBuffer[startIn + 3]));
174: startOut += 3;
175: startIn += 4;
176: } else if (dataLen - startIn > 2) {
177: outBuffer[startOut] = (byte) ((inBuffer[startIn] << 2) + (inBuffer[startIn + 1] >>> 4));
178: outBuffer[startOut + 1] = (byte) ((inBuffer[startIn + 1] << 4) + (inBuffer[startIn + 2] >>> 2));
179: startOut += 2;
180: startIn += 3;
181: } else if (dataLen - startIn > 1) {
182: outBuffer[startOut] = (byte) ((inBuffer[startIn] << 2) + (inBuffer[startIn + 1] >>> 4));
183: startOut += 1;
184: startIn += 2;
185: } else {
186: throw new IllegalStateException(
187: "com.jcorporate.expresso.core.misc."
188: + "Base64.Decode: Corrupt Input Data");
189: }
190: }
191:
192: //Copy the final bytes over to a real output array
193: byte[] finalOutput = new byte[startOut];
194:
195: for (i = 0; i < startOut; i++) {
196: finalOutput[i] = outBuffer[i];
197: }
198:
199: inBuffer = null;
200: outBuffer = null;
201:
202: return finalOutput;
203: } /* decode(String) */
204:
205: /**
206: * <B>Note: this function does not process stricly compliant Base64
207: * code. To process strictly compliant Base64 encoded strings, use encode
208: * and decode functions with padding</B>
209: * <p/>
210: * Decodes a Base64 encoded string back into its original binary format.
211: * The only difference from decode is that this function will bad the
212: * end with as many ='s as necessary to make things work properly.
213: *
214: * @param data the Base64 encoded string
215: * @return the byte array of the original data.
216: * @throws IllegalArgumentException if the input data is null or zero bytes.
217: */
218: static public byte[] decodeNoPadding(String data)
219: throws IllegalArgumentException {
220: String myName = "Base64.decodeNoPadding";
221:
222: if (data == null) {
223: throw new IllegalArgumentException(myName
224: + " parameter 'data'" + " must not be null");
225: }
226: if (data.length() == 0) {
227: throw new IllegalArgumentException(myName
228: + " parameter 'data'"
229: + " must not be greater than zero length");
230: }
231: //Append as many zeros as necessary to make things work.
232: for (; data.length() % 4 != 0; data = data + "=") {
233: ;
234: }
235:
236: return decode(data);
237: } /* decodeNoPadding(String) */
238:
239: /**
240: * encode - Encodes a binary byte array into an Ascii String (including 80
241: * column line wrap.
242: *
243: * @param inBuffer[] the data to encode
244: * @return
245: */
246: static public String encode(byte[] inBuffer)
247: throws IllegalArgumentException {
248: if (inBuffer.length == 0) {
249: throw new IllegalArgumentException("Base64.encode: "
250: + "inBuffer must be > zero bytes in length");
251: }
252:
253: int dataSize = inBuffer.length;
254: int startIn = 0;
255: int startOut = 0;
256:
257: //Create an output buffer that's twice the size of the input buffer
258: //that way we have enough room for sure
259: byte[] outBuffer;
260:
261: if (dataSize < 4) {
262: outBuffer = new byte[4];
263: } else {
264: outBuffer = new byte[dataSize * 2];
265: }
266: //Iteratate
267: while (startIn < dataSize) {
268: int dataLen = inBuffer.length;
269: byte a;
270: byte b;
271: byte c;
272:
273: //Check for linewrap
274: if ((startOut % bytesPerLine) + 4 > bytesPerLine) {
275: outBuffer[startOut] = (byte) '\r';
276: outBuffer[startOut + 1] = (byte) '\n';
277: startOut += 2;
278: }
279: if (dataLen - startIn > 2) {
280: a = inBuffer[startIn];
281: b = inBuffer[startIn + 1];
282: c = inBuffer[startIn + 2];
283: outBuffer[startOut] = (byte) vec[(a >>> 2) & 0x3F];
284: outBuffer[startOut + 1] = (byte) vec[((a << 4) & 0x30)
285: + ((b >>> 4) & 0xf)];
286: outBuffer[startOut + 2] = (byte) vec[((b << 2) & 0x3c)
287: + ((c >>> 6) & 0x3)];
288: outBuffer[startOut + 3] = (byte) vec[c & 0x3F];
289: startOut += 4;
290: startIn += 3;
291: } else if (dataLen - startIn == 1) {
292: a = inBuffer[startIn];
293: b = 0;
294: c = 0;
295: outBuffer[startOut] = (byte) vec[(a >>> 2) & 0x3F];
296: outBuffer[startOut + 1] = (byte) vec[((a << 4) & 0x30)
297: + ((b >>> 4) & 0xf)];
298: outBuffer[startOut + 2] = padding;
299: outBuffer[startOut + 3] = padding;
300: startOut += 4;
301: startIn += 2;
302: } else if (dataLen - startIn == 2) {
303: a = inBuffer[startIn];
304: b = inBuffer[startIn + 1];
305: c = 0;
306: outBuffer[startOut] = (byte) vec[(a >>> 2) & 0x3F];
307: outBuffer[startOut + 1] = (byte) vec[((a << 4) & 0x30)
308: + ((b >>> 4) & 0xf)];
309: outBuffer[startOut + 2] = (byte) vec[((b << 2) & 0x3c)
310: + ((c >>> 6) & 0x3)];
311: outBuffer[startOut + 3] = padding;
312: startOut += 4;
313: startIn += 2;
314: }
315: }
316:
317: return new String(outBuffer, 0, startOut);
318: } /* encode(byte) */
319:
320: /* */
321: /* */
322: /* Encoding Part */
323: /* */
324: /* */
325: /**
326: * <B>Note: this function does not process stricly compliant Base64
327: * code. To process strictly compliant Base64 encoded strings, use encode
328: * and decode functions with padding</B>
329: * <p/>
330: * Encodes a binary bite array into Base64 coding. The difference between
331: * this and the other encode function, is that this function strips all
332: * padding from the final result before returning
333: *
334: * @param inBuffer[] the byte array to encode.
335: * @return a string that is the Base64 equiv of the input data without
336: * padding characters
337: * @throws IllegalArgumentException if the input data is null or zero bytes.
338: */
339: static public String encodeNoPadding(byte[] inBuffer)
340: throws IllegalArgumentException {
341: String stringWithPadding = encode(inBuffer);
342: int equalsPlacement = stringWithPadding.indexOf((int) '=');
343:
344: if (equalsPlacement == -1) {
345: return stringWithPadding;
346: } else {
347: return stringWithPadding.substring(0, equalsPlacement);
348: }
349: } /* encodeNoPaggding(byte) */
350:
351: } /* Base64 */
|