001: /*
002: * The Apache Software License, Version 1.1
003: *
004: *
005: * Copyright (c) 1999 The Apache Software Foundation. All rights
006: * reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * 1. Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * 2. Redistributions in binary form must reproduce the above copyright
016: * notice, this list of conditions and the following disclaimer in
017: * the documentation and/or other materials provided with the
018: * distribution.
019: *
020: * 3. The end-user documentation included with the redistribution,
021: * if any, must include the following acknowledgment:
022: * "This product includes software developed by the
023: * Apache Software Foundation (http://www.apache.org/)."
024: * Alternately, this acknowledgment may appear in the software itself,
025: * if and wherever such third-party acknowledgments normally appear.
026: *
027: * 4. The names "Xerces" and "Apache Software Foundation" must
028: * not be used to endorse or promote products derived from this
029: * software without prior written permission. For written
030: * permission, please contact apache@apache.org.
031: *
032: * 5. Products derived from this software may not be called "Apache",
033: * nor may "Apache" appear in their name, without prior written
034: * permission of the Apache Software Foundation.
035: *
036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: * ====================================================================
049: *
050: * This software consists of voluntary contributions made by many
051: * individuals on behalf of the Apache Software Foundation and was
052: * originally based on software copyright (c) 1999, International
053: * Business Machines, Inc., http://www.apache.org. For more
054: * information on the Apache Software Foundation, please see
055: * <http://www.apache.org/>.
056: */
057:
058: package org.apache.xerces.utils;
059:
060: import java.lang.*;
061:
062: /**
063: * This class provides encode/decode for RFC 2045 Base64 as
064: * defined by RFC 2045, N. Freed and N. Borenstein.
065: * RFC 2045: Multipurpose Internet Mail Extensions (MIME)
066: * Part One: Format of Internet Message Bodies. Reference
067: * 1996 Available at: http://www.ietf.org/rfc/rfc2045.txt
068: * This class is used by XML Schema binary format validation
069: *
070: * This implementation does not encode/decode streaming
071: * data. You need the data that you will encode/decode
072: * already on a byte arrray.
073: *
074: * @author Jeffrey Rodriguez
075: * @author Sandy Gao
076: * @version $Id: Base64.java,v 1.8 2001/05/29 22:19:01 neilg Exp $
077: */
078: public final class Base64 {
079: static private final int BASELENGTH = 255;
080: static private final int LOOKUPLENGTH = 64;
081: static private final int TWENTYFOURBITGROUP = 24;
082: static private final int EIGHTBIT = 8;
083: static private final int SIXTEENBIT = 16;
084: static private final int SIXBIT = 6;
085: static private final int FOURBYTE = 4;
086: static private final int SIGN = -128;
087: static private final byte PAD = (byte) '=';
088: static private final boolean fDebug = false;
089: static private byte[] base64Alphabet = new byte[BASELENGTH];
090: static private byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
091:
092: static {
093:
094: for (int i = 0; i < BASELENGTH; i++) {
095: base64Alphabet[i] = -1;
096: }
097: for (int i = 'Z'; i >= 'A'; i--) {
098: base64Alphabet[i] = (byte) (i - 'A');
099: }
100: for (int i = 'z'; i >= 'a'; i--) {
101: base64Alphabet[i] = (byte) (i - 'a' + 26);
102: }
103:
104: for (int i = '9'; i >= '0'; i--) {
105: base64Alphabet[i] = (byte) (i - '0' + 52);
106: }
107:
108: base64Alphabet['+'] = 62;
109: base64Alphabet['/'] = 63;
110:
111: for (int i = 0; i <= 25; i++)
112: lookUpBase64Alphabet[i] = (byte) ('A' + i);
113:
114: for (int i = 26, j = 0; i <= 51; i++, j++)
115: lookUpBase64Alphabet[i] = (byte) ('a' + j);
116:
117: for (int i = 52, j = 0; i <= 61; i++, j++)
118: lookUpBase64Alphabet[i] = (byte) ('0' + j);
119: lookUpBase64Alphabet[62] = (byte) '+';
120: lookUpBase64Alphabet[63] = (byte) '/';
121:
122: }
123:
124: protected static boolean isWhiteSpace(byte octect) {
125: return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
126: }
127:
128: protected static boolean isPad(byte octect) {
129: return (octect == PAD);
130: }
131:
132: protected static boolean isData(byte octect) {
133: return (base64Alphabet[octect] != -1);
134: }
135:
136: public static boolean isBase64(String isValidString) {
137: if (isValidString == null)
138: return false;
139: return (isArrayByteBase64(isValidString.getBytes()));
140: }
141:
142: public static boolean isBase64(byte octect) {
143: return (isWhiteSpace(octect) || isPad(octect) || isData(octect));
144: }
145:
146: /**
147: * remove WhiteSpace from MIME containing encoded Base64
148: * data.
149: * e.g.
150: * " sdffferererrereresfsdfsdfsdff\n\r
151: * iiiiiiiiierejrlkwjerklwjerwerwr==\n\r"
152: *
153: * @param data
154: * @return
155: */
156: public static synchronized byte[] removeWhiteSpace(byte[] data) {
157: if (data == null)
158: return null;
159:
160: int newSize = 0;
161: int len = data.length;
162: int i = 0;
163: for (; i < len; i++) {
164: if (!isWhiteSpace(data[i]))
165: newSize++;
166: }
167:
168: if (newSize == len)
169: return data;//return input array since no whiteSpace
170:
171: byte[] arrayWithoutSpaces = new byte[newSize];//Allocate new array without whiteSpace
172:
173: int j = 0;
174: for (i = 0; i < len; i++) {
175: if (isWhiteSpace(data[i]))
176: continue;
177: else
178: arrayWithoutSpaces[j++] = data[i];//copy non-WhiteSpace
179: }
180: return arrayWithoutSpaces;
181:
182: }
183:
184: public static synchronized boolean isArrayByteBase64(
185: byte[] arrayOctect) {
186: return (getDecodedDataLength(arrayOctect) >= 0);
187: }
188:
189: /**
190: * Encodes hex octects into Base64
191: *
192: * @param binaryData Array containing binaryData
193: * @return Encoded Base64 array
194: */
195: public static synchronized byte[] encode(byte[] binaryData) {
196: if (binaryData == null)
197: return null;
198:
199: int lengthDataBits = binaryData.length * EIGHTBIT;
200: int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
201: int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
202: byte encodedData[] = null;
203:
204: if (fewerThan24bits != 0) //data not divisible by 24 bit
205: encodedData = new byte[(numberTriplets + 1) * 4];
206: else
207: // 16 or 8 bit
208: encodedData = new byte[numberTriplets * 4];
209:
210: byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
211:
212: int encodedIndex = 0;
213: int dataIndex = 0;
214: int i = 0;
215: if (fDebug) {
216: System.out
217: .println("number of triplets = " + numberTriplets);
218: }
219: for (i = 0; i < numberTriplets; i++) {
220:
221: dataIndex = i * 3;
222: b1 = binaryData[dataIndex];
223: b2 = binaryData[dataIndex + 1];
224: b3 = binaryData[dataIndex + 2];
225:
226: if (fDebug) {
227: System.out.println("b1= " + b1 + ", b2= " + b2
228: + ", b3= " + b3);
229: }
230:
231: l = (byte) (b2 & 0x0f);
232: k = (byte) (b1 & 0x03);
233:
234: encodedIndex = i * 4;
235: byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
236: : (byte) ((b1) >> 2 ^ 0xc0);
237:
238: byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
239: : (byte) ((b2) >> 4 ^ 0xf0);
240: byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6)
241: : (byte) ((b3) >> 6 ^ 0xfc);
242:
243: encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
244: if (fDebug) {
245: System.out.println("val2 = " + val2);
246: System.out.println("k4 = " + (k << 4));
247: System.out.println("vak = " + (val2 | (k << 4)));
248: }
249:
250: encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2
251: | (k << 4)];
252: encodedData[encodedIndex + 2] = lookUpBase64Alphabet[(l << 2)
253: | val3];
254: encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
255: }
256:
257: // form integral number of 6-bit groups
258: dataIndex = i * 3;
259: encodedIndex = i * 4;
260: if (fewerThan24bits == EIGHTBIT) {
261: b1 = binaryData[dataIndex];
262: k = (byte) (b1 & 0x03);
263: if (fDebug) {
264: System.out.println("b1=" + b1);
265: System.out.println("b1<<2 = " + (b1 >> 2));
266: }
267: byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
268: : (byte) ((b1) >> 2 ^ 0xc0);
269: encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
270: encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
271: encodedData[encodedIndex + 2] = PAD;
272: encodedData[encodedIndex + 3] = PAD;
273: } else if (fewerThan24bits == SIXTEENBIT) {
274:
275: b1 = binaryData[dataIndex];
276: b2 = binaryData[dataIndex + 1];
277: l = (byte) (b2 & 0x0f);
278: k = (byte) (b1 & 0x03);
279:
280: byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
281: : (byte) ((b1) >> 2 ^ 0xc0);
282: byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
283: : (byte) ((b2) >> 4 ^ 0xf0);
284:
285: encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
286: encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2
287: | (k << 4)];
288: encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
289: encodedData[encodedIndex + 3] = PAD;
290: }
291: return encodedData;
292: }
293:
294: /**
295: * Decodes Base64 data into octects
296: *
297: * @param binaryData Byte array containing Base64 data
298: * @return Array containind decoded data.
299: */
300: public static synchronized byte[] decode(byte[] base64Data) {
301:
302: if (base64Data == null)
303: return null;
304:
305: byte[] normalizedBase64Data = removeWhiteSpace(base64Data);
306:
307: if (normalizedBase64Data.length % FOURBYTE != 0) {
308: return null;//should be divisible by four
309: }
310:
311: int numberQuadruple = (normalizedBase64Data.length / FOURBYTE);
312:
313: if (numberQuadruple == 0)
314: return new byte[0];
315:
316: byte decodedData[] = null;
317: byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
318: byte d1 = 0, d2 = 0, d3 = 0, d4 = 0;
319:
320: // Throw away anything not in normalizedBase64Data
321: // Adjust size
322: int i = 0;
323: int encodedIndex = 0;
324: int dataIndex = 0;
325: decodedData = new byte[(numberQuadruple) * 3];
326:
327: for (; i < numberQuadruple - 1; i++) {
328:
329: if (!isData((d1 = normalizedBase64Data[dataIndex++]))
330: || !isData((d2 = normalizedBase64Data[dataIndex++]))
331: || !isData((d3 = normalizedBase64Data[dataIndex++]))
332: || !isData((d4 = normalizedBase64Data[dataIndex++])))
333: return null;//if found "no data" just return null
334:
335: b1 = base64Alphabet[d1];
336: b2 = base64Alphabet[d2];
337: b3 = base64Alphabet[d3];
338: b4 = base64Alphabet[d4];
339:
340: decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
341: decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
342: decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
343: }
344:
345: if (!isData((d1 = normalizedBase64Data[dataIndex++]))
346: || !isData((d2 = normalizedBase64Data[dataIndex++]))) {
347: return null;//if found "no data" just return null
348: }
349:
350: b1 = base64Alphabet[d1];
351: b2 = base64Alphabet[d2];
352:
353: d3 = normalizedBase64Data[dataIndex++];
354: d4 = normalizedBase64Data[dataIndex++];
355: if (!isData((d3)) || !isData((d4))) {//Check if they are PAD characters
356: if (isPad(d3) && isPad(d4)) { //Two PAD e.g. 3c[Pad][Pad]
357: if ((b2 & 0xf) != 0)//last 4 bits should be zero
358: return null;
359: byte[] tmp = new byte[i * 3 + 1];
360: System.arraycopy(decodedData, 0, tmp, 0, i * 3);
361: tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
362: return tmp;
363: } else if (!isPad(d3) && isPad(d4)) { //One PAD e.g. 3cQ[Pad]
364: b3 = base64Alphabet[d3];
365: if ((b3 & 0x3) != 0)//last 2 bits should be zero
366: return null;
367: byte[] tmp = new byte[i * 3 + 2];
368: System.arraycopy(decodedData, 0, tmp, 0, i * 3);
369: tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
370: tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
371: return tmp;
372: } else {
373: return null;//an error like "3c[Pad]r", "3cdX", "3cXd", "3cXX" where X is non data
374: }
375: } else { //No PAD e.g 3cQl
376: b3 = base64Alphabet[d3];
377: b4 = base64Alphabet[d4];
378: decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
379: decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
380: decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
381:
382: }
383:
384: return decodedData;
385: }
386:
387: /**
388: * returns length of decoded data given an
389: * array containing encoded data.
390: * WhiteSpace removing is done if data array not
391: * valid.
392: *
393: * @param base64Data
394: * @return a -1 would be return if not
395: */
396: static public synchronized int getDecodedDataLength(
397: byte[] base64Data) {
398:
399: if (base64Data == null)
400: return -1;
401:
402: if (base64Data.length == 0)
403: return 0;
404:
405: //byte[] normalizedBase64Data = removeWhiteSpace( base64Data );//Remove any whiteSpace
406: byte[] decodedData = null;
407:
408: if ((decodedData = decode(base64Data)) == null)//decode could return a null byte array
409: return -1;
410:
411: return decodedData.length;
412: }
413: }
|