00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 package org.apache.commons.codec.binary;
00018
00019 import org.apache.commons.codec.BinaryDecoder;
00020 import org.apache.commons.codec.BinaryEncoder;
00021 import org.apache.commons.codec.DecoderException;
00022 import org.apache.commons.codec.EncoderException;
00023
00036 public class Base64 implements BinaryEncoder, BinaryDecoder {
00037
00046 static final int CHUNK_SIZE = 76;
00047
00053 static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes();
00054
00058 static final int BASELENGTH = 255;
00059
00063 static final int LOOKUPLENGTH = 64;
00064
00068 static final int EIGHTBIT = 8;
00069
00073 static final int SIXTEENBIT = 16;
00074
00078 static final int TWENTYFOURBITGROUP = 24;
00079
00083 static final int FOURBYTE = 4;
00084
00088 static final int SIGN = -128;
00089
00093 static final byte PAD = (byte) '=';
00094
00095
00096
00097 private static byte[] base64Alphabet = new byte[BASELENGTH];
00098 private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
00099
00100
00101 static {
00102 for (int i = 0; i < BASELENGTH; i++) {
00103 base64Alphabet[i] = (byte) -1;
00104 }
00105 for (int i = 'Z'; i >= 'A'; i--) {
00106 base64Alphabet[i] = (byte) (i - 'A');
00107 }
00108 for (int i = 'z'; i >= 'a'; i--) {
00109 base64Alphabet[i] = (byte) (i - 'a' + 26);
00110 }
00111 for (int i = '9'; i >= '0'; i--) {
00112 base64Alphabet[i] = (byte) (i - '0' + 52);
00113 }
00114
00115 base64Alphabet['+'] = 62;
00116 base64Alphabet['/'] = 63;
00117
00118 for (int i = 0; i <= 25; i++) {
00119 lookUpBase64Alphabet[i] = (byte) ('A' + i);
00120 }
00121
00122 for (int i = 26, j = 0; i <= 51; i++, j++) {
00123 lookUpBase64Alphabet[i] = (byte) ('a' + j);
00124 }
00125
00126 for (int i = 52, j = 0; i <= 61; i++, j++) {
00127 lookUpBase64Alphabet[i] = (byte) ('0' + j);
00128 }
00129
00130 lookUpBase64Alphabet[62] = (byte) '+';
00131 lookUpBase64Alphabet[63] = (byte) '/';
00132 }
00133
00134 private static boolean isBase64(byte octect) {
00135 if (octect == PAD) {
00136 return true;
00137 } else if (base64Alphabet[octect] == -1) {
00138 return false;
00139 } else {
00140 return true;
00141 }
00142 }
00143
00152 public static boolean isArrayByteBase64(byte[] arrayOctect) {
00153
00154 arrayOctect = discardWhitespace(arrayOctect);
00155
00156 int length = arrayOctect.length;
00157 if (length == 0) {
00158
00159
00160 return true;
00161 }
00162 for (int i = 0; i < length; i++) {
00163 if (!isBase64(arrayOctect[i])) {
00164 return false;
00165 }
00166 }
00167 return true;
00168 }
00169
00177 public static byte[] encodeBase64(byte[] binaryData) {
00178 return encodeBase64(binaryData, false);
00179 }
00180
00188 public static byte[] encodeBase64Chunked(byte[] binaryData) {
00189 return encodeBase64(binaryData, true);
00190 }
00191
00192
00205 public Object decode(Object pObject) throws DecoderException {
00206 if (!(pObject instanceof byte[])) {
00207 throw new DecoderException("Parameter supplied to Base64 decode is not a byte[]");
00208 }
00209 return decode((byte[]) pObject);
00210 }
00211
00219 public byte[] decode(byte[] pArray) {
00220 return decodeBase64(pArray);
00221 }
00222
00232 public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
00233 int lengthDataBits = binaryData.length * EIGHTBIT;
00234 int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
00235 int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
00236 byte encodedData[] = null;
00237 int encodedDataLength = 0;
00238 int nbrChunks = 0;
00239
00240 if (fewerThan24bits != 0) {
00241
00242 encodedDataLength = (numberTriplets + 1) * 4;
00243 } else {
00244
00245 encodedDataLength = numberTriplets * 4;
00246 }
00247
00248
00249
00250
00251 if (isChunked) {
00252
00253 nbrChunks =
00254 (CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math.ceil((float) encodedDataLength / CHUNK_SIZE));
00255 encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
00256 }
00257
00258 encodedData = new byte[encodedDataLength];
00259
00260 byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
00261
00262 int encodedIndex = 0;
00263 int dataIndex = 0;
00264 int i = 0;
00265 int nextSeparatorIndex = CHUNK_SIZE;
00266 int chunksSoFar = 0;
00267
00268
00269 for (i = 0; i < numberTriplets; i++) {
00270 dataIndex = i * 3;
00271 b1 = binaryData[dataIndex];
00272 b2 = binaryData[dataIndex + 1];
00273 b3 = binaryData[dataIndex + 2];
00274
00275
00276
00277 l = (byte) (b2 & 0x0f);
00278 k = (byte) (b1 & 0x03);
00279
00280 byte val1 =
00281 ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
00282 byte val2 =
00283 ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
00284 byte val3 =
00285 ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
00286
00287 encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
00288
00289
00290
00291 encodedData[encodedIndex + 1] =
00292 lookUpBase64Alphabet[val2 | (k << 4)];
00293 encodedData[encodedIndex + 2] =
00294 lookUpBase64Alphabet[(l << 2) | val3];
00295 encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
00296
00297 encodedIndex += 4;
00298
00299
00300 if (isChunked) {
00301
00302 if (encodedIndex == nextSeparatorIndex) {
00303 System.arraycopy(
00304 CHUNK_SEPARATOR,
00305 0,
00306 encodedData,
00307 encodedIndex,
00308 CHUNK_SEPARATOR.length);
00309 chunksSoFar++;
00310 nextSeparatorIndex =
00311 (CHUNK_SIZE * (chunksSoFar + 1)) +
00312 (chunksSoFar * CHUNK_SEPARATOR.length);
00313 encodedIndex += CHUNK_SEPARATOR.length;
00314 }
00315 }
00316 }
00317
00318
00319 dataIndex = i * 3;
00320
00321 if (fewerThan24bits == EIGHTBIT) {
00322 b1 = binaryData[dataIndex];
00323 k = (byte) (b1 & 0x03);
00324
00325
00326 byte val1 =
00327 ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
00328 encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
00329 encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
00330 encodedData[encodedIndex + 2] = PAD;
00331 encodedData[encodedIndex + 3] = PAD;
00332 } else if (fewerThan24bits == SIXTEENBIT) {
00333
00334 b1 = binaryData[dataIndex];
00335 b2 = binaryData[dataIndex + 1];
00336 l = (byte) (b2 & 0x0f);
00337 k = (byte) (b1 & 0x03);
00338
00339 byte val1 =
00340 ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
00341 byte val2 =
00342 ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
00343
00344 encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
00345 encodedData[encodedIndex + 1] =
00346 lookUpBase64Alphabet[val2 | (k << 4)];
00347 encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
00348 encodedData[encodedIndex + 3] = PAD;
00349 }
00350
00351 if (isChunked) {
00352
00353 if (chunksSoFar < nbrChunks) {
00354 System.arraycopy(
00355 CHUNK_SEPARATOR,
00356 0,
00357 encodedData,
00358 encodedDataLength - CHUNK_SEPARATOR.length,
00359 CHUNK_SEPARATOR.length);
00360 }
00361 }
00362
00363 return encodedData;
00364 }
00365
00372 public static byte[] decodeBase64(byte[] base64Data) {
00373
00374 base64Data = discardNonBase64(base64Data);
00375
00376
00377 if (base64Data.length == 0) {
00378 return new byte[0];
00379 }
00380
00381 int numberQuadruple = base64Data.length / FOURBYTE;
00382 byte decodedData[] = null;
00383 byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
00384
00385
00386
00387 int encodedIndex = 0;
00388 int dataIndex = 0;
00389 {
00390
00391 int lastData = base64Data.length;
00392
00393 while (base64Data[lastData - 1] == PAD) {
00394 if (--lastData == 0) {
00395 return new byte[0];
00396 }
00397 }
00398 decodedData = new byte[lastData - numberQuadruple];
00399 }
00400
00401 for (int i = 0; i < numberQuadruple; i++) {
00402 dataIndex = i * 4;
00403 marker0 = base64Data[dataIndex + 2];
00404 marker1 = base64Data[dataIndex + 3];
00405
00406 b1 = base64Alphabet[base64Data[dataIndex]];
00407 b2 = base64Alphabet[base64Data[dataIndex + 1]];
00408
00409 if (marker0 != PAD && marker1 != PAD) {
00410
00411 b3 = base64Alphabet[marker0];
00412 b4 = base64Alphabet[marker1];
00413
00414 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
00415 decodedData[encodedIndex + 1] =
00416 (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
00417 decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
00418 } else if (marker0 == PAD) {
00419
00420 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
00421 } else if (marker1 == PAD) {
00422
00423 b3 = base64Alphabet[marker0];
00424
00425 decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
00426 decodedData[encodedIndex + 1] =
00427 (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
00428 }
00429 encodedIndex += 3;
00430 }
00431 return decodedData;
00432 }
00433
00441 static byte[] discardWhitespace(byte[] data) {
00442 byte groomedData[] = new byte[data.length];
00443 int bytesCopied = 0;
00444
00445 for (int i = 0; i < data.length; i++) {
00446 switch (data[i]) {
00447 case (byte) ' ' :
00448 case (byte) '\n' :
00449 case (byte) '\r' :
00450 case (byte) '\t' :
00451 break;
00452 default:
00453 groomedData[bytesCopied++] = data[i];
00454 }
00455 }
00456
00457 byte packedData[] = new byte[bytesCopied];
00458
00459 System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
00460
00461 return packedData;
00462 }
00463
00473 static byte[] discardNonBase64(byte[] data) {
00474 byte groomedData[] = new byte[data.length];
00475 int bytesCopied = 0;
00476
00477 for (int i = 0; i < data.length; i++) {
00478 if (isBase64(data[i])) {
00479 groomedData[bytesCopied++] = data[i];
00480 }
00481 }
00482
00483 byte packedData[] = new byte[bytesCopied];
00484
00485 System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
00486
00487 return packedData;
00488 }
00489
00490
00491
00492
00505 public Object encode(Object pObject) throws EncoderException {
00506 if (!(pObject instanceof byte[])) {
00507 throw new EncoderException(
00508 "Parameter supplied to Base64 encode is not a byte[]");
00509 }
00510 return encode((byte[]) pObject);
00511 }
00512
00520 public byte[] encode(byte[] pArray) {
00521 return encodeBase64(pArray, false);
00522 }
00523
00524 }