/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2006-2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.core.utils;
/**
* Performs base 64 encoding and decoding on byte arrays.
*
* @author <A HREF="mailto:weave@oculan.com">Brian Weaver </A>
*/
public final class Base64 extends Object {
/**
* <P>
* The base64 encoding map. Using 6-bit values it is possible to map 24-bits
* into 4 characters. If there are not sufficent amount of bits to makeup
* six then it is padded with BASE64_PAD.
* </P>
*/
private static final char BASE64_CHARS[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
private static final byte BASE64_VALUES[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 16 - 31 */-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 32 - 47 */-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
/* 48 - 63 */52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1,
/* 64 - 79 */-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/* 80 - 95 */15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
/* 96 - 111 */-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
/* 112 - 127 */41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 };
/**
* The base64 padding character
*/
private static final char BASE64_PAD = '=';
/**
* <P>
* Encodes the passed byte array using the base64 rules. The base64 encoding
* schema is performed by grouping the bytes in to 6-bit quantities and then
* encoding them.
* </P>
*
* <P>
* For more information see RFC1341 for the format used for base64 encoding.
* </P>
*
* @param data
* The input byte array
* @return The converted data in a character stream.
*/
public static char[] encodeBase64(byte[] data) {
int destlen = ((data.length + 2) / 3) * 4;
char[] dest = new char[destlen];
int destndx = 0;
for (int i = 0; i < data.length; i += 3) {
int quantum = 0;
int pad = 0;
int bytes = data.length - i;
if (bytes >= 1) {
quantum = (data[i] < 0 ? (256 + (int) data[i]) : (int) data[i]);
pad = 2;
}
quantum <<= 8;
if (bytes >= 2) {
quantum |= (data[i + 1] < 0 ? (256 + (int) data[i + 1]) : (int) data[i + 1]);
pad = 1;
}
quantum <<= 8;
if (bytes > 2) {
quantum |= (data[i + 2] < 0 ? (256 + (int) data[i + 2]) : (int) data[i + 2]);
pad = 0;
}
for (int j = 3; j >= pad; j--) {
int ndx = (quantum >> (j * 6)) & 0x3f;
dest[destndx++] = BASE64_CHARS[ndx];
}
for (int j = pad; j > 0; j--) {
dest[destndx++] = BASE64_PAD;
}
}
return dest;
}
/**
* <P>
* Decodes a character array into the corresponding byte array. The buffer
* must be an intergral number of 4 character. I.E. size mod 4 is equal to
* zero or an exception will be thrown. Likewise, if there is an invalid
* character in the input array then an exception will be thrown.
* </P>
*
* @param data
* The data stream to be filtered.
* @return The coverted array of bytes.
* @exception java.lang.IllegalArgumentException
* Thrown if an invalid buffer that cannot be decoded is
* passed.
*/
public static byte[] decodeBase64(char[] data) {
//. If the data is zero length just return a zero length byte array
if (data.length == 0) {
return new byte[0];
}
//
// check the length, it must be an integral number of 4 characters.
//
if ((data.length % 4) != 0)
throw new IllegalArgumentException("Invalid base64 encoding, improper length");
//
// get the raw length and check for
// the appended padding characters
// if any.
//
int rawlen = (data.length / 4) * 3;
for (int i = 1; i <= 2; i++) {
if (data[data.length - i] == BASE64_PAD)
--rawlen;
}
//
// allocate the new buffer
//
byte[] rawdata = new byte[rawlen];
int rawndx = 0;
//
// convert the character array into
// a byte array.
//
int quantum = 0;
for (int i = 0; i < data.length; i++) {
if ((i % 4) == 0 && i > 0) {
int c = ((quantum >> 16) & 0xff);
rawdata[rawndx++] = (byte) (c > 127 ? c - 256 : c);
c = ((quantum >> 8) & 0xff);
rawdata[rawndx++] = (byte) (c > 127 ? c - 256 : c);
c = quantum & 0xff;
rawdata[rawndx++] = (byte) (c > 127 ? c - 256 : c);
quantum = 0;
}
quantum <<= 6;
char c = data[i];
if ((int) c >= BASE64_VALUES.length || BASE64_VALUES[(int) c] == -1)
throw new IllegalArgumentException("Invalid character in decode stream");
quantum |= BASE64_VALUES[(int) c];
}
//
// hand the last byte(s) of data
//
int c = ((quantum >> 16) & 0xff);
rawdata[rawndx++] = (byte) (c > 127 ? c - 256 : c);
if (rawndx < rawlen) {
c = ((quantum >> 8) & 0xff);
rawdata[rawndx++] = (byte) (c > 127 ? c - 256 : c);
}
if (rawndx < rawlen) {
c = quantum & 0xff;
rawdata[rawndx++] = (byte) (c > 127 ? c - 256 : c);
}
//
// return the raw data
//
return rawdata;
}
/** Empty, private constructor so this object will not be instantiated. */
private Base64() {
}
}