Java/Network Protocol/Base64 Encoding
Версия от 18:01, 31 мая 2010; (обсуждение)
Содержание
- 1 Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One
- 2 Base64 encoding and decoding with URL and filename safe alphabet as defined by RFC 3548, section 4
- 3 Encode/decode for RFC 2045 Base64 as defined by RFC 2045 N. Freed and N. Borenstein.
- 4 Provides Base64 encoding and decoding as defined by RFC 2045
- 5 Provides encoding of raw bytes to base64-encoded characters, and decoding of base64 characters to raw bytes.
Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One
/****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
****************************************************************/
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;
/**
* This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite>
* from RFC 2045 <cite>Multipurpose Internet Mail Extensions (MIME) Part One:
* Format of Internet Message Bodies</cite> by Freed and Borenstein.
* <p>
* Code is based on Base64 and Base64OutputStream code from Commons-Codec 1.4.
*
* @see
*/
public class Base64OutputStream extends FilterOutputStream {
// Default line length per RFC 2045 section 6.8.
private static final int DEFAULT_LINE_LENGTH = 76;
// CRLF line separator per RFC 2045 section 2.1.
private static final byte[] CRLF_SEPARATOR = { "\r", "\n" };
// This 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.
static final byte[] BASE64_TABLE = { "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", "+", "/" };
// Byte used to pad output.
private static final byte BASE64_PAD = "=";
// This set contains all base64 characters including the pad character. Used
// solely to check if a line separator contains any of these characters.
private static final Set<Byte> BASE64_CHARS = new HashSet<Byte>();
static {
for (byte b : BASE64_TABLE) {
BASE64_CHARS.add(b);
}
BASE64_CHARS.add(BASE64_PAD);
}
// Mask used to extract 6 bits
private static final int MASK_6BITS = 0x3f;
private static final int ENCODED_BUFFER_SIZE = 2048;
private final byte[] singleByte = new byte[1];
private final int lineLength;
private final byte[] lineSeparator;
private boolean closed = false;
private final byte[] encoded;
private int position = 0;
private int data = 0;
private int modulus = 0;
private int linePosition = 0;
/**
* Creates a <code>Base64OutputStream</code> that writes the encoded data
* to the given output stream using the default line length (76) and line
* separator (CRLF).
*
* @param out
* underlying output stream.
*/
public Base64OutputStream(OutputStream out) {
this(out, DEFAULT_LINE_LENGTH, CRLF_SEPARATOR);
}
/**
* Creates a <code>Base64OutputStream</code> that writes the encoded data
* to the given output stream using the given line length and the default
* line separator (CRLF).
* <p>
* The given line length will be rounded up to the nearest multiple of 4. If
* the line length is zero then the output will not be split into lines.
*
* @param out
* underlying output stream.
* @param lineLength
* desired line length.
*/
public Base64OutputStream(OutputStream out, int lineLength) {
this(out, lineLength, CRLF_SEPARATOR);
}
/**
* Creates a <code>Base64OutputStream</code> that writes the encoded data
* to the given output stream using the given line length and line
* separator.
* <p>
* The given line length will be rounded up to the nearest multiple of 4. If
* the line length is zero then the output will not be split into lines and
* the line separator is ignored.
* <p>
* The line separator must not include characters from the BASE64 alphabet
* (including the padding character <code>=</code>).
*
* @param out
* underlying output stream.
* @param lineLength
* desired line length.
* @param lineSeparator
* line separator to use.
*/
public Base64OutputStream(OutputStream out, int lineLength,
byte[] lineSeparator) {
super(out);
if (out == null)
throw new IllegalArgumentException();
if (lineLength < 0)
throw new IllegalArgumentException();
checkLineSeparator(lineSeparator);
this.lineLength = lineLength;
this.lineSeparator = new byte[lineSeparator.length];
System.arraycopy(lineSeparator, 0, this.lineSeparator, 0,
lineSeparator.length);
this.encoded = new byte[ENCODED_BUFFER_SIZE];
}
@Override
public final void write(final int b) throws IOException {
if (closed)
throw new IOException("Base64OutputStream has been closed");
singleByte[0] = (byte) b;
write0(singleByte, 0, 1);
}
@Override
public final void write(final byte[] buffer) throws IOException {
if (closed)
throw new IOException("Base64OutputStream has been closed");
if (buffer == null)
throw new NullPointerException();
if (buffer.length == 0)
return;
write0(buffer, 0, buffer.length);
}
@Override
public final void write(final byte[] buffer, final int offset,
final int length) throws IOException {
if (closed)
throw new IOException("Base64OutputStream has been closed");
if (buffer == null)
throw new NullPointerException();
if (offset < 0 || length < 0 || offset + length > buffer.length)
throw new IndexOutOfBoundsException();
if (length == 0)
return;
write0(buffer, offset, offset + length);
}
@Override
public void flush() throws IOException {
if (closed)
throw new IOException("Base64OutputStream has been closed");
flush0();
}
@Override
public void close() throws IOException {
if (closed)
return;
closed = true;
close0();
}
private void write0(final byte[] buffer, final int from, final int to)
throws IOException {
for (int i = from; i < to; i++) {
data = (data << 8) | (buffer[i] & 0xff);
if (++modulus == 3) {
modulus = 0;
// write line separator if necessary
if (lineLength > 0 && linePosition >= lineLength) {
// writeLineSeparator() inlined for performance reasons
linePosition = 0;
if (encoded.length - position < lineSeparator.length)
flush0();
for (byte ls : lineSeparator)
encoded[position++] = ls;
}
// encode data into 4 bytes
if (encoded.length - position < 4)
flush0();
encoded[position++] = BASE64_TABLE[(data >> 18) & MASK_6BITS];
encoded[position++] = BASE64_TABLE[(data >> 12) & MASK_6BITS];
encoded[position++] = BASE64_TABLE[(data >> 6) & MASK_6BITS];
encoded[position++] = BASE64_TABLE[data & MASK_6BITS];
linePosition += 4;
}
}
}
private void flush0() throws IOException {
if (position > 0) {
out.write(encoded, 0, position);
position = 0;
}
}
private void close0() throws IOException {
if (modulus != 0)
writePad();
// write line separator at the end of the encoded data
if (lineLength > 0 && linePosition > 0) {
writeLineSeparator();
}
flush0();
}
private void writePad() throws IOException {
// write line separator if necessary
if (lineLength > 0 && linePosition >= lineLength) {
writeLineSeparator();
}
// encode data into 4 bytes
if (encoded.length - position < 4)
flush0();
if (modulus == 1) {
encoded[position++] = BASE64_TABLE[(data >> 2) & MASK_6BITS];
encoded[position++] = BASE64_TABLE[(data << 4) & MASK_6BITS];
encoded[position++] = BASE64_PAD;
encoded[position++] = BASE64_PAD;
} else {
assert modulus == 2;
encoded[position++] = BASE64_TABLE[(data >> 10) & MASK_6BITS];
encoded[position++] = BASE64_TABLE[(data >> 4) & MASK_6BITS];
encoded[position++] = BASE64_TABLE[(data << 2) & MASK_6BITS];
encoded[position++] = BASE64_PAD;
}
linePosition += 4;
}
private void writeLineSeparator() throws IOException {
linePosition = 0;
if (encoded.length - position < lineSeparator.length)
flush0();
for (byte ls : lineSeparator)
encoded[position++] = ls;
}
private void checkLineSeparator(byte[] lineSeparator) {
if (lineSeparator.length > ENCODED_BUFFER_SIZE)
throw new IllegalArgumentException("line separator length exceeds "
+ ENCODED_BUFFER_SIZE);
for (byte b : lineSeparator) {
if (BASE64_CHARS.contains(b)) {
throw new IllegalArgumentException(
"line separator must not contain base64 character ""
+ (char) (b & 0xff) + """);
}
}
}
}
Base64 encoding and decoding with URL and filename safe alphabet as defined by RFC 3548, section 4
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Provides Base64 encoding and decoding with URL and filename safe alphabet as defined by RFC 3548,
* section 4. <p/> This Base64 encoder is modified to meet URL requirements. The changes are: "+" =>
* "*", "/" => "-", and no padding. <p/> This class is taken from the Apache commons-codec, and
* adjusted to fit the Wicket framework"s needs, especially external dependencies have been removed.
* </p>
* <p/> This class implements section <cite>4. Base 64 Encoding with URL and Filename Safe Alphabet</cite>
* from RFC 3548 <cite>The Base16, Base32, and Base64 Data Encodings</cite> by Simon Josefsson.
* </p>
*
* @author Apache Software Foundation
* @author Juergen Donnerstag
*
* @since 1.2
*/
public class Base64UrlSafe
{
/**
* The base length.
*/
static final int BASELENGTH = 255;
/**
* Lookup length.
*/
static final int LOOKUPLENGTH = 64;
/**
* Used to calculate the number of bits in a byte.
*/
static final int EIGHTBIT = 8;
/**
* Used when encoding something which has fewer than 24 bits.
*/
static final int SIXTEENBIT = 16;
/**
* Used to determine how many bits data contains.
*/
static final int TWENTYFOURBITGROUP = 24;
/**
* Used to get the number of Quadruples.
*/
static final int FOURBYTE = 4;
/**
* Used to test the sign of a byte.
*/
static final int SIGN = -128;
/**
* Contains the Base64 values <code>0</code> through <code>63</code> accessed by using
* character encodings as indices. <p/> For example, <code>base64Alphabet["+"]</code> returns
* <code>62</code>.
* </p>
* <p/> The value of undefined encodings is <code>-1</code>.
* </p>
*/
private static byte[] base64Alphabet = new byte[BASELENGTH];
/**
* <p/> Contains the Base64 encodings <code>A</code> through <code>Z</code>, followed by
* <code>a</code> through <code>z</code>, followed by <code>0</code> through
* <code>9</code>, followed by <code>+</code>, and <code>/</code>.
* </p>
* <p/> This array is accessed by using character values as indices.
* </p>
* <p/> For example, <code>lookUpBase64Alphabet[62] </code> returns <code>"+"</code>.
* </p>
*/
private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
// Populating the lookup and character arrays
static
{
for (int i = 0; i < BASELENGTH; i++)
{
base64Alphabet[i] = (byte)-1;
}
for (int i = "Z"; i >= "A"; i--)
{
base64Alphabet[i] = (byte)(i - "A");
}
for (int i = "z"; i >= "a"; i--)
{
base64Alphabet[i] = (byte)(i - "a" + 26);
}
for (int i = "9"; i >= "0"; i--)
{
base64Alphabet[i] = (byte)(i - "0" + 52);
}
base64Alphabet["*"] = 62;
base64Alphabet["-"] = 63;
for (int i = 0; i <= 25; i++)
{
lookUpBase64Alphabet[i] = (byte)("A" + i);
}
for (int i = 26, j = 0; i <= 51; i++, j++)
{
lookUpBase64Alphabet[i] = (byte)("a" + j);
}
for (int i = 52, j = 0; i <= 61; i++, j++)
{
lookUpBase64Alphabet[i] = (byte)("0" + j);
}
lookUpBase64Alphabet[62] = (byte)"*";
lookUpBase64Alphabet[63] = (byte)"-";
}
/**
* Returns whether or not the <code>octet</code> is in the base 64 alphabet.
*
* @param octet
* The value to test
* @return <code>true</code> if the value is defined in the the base 64 alphabet,
* <code>false</code> otherwise.
*/
private static boolean isBase64(byte octet)
{
if (octet < 0 || base64Alphabet[octet] == -1)
{
return false;
}
else
{
return true;
}
}
/**
* Tests a given byte array to see if it contains only valid characters within the Base64
* alphabet.
*
* @param arrayOctect
* byte array to test
* @return <code>true</code> if all bytes are valid characters in the Base64 alphabet or if
* the byte array is empty; false, otherwise
*/
public static boolean isArrayByteBase64(byte[] arrayOctect)
{
arrayOctect = discardWhitespace(arrayOctect);
int length = arrayOctect.length;
if (length == 0)
{
// shouldn"t a 0 length array be valid base64 data?
// return false;
return true;
}
for (int i = 0; i < length; i++)
{
if (!isBase64(arrayOctect[i]))
{
return false;
}
}
return true;
}
/**
* 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[].
*
* @param pObject
* Object to decode
* @return An object (of type byte[]) containing the binary data which corresponds to the byte[]
* supplied.
* @throws IllegalArgumentException
* if the parameter supplied is not of type byte[]
*/
public Object decode(Object pObject)
{
if (!(pObject instanceof byte[]))
{
throw new IllegalArgumentException(
"Parameter supplied to Base64 decode is not a byte[]");
}
return decode((byte[])pObject);
}
/**
* Decodes a byte[] containing containing characters in the Base64 alphabet.
*
* @param pArray
* A byte array containing Base64 character data
* @return a byte array containing binary data
*/
public byte[] decode(byte[] pArray)
{
return decodeBase64(pArray);
}
/**
* Encodes binary data using the base64 algorithm.
*
* @param binaryData
* Array containing binary data to encode.
* @return Base64-encoded data.
*/
public static byte[] encodeBase64(byte[] binaryData)
{
int lengthDataBits = binaryData.length * EIGHTBIT;
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
byte encodedData[] = null;
int encodedDataLength = 0;
if (fewerThan24bits != 0)
{
// data not divisible by 24 bit
encodedDataLength = (numberTriplets + 1) * 4;
}
else
{
// 16 or 8 bit
encodedDataLength = numberTriplets * 4;
}
if (fewerThan24bits == EIGHTBIT)
{
encodedDataLength -= 2;
}
else if (fewerThan24bits == SIXTEENBIT)
{
encodedDataLength -= 1;
}
encodedData = new byte[encodedDataLength];
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
int encodedIndex = 0;
int dataIndex = 0;
int i = 0;
// log.debug("number of triplets = " + numberTriplets);
for (i = 0; i < numberTriplets; i++)
{
dataIndex = i * 3;
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
b3 = binaryData[dataIndex + 2];
// log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
l = (byte)(b2 & 0x0f);
k = (byte)(b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte)(b1 >> 2) : (byte)((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte)(b2 >> 4) : (byte)((b2) >> 4 ^ 0xf0);
byte val3 = ((b3 & SIGN) == 0) ? (byte)(b3 >> 6) : (byte)((b3) >> 6 ^ 0xfc);
encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
// log.debug( "val2 = " + val2 );
// log.debug( "k4 = " + (k<<4) );
// log.debug( "vak = " + (val2 | (k<<4)) );
encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex + 2] = lookUpBase64Alphabet[(l << 2) | val3];
encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
encodedIndex += 4;
}
// form integral number of 6-bit groups
dataIndex = i * 3;
if (fewerThan24bits == EIGHTBIT)
{
b1 = binaryData[dataIndex];
k = (byte)(b1 & 0x03);
// log.debug("b1=" + b1);
// log.debug("b1<<2 = " + (b1>>2) );
byte val1 = ((b1 & SIGN) == 0) ? (byte)(b1 >> 2) : (byte)((b1) >> 2 ^ 0xc0);
encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
}
else if (fewerThan24bits == SIXTEENBIT)
{
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
l = (byte)(b2 & 0x0f);
k = (byte)(b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte)(b1 >> 2) : (byte)((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte)(b2 >> 4) : (byte)((b2) >> 4 ^ 0xf0);
encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
}
return encodedData;
}
/**
* Decodes Base64 data into octets
*
* @param base64Data
* Byte array containing Base64 data
* @return Array containing decoded data.
*/
public static byte[] decodeBase64(byte[] base64Data)
{
// RFC 2045 requires that we discard ALL non-Base64 characters
base64Data = discardNonBase64(base64Data);
// handle the edge case, so we don"t have to worry about it later
if (base64Data.length == 0)
{
return new byte[0];
}
int numberQuadruple = (base64Data.length + 3) / FOURBYTE;
byte decodedData[] = new byte[base64Data.length - numberQuadruple];
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
// Throw away anything not in base64Data
int encodedIndex = 0;
int dataIndex = 0;
for (int i = 0; i < numberQuadruple; i++)
{
dataIndex = i * 4;
b1 = base64Alphabet[base64Data[dataIndex]];
b2 = base64Alphabet[base64Data[dataIndex + 1]];
if ((dataIndex + 3) < base64Data.length)
{
// No PAD e.g 3cQl
b3 = base64Alphabet[base64Data[dataIndex + 2]];
b4 = base64Alphabet[base64Data[dataIndex + 3]];
decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4);
decodedData[encodedIndex + 1] = (byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex + 2] = (byte)(b3 << 6 | b4);
}
else if ((dataIndex + 2) < base64Data.length)
{
// One PAD e.g. 3cQ[Pad]
b3 = base64Alphabet[base64Data[dataIndex + 2]];
decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4);
decodedData[encodedIndex + 1] = (byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
}
else if ((dataIndex + 1) < base64Data.length)
{
// Two PAD e.g. 3c[Pad][Pad]
decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4);
}
encodedIndex += 3;
}
return decodedData;
}
/**
* Discards any whitespace from a base-64 encoded block.
*
* @param data
* The base-64 encoded data to discard the whitespace from.
* @return The data, less whitespace (see RFC 2045).
*/
static byte[] discardWhitespace(byte[] data)
{
byte groomedData[] = new byte[data.length];
int bytesCopied = 0;
for (int i = 0; i < data.length; i++)
{
switch (data[i])
{
case (byte)" " :
case (byte)"\n" :
case (byte)"\r" :
case (byte)"\t" :
break;
default :
groomedData[bytesCopied++] = data[i];
}
}
byte packedData[] = new byte[bytesCopied];
System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
return packedData;
}
/**
* 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."
*
* @param data
* The base-64 encoded data to groom
* @return The data, less non-base64 characters (see RFC 2045).
*/
static byte[] discardNonBase64(byte[] 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;
}
// Implementation of the Encoder Interface
/**
* 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[].
*
* @param pObject
* Object to encode
* @return An object (of type byte[]) containing the base64 encoded data which corresponds to
* the byte[] supplied.
* @throws IllegalArgumentException
* if the parameter supplied is not of type byte[]
*/
public Object encode(Object pObject)
{
if (!(pObject instanceof byte[]))
{
throw new IllegalArgumentException(
"Parameter supplied to Base64 encode is not a byte[]");
}
return encode((byte[])pObject);
}
/**
* Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64
* alphabet.
*
* @param pArray
* a byte array containing binary data
* @return A byte array containing only Base64 character data
*/
public byte[] encode(byte[] pArray)
{
return encodeBase64(pArray);
}
}
Encode/decode for RFC 2045 Base64 as defined by RFC 2045 N. Freed and N. Borenstein.
/*
* $Header: /home/cvs/jakarta-commons/codec/src/java/org/apache/commons/codec/binary/Base64.java,v 1.1 2003/04/25 17:50:56 tobrien Exp $
* $Revision: 1.1 $
* $Date: 2003/04/25 17:50:56 $
*
*
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS"" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
/**
* This class provides encode/decode for RFC 2045 Base64 as
* defined by RFC 2045, N. Freed and N. Borenstein.
* @since 1.0-dev
*
*/
@SuppressWarnings({"ALL"})
public class Base64 {
// Create constants pertaining to the chunk requirement
static final int CHUNK_SIZE = 76;
static final byte[] CHUNK_SEPARATOR = "\n".getBytes();
// Create numerical and byte constants
static final int BASELENGTH = 255;
static final int LOOKUPLENGTH = 64;
static final int TWENTYFOURBITGROUP = 24;
static final int EIGHTBIT = 8;
static final int SIXTEENBIT = 16;
static final int SIXBIT = 6;
static final int FOURBYTE = 4;
static final int SIGN = -128;
static final byte PAD = (byte) "=";
// Create arrays to hold the base64 characters and a
// lookup for base64 chars
private static byte[] base64Alphabet = new byte[BASELENGTH];
private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
// Populating the lookup and character arrays
static {
for (int i = 0; i < BASELENGTH; i++) {
base64Alphabet[i] = (byte) -1;
}
for (int i = "Z"; i >= "A"; i--) {
base64Alphabet[i] = (byte) (i - "A");
}
for (int i = "z"; i >= "a"; i--) {
base64Alphabet[i] = (byte) (i - "a" + 26);
}
for (int i = "9"; i >= "0"; i--) {
base64Alphabet[i] = (byte) (i - "0" + 52);
}
base64Alphabet["+"] = 62;
base64Alphabet["/"] = 63;
for (int i = 0; i <= 25; i++) {
lookUpBase64Alphabet[i] = (byte) ("A" + i);
}
for (int i = 26, j = 0; i <= 51; i++, j++) {
lookUpBase64Alphabet[i] = (byte) ("a" + j);
}
for (int i = 52, j = 0; i <= 61; i++, j++) {
lookUpBase64Alphabet[i] = (byte) ("0" + j);
}
lookUpBase64Alphabet[62] = (byte) "+";
lookUpBase64Alphabet[63] = (byte) "/";
}
private static boolean isBase64(byte octect) {
if (octect == PAD) {
return true;
}
else if (base64Alphabet[octect] == -1) {
return false;
}
else {
return true;
}
}
public static boolean isArrayByteBase64(byte[] arrayOctect) {
arrayOctect = discardWhitespace(arrayOctect);
int length = arrayOctect.length;
if (length == 0) {
// shouldn"t a 0 length array be valid base64 data?
// return false;
return true;
}
for (int i = 0; i < length; i++) {
if (!isBase64(arrayOctect[i])) {
return false;
}
}
return true;
}
public static byte[] encodeBase64(byte[] binaryData) {
return (encodeBase64(binaryData, false));
}
public static byte[] encodeBase64Chunked(byte[] binaryData) {
return (encodeBase64(binaryData, true));
}
public Object decode(Object pObject) throws IllegalArgumentException {
Object result;
if (!(pObject instanceof byte[])) {
throw new IllegalArgumentException(
"Parameter supplied to "
+ "Base64 "
+ "decode is not a byte[]");
}
else {
result = decode((byte[]) pObject);
}
return result;
}
public byte[] decode(byte[] pArray) throws IllegalArgumentException {
byte[] result;
if (!isArrayByteBase64(pArray)) {
throw new IllegalArgumentException(
"Parameter supplied to "
+ "Base64 "
+ "decode is not a valid base64 data.");
}
else {
result = decodeBase64(pArray);
}
return (result);
}
/**
* Encodes hex octects into Base64.
*
* @param binaryData Array containing binary data to encode.
* @return Base64-encoded data.
*/
public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
int lengthDataBits = binaryData.length * EIGHTBIT;
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
byte encodedData[] = null;
int encodedDataLength = 0;
int nbrChunks = 0;
if (fewerThan24bits != 0) {
//data not divisible by 24 bit
encodedDataLength = (numberTriplets + 1) * 4;
}
else {
// 16 or 8 bit
encodedDataLength = numberTriplets * 4;
}
// If the output is to be "chunked" into 76 character sections,
// for compliance with RFC 2045 MIME, then it is important to
// allow for extra length to account for the separator(s)
if (isChunked) {
nbrChunks =
(CHUNK_SEPARATOR.length == 0
? 0
: (int) Math.ceil((float) encodedDataLength / CHUNK_SIZE));
encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
}
encodedData = new byte[encodedDataLength];
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
int encodedIndex = 0;
int dataIndex = 0;
int i = 0;
int nextSeparatorIndex = CHUNK_SIZE;
int chunksSoFar = 0;
//log.debug("number of triplets = " + numberTriplets);
for (i = 0; i < numberTriplets; i++) {
dataIndex = i * 3;
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
b3 = binaryData[dataIndex + 2];
//log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 =
((b1 & SIGN) == 0)
? (byte) (b1 >> 2)
: (byte) ((b1) >> 2 ^ 0xc0);
byte val2 =
((b2 & SIGN) == 0)
? (byte) (b2 >> 4)
: (byte) ((b2) >> 4 ^ 0xf0);
byte val3 =
((b3 & SIGN) == 0)
? (byte) (b3 >> 6)
: (byte) ((b3) >> 6 ^ 0xfc);
encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
//log.debug( "val2 = " + val2 );
//log.debug( "k4 = " + (k<<4) );
//log.debug( "vak = " + (val2 | (k<<4)) );
encodedData[encodedIndex + 1] =
lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex + 2] =
lookUpBase64Alphabet[(l << 2) | val3];
encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
encodedIndex += 4;
// If we are chunking, let"s put a chunk separator down.
if (isChunked) {
// this assumes that CHUNK_SIZE % 4 == 0
if (encodedIndex == nextSeparatorIndex) {
System.arraycopy(
CHUNK_SEPARATOR,
0,
encodedData,
encodedIndex,
CHUNK_SEPARATOR.length);
chunksSoFar++;
nextSeparatorIndex =
(CHUNK_SIZE * (chunksSoFar + 1))
+ (chunksSoFar * CHUNK_SEPARATOR.length);
encodedIndex += CHUNK_SEPARATOR.length;
}
}
}
// form integral number of 6-bit groups
dataIndex = i * 3;
if (fewerThan24bits == EIGHTBIT) {
b1 = binaryData[dataIndex];
k = (byte) (b1 & 0x03);
//log.debug("b1=" + b1);
//log.debug("b1<<2 = " + (b1>>2) );
byte val1 =
((b1 & SIGN) == 0)
? (byte) (b1 >> 2)
: (byte) ((b1) >> 2 ^ 0xc0);
encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
encodedData[encodedIndex + 2] = PAD;
encodedData[encodedIndex + 3] = PAD;
}
else if (fewerThan24bits == SIXTEENBIT) {
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 =
((b1 & SIGN) == 0)
? (byte) (b1 >> 2)
: (byte) ((b1) >> 2 ^ 0xc0);
byte val2 =
((b2 & SIGN) == 0)
? (byte) (b2 >> 4)
: (byte) ((b2) >> 4 ^ 0xf0);
encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex + 1] =
lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
encodedData[encodedIndex + 3] = PAD;
}
if (isChunked) {
// we also add a separator to the end of the final chunk.
if (chunksSoFar < nbrChunks) {
System.arraycopy(
CHUNK_SEPARATOR,
0,
encodedData,
encodedDataLength - CHUNK_SEPARATOR.length,
CHUNK_SEPARATOR.length);
}
}
return encodedData;
}
/**
* Decodes Base64 data into octects
*
* @param base64Data Byte array containing Base64 data
* @return Array containing decoded data.
*/
public static byte[] decodeBase64(byte[] base64Data) {
// RFC 2045 suggests line wrapping at (no more than) 76
// characters -- we may have embedded whitespace.
base64Data = discardWhitespace(base64Data);
// handle the edge case, so we don"t have to worry about it later
if (base64Data.length == 0) {
return new byte[0];
}
int numberQuadruple = base64Data.length / FOURBYTE;
byte decodedData[] = null;
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
// Throw away anything not in base64Data
int encodedIndex = 0;
int dataIndex = 0;
{
// this sizes the output array properly - rlw
int lastData = base64Data.length;
// ignore the "=" padding
while (base64Data[lastData - 1] == PAD) {
if (--lastData == 0) {
return new byte[0];
}
}
decodedData = new byte[lastData - numberQuadruple];
}
for (int i = 0; i < numberQuadruple; i++) {
dataIndex = i * 4;
marker0 = base64Data[dataIndex + 2];
marker1 = base64Data[dataIndex + 3];
b1 = base64Alphabet[base64Data[dataIndex]];
b2 = base64Alphabet[base64Data[dataIndex + 1]];
if (marker0 != PAD && marker1 != PAD) {
//No PAD e.g 3cQl
b3 = base64Alphabet[marker0];
b4 = base64Alphabet[marker1];
decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex + 1] =
(byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
}
else if (marker0 == PAD) {
//Two PAD e.g. 3c[Pad][Pad]
decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
}
else if (marker1 == PAD) {
//One PAD e.g. 3cQ[Pad]
b3 = base64Alphabet[marker0];
decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex + 1] =
(byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
}
encodedIndex += 3;
}
return decodedData;
}
/**
* Discards any whitespace from a base-64 encoded block.
*
* @param data The base-64 encoded data to discard the whitespace
* from.
* @return The data, less whitespace (see RFC 2045).
*/
static byte[] discardWhitespace(byte[] data) {
byte groomedData[] = new byte[data.length];
int bytesCopied = 0;
for (int i = 0; i < data.length; i++) {
switch (data[i]) {
case (byte) " " :
case (byte) "\n" :
case (byte) "\r" :
case (byte) "\t" :
break;
default:
groomedData[bytesCopied++] = data[i];
}
}
byte packedData[] = new byte[bytesCopied];
System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
return packedData;
}
// Implementation of the Encoder Interface
/**
* encode an Object
*/
public Object encode(Object pObject) throws IllegalArgumentException {
Object result;
if (!(pObject instanceof byte[])) {
throw new IllegalArgumentException(
"Parameter supplied to "
+ "Base64 "
+ "encode is not a byte[]");
}
else {
result = encode((byte[]) pObject);
}
return result;
}
public byte[] encode(byte[] pArray) throws IllegalArgumentException {
return (encodeBase64(pArray, false));
}
}
Provides Base64 encoding and decoding as defined by RFC 2045
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Provides Base64 encoding and decoding as defined by RFC 2045.
*
* <p>This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite>
* from RFC 2045 <cite>Multipurpose Internet Mail Extensions (MIME) Part One:
* Format of Internet Message Bodies</cite> by Freed and Borenstein.</p>
*
* @see
*/
static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes();
/**
* The base length.
*/
static final int BASELENGTH = 255;
/**
* Lookup length.
*/
static final int LOOKUPLENGTH = 64;
/**
* Used to calculate the number of bits in a byte.
*/
static final int EIGHTBIT = 8;
/**
* Used when encoding something which has fewer than 24 bits.
*/
static final int SIXTEENBIT = 16;
/**
* Used to determine how many bits data contains.
*/
static final int TWENTYFOURBITGROUP = 24;
/**
* Used to get the number of Quadruples.
*/
static final int FOURBYTE = 4;
/**
* Used to test the sign of a byte.
*/
static final int SIGN = -128;
/**
* Byte used to pad output.
*/
static final byte PAD = (byte) "=";
// Create arrays to hold the base64 characters and a
// lookup for base64 chars
private static byte[] base64Alphabet = new byte[BASELENGTH];
private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
// Populating the lookup and character arrays
static {
for (int i = 0; i < BASELENGTH; i++) {
base64Alphabet[i] = (byte) -1;
}
for (int i = "Z"; i >= "A"; i--) {
base64Alphabet[i] = (byte) (i - "A");
}
for (int i = "z"; i >= "a"; i--) {
base64Alphabet[i] = (byte) (i - "a" + 26);
}
for (int i = "9"; i >= "0"; i--) {
base64Alphabet[i] = (byte) (i - "0" + 52);
}
base64Alphabet["+"] = 62;
base64Alphabet["/"] = 63;
for (int i = 0; i <= 25; i++) {
lookUpBase64Alphabet[i] = (byte) ("A" + i);
}
for (int i = 26, j = 0; i <= 51; i++, j++) {
lookUpBase64Alphabet[i] = (byte) ("a" + j);
}
for (int i = 52, j = 0; i <= 61; i++, j++) {
lookUpBase64Alphabet[i] = (byte) ("0" + j);
}
lookUpBase64Alphabet[62] = (byte) "+";
lookUpBase64Alphabet[63] = (byte) "/";
}
private static boolean isBase64(byte octect) {
if (octect == PAD) {
return true;
} else if (base64Alphabet[octect] == -1) {
return false;
} else {
return true;
}
}
/**
* Tests a given byte array to see if it contains
* only valid characters within the Base64 alphabet.
*
* @param arrayOctect byte array to test
* @return true if all bytes are valid characters in the Base64
* alphabet or if the byte array is empty; false, otherwise
*/
public static boolean isArrayByteBase64(byte[] arrayOctect) {
arrayOctect = discardWhitespace(arrayOctect);
int length = arrayOctect.length;
if (length == 0) {
// shouldn"t a 0 length array be valid base64 data?
// return false;
return true;
}
for (int i = 0; i < length; i++) {
if (!isBase64(arrayOctect[i])) {
return false;
}
}
return true;
}
/**
* Encodes binary data using the base64 algorithm but
* does not chunk the output.
*
* @param binaryData binary data to encode
* @return Base64 characters
*/
public static byte[] encodeBase64(byte[] binaryData) {
return encodeBase64(binaryData, false);
}
/**
* Encodes binary data using the base64 algorithm and chunks
* the encoded output into 76 character blocks
*
* @param binaryData binary data to encode
* @return Base64 characters chunked in 76 character blocks
*/
public static byte[] encodeBase64Chunked(byte[] binaryData) {
return encodeBase64(binaryData, true);
}
/**
* 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[].
*
* @param pObject Object to decode
* @return An object (of type byte[]) containing the
* binary data which corresponds to the byte[] supplied.
* @throws DecoderException if the parameter supplied is not
* of type byte[]
*/
public Object decode(Object pObject) {
if (!(pObject instanceof byte[])) {
throw new RuntimeException("Parameter supplied to Base64 decode is not a byte[]");
}
return decode((byte[]) pObject);
}
/**
* Decodes a byte[] containing containing
* characters in the Base64 alphabet.
*
* @param pArray A byte array containing Base64 character data
* @return a byte array containing binary data
*/
public byte[] decode(byte[] pArray) {
return decodeBase64(pArray);
}
/**
* Encodes binary data using the base64 algorithm, optionally
* chunking the output into 76 character blocks.
*
* @param binaryData Array containing binary data to encode.
* @param isChunked if isChunked is true this encoder will chunk
* the base64 output into 76 character blocks
* @return Base64-encoded data.
*/
public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
int lengthDataBits = binaryData.length * EIGHTBIT;
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
byte encodedData[] = null;
int encodedDataLength = 0;
int nbrChunks = 0;
if (fewerThan24bits != 0) {
//data not divisible by 24 bit
encodedDataLength = (numberTriplets + 1) * 4;
} else {
// 16 or 8 bit
encodedDataLength = numberTriplets * 4;
}
// If the output is to be "chunked" into 76 character sections,
// for compliance with RFC 2045 MIME, then it is important to
// allow for extra length to account for the separator(s)
if (isChunked) {
nbrChunks =
(CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math.ceil((float) encodedDataLength / CHUNK_SIZE));
encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
}
encodedData = new byte[encodedDataLength];
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
int encodedIndex = 0;
int dataIndex = 0;
int i = 0;
int nextSeparatorIndex = CHUNK_SIZE;
int chunksSoFar = 0;
//log.debug("number of triplets = " + numberTriplets);
for (i = 0; i < numberTriplets; i++) {
dataIndex = i * 3;
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
b3 = binaryData[dataIndex + 2];
//log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 =
((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
byte val2 =
((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
byte val3 =
((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
//log.debug( "val2 = " + val2 );
//log.debug( "k4 = " + (k<<4) );
//log.debug( "vak = " + (val2 | (k<<4)) );
encodedData[encodedIndex + 1] =
lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex + 2] =
lookUpBase64Alphabet[(l << 2) | val3];
encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
encodedIndex += 4;
// If we are chunking, let"s put a chunk separator down.
if (isChunked) {
// this assumes that CHUNK_SIZE % 4 == 0
if (encodedIndex == nextSeparatorIndex) {
System.arraycopy(
CHUNK_SEPARATOR,
0,
encodedData,
encodedIndex,
CHUNK_SEPARATOR.length);
chunksSoFar++;
nextSeparatorIndex =
(CHUNK_SIZE * (chunksSoFar + 1)) +
(chunksSoFar * CHUNK_SEPARATOR.length);
encodedIndex += CHUNK_SEPARATOR.length;
}
}
}
// form integral number of 6-bit groups
dataIndex = i * 3;
if (fewerThan24bits == EIGHTBIT) {
b1 = binaryData[dataIndex];
k = (byte) (b1 & 0x03);
//log.debug("b1=" + b1);
//log.debug("b1<<2 = " + (b1>>2) );
byte val1 =
((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
encodedData[encodedIndex + 2] = PAD;
encodedData[encodedIndex + 3] = PAD;
} else if (fewerThan24bits == SIXTEENBIT) {
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 =
((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
byte val2 =
((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex + 1] =
lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
encodedData[encodedIndex + 3] = PAD;
}
if (isChunked) {
// we also add a separator to the end of the final chunk.
if (chunksSoFar < nbrChunks) {
System.arraycopy(
CHUNK_SEPARATOR,
0,
encodedData,
encodedDataLength - CHUNK_SEPARATOR.length,
CHUNK_SEPARATOR.length);
}
}
return encodedData;
}
/**
* Decodes Base64 data into octects
*
* @param base64Data Byte array containing Base64 data
* @return Array containing decoded data.
*/
public static byte[] decodeBase64(byte[] base64Data) {
// RFC 2045 requires that we discard ALL non-Base64 characters
base64Data = discardNonBase64(base64Data);
// handle the edge case, so we don"t have to worry about it later
if (base64Data.length == 0) {
return new byte[0];
}
int numberQuadruple = base64Data.length / FOURBYTE;
byte decodedData[] = null;
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
// Throw away anything not in base64Data
int encodedIndex = 0;
int dataIndex = 0;
{
// this sizes the output array properly - rlw
int lastData = base64Data.length;
// ignore the "=" padding
while (base64Data[lastData - 1] == PAD) {
if (--lastData == 0) {
return new byte[0];
}
}
decodedData = new byte[lastData - numberQuadruple];
}
for (int i = 0; i < numberQuadruple; i++) {
dataIndex = i * 4;
marker0 = base64Data[dataIndex + 2];
marker1 = base64Data[dataIndex + 3];
b1 = base64Alphabet[base64Data[dataIndex]];
b2 = base64Alphabet[base64Data[dataIndex + 1]];
if (marker0 != PAD && marker1 != PAD) {
//No PAD e.g 3cQl
b3 = base64Alphabet[marker0];
b4 = base64Alphabet[marker1];
decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex + 1] =
(byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
} else if (marker0 == PAD) {
//Two PAD e.g. 3c[Pad][Pad]
decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
} else if (marker1 == PAD) {
//One PAD e.g. 3cQ[Pad]
b3 = base64Alphabet[marker0];
decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex + 1] =
(byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
}
encodedIndex += 3;
}
return decodedData;
}
/**
* Discards any whitespace from a base-64 encoded block.
*
* @param data The base-64 encoded data to discard the whitespace
* from.
* @return The data, less whitespace (see RFC 2045).
*/
static byte[] discardWhitespace(byte[] data) {
byte groomedData[] = new byte[data.length];
int bytesCopied = 0;
for (int i = 0; i < data.length; i++) {
switch (data[i]) {
case (byte) " " :
case (byte) "\n" :
case (byte) "\r" :
case (byte) "\t" :
break;
default:
groomedData[bytesCopied++] = data[i];
}
}
byte packedData[] = new byte[bytesCopied];
System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
return packedData;
}
/**
* 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."
*
* @param data The base-64 encoded data to groom
* @return The data, less non-base64 characters (see RFC 2045).
*/
static byte[] discardNonBase64(byte[] 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;
}
// Implementation of the Encoder Interface
/**
* 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[].
*
* @param pObject Object to encode
* @return An object (of type byte[]) containing the
* base64 encoded data which corresponds to the byte[] supplied.
* @throws EncoderException if the parameter supplied is not
* of type byte[]
*/
public Object encode(Object pObject) {
if (!(pObject instanceof byte[])) {
throw new RuntimeException(
"Parameter supplied to Base64 encode is not a byte[]");
}
return encode((byte[]) pObject);
}
/**
* Encodes a byte[] containing binary data, into a byte[] containing
* characters in the Base64 alphabet.
*
* @param pArray a byte array containing binary data
* @return A byte array containing only Base64 character data
*/
public byte[] encode(byte[] pArray) {
return encodeBase64(pArray, false);
}
}
Provides encoding of raw bytes to base64-encoded characters, and decoding of base64 characters to raw bytes.
/*
* JCommon : a free general purpose class library for the Java(tm) platform
*
*
* (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
*
* Project Info: http://www.jfree.org/jcommon/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* -------------------------------------
* AbstractElementDefinitionHandler.java
* -------------------------------------
* (C)opyright 2003-2005, by Thomas Morgner and Contributors.
*
* Original Author: Kevin Kelley <kelley@ruralnet.net> -
* 30718 Rd. 28, La Junta, CO, 81050 USA.
*
* $Id: Base64.java,v 1.4 2005/10/18 13:33:53 mungady Exp $
*
* Changes
* -------------------------
* 23.09.2003 : Initial version
*
*/
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
/**
* Provides encoding of raw bytes to base64-encoded characters, and decoding of
* base64 characters to raw bytes. date: 06 August 1998 modified: 14 February
* 2000 modified: 22 September 2000
*
* @author Kevin Kelley (kelley@ruralnet.net)
* @version 1.3
*/
public class Base64 {
private Base64() {
}
/**
* returns an array of base64-encoded characters to represent the passed data
* array.
*
* @param data
* the array of bytes to encode
* @return base64-coded character array.
*/
public static char[] encode(final byte[] data) {
final char[] out = new char[((data.length + 2) / 3) * 4];
//
// 3 bytes encode to 4 chars. Output is always an even
// multiple of 4 characters.
//
for (int i = 0, index = 0; i < data.length; i += 3, index += 4) {
boolean quad = false;
boolean trip = false;
int val = (0xFF & data[i]);
val <<= 8;
if ((i + 1) < data.length) {
val |= (0xFF & data[i + 1]);
trip = true;
}
val <<= 8;
if ((i + 2) < data.length) {
val |= (0xFF & data[i + 2]);
quad = true;
}
out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
val >>= 6;
out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
val >>= 6;
out[index + 1] = alphabet[val & 0x3F];
val >>= 6;
out[index + 0] = alphabet[val & 0x3F];
}
return out;
}
/**
* Decodes a BASE-64 encoded stream to recover the original data. White space
* before and after will be trimmed away, but no other manipulation of the
* input will be performed.
*
* As of version 1.2 this method will properly handle input containing junk
* characters (newlines and the like) rather than throwing an error. It does
* this by pre-parsing the input and generating from that a count of VALID
* input characters.
*
* @param data
* the character data.
*
* @return The decoded data.
*/
public static byte[] decode(final char[] data) {
// as our input could contain non-BASE64 data (newlines,
// whitespace of any sort, whatever) we must first adjust
// our count of USABLE data so that...
// (a) we don"t misallocate the output array, and
// (b) think that we miscalculated our data length
// just because of extraneous throw-away junk
int tempLen = data.length;
for (int ix = 0; ix < data.length; ix++) {
if ((data[ix] > 255) || codes[data[ix]] < 0) {
--tempLen; // ignore non-valid chars and padding
}
}
// calculate required length:
// -- 3 bytes for every 4 valid base64 chars
// -- plus 2 bytes if there are 3 extra base64 chars,
// or plus 1 byte if there are 2 extra.
int len = (tempLen / 4) * 3;
if ((tempLen % 4) == 3) {
len += 2;
}
if ((tempLen % 4) == 2) {
len += 1;
}
final byte[] out = new byte[len];
int shift = 0; // # of excess bits stored in accum
int accum = 0; // excess bits
int index = 0;
// we now go through the entire array (NOT using the "tempLen" value)
for (int ix = 0; ix < data.length; ix++) {
final int value = (data[ix] > 255) ? -1 : codes[data[ix]];
if (value >= 0) { // skip over non-code
accum <<= 6; // bits shift up by 6 each time thru
shift += 6; // loop, with new bits being put in
accum |= value; // at the bottom.
if (shift >= 8) { // whenever there are 8 or more shifted in,
shift -= 8; // write them out (from the top, leaving any
out[index++] = // excess at the bottom for next iteration.
(byte) ((accum >> shift) & 0xff);
}
}
// we will also have skipped processing a padding null byte ("=") here;
// these are used ONLY for padding to an even length and do not legally
// occur as encoded data. for this reason we can ignore the fact that
// no index++ operation occurs in that special case: the out[] array is
// initialized to all-zero bytes to start with and that works to our
// advantage in this combination.
}
// if there is STILL something wrong we just have to throw up now!
if (index != out.length) {
throw new Error("Miscalculated data length (wrote " + index + " instead of " + out.length
+ ")");
}
return out;
}
//
// code characters for values 0..63
//
private static char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
.toCharArray();
//
// lookup table for converting base64 characters to value in range 0..63
//
private static byte[] codes = new byte[256];
static {
for (int i = 0; i < 256; i++) {
codes[i] = -1;
}
for (int i = "A"; i <= "Z"; i++) {
codes[i] = (byte) (i - "A");
}
for (int i = "a"; i <= "z"; i++) {
codes[i] = (byte) (26 + i - "a");
}
for (int i = "0"; i <= "9"; i++) {
codes[i] = (byte) (52 + i - "0");
}
codes["+"] = 62;
codes["/"] = 63;
}
// /////////////////////////////////////////////////
// remainder (main method and helper functions) is
// for testing purposes only, feel free to clip it.
// /////////////////////////////////////////////////
/**
* Entry point.
*
* @param args
* the command line arguments.
*/
public static void main(final String[] args) {
boolean decode = false;
if (args.length == 0) {
System.out.println("usage: java Base64 [-d[ecode]] filename");
System.exit(0);
}
for (int i = 0; i < args.length; i++) {
if ("-decode".equalsIgnoreCase(args[i])) {
decode = true;
} else if ("-d".equalsIgnoreCase(args[i])) {
decode = true;
}
}
final String filename = args[args.length - 1];
final File file = new File(filename);
if (!file.exists()) {
System.out.println("Error: file "" + filename + "" doesn"t exist!");
System.exit(0);
}
if (decode) {
final char[] encoded = readChars(file);
final byte[] decoded = decode(encoded);
writeBytes(file, decoded);
} else {
final byte[] decoded = readBytes(file);
final char[] encoded = encode(decoded);
writeChars(file, encoded);
}
}
private static byte[] readBytes(final File file) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
final InputStream fis = new FileInputStream(file);
final InputStream is = new BufferedInputStream(fis);
int count;
final byte[] buf = new byte[16384];
while ((count = is.read(buf)) != -1) {
if (count > 0) {
baos.write(buf, 0, count);
}
}
is.close();
} catch (Exception e) {
e.printStackTrace();
}
return baos.toByteArray();
}
private static char[] readChars(final File file) {
final CharArrayWriter caw = new CharArrayWriter();
try {
final Reader fr = new FileReader(file);
final Reader in = new BufferedReader(fr);
int count;
final char[] buf = new char[16384];
while ((count = in.read(buf)) != -1) {
if (count > 0) {
caw.write(buf, 0, count);
}
}
in.close();
} catch (Exception e) {
e.printStackTrace();
}
return caw.toCharArray();
}
private static void writeBytes(final File file, final byte[] data) {
try {
final OutputStream fos = new FileOutputStream(file);
final OutputStream os = new BufferedOutputStream(fos);
os.write(data);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void writeChars(final File file, final char[] data) {
try {
final Writer fos = new FileWriter(file);
final Writer os = new BufferedWriter(fos);
os.write(data);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// /////////////////////////////////////////////////
// end of test code.
// /////////////////////////////////////////////////
}