Base64public class Base64 extends Object implements BinaryEncoder, BinaryDecoderProvides Base64 encoding and decoding as defined by RFC 2045.
This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose
Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein.
|
Fields Summary |
---|
static final int | CHUNK_SIZEChunk size per RFC 2045 section 6.8.
The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any
equal signs.
| static final byte[] | CHUNK_SEPARATORChunk separator per RFC 2045 section 2.1. | private static final byte[] | intToBase64This array is a lookup table that translates 6-bit positive integer
index values into their "Base64 Alphabet" equivalents as specified
in Table 1 of RFC 2045.
Thanks to "commons" project in ws.apache.org for this code.
http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ | private static final byte | PADByte used to pad output. | private static final byte[] | base64ToIntThis array is a lookup table that translates unicode characters
drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
into their 6-bit positive integer equivalents. Characters that
are not in the Base64 alphabet but fall within the bounds of the
array are translated to -1.
Thanks to "commons" project in ws.apache.org for this code.
http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ | private static final int | MASK_6BITSMask used to extract 6 bits, used when encoding | private static final int | MASK_8BITSMask used to extract 8 bits, used in decoding base64 bytes | private final int | lineLengthLine length for encoding. Not used when decoding. A value of zero or less implies
no chunking of the base64 encoded data. | private final byte[] | lineSeparatorLine separator for encoding. Not used when decoding. Only used if lineLength > 0. | private final int | decodeSizeConvenience variable to help us determine when our buffer is going to run out of
room and needs resizing. decodeSize = 3 + lineSeparator.length; | private final int | encodeSizeConvenience variable to help us determine when our buffer is going to run out of
room and needs resizing. encodeSize = 4 + lineSeparator.length; | private byte[] | bufBuffer for streaming. | private int | posPosition where next character should be written in the buffer. | private int | readPosPosition where next character should be read from the buffer. | private int | currentLinePosVariable tracks how many characters have been written to the current line.
Only used when encoding. We use it to make sure each encoded line never
goes beyond lineLength (if lineLength > 0). | private int | modulusWrites to the buffer only occur after every 3 reads when encoding, an
every 4 reads when decoding. This variable helps track that. | private boolean | eofBoolean flag to indicate the EOF has been reached. Once EOF has been
reached, this Base64 object becomes useless, and must be thrown away. | private int | xPlace holder for the 3 bytes we're dealing with for our base64 logic.
Bitwise operations store and extract the base64 encoding or decoding from
this variable. |
Constructors Summary |
---|
public Base64()Default constructor: lineLength is 76, and the lineSeparator is CRLF
when encoding, and all forms can be decoded.
this(CHUNK_SIZE, CHUNK_SEPARATOR);
| public Base64(int lineLength)
Consumer can use this constructor to choose a different lineLength
when encoding (lineSeparator is still CRLF). All forms of data can
be decoded.
Note: lineLengths that aren't multiples of 4 will still essentially
end up being multiples of 4 in the encoded data.
this(lineLength, CHUNK_SEPARATOR);
| public Base64(int lineLength, byte[] lineSeparator)
Consumer can use this constructor to choose a different lineLength
and lineSeparator when encoding. All forms of data can
be decoded.
Note: lineLengths that aren't multiples of 4 will still essentially
end up being multiples of 4 in the encoded data.
this.lineLength = lineLength;
this.lineSeparator = new byte[lineSeparator.length];
System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);
if (lineLength > 0) {
this.encodeSize = 4 + lineSeparator.length;
} else {
this.encodeSize = 4;
}
this.decodeSize = encodeSize - 1;
if (containsBase64Byte(lineSeparator)) {
String sep;
try {
sep = new String(lineSeparator, "UTF-8");
} catch (UnsupportedEncodingException uee) {
sep = new String(lineSeparator);
}
throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]");
}
|
Methods Summary |
---|
int | avail()Returns the amount of buffered data available for reading. return buf != null ? pos - readPos : 0;
| private static boolean | containsBase64Byte(byte[] arrayOctet)
for (int i = 0; i < arrayOctet.length; i++) {
if (isBase64(arrayOctet[i])) {
return true;
}
}
return false;
| void | decode(byte[] in, int inPos, int inAvail)
Decodes all of the provided data, starting at inPos, for inAvail bytes.
Should be called at least twice: once with the data to decode, and once
with inAvail set to "-1" to alert decoder that EOF has been reached.
The "-1" call is not necessary when decoding, but it doesn't hurt, either.
Ignores all non-base64 characters. This is how chunked (e.g. 76 character)
data is handled, since CR and LF are silently ignored, but has implications
for other bytes, too. This method subscribes to the garbage-in, garbage-out
philosophy: it will not check the provided data for validity.
Thanks to "commons" project in ws.apache.org for the bitwise operations,
and general approach.
http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
if (eof) {
return;
}
if (inAvail < 0) {
eof = true;
}
for (int i = 0; i < inAvail; i++) {
if (buf == null || buf.length - pos < decodeSize) {
resizeBuf();
}
byte b = in[inPos++];
if (b == PAD) {
x = x << 6;
switch (modulus) {
case 2:
x = x << 6;
buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
break;
case 3:
buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
buf[pos++] = (byte) ((x >> 8) & MASK_8BITS);
break;
}
// WE'RE DONE!!!!
eof = true;
return;
} else {
if (b >= 0 && b < base64ToInt.length) {
int result = base64ToInt[b];
if (result >= 0) {
modulus = (++modulus) % 4;
x = (x << 6) + result;
if (modulus == 0) {
buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
buf[pos++] = (byte) ((x >> 8) & MASK_8BITS);
buf[pos++] = (byte) (x & MASK_8BITS);
}
}
}
}
}
| public java.lang.Object | decode(java.lang.Object pObject)Decodes an Object using the base64 algorithm. This method is provided in order to satisfy the requirements of the
Decoder interface, and will throw a DecoderException if the supplied object is not of type byte[].
if (!(pObject instanceof byte[])) {
throw new DecoderException("Parameter supplied to Base64 decode is not a byte[]");
}
return decode((byte[]) pObject);
| public byte[] | decode(byte[] pArray)Decodes a byte[] containing containing characters in the Base64 alphabet.
return decodeBase64(pArray);
| public static byte[] | decodeBase64(byte[] base64Data)Decodes Base64 data into octets
if (base64Data == null || base64Data.length == 0) {
return base64Data;
}
Base64 b64 = new Base64();
long len = (base64Data.length * 3) / 4;
byte[] buf = new byte[(int) len];
b64.setInitialBuffer(buf, 0, buf.length);
b64.decode(base64Data, 0, base64Data.length);
b64.decode(base64Data, 0, -1); // Notify decoder of EOF.
// We have no idea what the line-length was, so we
// cannot know how much of our array wasn't used.
byte[] result = new byte[b64.pos];
b64.readResults(result, 0, result.length);
return result;
| public static java.math.BigInteger | decodeInteger(byte[] pArray)Decode a byte64-encoded integer according to crypto
standards such as W3C's XML-Signature
return new BigInteger(1, decodeBase64(pArray));
| static byte[] | discardNonBase64(byte[] data)Discards any characters outside of the base64 alphabet, per the requirements on page 25 of RFC 2045 - "Any
characters outside of the base64 alphabet are to be ignored in base64 encoded data."
byte groomedData[] = new byte[data.length];
int bytesCopied = 0;
for (int i = 0; i < data.length; i++) {
if (isBase64(data[i])) {
groomedData[bytesCopied++] = data[i];
}
}
byte packedData[] = new byte[bytesCopied];
System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
return packedData;
| static byte[] | discardWhitespace(byte[] data)Discards any whitespace from a base-64 encoded block.
byte groomedData[] = new byte[data.length];
int bytesCopied = 0;
for (int i = 0; i < data.length; i++) {
switch (data[i]) {
case ' " :
case '\n" :
case '\r" :
case '\t" :
break;
default :
groomedData[bytesCopied++] = data[i];
}
}
byte packedData[] = new byte[bytesCopied];
System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
return packedData;
| public java.lang.Object | encode(java.lang.Object pObject)Encodes an Object using the base64 algorithm. This method is provided in order to satisfy the requirements of the
Encoder interface, and will throw an EncoderException if the supplied object is not of type byte[].
if (!(pObject instanceof byte[])) {
throw new EncoderException("Parameter supplied to Base64 encode is not a byte[]");
}
return encode((byte[]) pObject);
| public byte[] | encode(byte[] pArray)Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet.
return encodeBase64(pArray, false);
| void | encode(byte[] in, int inPos, int inAvail)
Encodes all of the provided data, starting at inPos, for inAvail bytes.
Must be called at least twice: once with the data to encode, and once
with inAvail set to "-1" to alert encoder that EOF has been reached,
so flush last remaining bytes (if not multiple of 3).
Thanks to "commons" project in ws.apache.org for the bitwise operations,
and general approach.
http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
if (eof) {
return;
}
// inAvail < 0 is how we're informed of EOF in the underlying data we're
// encoding.
if (inAvail < 0) {
eof = true;
if (buf == null || buf.length - pos < encodeSize) {
resizeBuf();
}
switch (modulus) {
case 1:
buf[pos++] = intToBase64[(x >> 2) & MASK_6BITS];
buf[pos++] = intToBase64[(x << 4) & MASK_6BITS];
buf[pos++] = PAD;
buf[pos++] = PAD;
break;
case 2:
buf[pos++] = intToBase64[(x >> 10) & MASK_6BITS];
buf[pos++] = intToBase64[(x >> 4) & MASK_6BITS];
buf[pos++] = intToBase64[(x << 2) & MASK_6BITS];
buf[pos++] = PAD;
break;
}
if (lineLength > 0) {
System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length);
pos += lineSeparator.length;
}
} else {
for (int i = 0; i < inAvail; i++) {
if (buf == null || buf.length - pos < encodeSize) {
resizeBuf();
}
modulus = (++modulus) % 3;
int b = in[inPos++];
if (b < 0) { b += 256; }
x = (x << 8) + b;
if (0 == modulus) {
buf[pos++] = intToBase64[(x >> 18) & MASK_6BITS];
buf[pos++] = intToBase64[(x >> 12) & MASK_6BITS];
buf[pos++] = intToBase64[(x >> 6) & MASK_6BITS];
buf[pos++] = intToBase64[x & MASK_6BITS];
currentLinePos += 4;
if (lineLength > 0 && lineLength <= currentLinePos) {
System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length);
pos += lineSeparator.length;
currentLinePos = 0;
}
}
}
}
| public static byte[] | encodeBase64(byte[] binaryData)Encodes binary data using the base64 algorithm but does not chunk the output.
return encodeBase64(binaryData, false);
| public static byte[] | encodeBase64(byte[] binaryData, boolean isChunked)Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
if (binaryData == null || binaryData.length == 0) {
return binaryData;
}
Base64 b64 = isChunked ? new Base64() : new Base64(0);
long len = (binaryData.length * 4) / 3;
long mod = len % 4;
if (mod != 0) {
len += 4 - mod;
}
// If chunked, add space for one CHUNK_SEPARATOR per chunk. (Technically, these are chunk
// terminators, because even a single chunk message has one.)
//
// User length Encoded length Rounded up by 4 Num chunks Final buf len
// 56 74 76 1 78
// 57 76 76 1 78
// 58 77 80 2 84
// 59 78 80 2 84
//
// Or...
// Rounded up size: 4...76 Chunks: 1
// Rounded up size: 80..152 Chunks: 2
// Rounded up size: 156..228 Chunks: 3 ...etc...
if (isChunked) {
len += ((len + CHUNK_SIZE - 1) / CHUNK_SIZE) * CHUNK_SEPARATOR.length;
}
if (len > Integer.MAX_VALUE) {
throw new IllegalArgumentException(
"Input array too big, output array would be bigger than Integer.MAX_VALUE=" + Integer.MAX_VALUE);
}
byte[] buf = new byte[(int) len];
b64.setInitialBuffer(buf, 0, buf.length);
b64.encode(binaryData, 0, binaryData.length);
b64.encode(binaryData, 0, -1); // Notify encoder of EOF.
// Encoder might have resized, even though it was unnecessary.
if (b64.buf != buf) {
b64.readResults(buf, 0, buf.length);
}
return buf;
| public static byte[] | encodeBase64Chunked(byte[] binaryData)Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks
return encodeBase64(binaryData, true);
| public static byte[] | encodeInteger(java.math.BigInteger bigInt)Encode to a byte64-encoded integer according to crypto
standards such as W3C's XML-Signature
if(bigInt == null) {
throw new NullPointerException("encodeInteger called with null parameter");
}
return encodeBase64(toIntegerBytes(bigInt), false);
| boolean | hasData()Returns true if this Base64 object has buffered data for reading. return buf != null;
| public static boolean | isArrayByteBase64(byte[] arrayOctet)Tests a given byte array to see if it contains only valid characters within the Base64 alphabet.
Currently the method treats whitespace as valid.
for (int i = 0; i < arrayOctet.length; i++) {
if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) {
return false;
}
}
return true;
| public static boolean | isBase64(byte octet)Returns whether or not the octet is in the base 64 alphabet.
return octet == PAD || (octet >= 0 && octet < base64ToInt.length && base64ToInt[octet] != -1);
| private static boolean | isWhiteSpace(byte byteToCheck)Check if a byte value is whitespace or not.
switch (byteToCheck) {
case ' " :
case '\n" :
case '\r" :
case '\t" :
return true;
default :
return false;
}
| int | readResults(byte[] b, int bPos, int bAvail)Extracts buffered data into the provided byte[] array, starting
at position bPos, up to a maximum of bAvail bytes. Returns how
many bytes were actually extracted.
if (buf != null) {
int len = Math.min(avail(), bAvail);
if (buf != b) {
System.arraycopy(buf, readPos, b, bPos, len);
readPos += len;
if (readPos >= pos) {
buf = null;
}
} else {
// Re-using the original consumer's output array is only
// allowed for one round.
buf = null;
}
return len;
} else {
return eof ? -1 : 0;
}
| private void | resizeBuf()Doubles our buffer.
if (buf == null) {
buf = new byte[8192];
pos = 0;
readPos = 0;
} else {
byte[] b = new byte[buf.length * 2];
System.arraycopy(buf, 0, b, 0, buf.length);
buf = b;
}
| void | setInitialBuffer(byte[] out, int outPos, int outAvail)Small optimization where we try to buffer directly to the consumer's
output array for one round (if consumer calls this method first!) instead
of starting our own buffer.
// We can re-use consumer's original output array under
// special circumstances, saving on some System.arraycopy().
if (out != null && out.length == outAvail) {
buf = out;
pos = outPos;
readPos = outPos;
}
| static byte[] | toIntegerBytes(java.math.BigInteger bigInt)Returns a byte-array representation of a BigInteger
without sign bit.
int bitlen = bigInt.bitLength();
// round bitlen
bitlen = ((bitlen + 7) >> 3) << 3;
byte[] bigBytes = bigInt.toByteArray();
if(((bigInt.bitLength() % 8) != 0) &&
(((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) {
return bigBytes;
}
// set up params for copying everything but sign bit
int startSrc = 0;
int len = bigBytes.length;
// if bigInt is exactly byte-aligned, just skip signbit in copy
if((bigInt.bitLength() % 8) == 0) {
startSrc = 1;
len--;
}
int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
byte[] resizedBytes = new byte[bitlen / 8];
System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);
return resizedBytes;
|
|