001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.midp.io;
028:
029: import java.io.IOException;
030:
031: /**
032: * This class contains a Base64 encoder and decoder. It cannot be
033: * instantiated. The encoder and decoder are based on RFC 1521, except that
034: * encoder do not break up lines and the decoder will treat a line break as
035: * as invalid characters. This done since MIDP JAD's can not have line breaks
036: * with in an attribute. Also different RFC's use different lengths for
037: * base64 strings such as; 76 for 1521, 64 for 1421, and no breaks for 2228.
038: * <p>
039: * For example to decode:
040: * <pre>
041: * String encodedData;
042: * byte binaryData[];
043: *
044: * binaryData = Base64.decode(encodedData);
045: * </pre>
046: * This will decode the String in <i>encodedData</i> and give you an array
047: * of bytes in the array <i>binaryData</i>.
048: *
049: * On errors, this class throws an IOException with the following detail
050: * strings:
051: * <pre>
052: * "Base64 string not a mulitple of 4"
053: * "Invalid character in Base64 string"
054: * </pre>
055: */
056:
057: public class Base64 {
058: /** prevents anyone from instantiating this class */
059: private Base64() {
060: }
061:
062: /**
063: * This character array provides the alphabet map from RFC1521.
064: */
065: private final static char ALPHABET[] = {
066: // 0 1 2 3 4 5 6 7
067: 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0
068: 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
069: 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
070: 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
071: 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
072: 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
073: 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
074: '4', '5', '6', '7', '8', '9', '+', '/' // 7
075: };
076:
077: /**
078: * Decodes a 7 bit Base64 character into its binary value.
079: */
080: private static int valueDecoding[] = new int[128];
081:
082: /**
083: * initializes the value decoding array from the
084: * character map
085: */
086: static {
087: for (int i = 0; i < valueDecoding.length; i++) {
088: valueDecoding[i] = -1;
089: }
090:
091: for (int i = 0; i < ALPHABET.length; i++) {
092: valueDecoding[ALPHABET[i]] = i;
093: }
094: }
095:
096: /**
097: * Converts a byte array into a Base64 encoded string.
098: * @param data bytes to encode
099: * @param offset which byte to start at
100: * @param length how many bytes to encode; padding will be added if needed
101: * @return base64 encoding of data; 4 chars for every 3 bytes
102: */
103: public static String encode(byte[] data, int offset, int length) {
104: int i;
105: int encodedLen;
106: char[] encoded;
107:
108: // 4 chars for 3 bytes, run input up to a multiple of 3
109: encodedLen = (length + 2) / 3 * 4;
110: encoded = new char[encodedLen];
111:
112: for (i = 0, encodedLen = 0; encodedLen < encoded.length; i += 3, encodedLen += 4) {
113: encodeQuantum(data, offset + i, length - i, encoded,
114: encodedLen);
115: }
116:
117: return new String(encoded);
118: }
119:
120: /**
121: * Encodes 1, 2, or 3 bytes of data as 4 Base64 chars.
122: *
123: * @param in buffer of bytes to encode
124: * @param inOffset where the first byte to encode is
125: * @param len how many bytes to encode
126: * @param out buffer to put the output in
127: * @param outOffset where in the output buffer to put the chars
128: */
129: private static void encodeQuantum(byte in[], int inOffset, int len,
130: char out[], int outOffset) {
131: byte a = 0, b = 0, c = 0;
132:
133: a = in[inOffset];
134: out[outOffset] = ALPHABET[(a >>> 2) & 0x3F];
135:
136: if (len > 2) {
137: b = in[inOffset + 1];
138: c = in[inOffset + 2];
139: out[outOffset + 1] = ALPHABET[((a << 4) & 0x30)
140: + ((b >>> 4) & 0xf)];
141: out[outOffset + 2] = ALPHABET[((b << 2) & 0x3c)
142: + ((c >>> 6) & 0x3)];
143: out[outOffset + 3] = ALPHABET[c & 0x3F];
144: } else if (len > 1) {
145: b = in[inOffset + 1];
146: out[outOffset + 1] = ALPHABET[((a << 4) & 0x30)
147: + ((b >>> 4) & 0xf)];
148: out[outOffset + 2] = ALPHABET[((b << 2) & 0x3c)
149: + ((c >>> 6) & 0x3)];
150: out[outOffset + 3] = '=';
151: } else {
152: out[outOffset + 1] = ALPHABET[((a << 4) & 0x30)
153: + ((b >>> 4) & 0xf)];
154: out[outOffset + 2] = '=';
155: out[outOffset + 3] = '=';
156: }
157: }
158:
159: /**
160: * Converts a Base64 encoded string to a byte array.
161: * @param encoded Base64 encoded data
162: * @return decode binary data; 3 bytes for every 4 chars - minus padding
163: * @exception IOException is thrown, if an I/O error occurs reading the data
164: */
165: public static byte[] decode(String encoded) throws IOException {
166: return decode(encoded, 0, encoded.length());
167: }
168:
169: /**
170: * Converts an embedded Base64 encoded string to a byte array.
171: * @param encoded a String with Base64 data embedded in it
172: * @param offset which char of the String to start at
173: * @param length how many chars to decode; must be a multiple of 4
174: * @return decode binary data; 3 bytes for every 4 chars - minus padding
175: * @exception IOException is thrown, if an I/O error occurs reading the data
176: */
177: public static byte[] decode(String encoded, int offset, int length)
178: throws IOException {
179: int i;
180: int decodedLen;
181: byte[] decoded;
182:
183: // the input must be a multiple of 4
184: if (length % 4 != 0) {
185: throw new IOException(
186: "Base64 string length is not multiple of 4");
187: }
188:
189: // 4 chars for 3 bytes, but there may have been pad bytes
190: decodedLen = length / 4 * 3;
191: if (encoded.charAt(offset + length - 1) == '=') {
192: decodedLen--;
193: if (encoded.charAt(offset + length - 2) == '=') {
194: decodedLen--;
195: }
196: }
197:
198: decoded = new byte[decodedLen];
199: for (i = 0, decodedLen = 0; i < length; i += 4, decodedLen += 3) {
200: decodeQuantum(encoded.charAt(offset + i), encoded
201: .charAt(offset + i + 1), encoded.charAt(offset + i
202: + 2), encoded.charAt(offset + i + 3), decoded,
203: decodedLen);
204: }
205:
206: return decoded;
207: }
208:
209: /**
210: * Decode 4 Base64 chars as 1, 2, or 3 bytes of data.
211: *
212: * @param in1 first char of quantum to decode
213: * @param in2 second char of quantum to decode
214: * @param in3 third char of quantum to decode
215: * @param in4 forth char of quantum to decode
216: * @param out buffer to put the output in
217: * @param outOffset where in the output buffer to put the bytes
218: */
219: private static void decodeQuantum(char in1, char in2, char in3,
220: char in4, byte[] out, int outOffset) throws IOException {
221: int a = 0, b = 0, c = 0, d = 0;
222: int pad = 0;
223:
224: a = valueDecoding[in1 & 127];
225: b = valueDecoding[in2 & 127];
226:
227: if (in4 == '=') {
228: pad++;
229: if (in3 == '=') {
230: pad++;
231: } else {
232: c = valueDecoding[in3 & 127];
233: }
234: } else {
235: c = valueDecoding[in3 & 127];
236: d = valueDecoding[in4 & 127];
237: }
238:
239: if (a < 0 || b < 0 || c < 0 || d < 0) {
240: throw new IOException("Invalid character in Base64 string");
241: }
242:
243: // the first byte is the 6 bits of a and 2 bits of b
244: out[outOffset] = (byte) (((a << 2) & 0xfc) | ((b >>> 4) & 3));
245:
246: if (pad < 2) {
247: // the second byte is 4 bits of b and 4 bits of c
248: out[outOffset + 1] = (byte) (((b << 4) & 0xf0) | ((c >>> 2) & 0xf));
249:
250: if (pad < 1) {
251: // the third byte is 2 bits of c and 4 bits of d
252: out[outOffset + 2] = (byte) (((c << 6) & 0xc0) | (d & 0x3f));
253: }
254: }
255: }
256: }
|