package gnu.crypto.util;
// ----------------------------------------------------------------------------
// $Id: Base64.java,v 1.2 2003/05/17 04:37:13 raif Exp $
//
// This file is part of GNU Crypto.
//
// GNU Crypto 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 2, or (at your option)
// any later version.
//
// GNU Crypto 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 this program; see the file COPYING. If not, write to the
//
// Free Software Foundation Inc.,
// 59 Temple Place - Suite 330,
// Boston, MA 02111-1307
// USA
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library. Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give
// you permission to link this library with independent modules to
// produce an executable, regardless of the license terms of these
// independent modules, and to copy and distribute the resulting
// executable under terms of your choice, provided that you also meet,
// for each linked independent module, the terms and conditions of the
// license of that module. An independent module is a module which is
// not derived from or based on this library. If you modify this
// library, you may extend this exception to your version of the
// library, but you are not obligated to do so. If you do not wish to
// do so, delete this exception statement from your version.
// ----------------------------------------------------------------------------
import java.io.PrintWriter;
/**
* Most of this implementation is from Robert Harder's public domain Base64
* code (version 1.4.1 available from <http://iharder.net/xmlizable>).
*
* @version $Revision: 1.2 $
*/
public class Base64 {
// Debugging methods and variables
// -------------------------------------------------------------------------
private static final String NAME = "Base64";
private static final boolean DEBUG = true;
private static final int debuglevel = 9;
private static final PrintWriter err = new PrintWriter(System.out, true);
private static void debug(String s) {
err.println(">>> "+NAME+": "+s);
}
// Constants and variables
// -------------------------------------------------------------------------
/** Maximum line length (76) of Base64 output. */
private static final int MAX_LINE_LENGTH = 76;
/** The new line character (\n) as one byte. */
private static final byte NEW_LINE = (byte) '\n';
/** The equals sign (=) as a byte. */
private static final byte EQUALS_SIGN = (byte) '=';
private static final byte WHITE_SPACE_ENC = -5; // white space in encoding
private static final byte EQUALS_SIGN_ENC = -1; // equals sign in encoding
/** The 64 valid Base64 values. */
private static final byte[] ALPHABET = {
(byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
(byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
(byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
(byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
(byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
(byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
(byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
(byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
(byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
};
/**
* Translates a Base64 value to either its 6-bit reconstruction value or a
* negative number indicating some other meaning.
*/
private static final byte[] DECODABET = {
-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
-5,-5, // Whitespace: Tab and Linefeed
-9,-9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
-9,-9,-9,-9,-9, // Decimal 27 - 31
-5, // Whitespace: Space
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
62, // Plus sign at decimal 43
-9,-9,-9, // Decimal 44 - 46
63, // Slash at decimal 47
52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
-9,-9,-9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9,-9,-9, // Decimal 62 - 64
0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
-9,-9,-9,-9,-9,-9, // Decimal 91 - 96
26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
-9,-9,-9,-9 // Decimal 123 - 126
};
// Constructor(s)
// -------------------------------------------------------------------------
/** Trivial private ctor to enfore Singleton pattern. */
private Base64() {
super();
}
// Class methods
// -------------------------------------------------------------------------
/**
* Encodes a byte array into Base64 notation. Equivalent to calling
* encode(source, 0, source.length)
.
*
* @param src the data to convert.
*/
public static final String encode(byte[] src) {
return encode(src, 0, src.length, true);
}
/**
* Encodes a byte array into Base64 notation. Equivalent to calling
* encode(src, 0, src.length)
.
*
* @param src the data to convert.
* @param breakLines break lines at 80 characters or less.
*/
// public static final String encode(byte[] src, boolean breakLines) {
// return encode(src, 0, src.length, breakLines);
// }
/**
* Encodes a byte array into Base64 notation.
*
* @param src the data to convert.
* @param off offset in array where conversion should begin.
* @param len length of data to convert.
*/
// public static final String encode(byte[] src, int off, int len) {
// return encode(src, off, len, true);
// }
/**
* Encodes a byte array into Base64 notation.
*
* @param src the data to convert.
* @param off offset in array where conversion should begin.
* @param len length of data to convert.
* @param breakLines break lines at 80 characters or less.
*/
public static final String encode(byte[] src, int off, int len, boolean breakLines) {
int len43 = len * 4 / 3;
byte[] outBuff = new byte[
len43 // Main 4:3
+ ((len % 3) > 0 ? 4 : 0) // Account for padding
+ (breakLines ? (len43 / MAX_LINE_LENGTH ) : 0)]; // New lines
int d = 0;
int e = 0;
int len2 = len - 2;
int lineLength = 0;
for ( ; d < len2; d += 3, e += 4) {
encode3to4(src, d + off, 3, outBuff, e);
lineLength += 4;
if (breakLines && lineLength == MAX_LINE_LENGTH) {
outBuff[e + 4] = NEW_LINE;
e++;
lineLength = 0;
}
}
if (d < len) { // padding needed
encode3to4(src, d + off, len - d, outBuff, e);
e += 4;
}
return new String(outBuff, 0, e);
}
/**
* Decodes data from Base64 notation.
*
* @param s the string to decode.
* @return the decoded data.
*/
public static final byte[] decode(String s) {
byte[] bytes = s.getBytes();
return decode(bytes, 0, bytes.length);
}
/**
* Decodes Base64 content in byte array format and returns the decoded byte
* array.
*
* @param src the Base64 encoded data.
* @param off the offset of where to begin decoding.
* @param len the length of characters to decode.
* @return the decoded data.
* @throws IllegalArgumentException if src
contains an illegal
* Base-64 character.
*/
public static byte[] decode(byte[] src, int off, int len) {
int len34 = len * 3 / 4;
byte[] outBuff = new byte[len34]; // Upper limit on size of output
int outBuffPosn = 0;
byte[] b4 = new byte[4];
int b4Posn = 0;
int i = 0;
byte sbiCrop = 0;
byte sbiDecode = 0;
for (i = off; i < off + len; i++) {
sbiCrop = (byte)(src[i] & 0x7F); // Only the low seven bits
sbiDecode = DECODABET[sbiCrop];
if (sbiDecode >= WHITE_SPACE_ENC) { // White space, Equals sign or better
if (sbiDecode >= EQUALS_SIGN_ENC) {
b4[b4Posn++] = sbiCrop;
if (b4Posn > 3) {
outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
b4Posn = 0;
// If that was the equals sign, break out of 'for' loop
if (sbiCrop == EQUALS_SIGN)
break;
} // end if: quartet built
} // end if: equals sign or better
} else {
throw new IllegalArgumentException(
"Illegal BASE-64 character at #"+i+": "+src[i]+"(decimal)");
}
}
byte[] result = new byte[outBuffPosn];
System.arraycopy(outBuff, 0, result, 0, outBuffPosn);
return result;
}
/**
*
Encodes up to three bytes of the array src
and writes
* the resulting four Base64 bytes to dest
. The source and
* destination arrays can be manipulated anywhere along their length by
* specifying sOffset
and dOffset
.
This method does not check to make sure the arrays are large enough to
* accomodate sOffset + 3
for the src
array or
* dOffset + 4
for the dest
array. The actual
* number of significant bytes in the input array is given by
* numBytes
.
destination
array.
*/
private static final byte[]
encode3to4(byte[] src, int sOffset, int numBytes, byte[] dest, int dOffset) {
// 1 2 3
// 01234567890123456789012345678901 Bit position
// --------000000001111111122222222 Array position from threeBytes
// --------| || || || | Six bit groups to index ALPHABET
// >>18 >>12 >> 6 >> 0 Right shift necessary
// 0x3F 0x3F 0x3F Additional AND
// Create buffer with zero-padding if there are only one or two
// significant bytes passed in the array.
// We have to shift left 24 in order to flush out the 1's that appear
// when Java treats a value as negative that is cast from a byte to an int.
int inBuff = (numBytes > 0 ? ((src[sOffset ] << 24) >>> 8) : 0)
| (numBytes > 1 ? ((src[sOffset + 1] << 24) >>> 16) : 0)
| (numBytes > 2 ? ((src[sOffset + 2] << 24) >>> 24) : 0);
switch (numBytes) {
case 3:
dest[dOffset ] = ALPHABET[(inBuff >>> 18) ];
dest[dOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3F];
dest[dOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3F];
dest[dOffset + 3] = ALPHABET[(inBuff ) & 0x3F];
break;
case 2:
dest[dOffset ] = ALPHABET[(inBuff >>> 18) ];
dest[dOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3F];
dest[dOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3F];
dest[dOffset + 3] = EQUALS_SIGN;
break;
case 1:
dest[dOffset ] = ALPHABET[(inBuff >>> 18) ];
dest[dOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3F];
dest[dOffset + 2] = EQUALS_SIGN;
dest[dOffset + 3] = EQUALS_SIGN;
break;
}
return dest;
}
/**
* Decodes four bytes from array src
and writes the
* resulting bytes (up to three of them) to dest
.
The source and destination arrays can be manipulated anywhere along
* their length by specifying sOffset
and dOffset
.
*
This method does not check to make sure your arrays are large enough
* to accomodate sOffset + 4
for the src
array or
* dOffset + 3
for the dest
array. This method
* returns the actual number of bytes that were converted from the Base64
* encoding.