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: package org.apache.wicket.util.crypt;
018:
019: /**
020: * Provides Base64 encoding and decoding with URL and filename safe alphabet
021: * as defined by RFC 3548, section 4.
022: * <p/>
023: * This Base64 encoder is modified to meet URL requirements. The changes are:
024: * '+' => '*',
025: * '/' => '-',
026: * and no padding.
027: * <p/>
028: * This class is taken from the Apache commons-codec, and adjusted to fit the
029: * Wicket framework's needs, especially external dependencies have been removed.
030: * </p>
031: * <p/>
032: * This class implements section <cite>4. Base 64 Encoding with URL and Filename Safe Alphabet</cite>
033: * from RFC 3548 <cite>The Base16, Base32, and Base64 Data Encodings</cite> by Simon Josefsson.</p>
034: *
035: * @author Apache Software Foundation
036: * @author Juergen Donnerstag
037: *
038: * @since 1.2
039: */
040: public class Base64UrlSafe {
041: /**
042: * The base length.
043: */
044: static final int BASELENGTH = 255;
045:
046: /**
047: * Lookup length.
048: */
049: static final int LOOKUPLENGTH = 64;
050:
051: /**
052: * Used to calculate the number of bits in a byte.
053: */
054: static final int EIGHTBIT = 8;
055:
056: /**
057: * Used when encoding something which has fewer than 24 bits.
058: */
059: static final int SIXTEENBIT = 16;
060:
061: /**
062: * Used to determine how many bits data contains.
063: */
064: static final int TWENTYFOURBITGROUP = 24;
065:
066: /**
067: * Used to get the number of Quadruples.
068: */
069: static final int FOURBYTE = 4;
070:
071: /**
072: * Used to test the sign of a byte.
073: */
074: static final int SIGN = -128;
075:
076: /**
077: * Contains the Base64 values <code>0</code> through <code>63</code> accessed by using character encodings as
078: * indices.
079: * <p/>
080: * For example, <code>base64Alphabet['+']</code> returns <code>62</code>.
081: * </p>
082: * <p/>
083: * The value of undefined encodings is <code>-1</code>.
084: * </p>
085: */
086: private static byte[] base64Alphabet = new byte[BASELENGTH];
087:
088: /**
089: * <p/>
090: * Contains the Base64 encodings <code>A</code> through <code>Z</code>, followed by <code>a</code> through
091: * <code>z</code>, followed by <code>0</code> through <code>9</code>, followed by <code>+</code>, and
092: * <code>/</code>.
093: * </p>
094: * <p/>
095: * This array is accessed by using character values as indices.
096: * </p>
097: * <p/>
098: * For example, <code>lookUpBase64Alphabet[62] </code> returns <code>'+'</code>.
099: * </p>
100: */
101: private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
102:
103: // Populating the lookup and character arrays
104: static {
105: for (int i = 0; i < BASELENGTH; i++) {
106: base64Alphabet[i] = (byte) -1;
107: }
108: for (int i = 'Z'; i >= 'A'; i--) {
109: base64Alphabet[i] = (byte) (i - 'A');
110: }
111: for (int i = 'z'; i >= 'a'; i--) {
112: base64Alphabet[i] = (byte) (i - 'a' + 26);
113: }
114: for (int i = '9'; i >= '0'; i--) {
115: base64Alphabet[i] = (byte) (i - '0' + 52);
116: }
117:
118: base64Alphabet['*'] = 62;
119: base64Alphabet['-'] = 63;
120:
121: for (int i = 0; i <= 25; i++) {
122: lookUpBase64Alphabet[i] = (byte) ('A' + i);
123: }
124:
125: for (int i = 26, j = 0; i <= 51; i++, j++) {
126: lookUpBase64Alphabet[i] = (byte) ('a' + j);
127: }
128:
129: for (int i = 52, j = 0; i <= 61; i++, j++) {
130: lookUpBase64Alphabet[i] = (byte) ('0' + j);
131: }
132:
133: lookUpBase64Alphabet[62] = (byte) '*';
134: lookUpBase64Alphabet[63] = (byte) '-';
135: }
136:
137: /**
138: * Returns whether or not the <code>octect</code> is in the base 64 alphabet.
139: *
140: * @param octect The value to test
141: * @return <code>true</code> if the value is defined in the the base 64 alphabet, <code>false</code> otherwise.
142: */
143: private static boolean isBase64(byte octect) {
144: if (octect < 0 || base64Alphabet[octect] == -1) {
145: return false;
146: } else {
147: return true;
148: }
149: }
150:
151: /**
152: * Tests a given byte array to see if it contains
153: * only valid characters within the Base64 alphabet.
154: *
155: * @param arrayOctect byte array to test
156: * @return <code>true</code> if all bytes are valid characters in the Base64
157: * alphabet or if the byte array is empty; false, otherwise
158: */
159: public static boolean isArrayByteBase64(byte[] arrayOctect) {
160: arrayOctect = discardWhitespace(arrayOctect);
161:
162: int length = arrayOctect.length;
163: if (length == 0) {
164: // shouldn't a 0 length array be valid base64 data?
165: // return false;
166: return true;
167: }
168: for (int i = 0; i < length; i++) {
169: if (!isBase64(arrayOctect[i])) {
170: return false;
171: }
172: }
173: return true;
174: }
175:
176: /**
177: * Decodes an Object using the base64 algorithm. This method
178: * is provided in order to satisfy the requirements of the
179: * Decoder interface, and will throw a DecoderException if the
180: * supplied object is not of type byte[].
181: *
182: * @param pObject Object to decode
183: * @return An object (of type byte[]) containing the
184: * binary data which corresponds to the byte[] supplied.
185: * @throws IllegalArgumentException if the parameter supplied is not
186: * of type byte[]
187: */
188: public Object decode(Object pObject) {
189: if (!(pObject instanceof byte[])) {
190: throw new IllegalArgumentException(
191: "Parameter supplied to Base64 decode is not a byte[]");
192: }
193: return decode((byte[]) pObject);
194: }
195:
196: /**
197: * Decodes a byte[] containing containing
198: * characters in the Base64 alphabet.
199: *
200: * @param pArray A byte array containing Base64 character data
201: * @return a byte array containing binary data
202: */
203: public byte[] decode(byte[] pArray) {
204: return decodeBase64(pArray);
205: }
206:
207: /**
208: * Encodes binary data using the base64 algorithm.
209: *
210: * @param binaryData Array containing binary data to encode.
211: * @return Base64-encoded data.
212: */
213: public static byte[] encodeBase64(byte[] binaryData) {
214: int lengthDataBits = binaryData.length * EIGHTBIT;
215: int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
216: int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
217: byte encodedData[] = null;
218: int encodedDataLength = 0;
219:
220: if (fewerThan24bits != 0) {
221: //data not divisible by 24 bit
222: encodedDataLength = (numberTriplets + 1) * 4;
223: } else {
224: // 16 or 8 bit
225: encodedDataLength = numberTriplets * 4;
226: }
227:
228: if (fewerThan24bits == EIGHTBIT) {
229: encodedDataLength -= 2;
230: } else if (fewerThan24bits == SIXTEENBIT) {
231: encodedDataLength -= 1;
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:
242: //log.debug("number of triplets = " + numberTriplets);
243: for (i = 0; i < numberTriplets; i++) {
244: dataIndex = i * 3;
245: b1 = binaryData[dataIndex];
246: b2 = binaryData[dataIndex + 1];
247: b3 = binaryData[dataIndex + 2];
248:
249: //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
250:
251: l = (byte) (b2 & 0x0f);
252: k = (byte) (b1 & 0x03);
253:
254: byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
255: : (byte) ((b1) >> 2 ^ 0xc0);
256: byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
257: : (byte) ((b2) >> 4 ^ 0xf0);
258: byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6)
259: : (byte) ((b3) >> 6 ^ 0xfc);
260:
261: encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
262: //log.debug( "val2 = " + val2 );
263: //log.debug( "k4 = " + (k<<4) );
264: //log.debug( "vak = " + (val2 | (k<<4)) );
265: encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2
266: | (k << 4)];
267: encodedData[encodedIndex + 2] = lookUpBase64Alphabet[(l << 2)
268: | val3];
269: encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
270:
271: encodedIndex += 4;
272: }
273:
274: // form integral number of 6-bit groups
275: dataIndex = i * 3;
276:
277: if (fewerThan24bits == EIGHTBIT) {
278: b1 = binaryData[dataIndex];
279: k = (byte) (b1 & 0x03);
280: //log.debug("b1=" + b1);
281: //log.debug("b1<<2 = " + (b1>>2) );
282: byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
283: : (byte) ((b1) >> 2 ^ 0xc0);
284: encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
285: encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
286: } else if (fewerThan24bits == SIXTEENBIT) {
287: b1 = binaryData[dataIndex];
288: b2 = binaryData[dataIndex + 1];
289: l = (byte) (b2 & 0x0f);
290: k = (byte) (b1 & 0x03);
291:
292: byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2)
293: : (byte) ((b1) >> 2 ^ 0xc0);
294: byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4)
295: : (byte) ((b2) >> 4 ^ 0xf0);
296:
297: encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
298: encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2
299: | (k << 4)];
300: encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
301: }
302:
303: return encodedData;
304: }
305:
306: /**
307: * Decodes Base64 data into octects
308: *
309: * @param base64Data Byte array containing Base64 data
310: * @return Array containing decoded data.
311: */
312: public static byte[] decodeBase64(byte[] base64Data) {
313: // RFC 2045 requires that we discard ALL non-Base64 characters
314: base64Data = discardNonBase64(base64Data);
315:
316: // handle the edge case, so we don't have to worry about it later
317: if (base64Data.length == 0) {
318: return new byte[0];
319: }
320:
321: int numberQuadruple = (base64Data.length + 3) / FOURBYTE;
322: byte decodedData[] = new byte[base64Data.length
323: - numberQuadruple];
324: byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
325:
326: // Throw away anything not in base64Data
327: int encodedIndex = 0;
328: int dataIndex = 0;
329:
330: for (int i = 0; i < numberQuadruple; i++) {
331: dataIndex = i * 4;
332:
333: b1 = base64Alphabet[base64Data[dataIndex]];
334: b2 = base64Alphabet[base64Data[dataIndex + 1]];
335:
336: if ((dataIndex + 3) < base64Data.length) {
337: //No PAD e.g 3cQl
338: b3 = base64Alphabet[base64Data[dataIndex + 2]];
339: b4 = base64Alphabet[base64Data[dataIndex + 3]];
340:
341: decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
342: decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
343: decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
344: } else if ((dataIndex + 2) < base64Data.length) {
345: //One PAD e.g. 3cQ[Pad]
346: b3 = base64Alphabet[base64Data[dataIndex + 2]];
347:
348: decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
349: decodedData[encodedIndex + 1] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
350: } else if ((dataIndex + 1) < base64Data.length) {
351: //Two PAD e.g. 3c[Pad][Pad]
352: decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
353: }
354: encodedIndex += 3;
355: }
356: return decodedData;
357: }
358:
359: /**
360: * Discards any whitespace from a base-64 encoded block.
361: *
362: * @param data The base-64 encoded data to discard the whitespace
363: * from.
364: * @return The data, less whitespace (see RFC 2045).
365: */
366: static byte[] discardWhitespace(byte[] data) {
367: byte groomedData[] = new byte[data.length];
368: int bytesCopied = 0;
369:
370: for (int i = 0; i < data.length; i++) {
371: switch (data[i]) {
372: case (byte) ' ':
373: case (byte) '\n':
374: case (byte) '\r':
375: case (byte) '\t':
376: break;
377: default:
378: groomedData[bytesCopied++] = data[i];
379: }
380: }
381:
382: byte packedData[] = new byte[bytesCopied];
383:
384: System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
385:
386: return packedData;
387: }
388:
389: /**
390: * Discards any characters outside of the base64 alphabet, per
391: * the requirements on page 25 of RFC 2045 - "Any characters
392: * outside of the base64 alphabet are to be ignored in base64
393: * encoded data."
394: *
395: * @param data The base-64 encoded data to groom
396: * @return The data, less non-base64 characters (see RFC 2045).
397: */
398: static byte[] discardNonBase64(byte[] data) {
399: byte groomedData[] = new byte[data.length];
400: int bytesCopied = 0;
401:
402: for (int i = 0; i < data.length; i++) {
403: if (isBase64(data[i])) {
404: groomedData[bytesCopied++] = data[i];
405: }
406: }
407:
408: byte packedData[] = new byte[bytesCopied];
409:
410: System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
411:
412: return packedData;
413: }
414:
415: // Implementation of the Encoder Interface
416:
417: /**
418: * Encodes an Object using the base64 algorithm. This method
419: * is provided in order to satisfy the requirements of the
420: * Encoder interface, and will throw an EncoderException if the
421: * supplied object is not of type byte[].
422: *
423: * @param pObject Object to encode
424: * @return An object (of type byte[]) containing the
425: * base64 encoded data which corresponds to the byte[] supplied.
426: * @throws IllegalArgumentException if the parameter supplied is not
427: * of type byte[]
428: */
429: public Object encode(Object pObject) {
430: if (!(pObject instanceof byte[])) {
431: throw new IllegalArgumentException(
432: "Parameter supplied to Base64 encode is not a byte[]");
433: }
434: return encode((byte[]) pObject);
435: }
436:
437: /**
438: * Encodes a byte[] containing binary data, into a byte[] containing
439: * characters in the Base64 alphabet.
440: *
441: * @param pArray a byte array containing binary data
442: * @return A byte array containing only Base64 character data
443: */
444: public byte[] encode(byte[] pArray) {
445: return encodeBase64(pArray);
446: }
447: }
|