package gnu.crypto.util; // ---------------------------------------------------------------------------- // $Id: Base64.java,v 1.4 2005/10/06 04:24:18 rsdio 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., // 51 Franklin Street, Fifth Floor, // Boston, MA 02110-1301 // 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; import java.io.UnsupportedEncodingException; /** * 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.4 $ */ 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 * <code>encode(source, 0, source.length)</code>. * * @param src the data to convert. */ public static final String encode(final byte[] src) { return encode(src, 0, src.length, 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(final byte[] src, final int off, final int len, final boolean breakLines) { final int len43 = len * 4 / 3; final 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; final 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(final String s) throws UnsupportedEncodingException { final byte[] bytes; bytes = s.getBytes("US-ASCII"); 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 <code>src</code> contains an illegal * Base-64 character. */ public static byte[] decode(final byte[] src, final int off, final int len) { final int len34 = len * 3 / 4; final byte[] outBuff = new byte[len34]; // Upper limit on size of output int outBuffPosn = 0; final byte[] b4 = new byte[4]; int b4Posn = 0; int i; byte sbiCrop, sbiDecode; 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)"); } } final byte[] result = new byte[outBuffPosn]; System.arraycopy(outBuff, 0, result, 0, outBuffPosn); return result; } /** * <p>Encodes up to three bytes of the array <code>src</code> and writes * the resulting four Base64 bytes to <code>dest</code>. The source and * destination arrays can be manipulated anywhere along their length by * specifying <code>sOffset</code> and <code>dOffset</code>.</p> * * <p>This method does not check to make sure the arrays are large enough to * accomodate <code>sOffset + 3</code> for the <code>src</code> array or * <code>dOffset + 4</code> for the <code>dest</code> array. The actual * number of significant bytes in the input array is given by * <code>numBytes</code>.</p> * * @param src the array to convert. * @param sOffset the index where conversion begins. * @param numBytes the number of significant bytes in your array. * @param dest the array to hold the conversion. * @param dOffset the index where output will be put. * @return the <code>destination</code> array. */ private static final byte[] encode3to4(final byte[] src, final int sOffset, final int numBytes, final byte[] dest, final 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. final 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; } /** * <p>Decodes four bytes from array <code>src</code> and writes the * resulting bytes (up to three of them) to <code>dest</code>.</p> * * <p>The source and destination arrays can be manipulated anywhere along * their length by specifying <code>sOffset</code> and <code>dOffset</code>. * </p> * * <p>This method does not check to make sure your arrays are large enough * to accomodate <code>sOffset + 4</code> for the <code>src</code> array or * <code>dOffset + 3</code> for the <code>dest</code> array. This method * returns the actual number of bytes that were converted from the Base64 * encoding.</p> * * @param src the array to convert. * @param sOffset the index where conversion begins. * @param dest the array to hold the conversion. * @param dOffset the index where output will be put. * @return the number of decoded bytes converted. */ private static final int decode4to3(final byte[] src, final int sOffset, final byte[] dest, final int dOffset) { if (src[sOffset + 2] == EQUALS_SIGN) { // Example: Dk== final int outBuff = ((DECODABET[src[sOffset ]] & 0xFF) << 18) | ((DECODABET[src[sOffset + 1]] & 0xFF) << 12); dest[dOffset] = (byte)(outBuff >>> 16); return 1; } if (src[sOffset + 3] == EQUALS_SIGN) { // Example: DkL= final int outBuff = ((DECODABET[src[sOffset ]] & 0xFF) << 18) | ((DECODABET[src[sOffset + 1]] & 0xFF) << 12) | ((DECODABET[src[sOffset + 2]] & 0xFF) << 6); dest[dOffset ] = (byte)(outBuff >>> 16); dest[dOffset + 1] = (byte)(outBuff >>> 8); return 2; } try { // Example: DkLE final int outBuff = ((DECODABET[src[sOffset ]] & 0xFF) << 18) | ((DECODABET[src[sOffset + 1]] & 0xFF) << 12) | ((DECODABET[src[sOffset + 2]] & 0xFF) << 6) | ((DECODABET[src[sOffset + 3]] & 0xFF) ); dest[dOffset ] = (byte)(outBuff >> 16); dest[dOffset + 1] = (byte)(outBuff >> 8); dest[dOffset + 2] = (byte) outBuff; return 3; } catch (Exception x) { if (DEBUG && debuglevel > 8) { debug(""+src[sOffset ]+": "+(DECODABET[src[sOffset ]])); debug(""+src[sOffset+1]+": "+(DECODABET[src[sOffset + 1]])); debug(""+src[sOffset+2]+": "+(DECODABET[src[sOffset + 2]])); debug(""+src[sOffset+3]+": "+(DECODABET[src[sOffset + 3]])); } return -1; } } }