001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.xerces.impl.dv.util;
019:
020: /**
021: * This class provides encode/decode for RFC 2045 Base64 as
022: * defined by RFC 2045, N. Freed and N. Borenstein.
023: * RFC 2045: Multipurpose Internet Mail Extensions (MIME)
024: * Part One: Format of Internet Message Bodies. Reference
025: * 1996 Available at: http://www.ietf.org/rfc/rfc2045.txt
026: * This class is used by XML Schema binary format validation
027: *
028: * This implementation does not encode/decode streaming
029: * data. You need the data that you will encode/decode
030: * already on a byte arrray.
031: *
032: * @xerces.internal
033: *
034: * @author Jeffrey Rodriguez
035: * @author Sandy Gao
036: * @version $Id: Base64.java 446747 2006-09-15 21:46:20Z mrglavas $
037: */
038: public final class Base64 {
039:
040: static private final int BASELENGTH = 128;
041: static private final int LOOKUPLENGTH = 64;
042: static private final int TWENTYFOURBITGROUP = 24;
043: static private final int EIGHTBIT = 8;
044: static private final int SIXTEENBIT = 16;
045: static private final int SIXBIT = 6;
046: static private final int FOURBYTE = 4;
047: static private final int SIGN = -128;
048: static private final char PAD = '=';
049: static private final boolean fDebug = false;
050: static final private byte[] base64Alphabet = new byte[BASELENGTH];
051: static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
052:
053: static {
054:
055: for (int i = 0; i < BASELENGTH; ++i) {
056: base64Alphabet[i] = -1;
057: }
058: for (int i = 'Z'; i >= 'A'; i--) {
059: base64Alphabet[i] = (byte) (i - 'A');
060: }
061: for (int i = 'z'; i >= 'a'; i--) {
062: base64Alphabet[i] = (byte) (i - 'a' + 26);
063: }
064:
065: for (int i = '9'; i >= '0'; i--) {
066: base64Alphabet[i] = (byte) (i - '0' + 52);
067: }
068:
069: base64Alphabet['+'] = 62;
070: base64Alphabet['/'] = 63;
071:
072: for (int i = 0; i <= 25; i++)
073: lookUpBase64Alphabet[i] = (char) ('A' + i);
074:
075: for (int i = 26, j = 0; i <= 51; i++, j++)
076: lookUpBase64Alphabet[i] = (char) ('a' + j);
077:
078: for (int i = 52, j = 0; i <= 61; i++, j++)
079: lookUpBase64Alphabet[i] = (char) ('0' + j);
080: lookUpBase64Alphabet[62] = (char) '+';
081: lookUpBase64Alphabet[63] = (char) '/';
082:
083: }
084:
085: protected static boolean isWhiteSpace(char octect) {
086: return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
087: }
088:
089: protected static boolean isPad(char octect) {
090: return (octect == PAD);
091: }
092:
093: protected static boolean isData(char octect) {
094: return (octect < BASELENGTH && base64Alphabet[octect] != -1);
095: }
096:
097: protected static boolean isBase64(char octect) {
098: return (isWhiteSpace(octect) || isPad(octect) || isData(octect));
099: }
100:
101: /**
102: * Encodes hex octects into Base64
103: *
104: * @param binaryData Array containing binaryData
105: * @return Encoded Base64 array
106: */
107: public static String encode(byte[] binaryData) {
108:
109: if (binaryData == null)
110: return null;
111:
112: int lengthDataBits = binaryData.length * EIGHTBIT;
113: if (lengthDataBits == 0) {
114: return "";
115: }
116:
117: int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
118: int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
119: int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1
120: : numberTriplets;
121: char encodedData[] = null;
122:
123: encodedData = new char[numberQuartet * 4];
124:
125: byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
126:
127: int encodedIndex = 0;
128: int dataIndex = 0;
129: if (fDebug) {
130: System.out
131: .println("number of triplets = " + numberTriplets);
132: }
133:
134: for (int i = 0; i < numberTriplets; i++) {
135: b1 = binaryData[dataIndex++];
136: b2 = binaryData[dataIndex++];
137: b3 = binaryData[dataIndex++];
138:
139: if (fDebug) {
140: System.out.println("b1= " + b1 + ", b2= " + b2
141: + ", b3= " + b3);
142: }
143:
144: l = (byte) (b2 & 0x0f);
145: k = (byte) (b1 & 0x03);
146:
147: byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
148: : (byte) ((b1) >> 2 ^ 0xc0);
149:
150: byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
151: : (byte) ((b2) >> 4 ^ 0xf0);
152: byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6)
153: : (byte) ((b3) >> 6 ^ 0xfc);
154:
155: if (fDebug) {
156: System.out.println("val2 = " + val2);
157: System.out.println("k4 = " + (k << 4));
158: System.out.println("vak = " + (val2 | (k << 4)));
159: }
160:
161: encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
162: encodedData[encodedIndex++] = lookUpBase64Alphabet[val2
163: | (k << 4)];
164: encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2)
165: | val3];
166: encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
167: }
168:
169: // form integral number of 6-bit groups
170: if (fewerThan24bits == EIGHTBIT) {
171: b1 = binaryData[dataIndex];
172: k = (byte) (b1 & 0x03);
173: if (fDebug) {
174: System.out.println("b1=" + b1);
175: System.out.println("b1<<2 = " + (b1 >> 2));
176: }
177: byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
178: : (byte) ((b1) >> 2 ^ 0xc0);
179: encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
180: encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
181: encodedData[encodedIndex++] = PAD;
182: encodedData[encodedIndex++] = PAD;
183: } else if (fewerThan24bits == SIXTEENBIT) {
184: b1 = binaryData[dataIndex];
185: b2 = binaryData[dataIndex + 1];
186: l = (byte) (b2 & 0x0f);
187: k = (byte) (b1 & 0x03);
188:
189: byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
190: : (byte) ((b1) >> 2 ^ 0xc0);
191: byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
192: : (byte) ((b2) >> 4 ^ 0xf0);
193:
194: encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
195: encodedData[encodedIndex++] = lookUpBase64Alphabet[val2
196: | (k << 4)];
197: encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
198: encodedData[encodedIndex++] = PAD;
199: }
200:
201: return new String(encodedData);
202: }
203:
204: /**
205: * Decodes Base64 data into octects
206: *
207: * @param encoded string containing Base64 data
208: * @return Array containind decoded data.
209: */
210: public static byte[] decode(String encoded) {
211:
212: if (encoded == null)
213: return null;
214:
215: char[] base64Data = encoded.toCharArray();
216: // remove white spaces
217: int len = removeWhiteSpace(base64Data);
218:
219: if (len % FOURBYTE != 0) {
220: return null;//should be divisible by four
221: }
222:
223: int numberQuadruple = (len / FOURBYTE);
224:
225: if (numberQuadruple == 0)
226: return new byte[0];
227:
228: byte decodedData[] = null;
229: byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
230: char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
231:
232: int i = 0;
233: int encodedIndex = 0;
234: int dataIndex = 0;
235: decodedData = new byte[(numberQuadruple) * 3];
236:
237: for (; i < numberQuadruple - 1; i++) {
238:
239: if (!isData((d1 = base64Data[dataIndex++]))
240: || !isData((d2 = base64Data[dataIndex++]))
241: || !isData((d3 = base64Data[dataIndex++]))
242: || !isData((d4 = base64Data[dataIndex++])))
243: return null;//if found "no data" just return null
244:
245: b1 = base64Alphabet[d1];
246: b2 = base64Alphabet[d2];
247: b3 = base64Alphabet[d3];
248: b4 = base64Alphabet[d4];
249:
250: decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
251: decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
252: decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
253: }
254:
255: if (!isData((d1 = base64Data[dataIndex++]))
256: || !isData((d2 = base64Data[dataIndex++]))) {
257: return null;//if found "no data" just return null
258: }
259:
260: b1 = base64Alphabet[d1];
261: b2 = base64Alphabet[d2];
262:
263: d3 = base64Data[dataIndex++];
264: d4 = base64Data[dataIndex++];
265: if (!isData((d3)) || !isData((d4))) {//Check if they are PAD characters
266: if (isPad(d3) && isPad(d4)) { //Two PAD e.g. 3c[Pad][Pad]
267: if ((b2 & 0xf) != 0)//last 4 bits should be zero
268: return null;
269: byte[] tmp = new byte[i * 3 + 1];
270: System.arraycopy(decodedData, 0, tmp, 0, i * 3);
271: tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
272: return tmp;
273: } else if (!isPad(d3) && isPad(d4)) { //One PAD e.g. 3cQ[Pad]
274: b3 = base64Alphabet[d3];
275: if ((b3 & 0x3) != 0)//last 2 bits should be zero
276: return null;
277: byte[] tmp = new byte[i * 3 + 2];
278: System.arraycopy(decodedData, 0, tmp, 0, i * 3);
279: tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
280: tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
281: return tmp;
282: } else {
283: return null;//an error like "3c[Pad]r", "3cdX", "3cXd", "3cXX" where X is non data
284: }
285: } else { //No PAD e.g 3cQl
286: b3 = base64Alphabet[d3];
287: b4 = base64Alphabet[d4];
288: decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
289: decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
290: decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
291:
292: }
293:
294: return decodedData;
295: }
296:
297: /**
298: * remove WhiteSpace from MIME containing encoded Base64 data.
299: *
300: * @param data the byte array of base64 data (with WS)
301: * @return the new length
302: */
303: protected static int removeWhiteSpace(char[] data) {
304: if (data == null)
305: return 0;
306:
307: // count characters that's not whitespace
308: int newSize = 0;
309: int len = data.length;
310: for (int i = 0; i < len; i++) {
311: if (!isWhiteSpace(data[i]))
312: data[newSize++] = data[i];
313: }
314: return newSize;
315: }
316: }
|