Java/File Input Output/Base64 Stream — различия между версиями

Материал из Java эксперт
Перейти к: навигация, поиск
 
м (1 версия)
 
(нет различий)

Текущая версия на 06:02, 1 июня 2010

Apply a ASCII Hex encoding to the stream

    
/*
 * 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.
 */
/* $Id: ASCIIHexOutputStream.java 426584 2006-07-28 16:01:47Z jeremias $ */

import java.io.OutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
/**
 * This class applies a ASCII Hex encoding to the stream.
 *
 * @version $Id: ASCIIHexOutputStream.java 426584 2006-07-28 16:01:47Z jeremias $
 */
public class ASCIIHexOutputStream extends FilterOutputStream
        {
    private static final int EOL   = 0x0A; //"\n"
    private static final int EOD   = 0x3E; //">"
    private static final int ZERO  = 0x30; //"0"
    private static final int NINE  = 0x39; //"9"
    private static final int A     = 0x41; //"A"
    private static final int ADIFF = A - NINE - 1;
    private int posinline = 0;

    /** @see java.io.FilterOutputStream **/
    public ASCIIHexOutputStream(OutputStream out) {
        super(out);
    }

    /** @see java.io.FilterOutputStream **/
    public void write(int b) throws IOException {
        b &= 0xFF;
        int digit1 = ((b & 0xF0) >> 4) + ZERO;
        if (digit1 > NINE) {
            digit1 += ADIFF;
        }
        out.write(digit1);
        int digit2 = (b & 0x0F) + ZERO;
        if (digit2 > NINE) {
            digit2 += ADIFF;
        }
        out.write(digit2);
        posinline++;
        checkLineWrap();
    }

    private void checkLineWrap() throws IOException {
        //Maximum line length is 80 characters
        if (posinline >= 40) {
            out.write(EOL);
            posinline = 0;
        }
    }

    /** @see Finalizable **/
    public void finalizeStream() throws IOException {
        checkLineWrap();
        //Write closing character ">"
        super.write(EOD);
        flush();
   
    }

    /** @see java.io.FilterOutputStream **/
    public void close() throws IOException {
        finalizeStream();
        super.close();
    }

}





Base64 Character decoder as specified in RFC1113.

   

import java.io.IOException;
import java.io.InputStream;
/**
 * This class implements a Base64 Character decoder as specified in RFC1113.
 * Unlike some other encoding schemes there is nothing in this encoding that
 * tells the decoder where a buffer starts or stops, so to use it you will need
 * to isolate your encoded data into a single chunk and then feed them
 * this decoder. The simplest way to do that is to read all of the encoded
 * data into a string and then use:
 * <pre>
 *      byte    data[];
 *      InputStream is = new ByteArrayInputStream(data);
 *      is = new Base64DecodeStream(is);
 * </pre>
 *
 * On errors, this class throws a IOException with the following detail
 * strings:
 * <pre>
 *    "Base64DecodeStream: Bad Padding byte (2)."
 *    "Base64DecodeStream: Bad Padding byte (1)."
 * </pre>
 *
 * @author 
 * @author      Chuck McManis
 * @version $Id: Base64DecodeStream.java 501495 2007-01-30 18:00:36Z dvholten $
 */
public class Base64DecodeStream extends InputStream {
    InputStream src;
    public Base64DecodeStream(InputStream src) {
        this.src = src;
    }
    private static final byte[] pem_array = new byte[256];
    static {
        for (int i=0; i<pem_array.length; i++)
            pem_array[i] = -1;
        int idx = 0;
        for (char c="A"; c<="Z"; c++) {
            pem_array[c] = (byte)idx++;
        }
        for (char c="a"; c<="z"; c++) {
            pem_array[c] = (byte)idx++;
        }
        for (char c="0"; c<="9"; c++) {
            pem_array[c] = (byte)idx++;
        }
        pem_array["+"] = (byte)idx++;
        pem_array["/"] = (byte)idx++;
    }
    public boolean markSupported() { return false; }
    public void close()
        throws IOException {
        EOF = true;
    }
    public int available()
        throws IOException {
        return 3-out_offset;
    }
    byte[] decode_buffer = new byte[4];
    byte[] out_buffer = new byte[3];
    int  out_offset = 3;
    boolean EOF = false;
    public int read() throws IOException {
        if (out_offset == 3) {
            if (EOF || getNextAtom()) {
                EOF = true;
                return -1;
            }
        }
        return ((int)out_buffer[out_offset++])&0xFF;
    }
    public int read(byte []out, int offset, int len)
        throws IOException {
        int idx = 0;
        while (idx < len) {
            if (out_offset == 3) {
                if (EOF || getNextAtom()) {
                    EOF = true;
                    if (idx == 0) return -1;
                    else          return idx;
                }
            }
            out[offset+idx] = out_buffer[out_offset++];
            idx++;
        }
        return idx;
    }
    final boolean getNextAtom() throws IOException {
        int count, a, b, c, d;
        int off = 0;
        while(off != 4) {
            count = src.read(decode_buffer, off, 4-off);
            if (count == -1)
                return true;
            int in=off, out=off;
            while(in < off+count) {
                if ((decode_buffer[in] != "\n") &&
                    (decode_buffer[in] != "\r") &&
                    (decode_buffer[in] != " "))
                    decode_buffer[out++] = decode_buffer[in];
                in++;
            }
            off = out;
        }
        a = pem_array[((int)decode_buffer[0])&0xFF];
        b = pem_array[((int)decode_buffer[1])&0xFF];
        c = pem_array[((int)decode_buffer[2])&0xFF];
        d = pem_array[((int)decode_buffer[3])&0xFF];
        out_buffer[0] = (byte)((a<<2) | (b>>>4));
        out_buffer[1] = (byte)((b<<4) | (c>>>2));
        out_buffer[2] = (byte)((c<<6) |  d     );
        if (decode_buffer[3] != "=") {
            // All three bytes are good.
            out_offset=0;
        } else if (decode_buffer[2] == "=") {
            // Only one byte of output.
            out_buffer[2] = out_buffer[0];
            out_offset = 2;
            EOF=true;
        } else {
            // Only two bytes of output.
            out_buffer[2] = out_buffer[1];
            out_buffer[1] = out_buffer[0];
            out_offset = 1;
            EOF=true;
        }
        return false;
    }
}





Base64 Character encoder as specified in RFC1113.

   
/*
   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.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
/**
 * This class implements a Base64 Character encoder as specified in RFC1113.
 * Unlike some other encoding schemes there is nothing in this encoding
 * that indicates where a buffer starts or ends.
 *
 * This means that the encoded text will simply start with the first line
 * of encoded text and end with the last line of encoded text.
 *
 * @author 
 * @author      Chuck McManis
 * @version $Id: Base64EncoderStream.java 501495 2007-01-30 18:00:36Z dvholten $
 */
public class Base64EncoderStream extends OutputStream {
    /** This array maps the 6 bit values to their characters */
    private static final byte[] pem_array = {
    //   0   1   2   3   4   5   6   7
        "A","B","C","D","E","F","G","H", // 0
        "I","J","K","L","M","N","O","P", // 1
        "Q","R","S","T","U","V","W","X", // 2
        "Y","Z","a","b","c","d","e","f", // 3
        "g","h","i","j","k","l","m","n", // 4
        "o","p","q","r","s","t","u","v", // 5
        "w","x","y","z","0","1","2","3", // 6
        "4","5","6","7","8","9","+","/"  // 7
    };
    byte [] atom = new byte[3];
    int     atomLen = 0;
    byte [] encodeBuf = new byte[4];
    int     lineLen = 0;
    PrintStream  out;
    boolean closeOutOnClose;
    public Base64EncoderStream(OutputStream out) {
        this.out = new PrintStream(out);
        closeOutOnClose = true;
    }
    public Base64EncoderStream(OutputStream out, boolean closeOutOnClose) {
        this.out = new PrintStream(out);
        this.closeOutOnClose = closeOutOnClose;
    }
    public void close () throws IOException {
        if (out != null) {
            encodeAtom();
            out.flush();
            if (closeOutOnClose)
                out.close();
            out=null;
        }
    }
    /**
     * This can"t really flush out output since that may generate
     * "=" chars which would indicate the end of the stream.
     * Instead we flush out.  You can only be sure all output is
     * writen by closing this stream.
     */
    public void flush() throws IOException {
        out.flush();
    }
    public void write(int b) throws IOException {
        atom[atomLen++] = (byte)b;
        if (atomLen == 3)
            encodeAtom();
    }
    public void write(byte []data) throws IOException {
        encodeFromArray(data, 0, data.length);
    }
    public void write(byte [] data, int off, int len) throws IOException {
        encodeFromArray(data, off, len);
    }
    /**
     * enocodeAtom - Take three bytes of input and encode it as 4
     * printable characters. Note that if the length in len is less
     * than three is encodes either one or two "=" signs to indicate
     * padding characters.
     */
    void encodeAtom() throws IOException {
        byte a, b, c;
        switch (atomLen) {
        case 0: return;
        case 1:
            a = atom[0];
            encodeBuf[0] = pem_array[((a >>> 2) & 0x3F)];
            encodeBuf[1] = pem_array[((a <<  4) & 0x30)];
            encodeBuf[2] = encodeBuf[3] = "=";
            break;
        case 2:
            a = atom[0];
            b = atom[1];
            encodeBuf[0] = pem_array[((a >>> 2) & 0x3F)];
            encodeBuf[1] = pem_array[(((a << 4) & 0x30) | ((b >>> 4) & 0x0F))];
            encodeBuf[2] = pem_array[((b  << 2) & 0x3C)];
            encodeBuf[3] = "=";
            break;
        default:
            a = atom[0];
            b = atom[1];
            c = atom[2];
            encodeBuf[0] = pem_array[((a >>> 2) & 0x3F)];
            encodeBuf[1] = pem_array[(((a << 4) & 0x30) | ((b >>> 4) & 0x0F))];
            encodeBuf[2] = pem_array[(((b << 2) & 0x3C) | ((c >>> 6) & 0x03))];
            encodeBuf[3] = pem_array[c & 0x3F];
        }
        if (lineLen == 64) {
            out.println();
            lineLen = 0;
        }
        out.write(encodeBuf);
        lineLen += 4;
        atomLen = 0;
    }
    /**
     * enocodeAtom - Take three bytes of input and encode it as 4
     * printable characters. Note that if the length in len is less
     * than three is encodes either one or two "=" signs to indicate
     * padding characters.
     */
    void encodeFromArray(byte[] data, int offset, int len)
        throws IOException{
        byte a, b, c;
        if (len == 0)
            return;
        // System.out.println("atomLen: " + atomLen +
        //                    " len: " + len +
        //                    " offset:  " + offset);
        if (atomLen != 0) {
            switch(atomLen) {
            case 1:
                atom[1] = data[offset++]; len--; atomLen++;
                if (len == 0) return;
                atom[2] = data[offset++]; len--; atomLen++;
                break;
            case 2:
                atom[2] = data[offset++]; len--; atomLen++;
                break;
            default:
            }
            encodeAtom();
        }
        while (len >=3) {
            a = data[offset++];
            b = data[offset++];
            c = data[offset++];
            encodeBuf[0] = pem_array[((a >>> 2) & 0x3F)];
            encodeBuf[1] = pem_array[(((a << 4) & 0x30) | ((b >>> 4) & 0x0F))];
            encodeBuf[2] = pem_array[(((b << 2) & 0x3C) | ((c >>> 6) & 0x03))];
            encodeBuf[3] = pem_array[c & 0x3F];
            out.write(encodeBuf);
            lineLen += 4;
            if (lineLen == 64) {
                out.println();
                lineLen = 0;
            }
            len -=3;
        }
        switch (len) {
        case 1:
            atom[0] = data[offset];
            break;
        case 2:
            atom[0] = data[offset];
            atom[1] = data[offset+1];
            break;
        default:
        }
        atomLen = len;
    }

}





Base64 Codec

    
/**
 * Created Aug 28, 2006
 */
/*
 Copyright 2007 Robert C. Ilardi
 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.
 */

import java.io.ByteArrayOutputStream;
import java.util.HashMap;
/**
 * @author Robert C. Ilardi
 *
 */
public class Base64Codec {
  //NOTE: "=" is NOT a digit, it is a special terminator value. It has the index of 64 (the 65th element) for fast conversions...
  public static final char[] DIGITS = { "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", "+", "/", "=" };
  public static final HashMap<Character, Integer> DIGITS_MAP = new HashMap<Character, Integer>();
  static {
    //Init HashMap of Base 64 Digits to Base 10 Integers
    DIGITS_MAP.put("A", 0);
    DIGITS_MAP.put("B", 1);
    DIGITS_MAP.put("C", 2);
    DIGITS_MAP.put("D", 3);
    DIGITS_MAP.put("E", 4);
    DIGITS_MAP.put("F", 5);
    DIGITS_MAP.put("G", 6);
    DIGITS_MAP.put("H", 7);
    DIGITS_MAP.put("I", 8);
    DIGITS_MAP.put("J", 9);
    DIGITS_MAP.put("K", 10);
    DIGITS_MAP.put("L", 11);
    DIGITS_MAP.put("M", 12);
    DIGITS_MAP.put("N", 13);
    DIGITS_MAP.put("O", 14);
    DIGITS_MAP.put("P", 15);
    DIGITS_MAP.put("Q", 16);
    DIGITS_MAP.put("R", 17);
    DIGITS_MAP.put("S", 18);
    DIGITS_MAP.put("T", 19);
    DIGITS_MAP.put("U", 20);
    DIGITS_MAP.put("V", 21);
    DIGITS_MAP.put("W", 22);
    DIGITS_MAP.put("X", 23);
    DIGITS_MAP.put("Y", 24);
    DIGITS_MAP.put("Z", 25);
    DIGITS_MAP.put("a", 26);
    DIGITS_MAP.put("b", 27);
    DIGITS_MAP.put("c", 28);
    DIGITS_MAP.put("d", 29);
    DIGITS_MAP.put("e", 30);
    DIGITS_MAP.put("f", 31);
    DIGITS_MAP.put("g", 32);
    DIGITS_MAP.put("h", 33);
    DIGITS_MAP.put("i", 34);
    DIGITS_MAP.put("j", 35);
    DIGITS_MAP.put("k", 36);
    DIGITS_MAP.put("l", 37);
    DIGITS_MAP.put("m", 38);
    DIGITS_MAP.put("n", 39);
    DIGITS_MAP.put("o", 40);
    DIGITS_MAP.put("p", 41);
    DIGITS_MAP.put("q", 42);
    DIGITS_MAP.put("r", 43);
    DIGITS_MAP.put("s", 44);
    DIGITS_MAP.put("t", 45);
    DIGITS_MAP.put("u", 46);
    DIGITS_MAP.put("v", 47);
    DIGITS_MAP.put("w", 48);
    DIGITS_MAP.put("x", 49);
    DIGITS_MAP.put("y", 50);
    DIGITS_MAP.put("z", 51);
    DIGITS_MAP.put("0", 52);
    DIGITS_MAP.put("1", 53);
    DIGITS_MAP.put("2", 54);
    DIGITS_MAP.put("3", 55);
    DIGITS_MAP.put("4", 56);
    DIGITS_MAP.put("5", 57);
    DIGITS_MAP.put("6", 58);
    DIGITS_MAP.put("7", 59);
    DIGITS_MAP.put("8", 60);
    DIGITS_MAP.put("9", 61);
    DIGITS_MAP.put("+", 62);
    DIGITS_MAP.put("/", 63);
    DIGITS_MAP.put("=", -1);
  }
  public static final int PADDING_INDEX = 64;
  //Start Encoding Code Block--------------------------------------------------------------------------->
  public static String Encode(byte[] data) {
    return Encode(data, true);
  }
  /**
   * 
   * Base 64 Encoding works off of a 24 bit buffer.
   * It basically converts 3 bytes into 4, since normal
   * ASCII binary data is really base 256, we are stepping down
   * to base 64, therefore the number of "digits" for a single
   * number increases.
   * 
   */
  public static String Encode(byte[] data, boolean newlines) {
    StringBuffer b64Data = new StringBuffer();
    int ascii, buf, padding, cnt;
    int[] b64Set;
    String b64Num;
    if (data != null) {
      cnt = 0; //MUST be set to 0 NOT 1 because the first iteration of the for i loop executes cnt++ before appending to the string buffer
      for (int i = 0; i < data.length; i += 3) {
        buf = 0;
        padding = 2;
        //First byte (will eventually become left most 8 bits in the 24-bit buffer)
        ascii = data[i] & 0xFF;
        buf = ascii;
        buf = buf << 8;
        //Second byte (will eventually become middle 8 bits in the 24-bit buffer) 
        if ((i + 1) < data.length) {
          ascii = data[i + 1] & 0xFF;
          buf = buf + ascii;
          padding--;
        }
        buf = buf << 8;
        //Third byte (will eventually become the right most 8 bits in the 24-bit buffer)
        if ((i + 2) < data.length) {
          ascii = data[i + 2] & 0xFF;
          buf = buf + ascii;
          padding--;
        }
        b64Set = ConvertToBase64Set(buf, padding); //Obtain Digit Indexes 0-63 normally, with a special 64th index for terminator value
        b64Num = ConvertToBase64Number(b64Set); //Get actual string reprentation of the 3 bytes as a base 64 number (4 bytes).
        //RFC 1421 and RFC 2045 requires a CR+LF newline every 76 characters OR ever 19 sets of 4 bytes base 64 numbers.
        if (cnt == 19) {
          cnt = 1; //Because the first iteration of the for i loop the ELSE block is executed instead, we set to 1 instead of 0.
          if (newlines) {
            b64Data.append("\r\n"); //CR+LF
          }
        }
        else {
          cnt++;
        }
        b64Data.append(b64Num);
      } //End for i loop through data byte array
      //Trailing CR+LF newline if required
      if (cnt == 19 && b64Data.charAt(b64Data.length() - 1) != "=") {
        b64Data.append("\r\n"); //CR+LF
      }
    } //End data != null check
    return b64Data.toString();
  }
  /**
   * Does the actual base 64 conversion math 
   */
  private static int[] ConvertToBase64Set(int b10Num, int padding) {
    int[] b64Set = new int[4];
    int dividend, quotient, remainder, cnt;
    final int divisor = 64;
    cnt = 3;
    dividend = b10Num;
    do {
      quotient = dividend / divisor;
      remainder = dividend % divisor;
      dividend = quotient;
      b64Set[cnt] = remainder;
      cnt--;
    } while (quotient > 0);
    for (int i = 0; i < padding; i++) {
      b64Set[3 - i] = PADDING_INDEX;
    }
    return b64Set;
  }
  /**
   * Simply does a table lookup for the correct digits
   */
  private static String ConvertToBase64Number(int[] b64Set) {
    StringBuffer b64Num = new StringBuffer();
    for (int i = 0; i < b64Set.length; i++) {
      b64Num.append(DIGITS[b64Set[i]]);
    }
    return b64Num.toString();
  }
  //Start Decoding Code Block--------------------------------------------------------------------------->
  public static byte[] Decode(String b64Data) {
    return Decode(b64Data, true);
  }
  public static byte[] Decode(String b64Data, boolean newlines) {
    ByteArrayOutputStream baos = null;
    byte[] data = null, tmp;
    int b64Val, buf, b1, b2, b3, cnt, padding;
    if (b64Data != null && b64Data.length() > 0) {
      cnt = 0;
      tmp = new byte[3];
      baos = new ByteArrayOutputStream();
      for (int i = 0; i < b64Data.length(); i += 4) {
        buf = 0;
        padding = 0;
        b64Val = DIGITS_MAP.get(b64Data.charAt(i));
        buf = b64Val * ((int) Math.pow(64, 3));
        b64Val = DIGITS_MAP.get(b64Data.charAt(i + 1));
        buf += b64Val * ((int) Math.pow(64, 2));
        b64Val = DIGITS_MAP.get(b64Data.charAt(i + 2));
        if (b64Val != -1) {
          buf += b64Val * ((int) Math.pow(64, 1));
          b64Val = DIGITS_MAP.get(b64Data.charAt(i + 3));
          if (b64Val != -1) {
            buf += b64Val; //same as b64Val * ((int) Math.pow(64, 0))
          } //End second -1 check
          else {
            padding = 1;
          }
        } //End first -1 check
        else {
          padding = 2;
        }
        //First Byte
        b1 = buf >> 16;
        tmp[0] = (byte) b1;
        //Second Byte
        if (padding != 2) {
          b2 = buf - (b1 << 16);
          b2 = b2 >> 8;
          tmp[1] = (byte) b2;
          //Third Byte
          if (padding == 0) {
            b3 = buf - ((b1 << 16) + (b2 << 8));
            tmp[2] = (byte) b3;
          } //End padding == 0 check
        } //End padding != 2 check
        baos.write(tmp, 0, 3 - padding);
        //RFC 1421 and RFC 2045 requires a CR+LF newline every 76 characters base 64 numbers.
        cnt += 4;
        if (cnt == 76) {
          cnt = 0;
          if (newlines) {
            i += 2; //Advance i to skip the CR+LF newline
          }
        }
      } //End for i loop through b64Data String
      data = baos.toByteArray();
    } //End b64Data null and length check
    return data;
  }
}





BASE64 Decoder Stream

   
/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the "License").  You may not use this file except 
 * in compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * glassfish/bootstrap/legal/CDDLv1.0.txt or 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html. 
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * HEADER in each file and include the License file at 
 * glassfish/bootstrap/legal/CDDLv1.0.txt.  If applicable, 
 * add the following below this CDDL HEADER, with the 
 * fields enclosed by brackets "[]" replaced with your 
 * own identifying information: Portions Copyright [yyyy] 
 * [name of copyright owner]
 */
/*
 * @(#)BASE64DecoderStream.java 1.15 06/09/25
 *
 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
 */

import java.io.*;
/**
 * This class implements a BASE64 Decoder. It is implemented as
 * a FilterInputStream, so one can just wrap this class around
 * any input stream and read bytes from this filter. The decoding
 * is done as the bytes are read out.
 * 
 * @author John Mani
 * @author Bill Shannon
 */
public class BASE64DecoderStream extends FilterInputStream {
    // buffer of decoded bytes for single byte reads
    private byte[] buffer = new byte[3];
    private int bufsize = 0;  // size of the cache
    private int index = 0;  // index into the cache
    // buffer for almost 8K of typical 76 chars + CRLF lines,
    // used by getByte method.  this buffer contains encoded bytes.
    private byte[] input_buffer = new byte[78*105];
    private int input_pos = 0;
    private int input_len = 0;;
    private boolean ignoreErrors = false;
    /** 
     * Create a BASE64 decoder that decodes the specified input stream.
     * The System property <code>mail.mime.base64.ignoreerrors</code>
     * controls whether errors in the encoded data cause an exception
     * or are ignored.  The default is false (errors cause exception).
     *
     * @param in  the input stream
     */
    public BASE64DecoderStream(InputStream in) {
  super(in);
  try {
      String s = System.getProperty("mail.mime.base64.ignoreerrors");
      // default to false
      ignoreErrors = s != null && !s.equalsIgnoreCase("false");
  } catch (SecurityException sex) {
      // ignore it
  }
    }
    /** 
     * Create a BASE64 decoder that decodes the specified input stream.
     *
     * @param in  the input stream
     * @param ignoreErrors  ignore errors in encoded data?
     */
    public BASE64DecoderStream(InputStream in, boolean ignoreErrors) {
  super(in);
  this.ignoreErrors = ignoreErrors;
    }
    /**
     * Read the next decoded byte from this input stream. The byte
     * is returned as an <code>int</code> in the range <code>0</code> 
     * to <code>255</code>. If no byte is available because the end of 
     * the stream has been reached, the value <code>-1</code> is returned.
     * This method blocks until input data is available, the end of the 
     * stream is detected, or an exception is thrown.
     *
     * @return     next byte of data, or <code>-1</code> if the end of the
     *             stream is reached.
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.FilterInputStream#in
     */
    public int read() throws IOException {
  if (index >= bufsize) {
      bufsize = decode(buffer, 0, buffer.length);
      if (bufsize <= 0) // buffer is empty
    return -1;
      index = 0; // reset index into buffer
  }
  return buffer[index++] & 0xff; // Zero off the MSB
    }
    /**
     * Reads up to <code>len</code> decoded bytes of data from this input stream
     * into an array of bytes. This method blocks until some input is
     * available.
     * <p>
     *
     * @param      buf   the buffer into which the data is read.
     * @param      off   the start offset of the data.
     * @param      len   the maximum number of bytes read.
     * @return     the total number of bytes read into the buffer, or
     *             <code>-1</code> if there is no more data because the end of
     *             the stream has been reached.
     * @exception  IOException  if an I/O error occurs.
     */
    public int read(byte[] buf, int off, int len) throws IOException {
  // empty out single byte read buffer
  int off0 = off;
  while (index < bufsize && len > 0) {
      buf[off++] = buffer[index++];
      len--;
  }
  if (index >= bufsize)
      bufsize = index = 0;
  int bsize = (len / 3) * 3;  // round down to multiple of 3 bytes
  if (bsize > 0) {
      int size = decode(buf, off, bsize);
      off += size;
      len -= size;
      if (size != bsize) {  // hit EOF?
    if (off == off0)  // haven"t returned any data
        return -1;
    else      // returned some data before hitting EOF
        return off - off0;
      }
  }
  // finish up with a partial read if necessary
  for (; len > 0; len--) {
      int c = read();
      if (c == -1)  // EOF
    break;
      buf[off++] = (byte)c;
  }
  if (off == off0)  // haven"t returned any data
      return -1;
  else      // returned some data before hitting EOF
      return off - off0;
    }
    /**
     * Tests if this input stream supports marks. Currently this class
     * does not support marks
     */
    public boolean markSupported() {
  return false; // Maybe later ..
    }
    /**
     * Returns the number of bytes that can be read from this input
     * stream without blocking. However, this figure is only
     * a close approximation in case the original encoded stream
     * contains embedded CRLFs; since the CRLFs are discarded, not decoded
     */ 
    public int available() throws IOException {
   // This is only an estimate, since in.available()
   // might include CRLFs too ..
   return ((in.available() * 3)/4 + (bufsize-index));
    }
    /**
     * This character array provides the character to value map
     * based on RFC1521.
     */  
    private final static char pem_array[] = {
  "A","B","C","D","E","F","G","H", // 0
  "I","J","K","L","M","N","O","P", // 1
  "Q","R","S","T","U","V","W","X", // 2
  "Y","Z","a","b","c","d","e","f", // 3
  "g","h","i","j","k","l","m","n", // 4
  "o","p","q","r","s","t","u","v", // 5
  "w","x","y","z","0","1","2","3", // 6
  "4","5","6","7","8","9","+","/"  // 7
    };
    private final static byte pem_convert_array[] = new byte[256];
    static {
  for (int i = 0; i < 255; i++)
      pem_convert_array[i] = -1;
  for (int i = 0; i < pem_array.length; i++)
      pem_convert_array[pem_array[i]] = (byte)i;
    }
    /**
     * The decoder algorithm.  Most of the complexity here is dealing
     * with error cases.  Returns the number of bytes decoded, which
     * may be zero.  Decoding is done by filling an int with 4 6-bit
     * values by shifting them in from the bottom and then extracting
     * 3 8-bit bytes from the int by shifting them out from the bottom.
     *
     * @param outbuf  the buffer into which to put the decoded bytes
     * @param pos position in the buffer to start filling
     * @param len the number of bytes to fill
     * @return    the number of bytes filled, always a multiple
     *      of three, and may be zero
     * @exception IOException if the data is incorrectly formatted
     */
    private int decode(byte[] outbuf, int pos, int len) throws IOException {
  int pos0 = pos;
  while (len >= 3) {
      /*
       * We need 4 valid base64 characters before we start decoding.
       * We skip anything that"s not a valid base64 character (usually
       * just CRLF).
       */
      int got = 0;
      int val = 0;
      while (got < 4) {
    int i = getByte();
    if (i == -1 || i == -2) {
        boolean atEOF;
        if (i == -1) {
      if (got == 0)
          return pos - pos0;
      if (!ignoreErrors)
          throw new IOException("Error in encoded stream: " +
        "needed 4 valid base64 characters " +
        "but only got " + got + " before EOF" +
        recentChars());
      atEOF = true; // don"t read any more
        } else {  // i == -2
      // found a padding character, we"re at EOF
      // XXX - should do something to make EOF "sticky"
      if (got < 2 && !ignoreErrors)
          throw new IOException("Error in encoded stream: " +
        "needed at least 2 valid base64 characters," +
        " but only got " + got +
        " before padding character (=)" +
        recentChars());
      // didn"t get any characters before padding character?
      if (got == 0)
          return pos - pos0;
      atEOF = false;  // need to keep reading
        }
        // pad partial result with zeroes
        // how many bytes will we produce on output?
        // (got always < 4, so size always < 3)
        int size = got - 1;
        if (size == 0)
      size = 1;
        // handle the one padding character we"ve seen
        got++;
        val <<= 6;
        while (got < 4) {
      if (!atEOF) {
          // consume the rest of the padding characters,
          // filling with zeroes
          i = getByte();
          if (i == -1) {
        if (!ignoreErrors)
            throw new IOException(
          "Error in encoded stream: " +
          "hit EOF while looking for " +
          "padding characters (=)" +
          recentChars());
          } else if (i != -2) {
        if (!ignoreErrors)
            throw new IOException(
          "Error in encoded stream: " +
          "found valid base64 character after " +
          "a padding character (=)" +
          recentChars());
          }
      }
      val <<= 6;
      got++;
        }
        // now pull out however many valid bytes we got
        val >>= 8;    // always skip first one
        if (size == 2)
      outbuf[pos + 1] = (byte)(val & 0xff);
        val >>= 8;
        outbuf[pos] = (byte)(val & 0xff);
        // len -= size; // not needed, return below
        pos += size;
        return pos - pos0;
    } else {
        // got a valid byte
        val <<= 6;
        got++;
        val |= i;
    }
      }
      // read 4 valid characters, now extract 3 bytes
      outbuf[pos + 2] = (byte)(val & 0xff);
      val >>= 8;
      outbuf[pos + 1] = (byte)(val & 0xff);
      val >>= 8;
      outbuf[pos] = (byte)(val & 0xff);
      len -= 3;
      pos += 3;
  }
  return pos - pos0;
    }
    /**
     * Read the next valid byte from the input stream.
     * Buffer lots of data from underlying stream in input_buffer,
     * for efficiency.
     *
     * @return  the next byte, -1 on EOF, or -2 if next byte is "="
     *    (padding at end of encoded data)
     */
    private int getByte() throws IOException {
  int c;
  do {
      if (input_pos >= input_len) {
    try {
        input_len = in.read(input_buffer);
    } catch (EOFException ex) {
        return -1;
    }
    if (input_len <= 0)
        return -1;
    input_pos = 0;
      }
      // get the next byte in the buffer
      c = input_buffer[input_pos++] & 0xff;
      // is it a padding byte?
      if (c == "=")
    return -2;
      // no, convert it
      c = pem_convert_array[c];
      // loop until we get a legitimate byte
  } while (c == -1);
  return c;
    }
    /**
     * Return the most recent characters, for use in an error message.
     */
    private String recentChars() {
  // reach into the input buffer and extract up to 10
  // recent characters, to help in debugging.
  String errstr = "";
  int nc = input_pos > 10 ? 10 : input_pos;
  if (nc > 0) {
      errstr += ", the " + nc +
          " most recent characters were: \"";
      for (int k = input_pos - nc; k < input_pos; k++) {
    char c = (char)(input_buffer[k] & 0xff);
    switch (c) {
    case "\r":  errstr += "\\r"; break;
    case "\n":  errstr += "\\n"; break;
    case "\t":  errstr += "\\t"; break;
    default:
        if (c >= " " && c < 0177)
      errstr += c;
        else
      errstr += ("\\" + (int)c);
    }
      }
      errstr += "\"";
  }
  return errstr;
    }
    /**
     * Base64 decode a byte array.  No line breaks are allowed.
     * This method is suitable for short strings, such as those
     * in the IMAP AUTHENTICATE protocol, but not to decode the
     * entire content of a MIME part.
     *
     * NOTE: inbuf may only contain valid base64 characters.
     *       Whitespace is not ignored.
     */
    public static byte[] decode(byte[] inbuf) {
  int size = (inbuf.length / 4) * 3;
  if (size == 0)
      return inbuf;
  if (inbuf[inbuf.length - 1] == "=") {
      size--;
      if (inbuf[inbuf.length - 2] == "=")
    size--;
  }
  byte[] outbuf = new byte[size];
  int inpos = 0, outpos = 0;
  size = inbuf.length;
  while (size > 0) {
      int val;
      int osize = 3;
      val = pem_convert_array[inbuf[inpos++] & 0xff];
      val <<= 6;
      val |= pem_convert_array[inbuf[inpos++] & 0xff];
      val <<= 6;
      if (inbuf[inpos] != "=") // End of this BASE64 encoding
    val |= pem_convert_array[inbuf[inpos++] & 0xff];
      else
    osize--;
      val <<= 6;
      if (inbuf[inpos] != "=") // End of this BASE64 encoding
    val |= pem_convert_array[inbuf[inpos++] & 0xff];
      else
    osize--;
      if (osize > 2)
    outbuf[outpos + 2] = (byte)(val & 0xff);
      val >>= 8;
      if (osize > 1)
    outbuf[outpos + 1] = (byte)(val & 0xff);
      val >>= 8;
      outbuf[outpos] = (byte)(val & 0xff);
      outpos += osize;
      size -= 4;
  }
  return outbuf;
    }
    /*** begin TEST program ***
    public static void main(String argv[]) throws Exception {
      FileInputStream infile = new FileInputStream(argv[0]);
  BASE64DecoderStream decoder = new BASE64DecoderStream(infile);
  int c;
  while ((c = decoder.read()) != -1)
      System.out.print((char)c);
  System.out.flush();
    }
    *** end TEST program ***/
}





BASE64 Decoder Stream from Sun Microsystems

   
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 *
 * Contributor(s):
 *
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don"t indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
/*
 * @(#)BASE64DecoderStream.java 1.16 07/05/04
 */
import java.io.*;
/**
 * This class implements a BASE64 Decoder. It is implemented as
 * a FilterInputStream, so one can just wrap this class around
 * any input stream and read bytes from this filter. The decoding
 * is done as the bytes are read out.
 * 
 * @author John Mani
 * @author Bill Shannon
 */
public class BASE64DecoderStream extends FilterInputStream {
    // buffer of decoded bytes for single byte reads
    private byte[] buffer = new byte[3];
    private int bufsize = 0;  // size of the cache
    private int index = 0;  // index into the cache
    // buffer for almost 8K of typical 76 chars + CRLF lines,
    // used by getByte method.  this buffer contains encoded bytes.
    private byte[] input_buffer = new byte[78*105];
    private int input_pos = 0;
    private int input_len = 0;;
    private boolean ignoreErrors = false;
    /** 
     * Create a BASE64 decoder that decodes the specified input stream.
     * The System property <code>mail.mime.base64.ignoreerrors</code>
     * controls whether errors in the encoded data cause an exception
     * or are ignored.  The default is false (errors cause exception).
     *
     * @param in  the input stream
     */
    public BASE64DecoderStream(InputStream in) {
  super(in);
  try {
      String s = System.getProperty("mail.mime.base64.ignoreerrors");
      // default to false
      ignoreErrors = s != null && !s.equalsIgnoreCase("false");
  } catch (SecurityException sex) {
      // ignore it
  }
    }
    /** 
     * Create a BASE64 decoder that decodes the specified input stream.
     *
     * @param in  the input stream
     * @param ignoreErrors  ignore errors in encoded data?
     */
    public BASE64DecoderStream(InputStream in, boolean ignoreErrors) {
  super(in);
  this.ignoreErrors = ignoreErrors;
    }
    /**
     * Read the next decoded byte from this input stream. The byte
     * is returned as an <code>int</code> in the range <code>0</code> 
     * to <code>255</code>. If no byte is available because the end of 
     * the stream has been reached, the value <code>-1</code> is returned.
     * This method blocks until input data is available, the end of the 
     * stream is detected, or an exception is thrown.
     *
     * @return     next byte of data, or <code>-1</code> if the end of the
     *             stream is reached.
     * @exception  IOException  if an I/O error occurs.
     * @see        java.io.FilterInputStream#in
     */
    public int read() throws IOException {
  if (index >= bufsize) {
      bufsize = decode(buffer, 0, buffer.length);
      if (bufsize <= 0) // buffer is empty
    return -1;
      index = 0; // reset index into buffer
  }
  return buffer[index++] & 0xff; // Zero off the MSB
    }
    /**
     * Reads up to <code>len</code> decoded bytes of data from this input stream
     * into an array of bytes. This method blocks until some input is
     * available.
     * <p>
     *
     * @param      buf   the buffer into which the data is read.
     * @param      off   the start offset of the data.
     * @param      len   the maximum number of bytes read.
     * @return     the total number of bytes read into the buffer, or
     *             <code>-1</code> if there is no more data because the end of
     *             the stream has been reached.
     * @exception  IOException  if an I/O error occurs.
     */
    public int read(byte[] buf, int off, int len) throws IOException {
  // empty out single byte read buffer
  int off0 = off;
  while (index < bufsize && len > 0) {
      buf[off++] = buffer[index++];
      len--;
  }
  if (index >= bufsize)
      bufsize = index = 0;
  int bsize = (len / 3) * 3;  // round down to multiple of 3 bytes
  if (bsize > 0) {
      int size = decode(buf, off, bsize);
      off += size;
      len -= size;
      if (size != bsize) {  // hit EOF?
    if (off == off0)  // haven"t returned any data
        return -1;
    else      // returned some data before hitting EOF
        return off - off0;
      }
  }
  // finish up with a partial read if necessary
  for (; len > 0; len--) {
      int c = read();
      if (c == -1)  // EOF
    break;
      buf[off++] = (byte)c;
  }
  if (off == off0)  // haven"t returned any data
      return -1;
  else      // returned some data before hitting EOF
      return off - off0;
    }
    /**
     * Tests if this input stream supports marks. Currently this class
     * does not support marks
     */
    public boolean markSupported() {
  return false; // Maybe later ..
    }
    /**
     * Returns the number of bytes that can be read from this input
     * stream without blocking. However, this figure is only
     * a close approximation in case the original encoded stream
     * contains embedded CRLFs; since the CRLFs are discarded, not decoded
     */ 
    public int available() throws IOException {
   // This is only an estimate, since in.available()
   // might include CRLFs too ..
   return ((in.available() * 3)/4 + (bufsize-index));
    }
    /**
     * This character array provides the character to value map
     * based on RFC1521.
     */  
    private final static char pem_array[] = {
  "A","B","C","D","E","F","G","H", // 0
  "I","J","K","L","M","N","O","P", // 1
  "Q","R","S","T","U","V","W","X", // 2
  "Y","Z","a","b","c","d","e","f", // 3
  "g","h","i","j","k","l","m","n", // 4
  "o","p","q","r","s","t","u","v", // 5
  "w","x","y","z","0","1","2","3", // 6
  "4","5","6","7","8","9","+","/"  // 7
    };
    private final static byte pem_convert_array[] = new byte[256];
    static {
  for (int i = 0; i < 255; i++)
      pem_convert_array[i] = -1;
  for (int i = 0; i < pem_array.length; i++)
      pem_convert_array[pem_array[i]] = (byte)i;
    }
    /**
     * The decoder algorithm.  Most of the complexity here is dealing
     * with error cases.  Returns the number of bytes decoded, which
     * may be zero.  Decoding is done by filling an int with 4 6-bit
     * values by shifting them in from the bottom and then extracting
     * 3 8-bit bytes from the int by shifting them out from the bottom.
     *
     * @param outbuf  the buffer into which to put the decoded bytes
     * @param pos position in the buffer to start filling
     * @param len the number of bytes to fill
     * @return    the number of bytes filled, always a multiple
     *      of three, and may be zero
     * @exception IOException if the data is incorrectly formatted
     */
    private int decode(byte[] outbuf, int pos, int len) throws IOException {
  int pos0 = pos;
  while (len >= 3) {
      /*
       * We need 4 valid base64 characters before we start decoding.
       * We skip anything that"s not a valid base64 character (usually
       * just CRLF).
       */
      int got = 0;
      int val = 0;
      while (got < 4) {
    int i = getByte();
    if (i == -1 || i == -2) {
        boolean atEOF;
        if (i == -1) {
      if (got == 0)
          return pos - pos0;
      if (!ignoreErrors)
          throw new IOException("Error in encoded stream: " +
        "needed 4 valid base64 characters " +
        "but only got " + got + " before EOF" +
        recentChars());
      atEOF = true; // don"t read any more
        } else {  // i == -2
      // found a padding character, we"re at EOF
      // XXX - should do something to make EOF "sticky"
      if (got < 2 && !ignoreErrors)
          throw new IOException("Error in encoded stream: " +
        "needed at least 2 valid base64 characters," +
        " but only got " + got +
        " before padding character (=)" +
        recentChars());
      // didn"t get any characters before padding character?
      if (got == 0)
          return pos - pos0;
      atEOF = false;  // need to keep reading
        }
        // pad partial result with zeroes
        // how many bytes will we produce on output?
        // (got always < 4, so size always < 3)
        int size = got - 1;
        if (size == 0)
      size = 1;
        // handle the one padding character we"ve seen
        got++;
        val <<= 6;
        while (got < 4) {
      if (!atEOF) {
          // consume the rest of the padding characters,
          // filling with zeroes
          i = getByte();
          if (i == -1) {
        if (!ignoreErrors)
            throw new IOException(
          "Error in encoded stream: " +
          "hit EOF while looking for " +
          "padding characters (=)" +
          recentChars());
          } else if (i != -2) {
        if (!ignoreErrors)
            throw new IOException(
          "Error in encoded stream: " +
          "found valid base64 character after " +
          "a padding character (=)" +
          recentChars());
          }
      }
      val <<= 6;
      got++;
        }
        // now pull out however many valid bytes we got
        val >>= 8;    // always skip first one
        if (size == 2)
      outbuf[pos + 1] = (byte)(val & 0xff);
        val >>= 8;
        outbuf[pos] = (byte)(val & 0xff);
        // len -= size; // not needed, return below
        pos += size;
        return pos - pos0;
    } else {
        // got a valid byte
        val <<= 6;
        got++;
        val |= i;
    }
      }
      // read 4 valid characters, now extract 3 bytes
      outbuf[pos + 2] = (byte)(val & 0xff);
      val >>= 8;
      outbuf[pos + 1] = (byte)(val & 0xff);
      val >>= 8;
      outbuf[pos] = (byte)(val & 0xff);
      len -= 3;
      pos += 3;
  }
  return pos - pos0;
    }
    /**
     * Read the next valid byte from the input stream.
     * Buffer lots of data from underlying stream in input_buffer,
     * for efficiency.
     *
     * @return  the next byte, -1 on EOF, or -2 if next byte is "="
     *    (padding at end of encoded data)
     */
    private int getByte() throws IOException {
  int c;
  do {
      if (input_pos >= input_len) {
    try {
        input_len = in.read(input_buffer);
    } catch (EOFException ex) {
        return -1;
    }
    if (input_len <= 0)
        return -1;
    input_pos = 0;
      }
      // get the next byte in the buffer
      c = input_buffer[input_pos++] & 0xff;
      // is it a padding byte?
      if (c == "=")
    return -2;
      // no, convert it
      c = pem_convert_array[c];
      // loop until we get a legitimate byte
  } while (c == -1);
  return c;
    }
    /**
     * Return the most recent characters, for use in an error message.
     */
    private String recentChars() {
  // reach into the input buffer and extract up to 10
  // recent characters, to help in debugging.
  String errstr = "";
  int nc = input_pos > 10 ? 10 : input_pos;
  if (nc > 0) {
      errstr += ", the " + nc +
          " most recent characters were: \"";
      for (int k = input_pos - nc; k < input_pos; k++) {
    char c = (char)(input_buffer[k] & 0xff);
    switch (c) {
    case "\r":  errstr += "\\r"; break;
    case "\n":  errstr += "\\n"; break;
    case "\t":  errstr += "\\t"; break;
    default:
        if (c >= " " && c < 0177)
      errstr += c;
        else
      errstr += ("\\" + (int)c);
    }
      }
      errstr += "\"";
  }
  return errstr;
    }
    /**
     * Base64 decode a byte array.  No line breaks are allowed.
     * This method is suitable for short strings, such as those
     * in the IMAP AUTHENTICATE protocol, but not to decode the
     * entire content of a MIME part.
     *
     * NOTE: inbuf may only contain valid base64 characters.
     *       Whitespace is not ignored.
     */
    public static byte[] decode(byte[] inbuf) {
  int size = (inbuf.length / 4) * 3;
  if (size == 0)
      return inbuf;
  if (inbuf[inbuf.length - 1] == "=") {
      size--;
      if (inbuf[inbuf.length - 2] == "=")
    size--;
  }
  byte[] outbuf = new byte[size];
  int inpos = 0, outpos = 0;
  size = inbuf.length;
  while (size > 0) {
      int val;
      int osize = 3;
      val = pem_convert_array[inbuf[inpos++] & 0xff];
      val <<= 6;
      val |= pem_convert_array[inbuf[inpos++] & 0xff];
      val <<= 6;
      if (inbuf[inpos] != "=") // End of this BASE64 encoding
    val |= pem_convert_array[inbuf[inpos++] & 0xff];
      else
    osize--;
      val <<= 6;
      if (inbuf[inpos] != "=") // End of this BASE64 encoding
    val |= pem_convert_array[inbuf[inpos++] & 0xff];
      else
    osize--;
      if (osize > 2)
    outbuf[outpos + 2] = (byte)(val & 0xff);
      val >>= 8;
      if (osize > 1)
    outbuf[outpos + 1] = (byte)(val & 0xff);
      val >>= 8;
      outbuf[outpos] = (byte)(val & 0xff);
      outpos += osize;
      size -= 4;
  }
  return outbuf;
    }
    /*** begin TEST program ***
    public static void main(String argv[]) throws Exception {
      FileInputStream infile = new FileInputStream(argv[0]);
  BASE64DecoderStream decoder = new BASE64DecoderStream(infile);
  int c;
  while ((c = decoder.read()) != -1)
      System.out.print((char)c);
  System.out.flush();
    }
    *** end TEST program ***/
}





Base64 - encode/decode data using the Base64 encoding scheme

    
//////////////////////license & copyright header/////////////////////////
//                                                                     //
//    Base64 - encode/decode data using the Base64 encoding scheme     //
//                                                                     //
//                Copyright (c) 1998 by Kevin Kelley                   //
//                                                                     //
// 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., 59 Temple Place - Suite 330, Boston, MA           //
// 02111-1307, USA, or contact the author:                             //
//                                                                     //
// Kevin Kelley <kelley@ruralnet.net> - 30718 Rd. 28, La Junta, CO,    //
// 81050  USA.                                                         //
//                                                                     //
////////////////////end license & copyright header///////////////////////

import java.io.*;       // needed only for main() method.

/**
 * Provides encoding of raw bytes to base64-encoded characters, and
 * decoding of base64 characters to raw bytes.
 *
 * @author Kevin Kelley (kelley@ruralnet.net)
 * @version 1.3
 */
public class 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.
    */
   static public char[] encode(byte[] data)
   {
      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 & (int) data[i]);
         val <<= 8;
         if ((i+1) < data.length) {
            val |= (0xFF & (int) data[i+1]);
            trip = true;
         }
         val <<= 8;
         if ((i+2) < data.length) {
            val |= (0xFF & (int) 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;
   }
   public static String encodeToString(byte[] data) {
      return new String(encode(data));
   }
   /**
    * 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.
    **/
   static public byte[] decode(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;
      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++)
      {
         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;
   }
   public static byte[] decode(String data) {
      return decode(data.toCharArray());
   }
   //
   // code characters for values 0..63
   //
   static private char[] alphabet =
   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
   .toCharArray();
   //
   // lookup table for converting base64 characters to value in range 0..63
   //
   static private 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.
   ///////////////////////////////////////////////////
   public static void main(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;
      }
      String filename = args[args.length-1];
      File file = new File(filename);
      if (!file.exists()) {
         System.out.println("Error:  file "" + filename + "" doesn"t exist!");
         System.exit(0);
      }
      if (decode)
      {
         char[] encoded = readChars(file);
         byte[] decoded = decode(encoded);
         writeBytes(file, decoded);
      }
      else
      {
         byte[] decoded = readBytes(file);
         char[] encoded = encode(decoded);
         writeChars(file, encoded);
      }
   }
   private static byte[] readBytes(File file)
   {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      try
      {
         InputStream fis = new FileInputStream(file);
         InputStream is = new BufferedInputStream(fis);
         int count = 0;
         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(File file)
   {
      CharArrayWriter caw = new CharArrayWriter();
      try
      {
         Reader fr = new FileReader(file);
         Reader in = new BufferedReader(fr);
         int count = 0;
         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(File file, byte[] data) {
      try {
         OutputStream fos = new FileOutputStream(file);
         OutputStream os = new BufferedOutputStream(fos);
         os.write(data);
         os.close();
      }
      catch (Exception e) { e.printStackTrace(); }
   }
   private static void writeChars(File file, char[] data) {
      try {
         Writer fos = new FileWriter(file);
         Writer os = new BufferedWriter(fos);
         os.write(data);
         os.close();
      }
      catch (Exception e) { e.printStackTrace(); }
   }
   ///////////////////////////////////////////////////
   // end of test code.
   ///////////////////////////////////////////////////
}





BASE64 Encoder Stream

   
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 *
 * Contributor(s):
 *
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don"t indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
/*
 * @(#)BASE64EncoderStream.java 1.10 07/05/04
 */

import java.io.*;
/**
 * This class implements a BASE64 encoder.  It is implemented as
 * a FilterOutputStream, so one can just wrap this class around
 * any output stream and write bytes into this filter.  The encoding
 * is done as the bytes are written out.
 * 
 * @author John Mani
 * @author Bill Shannon
 */
public class BASE64EncoderStream extends FilterOutputStream {
    private byte[] buffer;  // cache of bytes that are yet to be encoded
    private int bufsize = 0;  // size of the cache
    private byte[] outbuf;  // line size output buffer
    private int count = 0;  // number of bytes that have been output
    private int bytesPerLine; // number of bytes per line
    private int lineLimit;  // number of input bytes to output bytesPerLine
    private boolean noCRLF = false;
    private static byte[] newline = new byte[] { "\r", "\n" };
    /**
     * Create a BASE64 encoder that encodes the specified output stream.
     *
     * @param out        the output stream
     * @param bytesPerLine  number of bytes per line. The encoder inserts
     *      a CRLF sequence after the specified number of bytes,
     *      unless bytesPerLine is Integer.MAX_VALUE, in which
     *      case no CRLF is inserted.  bytesPerLine is rounded
     *      down to a multiple of 4.
     */
    public BASE64EncoderStream(OutputStream out, int bytesPerLine) {
  super(out);
  buffer = new byte[3];
  if (bytesPerLine == Integer.MAX_VALUE || bytesPerLine < 4) {
      noCRLF = true;
      bytesPerLine = 76;
  }
  bytesPerLine = (bytesPerLine / 4) * 4;  // Rounded down to 4n
  this.bytesPerLine = bytesPerLine; // save it
        lineLimit = bytesPerLine / 4 * 3;
  if (noCRLF) {
      outbuf = new byte[bytesPerLine];
  } else {
      outbuf = new byte[bytesPerLine + 2];
      outbuf[bytesPerLine] = (byte)"\r";
      outbuf[bytesPerLine + 1] = (byte)"\n";
  }
    }
    /**
     * Create a BASE64 encoder that encodes the specified input stream.
     * Inserts the CRLF sequence after outputting 76 bytes.
     *
     * @param out        the output stream
     */
    public BASE64EncoderStream(OutputStream out) {
  this(out, 76);  
    }
    /**
     * Encodes <code>len</code> bytes from the specified
     * <code>byte</code> array starting at offset <code>off</code> to
     * this output stream.
     *
     * @param      b     the data.
     * @param      off   the start offset in the data.
     * @param      len   the number of bytes to write.
     * @exception  IOException  if an I/O error occurs.
     */
    public synchronized void write(byte[] b, int off, int len)
        throws IOException {
  int end = off + len;
  // finish off incomplete coding unit
  while (bufsize != 0 && off < end)
      write(b[off++]);
  // finish off line
  int blen = ((bytesPerLine - count) / 4) * 3;
  if (off + blen < end) {
      // number of bytes that will be produced in outbuf
      int outlen = encodedSize(blen);
      if (!noCRLF) {
    outbuf[outlen++] = (byte)"\r";
    outbuf[outlen++] = (byte)"\n";
      }
      out.write(encode(b, off, blen, outbuf), 0, outlen);
      off += blen;
      count = 0;
  }
  // do bulk encoding a line at a time.
  for (; off + lineLimit < end; off += lineLimit)
      out.write(encode(b, off, lineLimit, outbuf));
  // handle remaining partial line
  if (off + 3 < end) {
      blen = end - off;
      blen = (blen / 3) * 3;  // round down
      // number of bytes that will be produced in outbuf
      int outlen = encodedSize(blen);
      out.write(encode(b, off, blen, outbuf), 0, outlen);
      off += blen;
      count += outlen;
  }
  // start next coding unit
  for (; off < end; off++)
      write(b[off]);
    }
    /**
     * Encodes <code>b.length</code> bytes to this output stream.
     *
     * @param      b   the data to be written.
     * @exception  IOException  if an I/O error occurs.
     */
    public void write(byte[] b) throws IOException {
  write(b, 0, b.length);
    }
    /**
     * Encodes the specified <code>byte</code> to this output stream.
     *
     * @param      c   the <code>byte</code>.
     * @exception  IOException  if an I/O error occurs.
     */
    public synchronized void write(int c) throws IOException {
  buffer[bufsize++] = (byte)c;
  if (bufsize == 3) { // Encoding unit = 3 bytes
      encode();
      bufsize = 0;
  }
    }
    /**
     * Flushes this output stream and forces any buffered output bytes
     * to be encoded out to the stream. 
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public synchronized void flush() throws IOException {
  if (bufsize > 0) { // If there"s unencoded characters in the buffer ..
      encode();      // .. encode them
      bufsize = 0;
  }
  out.flush();
    }
    /**
     * Forces any buffered output bytes to be encoded out to the stream
     * and closes this output stream
     */
    public synchronized void close() throws IOException {
  flush();
  if (count > 0 && !noCRLF) {
      out.write(newline);
      out.flush();
  }
  out.close();
    }
    /** This array maps the characters to their 6 bit values */
    private final static char pem_array[] = {
  "A","B","C","D","E","F","G","H", // 0
  "I","J","K","L","M","N","O","P", // 1
  "Q","R","S","T","U","V","W","X", // 2
  "Y","Z","a","b","c","d","e","f", // 3
  "g","h","i","j","k","l","m","n", // 4
  "o","p","q","r","s","t","u","v", // 5
  "w","x","y","z","0","1","2","3", // 6
  "4","5","6","7","8","9","+","/"  // 7
    };
    /**
     * Encode the data stored in <code>buffer</code>.
     * Uses <code>outbuf</code> to store the encoded
     * data before writing.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    private void encode() throws IOException {
  int osize = encodedSize(bufsize);
  out.write(encode(buffer, 0, bufsize, outbuf), 0, osize);
  // increment count
  count += osize;
  // If writing out this encoded unit caused overflow,
  // start a new line.
  if (count >= bytesPerLine) {
      if (!noCRLF)
    out.write(newline);
      count = 0;
  }
    }
    /**
     * Base64 encode a byte array.  No line breaks are inserted.
     * This method is suitable for short strings, such as those
     * in the IMAP AUTHENTICATE protocol, but not to encode the
     * entire content of a MIME part.
     */
    public static byte[] encode(byte[] inbuf) {
  if (inbuf.length == 0)
      return inbuf;
  return encode(inbuf, 0, inbuf.length, null);
    }
    /**
     * Internal use only version of encode.  Allow specifying which
     * part of the input buffer to encode.  If outbuf is non-null,
     * it"s used as is.  Otherwise, a new output buffer is allocated.
     */
    private static byte[] encode(byte[] inbuf, int off, int size,
        byte[] outbuf) {
  if (outbuf == null)
      outbuf = new byte[encodedSize(size)];
  int inpos, outpos;
  int val;
  for (inpos = off, outpos = 0; size >= 3; size -= 3, outpos += 4) {
      val = inbuf[inpos++] & 0xff;
      val <<= 8;
      val |= inbuf[inpos++] & 0xff;
      val <<= 8;
      val |= inbuf[inpos++] & 0xff;
      outbuf[outpos+3] = (byte)pem_array[val & 0x3f];
      val >>= 6;
      outbuf[outpos+2] = (byte)pem_array[val & 0x3f];
      val >>= 6;
      outbuf[outpos+1] = (byte)pem_array[val & 0x3f];
      val >>= 6;
      outbuf[outpos+0] = (byte)pem_array[val & 0x3f];
  }
  // done with groups of three, finish up any odd bytes left
  if (size == 1) {
      val = inbuf[inpos++] & 0xff;
      val <<= 4;
      outbuf[outpos+3] = (byte)"="; // pad character;
      outbuf[outpos+2] = (byte)"="; // pad character;
      outbuf[outpos+1] = (byte)pem_array[val & 0x3f];
      val >>= 6;
      outbuf[outpos+0] = (byte)pem_array[val & 0x3f];
  } else if (size == 2) {
      val = inbuf[inpos++] & 0xff;
      val <<= 8;
      val |= inbuf[inpos++] & 0xff;
      val <<= 2;
      outbuf[outpos+3] = (byte)"="; // pad character;
      outbuf[outpos+2] = (byte)pem_array[val & 0x3f];
      val >>= 6;
      outbuf[outpos+1] = (byte)pem_array[val & 0x3f];
      val >>= 6;
      outbuf[outpos+0] = (byte)pem_array[val & 0x3f];
  }
  return outbuf;
    }
    /**
     * Return the corresponding encoded size for the given number
     * of bytes, not including any CRLF.
     */
    private static int encodedSize(int size) {
  return ((size + 2) / 3) * 4;
    }
    /*** begin TEST program
    public static void main(String argv[]) throws Exception {
  FileInputStream infile = new FileInputStream(argv[0]);
  BASE64EncoderStream encoder = new BASE64EncoderStream(System.out);
  int c;
  while ((c = infile.read()) != -1)
      encoder.write(c);
  encoder.close();
    }
    *** end TEST program **/
}





BASE64 Encoder Stream from Sun Microsystems

   
/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the "License").  You may not use this file except 
 * in compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * glassfish/bootstrap/legal/CDDLv1.0.txt or 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html. 
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * HEADER in each file and include the License file at 
 * glassfish/bootstrap/legal/CDDLv1.0.txt.  If applicable, 
 * add the following below this CDDL HEADER, with the 
 * fields enclosed by brackets "[]" replaced with your 
 * own identifying information: Portions Copyright [yyyy] 
 * [name of copyright owner]
 */
/*
 * @(#)BASE64EncoderStream.java 1.9 06/06/05
 *
 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
 */

import java.io.*;
/**
 * This class implements a BASE64 encoder.  It is implemented as
 * a FilterOutputStream, so one can just wrap this class around
 * any output stream and write bytes into this filter.  The encoding
 * is done as the bytes are written out.
 * 
 * @author John Mani
 * @author Bill Shannon
 */
public class BASE64EncoderStream extends FilterOutputStream {
    private byte[] buffer;  // cache of bytes that are yet to be encoded
    private int bufsize = 0;  // size of the cache
    private byte[] outbuf;  // line size output buffer
    private int count = 0;  // number of bytes that have been output
    private int bytesPerLine; // number of bytes per line
    private int lineLimit;  // number of input bytes to output bytesPerLine
    private boolean noCRLF = false;
    private static byte[] newline = new byte[] { "\r", "\n" };
    /**
     * Create a BASE64 encoder that encodes the specified output stream.
     *
     * @param out        the output stream
     * @param bytesPerLine  number of bytes per line. The encoder inserts
     *      a CRLF sequence after the specified number of bytes,
     *      unless bytesPerLine is Integer.MAX_VALUE, in which
     *      case no CRLF is inserted.  bytesPerLine is rounded
     *      down to a multiple of 4.
     */
    public BASE64EncoderStream(OutputStream out, int bytesPerLine) {
  super(out);
  buffer = new byte[3];
  if (bytesPerLine == Integer.MAX_VALUE || bytesPerLine < 4) {
      noCRLF = true;
      bytesPerLine = 76;
  }
  bytesPerLine = (bytesPerLine / 4) * 4;  // Rounded down to 4n
  this.bytesPerLine = bytesPerLine; // save it
        lineLimit = bytesPerLine / 4 * 3;
  if (noCRLF) {
      outbuf = new byte[bytesPerLine];
  } else {
      outbuf = new byte[bytesPerLine + 2];
      outbuf[bytesPerLine] = (byte)"\r";
      outbuf[bytesPerLine + 1] = (byte)"\n";
  }
    }
    /**
     * Create a BASE64 encoder that encodes the specified input stream.
     * Inserts the CRLF sequence after outputting 76 bytes.
     *
     * @param out        the output stream
     */
    public BASE64EncoderStream(OutputStream out) {
  this(out, 76);  
    }
    /**
     * Encodes <code>len</code> bytes from the specified
     * <code>byte</code> array starting at offset <code>off</code> to
     * this output stream.
     *
     * @param      b     the data.
     * @param      off   the start offset in the data.
     * @param      len   the number of bytes to write.
     * @exception  IOException  if an I/O error occurs.
     */
    public synchronized void write(byte[] b, int off, int len)
        throws IOException {
  int end = off + len;
  // finish off incomplete coding unit
  while (bufsize != 0 && off < end)
      write(b[off++]);
  // finish off line
  int blen = ((bytesPerLine - count) / 4) * 3;
  if (off + blen < end) {
      // number of bytes that will be produced in outbuf
      int outlen = encodedSize(blen);
      if (!noCRLF) {
    outbuf[outlen++] = (byte)"\r";
    outbuf[outlen++] = (byte)"\n";
      }
      out.write(encode(b, off, blen, outbuf), 0, outlen);
      off += blen;
      count = 0;
  }
  // do bulk encoding a line at a time.
  for (; off + lineLimit < end; off += lineLimit)
      out.write(encode(b, off, lineLimit, outbuf));
  // handle remaining partial line
  if (off + 3 < end) {
      blen = end - off;
      blen = (blen / 3) * 3;  // round down
      // number of bytes that will be produced in outbuf
      int outlen = encodedSize(blen);
      out.write(encode(b, off, blen, outbuf), 0, outlen);
      off += blen;
      count += outlen;
  }
  // start next coding unit
  for (; off < end; off++)
      write(b[off]);
    }
    /**
     * Encodes <code>b.length</code> bytes to this output stream.
     *
     * @param      b   the data to be written.
     * @exception  IOException  if an I/O error occurs.
     */
    public void write(byte[] b) throws IOException {
  write(b, 0, b.length);
    }
    /**
     * Encodes the specified <code>byte</code> to this output stream.
     *
     * @param      c   the <code>byte</code>.
     * @exception  IOException  if an I/O error occurs.
     */
    public synchronized void write(int c) throws IOException {
  buffer[bufsize++] = (byte)c;
  if (bufsize == 3) { // Encoding unit = 3 bytes
      encode();
      bufsize = 0;
  }
    }
    /**
     * Flushes this output stream and forces any buffered output bytes
     * to be encoded out to the stream. 
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public synchronized void flush() throws IOException {
  if (bufsize > 0) { // If there"s unencoded characters in the buffer ..
      encode();      // .. encode them
      bufsize = 0;
  }
  out.flush();
    }
    /**
     * Forces any buffered output bytes to be encoded out to the stream
     * and closes this output stream
     */
    public synchronized void close() throws IOException {
  flush();
  if (count > 0 && !noCRLF) {
      out.write(newline);
      out.flush();
  }
  out.close();
    }
    /** This array maps the characters to their 6 bit values */
    private final static char pem_array[] = {
  "A","B","C","D","E","F","G","H", // 0
  "I","J","K","L","M","N","O","P", // 1
  "Q","R","S","T","U","V","W","X", // 2
  "Y","Z","a","b","c","d","e","f", // 3
  "g","h","i","j","k","l","m","n", // 4
  "o","p","q","r","s","t","u","v", // 5
  "w","x","y","z","0","1","2","3", // 6
  "4","5","6","7","8","9","+","/"  // 7
    };
    /**
     * Encode the data stored in <code>buffer</code>.
     * Uses <code>outbuf</code> to store the encoded
     * data before writing.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    private void encode() throws IOException {
  int osize = encodedSize(bufsize);
  out.write(encode(buffer, 0, bufsize, outbuf), 0, osize);
  // increment count
  count += osize;
  // If writing out this encoded unit caused overflow,
  // start a new line.
  if (count >= bytesPerLine) {
      if (!noCRLF)
    out.write(newline);
      count = 0;
  }
    }
    /**
     * Base64 encode a byte array.  No line breaks are inserted.
     * This method is suitable for short strings, such as those
     * in the IMAP AUTHENTICATE protocol, but not to encode the
     * entire content of a MIME part.
     */
    public static byte[] encode(byte[] inbuf) {
  if (inbuf.length == 0)
      return inbuf;
  return encode(inbuf, 0, inbuf.length, null);
    }
    /**
     * Internal use only version of encode.  Allow specifying which
     * part of the input buffer to encode.  If outbuf is non-null,
     * it"s used as is.  Otherwise, a new output buffer is allocated.
     */
    private static byte[] encode(byte[] inbuf, int off, int size,
        byte[] outbuf) {
  if (outbuf == null)
      outbuf = new byte[encodedSize(size)];
  int inpos, outpos;
  int val;
  for (inpos = off, outpos = 0; size >= 3; size -= 3, outpos += 4) {
      val = inbuf[inpos++] & 0xff;
      val <<= 8;
      val |= inbuf[inpos++] & 0xff;
      val <<= 8;
      val |= inbuf[inpos++] & 0xff;
      outbuf[outpos+3] = (byte)pem_array[val & 0x3f];
      val >>= 6;
      outbuf[outpos+2] = (byte)pem_array[val & 0x3f];
      val >>= 6;
      outbuf[outpos+1] = (byte)pem_array[val & 0x3f];
      val >>= 6;
      outbuf[outpos+0] = (byte)pem_array[val & 0x3f];
  }
  // done with groups of three, finish up any odd bytes left
  if (size == 1) {
      val = inbuf[inpos++] & 0xff;
      val <<= 4;
      outbuf[outpos+3] = (byte)"="; // pad character;
      outbuf[outpos+2] = (byte)"="; // pad character;
      outbuf[outpos+1] = (byte)pem_array[val & 0x3f];
      val >>= 6;
      outbuf[outpos+0] = (byte)pem_array[val & 0x3f];
  } else if (size == 2) {
      val = inbuf[inpos++] & 0xff;
      val <<= 8;
      val |= inbuf[inpos++] & 0xff;
      val <<= 2;
      outbuf[outpos+3] = (byte)"="; // pad character;
      outbuf[outpos+2] = (byte)pem_array[val & 0x3f];
      val >>= 6;
      outbuf[outpos+1] = (byte)pem_array[val & 0x3f];
      val >>= 6;
      outbuf[outpos+0] = (byte)pem_array[val & 0x3f];
  }
  return outbuf;
    }
    /**
     * Return the corresponding encoded size for the given number
     * of bytes, not including any CRLF.
     */
    private static int encodedSize(int size) {
  return ((size + 2) / 3) * 4;
    }
    /*** begin TEST program
    public static void main(String argv[]) throws Exception {
  FileInputStream infile = new FileInputStream(argv[0]);
  BASE64EncoderStream encoder = new BASE64EncoderStream(System.out);
  int c;
  while ((c = infile.read()) != -1)
      encoder.write(c);
  encoder.close();
    }
    *** end TEST program **/
}





Base64 encoding from DbUnit.org

    
/*
 *
 * The DbUnit Database Testing Framework
 * Copyright (C)2002-2004, DbUnit.org
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
import java.io.FileInputStream;

/**
 * <p>
 * I am placing this code in the Public Domain. Do with it as you will.
 * This software comes with no guarantees or warranties but with
 * plenty of well-wishing instead!
 * Please visit 
 * periodically to check for updates or to contribute improvements.
 * </p>
 *
 * @author Robert Harder (rharder@usa.net)
 * @author Last changed by: $Author: gommma $
 * @version $Revision: 769 $ $Date: 2008-08-02 09:31:15 +0200 (sab, 02 ago 2008) $
 * @since 1.3
 */
public class Base64
{

    /** Specify encoding (value is <tt>true</tt>). */
    public final static boolean ENCODE = true;

    /** Specify decoding (value is <tt>false</tt>). */
    public final static boolean DECODE = false;

    /** Maximum line length (76) of Base64 output. */
    private final static int MAX_LINE_LENGTH = 76;

    /** The equals sign (=) as a byte. */
    private final static byte EQUALS_SIGN = (byte)"=";

    /** The new line character (\n) as a byte. */
    private final static byte NEW_LINE = (byte)"\n";

    /** The 64 valid Base64 values. */
    private final static 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 final static 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
                /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
                -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
                -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
                -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
                -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
                -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
                -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
                -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
                -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
                -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
            };
    private final static byte BAD_ENCODING = -9; // Indicates error in encoding
    private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
    private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding

    /** Defeats instantiation. */
    private Base64()
    {
    }

    /** Testing. */
    public static void main(String[] args)
    {
        String s = "Hello, world";
        s = "abcd";
        //s = System.getProperties().toString();
        //System.out.println( s + ": \n [" + encode( s ) + "]\n [" + decode(encode(s)) + "]" );
        byte[] b = encodeString(s).getBytes();
        byte[] c = decode(b, 0, b.length);
        System.out.println("\n\n" + s + ":" + new String(b) + ":" + new String(c));
        try
        {
            FileInputStream fis = new FileInputStream("c:\\abcd.txt");
            InputStream b64is = new InputStream(fis, DECODE);
            int ib = 0;
            while ((ib = b64is.read()) > 0)
            {   //System.out.print( new String( ""+(char)ib ) );
            }
        }   // end try
        catch (Exception e)
        {
           
            e.printStackTrace();
        }
    }

/* ********  E N C O D I N G   M E T H O D S  ******** */

    /**
     * Encodes the first three bytes of array <var>threeBytes</var>
     * and returns a four-byte array in Base64 notation.
     *
     * @param threeBytes the array to convert
     * @return four byte array in Base64 notation.
     * @since 1.3
     */
    private static byte[] encode3to4(byte[] threeBytes)
    {
      
        return encode3to4(threeBytes, 3);
    }   // end encodeToBytes

    /**
     * Encodes up to the first three bytes of array <var>threeBytes</var>
     * and returns a four-byte array in Base64 notation.
     * The actual number of significant bytes in your array is
     * given by <var>numSigBytes</var>.
     * The array <var>threeBytes</var> needs only be as big as
     * <var>numSigBytes</var>.
     *
     * @param threeBytes the array to convert
     * @param numSigBytes the number of significant bytes in your array
     * @return four byte array in Base64 notation.
     * @since 1.3
     */
    private static byte[] encode3to4(byte[] threeBytes, int numSigBytes)
    {
     
        byte[] dest = new byte[4];
        encode3to4(threeBytes, 0, numSigBytes, dest, 0);
        return dest;
    }

    /**
     * Encodes up to three bytes of the array <var>source</var>
     * and writes the resulting four Base64 bytes to <var>destination</var>.
     * The source and destination arrays can be manipulated
     * anywhere along their length by specifying
     * <var>srcOffset</var> and <var>destOffset</var>.
     * This method does not check to make sure your arrays
     * are large enough to accomodate <var>srcOffset</var> + 3 for
     * the <var>source</var> array or <var>destOffset</var> + 4 for
     * the <var>destination</var> array.
     * The actual number of significant bytes in your array is
     * given by <var>numSigBytes</var>.
     *
     * @param source the array to convert
     * @param srcOffset the index where conversion begins
     * @param numSigBytes the number of significant bytes in your array
     * @param destination the array to hold the conversion
     * @param destOffset the index where output will be put
     * @return the <var>destination</var> array
     * @since 1.3
     */
    private static byte[] encode3to4(
            byte[] source, int srcOffset, int numSigBytes,
            byte[] destination, int destOffset)
    {
        //           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 = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
                | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
                | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
        switch (numSigBytes)
        {
            case 3:
                destination[destOffset] = ALPHABET[(inBuff >>> 18)];
                destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
                destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
                destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
                return destination;
            case 2:
                destination[destOffset] = ALPHABET[(inBuff >>> 18)];
                destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
                destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
                destination[destOffset + 3] = EQUALS_SIGN;
                return destination;
            case 1:
                destination[destOffset] = ALPHABET[(inBuff >>> 18)];
                destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
                destination[destOffset + 2] = EQUALS_SIGN;
                destination[destOffset + 3] = EQUALS_SIGN;
                return destination;
            default:
                return destination;
        }   // end switch
    }   // end encode3to4

    /**
     * Serializes an object and returns the Base64-encoded
     * version of that serialized object. If the object
     * cannot be serialized or there is another error,
     * the method will return <tt>null</tt>.
     *
     * @param serializableObject The object to encode
     * @return The Base64-encoded object
     * @since 1.4
     */
    public static String encodeObject(java.io.Serializable serializableObject)
    {
     
        java.io.ByteArrayOutputStream baos = null;
        java.io.OutputStream b64os = null;
        java.io.ObjectOutputStream oos = null;
        try
        {
            baos = new java.io.ByteArrayOutputStream();
            b64os = new OutputStream(baos, Base64.ENCODE);
            oos = new java.io.ObjectOutputStream(b64os);
            oos.writeObject(serializableObject);
        }   // end try
        catch (java.io.IOException e)
        {
           
            e.printStackTrace();
            return null;
        }   // end catch
        finally
        {
            try
            {
                oos.close();
                b64os.close();
                baos.close();
            }
            catch (Exception e)
            {
            }
        }   // end finally
        return new String(baos.toByteArray());
    }   // end encode

    /**
     * Encodes a byte array into Base64 notation.
     * Equivalen to calling
     * <code>encodeBytes( source, 0, source.length )</code>
     *
     * @param source The data to convert
     * @since 1.4
     */
    public static String encodeBytes(byte[] source)
    {
        return encodeBytes(source, 0, source.length);
    }   // end encodeBytes

    /**
     * Encodes a byte array into Base64 notation.
     *
     * @param source The data to convert
     * @param off Offset in array where conversion should begin
     * @param len Length of data to convert
     * @since 1.4
     */
    public static String encodeBytes(byte[] source, int off, int len)
    {
        int len43 = len * 4 / 3;
        byte[] outBuff = new byte[(len43)                      // Main 4:3
                + ((len % 3) > 0 ? 4 : 0)      // Account for padding
                + (len43 / MAX_LINE_LENGTH)]; // New lines
        int d = 0;
        int e = 0;
        int len2 = len - 2;
        int lineLength = 0;
        for (; d < len2; d += 3, e += 4)
        {
            encode3to4(source, d, 3, outBuff, e);
            lineLength += 4;
            if (lineLength == MAX_LINE_LENGTH)
            {
                outBuff[e + 4] = NEW_LINE;
                e++;
                lineLength = 0;
            }   // end if: end of line
        }   // en dfor: each piece of array
        if (d < len)
        {
            encode3to4(source, d, len - d, outBuff, e);
            e += 4;
        }   // end if: some padding needed
        return new String(outBuff, 0, e);
    }   // end encodeBytes

    /**
     * Encodes a string in Base64 notation with line breaks
     * after every 75 Base64 characters.
     *
     * @param s the string to encode
     * @return the encoded string
     * @since 1.3
     */
    public static String encodeString(String s)
    {
        return encodeBytes(s.getBytes());
    }   // end encodeString


/* ********  D E C O D I N G   M E T H O D S  ******** */

    /**
     * Decodes the first four bytes of array <var>fourBytes</var>
     * and returns an array up to three bytes long with the
     * decoded values.
     *
     * @param fourBytes the array with Base64 content
     * @return array with decoded values
     * @since 1.3
     */
    private static byte[] decode4to3(byte[] fourBytes)
    {
        byte[] outBuff1 = new byte[3];
        int count = decode4to3(fourBytes, 0, outBuff1, 0);
        byte[] outBuff2 = new byte[count];
        for (int i = 0; i < count; i++)
            outBuff2[i] = outBuff1[i];
        return outBuff2;
    }

    /**
     * Decodes four bytes from array <var>source</var>
     * and writes the resulting bytes (up to three of them)
     * to <var>destination</var>.
     * The source and destination arrays can be manipulated
     * anywhere along their length by specifying
     * <var>srcOffset</var> and <var>destOffset</var>.
     * This method does not check to make sure your arrays
     * are large enough to accomodate <var>srcOffset</var> + 4 for
     * the <var>source</var> array or <var>destOffset</var> + 3 for
     * the <var>destination</var> array.
     * This method returns the actual number of bytes that
     * were converted from the Base64 encoding.
     *
     *
     * @param source the array to convert
     * @param srcOffset the index where conversion begins
     * @param destination the array to hold the conversion
     * @param destOffset the index where output will be put
     * @return the number of decoded bytes converted
     * @since 1.3
     */
    private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset)
    {
        // Example: Dk==
        if (source[srcOffset + 2] == EQUALS_SIGN)
        {
            int outBuff = ((DECODABET[source[srcOffset]] << 24) >>> 6)
                    | ((DECODABET[source[srcOffset + 1]] << 24) >>> 12);
            destination[destOffset] = (byte)(outBuff >>> 16);
            return 1;
        }
        // Example: DkL=
        else if (source[srcOffset + 3] == EQUALS_SIGN)
        {
            int outBuff = ((DECODABET[source[srcOffset]] << 24) >>> 6)
                    | ((DECODABET[source[srcOffset + 1]] << 24) >>> 12)
                    | ((DECODABET[source[srcOffset + 2]] << 24) >>> 18);
            destination[destOffset] = (byte)(outBuff >>> 16);
            destination[destOffset + 1] = (byte)(outBuff >>> 8);
            return 2;
        }
        // Example: DkLE
        else
        {
            int outBuff = ((DECODABET[source[srcOffset]] << 24) >>> 6)
                    | ((DECODABET[source[srcOffset + 1]] << 24) >>> 12)
                    | ((DECODABET[source[srcOffset + 2]] << 24) >>> 18)
                    | ((DECODABET[source[srcOffset + 3]] << 24) >>> 24);
            destination[destOffset] = (byte)(outBuff >> 16);
            destination[destOffset + 1] = (byte)(outBuff >> 8);
            destination[destOffset + 2] = (byte)(outBuff);
            return 3;
        }
    }   // end decodeToBytes

    /**
     * Decodes data from Base64 notation.
     *
     * @param s the string to decode
     * @return the decoded data
     * @since 1.4
     */
    public static byte[] decode(String s)
    {
        byte[] bytes = s.getBytes();
        return decode(bytes, 0, bytes.length);
    }   // end decode

    /**
     * Decodes data from Base64 notation and
     * returns it as a string.
     * Equivalent to calling
     * <code>new String( decode( s ) )</code>
     *
     * @param s the string to decode
     * @return The data as a string
     * @since 1.4
     */
    public static String decodeToString(String s)
    {
        return new String(decode(s));
    }   // end decodeToString

    /**
     * Attempts to decode Base64 data and deserialize a Java
     * Object within. Returns <tt>null</tt> if there was an error.
     *
     * @param encodedObject The Base64 data to decode
     * @return The decoded and deserialized object
     * @since 1.4
     */
    public static Object decodeToObject(String encodedObject)
    {
        byte[] objBytes = decode(encodedObject);
        java.io.ByteArrayInputStream bais = null;
        java.io.ObjectInputStream ois = null;
        try
        {
            bais = new java.io.ByteArrayInputStream(objBytes);
            ois = new java.io.ObjectInputStream(bais);
            return ois.readObject();
        }   // end try
        catch (java.io.IOException e)
        {
            e.printStackTrace();
            return null;
        }   // end catch
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
            return null;
        }   // end catch
        finally
        {
            try
            {
                bais.close();
                ois.close();
            }
            catch (Exception e)
            {
            }
        }   // end finally
    }   // end decodeObject

    /**
     * Decodes Base64 content in byte array format and returns
     * the decoded byte array.
     *
     * @param source The Base64 encoded data
     * @param off    The offset of where to begin decoding
     * @param len    The length of characters to decode
     * @return decoded data
     * @since 1.3
     */
    public static byte[] decode(byte[] source, 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 = 0; i < len; i++)
        {
            sbiCrop = (byte)(source[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
            }   // end if: white space, equals sign or better
            else
            {
                System.err.println("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
                return null;
            }   // end else:
        }   // each input character
        byte[] out = new byte[outBuffPosn];
        System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
        return out;
    }   // end decode


    /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */

    /**
     * A {@link Base64.InputStream} will read data from another
     * {@link java.io.InputStream}, given in the constructor,
     * and encode/decode to/from Base64 notation on the fly.
     *
     * @see Base64
     * @see java.io.FilterInputStream
     * @since 1.3
     */
    public static class InputStream extends java.io.FilterInputStream
    {
        private boolean encode;         // Encoding or decoding
        private int position;       // Current position in the buffer
        private byte[] buffer;         // Small buffer holding converted data
        private int bufferLength;   // Length of buffer (3 or 4)
        private int numSigBytes;    // Number of meaningful bytes in the buffer

        /**
         * Constructs a {@link Base64.InputStream} in DECODE mode.
         *
         * @param in the {@link java.io.InputStream} from which to read data.
         * @since 1.3
         */
        public InputStream(java.io.InputStream in)
        {
            this(in, Base64.DECODE);
        }   // end constructor

        /**
         * Constructs a {@link Base64.InputStream} in
         * either ENCODE or DECODE mode.
         *
         * @param in the {@link java.io.InputStream} from which to read data.
         * @param encode Conversion direction
         * @see Base64#ENCODE
         * @see Base64#DECODE
         * @since 1.3
         */
        public InputStream(java.io.InputStream in, boolean encode)
        {
            super(in);
            this.encode = encode;
            this.bufferLength = encode ? 4 : 3;
            this.buffer = new byte[bufferLength];
            this.position = -1;
        }   // end constructor
        /**
         * Reads enough of the input stream to convert
         * to/from Base64 and returns the next byte.
         *
         * @return next byte
         * @since 1.3
         */
        public int read() throws java.io.IOException
        {
            // Do we need to get data?
            if (position < 0)
            {
                if (encode)
                {
                    byte[] b3 = new byte[3];
                    numSigBytes = 0;
                    for (int i = 0; i < 3; i++)
                    {
                        try
                        {
                            int b = in.read();
                            // If end of stream, b is -1.
                            if (b >= 0)
                            {
                                b3[i] = (byte)b;
                                numSigBytes++;
                            }   // end if: not end of stream
                        }   // end try: read
                        catch (java.io.IOException e)
                        {
                            // Only a problem if we got no data at all.
                            if (i == 0)
                                throw e;
                        }   // end catch
                    }   // end for: each needed input byte
                    if (numSigBytes > 0)
                    {
                        encode3to4(b3, 0, numSigBytes, buffer, 0);
                        position = 0;
                    }   // end if: got data
                }   // end if: encoding
                // Else decoding
                else
                {
                    byte[] b4 = new byte[4];
                    int i = 0;
                    for (i = 0; i < 4; i++)
                    {
                        int b = 0;
                        do
                        {
                            b = in.read();
                        }
                        while (b >= 0 && DECODABET[b & 0x7f] < WHITE_SPACE_ENC);
                        if (b < 0)
                            break; // Reads a -1 if end of stream
                        b4[i] = (byte)b;
                    }   // end for: each needed input byte
                    if (i == 4)
                    {
                        numSigBytes = decode4to3(b4, 0, buffer, 0);
                        position = 0;
                    }   // end if: got four characters
                }   // end else: decode
            }   // end else: get data
            // Got data?
            if (position >= 0)
            {
                // End of relevant data?
                if (position >= numSigBytes)
                    return -1;
                int b = buffer[position++];
                if (position >= bufferLength)
                    position = -1;
                return b;
            }   // end if: position >= 0
            // Else error
            else
                return -1;
        }   // end read

        /**
         * Calls {@link #read} repeatedly until the end of stream
         * is reached or <var>len</var> bytes are read.
         * Returns number of bytes read into array or -1 if
         * end of stream is encountered.
         *
         * @param dest array to hold values
         * @param off offset for array
         * @param len max number of bytes to read into array
         * @return bytes read into array or -1 if end of stream is encountered.
         * @since 1.3
         */
        public int read(byte[] dest, int off, int len) throws java.io.IOException
        {
            int i;
            int b;
            for (i = 0; i < len; i++)
            {
                b = read();
                if (b < 0)
                    return -1;
                dest[off + i] = (byte)b;
            }   // end for: each byte read
            return i;
        }   // end read
    }   // end inner class InputStream



    /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */

    /**
     * A {@link Base64.OutputStream} will write data to another
     * {@link java.io.OutputStream}, given in the constructor,
     * and encode/decode to/from Base64 notation on the fly.
     *
     * @see Base64
     * @see java.io.FilterOutputStream
     * @since 1.3
     */
    public static class OutputStream extends java.io.FilterOutputStream
    {

        private boolean encode;
        private int position;
        private byte[] buffer;
        private int bufferLength;
        private int lineLength;

        /**
         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
         *
         * @param out the {@link java.io.OutputStream} to which data will be written.
         * @since 1.3
         */
        public OutputStream(java.io.OutputStream out)
        {
            this(out, Base64.ENCODE);
        }   // end constructor

        /**
         * Constructs a {@link Base64.OutputStream} in
         * either ENCODE or DECODE mode.
         *
         * @param out the {@link java.io.OutputStream} to which data will be written.
         * @param encode Conversion direction
         * @see Base64#ENCODE
         * @see Base64#DECODE
         * @since 1.3
         */
        public OutputStream(java.io.OutputStream out, boolean encode)
        {
            super(out);
            this.encode = encode;
            this.bufferLength = encode ? 3 : 4;
            this.buffer = new byte[bufferLength];
            this.position = 0;
            this.lineLength = 0;
        }   // end constructor

        /**
         * Writes the byte to the output stream after
         * converting to/from Base64 notation.
         * When encoding, bytes are buffered three
         * at a time before the output stream actually
         * gets a write() call.
         * When decoding, bytes are buffered four
         * at a time.
         *
         * @param theByte the byte to write
         * @since 1.3
         */
        public void write(int theByte) throws java.io.IOException
        {
            buffer[position++] = (byte)theByte;
            if (position >= bufferLength)
            {
                if (encode)
                {
                    out.write(Base64.encode3to4(buffer, bufferLength));
                    lineLength += 4;
                    if (lineLength >= MAX_LINE_LENGTH)
                    {
                        out.write(NEW_LINE);
                        lineLength = 0;
                    }   // end if: end o fline
                }   // end if: encoding
                else
                    out.write(Base64.decode4to3(buffer));
                position = 0;
            }   // end if: convert and flush
        }   // end write

        /**
         * Calls {@link #write} repeatedly until <var>len</var>
         * bytes are written.
         *
         * @param theBytes array from which to read bytes
         * @param off offset for array
         * @param len max number of bytes to read into array
         * @since 1.3
         */
        public void write(byte[] theBytes, int off, int len) throws java.io.IOException
        {
            for (int i = 0; i < len; i++)
            {
                write(theBytes[off + i]);
            }   // end for: each byte written
        }   // end write

        /**
         * Appropriately pads Base64 notation when encoding
         * or throws an exception if Base64 input is not
         * properly padded when decoding.
         *
         * @since 1.3
         */
        public void flush() throws java.io.IOException
        {
            if (position > 0)
            {
                if (encode)
                {
                    out.write(Base64.encode3to4(buffer, position));
                }   // end if: encoding
                else
                {
                    throw new java.io.IOException("Base64 input not properly padded.");
                }   // end else: decoding
            }   // end if: buffer partially full
            super.flush();
            out.flush();
        }   // end flush

        /**
         * Flushes and closes stream.
         *
         * @since 1.3
         */
        public void close() throws java.io.IOException
        {
            this.flush();
            super.close();
            out.close();
            buffer = null;
            out = null;
        }   // end close
    }   // end inner class OutputStream
}   // end class Base64





Base64 from Eric Glass jcifs at samba dot org

    
/* Encodes and decodes to and from Base64 notation.
 * Copyright (C) 2003 "Eric Glass" <jcifs at samba dot org>
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

public class Base64 {
    private static final String ALPHABET =
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    /**
     * Base-64 encodes the supplied block of data.  Line wrapping is not
     * applied on output.
     *
     * @param bytes The block of data that is to be Base-64 encoded.
     * @return A <code>String</code> containing the encoded data.
     */
    public static String encode(byte[] bytes) {
        int length = bytes.length;
        if (length == 0) return "";
        StringBuffer buffer =
                new StringBuffer((int) Math.ceil((double) length / 3d) * 4);
        int remainder = length % 3;
        length -= remainder;
        int block;
        int i = 0;
        while (i < length) {
            block = ((bytes[i++] & 0xff) << 16) | ((bytes[i++] & 0xff) << 8) |
                    (bytes[i++] & 0xff);
            buffer.append(ALPHABET.charAt(block >>> 18));
            buffer.append(ALPHABET.charAt((block >>> 12) & 0x3f));
            buffer.append(ALPHABET.charAt((block >>> 6) & 0x3f));
            buffer.append(ALPHABET.charAt(block & 0x3f));
        }
        if (remainder == 0) return buffer.toString();
        if (remainder == 1) {
            block = (bytes[i] & 0xff) << 4;
            buffer.append(ALPHABET.charAt(block >>> 6));
            buffer.append(ALPHABET.charAt(block & 0x3f));
            buffer.append("==");
            return buffer.toString();
        }
        block = (((bytes[i++] & 0xff) << 8) | ((bytes[i]) & 0xff)) << 2;
        buffer.append(ALPHABET.charAt(block >>> 12));
        buffer.append(ALPHABET.charAt((block >>> 6) & 0x3f));
        buffer.append(ALPHABET.charAt(block & 0x3f));
        buffer.append("=");
        return buffer.toString();
    }
    /**
     * Decodes the supplied Base-64 encoded string.
     *
     * @param string The Base-64 encoded string that is to be decoded.
     * @return A <code>byte[]</code> containing the decoded data block.
     */
    public static byte[] decode(String string) {
        int length = string.length();
        if (length == 0) return new byte[0];
        int pad = (string.charAt(length - 2) == "=") ? 2 :
                (string.charAt(length - 1) == "=") ? 1 : 0;
        int size = length * 3 / 4 - pad;
        byte[] buffer = new byte[size];
        int block;
        int i = 0;
        int index = 0;
        while (i < length) {
            block = (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 18 |
                    (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 12 |
                    (ALPHABET.indexOf(string.charAt(i++)) & 0xff) << 6 |
                    (ALPHABET.indexOf(string.charAt(i++)) & 0xff);
            buffer[index++] = (byte) (block >>> 16);
            if (index < size) buffer[index++] = (byte) ((block >>> 8) & 0xff);
            if (index < size) buffer[index++] = (byte) (block & 0xff);
        }
        return buffer;
    }
}





Base64 provides Base64 encoding/decoding of strings and streams

    
/*
 * 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.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
/**
 * <code>Base64</code> provides Base64 encoding/decoding of strings and streams.
 */
public class Base64 {
    // charset used for base64 encoded data (7-bit ASCII)
    private static final String CHARSET = "US-ASCII";
    // encoding table (the 64 valid base64 characters)
    private static final char[] BASE64CHARS =
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
    // decoding table (used to lookup original 6-bit with base64 character
    // as table index)
    private static final byte[] DECODETABLE = new byte[128];
    static {
        // initialize decoding table
        for (int i = 0; i < DECODETABLE.length; i++) {
            DECODETABLE[i] = 0x7f;
        }
        // build decoding table
        for (int i = 0; i < BASE64CHARS.length; i++) {
            DECODETABLE[BASE64CHARS[i]] = (byte) i;
        }
    }
    // pad character
    private static final char BASE64PAD = "=";
    /**
     * empty private constructor
     */
    private Base64() {
    }
    /**
     * Calculates the size (i.e. number of bytes) of the base64 encoded output
     * given the length (i.e. number of bytes) of the data to be encoded.
     *
     * @param dataLength length (i.e. number of bytes) of the data to be encoded
     * @return size (i.e. number of bytes) of the base64 encoded output
     */
    public static long calcEncodedLength(long dataLength) {
        long encLen = dataLength * 4 / 3;
        encLen += (encLen + 4) % 4;
        return encLen;
    }
    /**
     * Pessimistically guesses the size (i.e. number of bytes) of the decoded
     * output given the length (i.e. number of bytes) of the base64 encoded
     * data.
     *
     * @param encLength length (i.e. number of bytes) of the base64 encoded data
     * @return size (i.e. number of bytes) of the decoded output
     */
    public static long guessDecodedLength(long encLength) {
        long decLen = encLength * 3 / 4;
        return decLen + 3;
    }
    /**
     * Outputs base64 representation of the specified stream data to a
     * <code>Writer</code>.
     *
     * @param in     stream data to be encoded
     * @param writer writer to output the encoded data
     * @throws java.io.IOException if an i/o error occurs
     */
    public static void encode(InputStream in, Writer writer)
            throws IOException {
        // encode stream data in chunks;
        // chunksize must be a multiple of 3 in order
        // to avoid padding within output
        byte[] buffer = new byte[9 * 1024];
        int read;
        while ((read = in.read(buffer)) > 0) {
            encode(buffer, 0, read, writer);
        }
    }
    /**
     * Outputs base64 representation of the specified stream data to an
     * <code>OutputStream</code>.
     *
     * @param in  stream data to be encoded
     * @param out stream where the encoded data should be written to
     * @throws java.io.IOException if an i/o error occurs
     */
    public static void encode(InputStream in, OutputStream out)
            throws IOException {
        Writer writer = new OutputStreamWriter(out, CHARSET);
        encode(in, writer);
    }
    /**
     * Outputs base64 representation of the specified data to a
     * <code>Writer</code>.
     *
     * @param data   data to be encoded
     * @param off    offset within data at which to start encoding
     * @param len    length of data to encode
     * @param writer writer to output the encoded data
     * @throws java.io.IOException if an i/o error occurs
     */
    public static void encode(byte[] data, int off, int len, Writer writer)
            throws IOException {
        if (len == 0) {
            return;
        }
        if (len < 0 || off >= data.length
                || len + off > data.length) {
            throw new IllegalArgumentException();
        }
        char[] enc = new char[4];
        while (len >= 3) {
            int i = ((data[off] & 0xff) << 16)
                    + ((data[off + 1] & 0xff) << 8)
                    + (data[off + 2] & 0xff);
            enc[0] = BASE64CHARS[i >> 18];
            enc[1] = BASE64CHARS[(i >> 12) & 0x3f];
            enc[2] = BASE64CHARS[(i >> 6) & 0x3f];
            enc[3] = BASE64CHARS[i & 0x3f];
            writer.write(enc, 0, 4);
            off += 3;
            len -= 3;
        }
        // add padding if necessary
        if (len == 1) {
            int i = data[off] & 0xff;
            enc[0] = BASE64CHARS[i >> 2];
            enc[1] = BASE64CHARS[(i << 4) & 0x3f];
            enc[2] = BASE64PAD;
            enc[3] = BASE64PAD;
            writer.write(enc, 0, 4);
        } else if (len == 2) {
            int i = ((data[off] & 0xff) << 8) + (data[off + 1] & 0xff);
            enc[0] = BASE64CHARS[i >> 10];
            enc[1] = BASE64CHARS[(i >> 4) & 0x3f];
            enc[2] = BASE64CHARS[(i << 2) & 0x3f];
            enc[3] = BASE64PAD;
            writer.write(enc, 0, 4);
        }
    }
    /**
     * Decode base64 encoded data.
     *
     * @param reader reader for the base64 encoded data to be decoded
     * @param out    stream where the decoded data should be written to
     * @throws java.io.IOException if an i/o error occurs
     */
    public static void decode(Reader reader, OutputStream out)
            throws IOException {
        char[] chunk = new char[8192];
        int read;
        while ((read = reader.read(chunk)) > -1) {
            decode(chunk, 0, read, out);
        }
    }
    /**
     * Decode base64 encoded data. The data read from the inputstream is
     * assumed to be of charset "US-ASCII".
     *
     * @param in  inputstream of the base64 encoded data to be decoded
     * @param out stream where the decoded data should be written to
     * @throws java.io.IOException if an i/o error occurs
     */
    public static void decode(InputStream in, OutputStream out)
            throws IOException {
        decode(new InputStreamReader(in, CHARSET), out);
    }
    /**
     * Decode base64 encoded data.
     *
     * @param data the base64 encoded data to be decoded
     * @param out  stream where the decoded data should be written to
     * @throws java.io.IOException if an i/o error occurs
     */
    public static void decode(String data, OutputStream out)
            throws IOException {
        char[] chars = data.toCharArray();
        decode(chars, 0, chars.length, out);
    }
    /**
     * Decode base64 encoded data.
     *
     * @param chars the base64 encoded data to be decoded
     * @param out   stream where the decoded data should be written to
     * @throws java.io.IOException if an i/o error occurs
     */
    public static void decode(char[] chars, OutputStream out)
            throws IOException {
        decode(chars, 0, chars.length, out);
    }
    /**
     * Decode base64 encoded data.
     *
     * @param chars the base64 encoded data to be decoded
     * @param off   offset within data at which to start decoding
     * @param len   length of data to decode
     * @param out   stream where the decoded data should be written to
     * @throws java.io.IOException if an i/o error occurs
     */
    public static void decode(char[] chars, int off, int len, OutputStream out)
            throws IOException {
        if (len == 0) {
            return;
        }
        if (len < 0 || off >= chars.length
                || len + off > chars.length) {
            throw new IllegalArgumentException();
        }
        char[] chunk = new char[4];
        byte[] dec = new byte[3];
        int posChunk = 0;
        // decode in chunks of 4 characters
        for (int i = off; i < (off + len); i++) {
            char c = chars[i];
            if (c < DECODETABLE.length && DECODETABLE[c] != 0x7f
                    || c == BASE64PAD) {
                chunk[posChunk++] = c;
                if (posChunk == chunk.length) {
                    int b0 = DECODETABLE[chunk[0]];
                    int b1 = DECODETABLE[chunk[1]];
                    int b2 = DECODETABLE[chunk[2]];
                    int b3 = DECODETABLE[chunk[3]];
                    if (chunk[3] == BASE64PAD && chunk[2] == BASE64PAD) {
                        dec[0] = (byte) (b0 << 2 & 0xfc | b1 >> 4 & 0x3);
                        out.write(dec, 0, 1);
                    } else if (chunk[3] == BASE64PAD) {
                        dec[0] = (byte) (b0 << 2 & 0xfc | b1 >> 4 & 0x3);
                        dec[1] = (byte) (b1 << 4 & 0xf0 | b2 >> 2 & 0xf);
                        out.write(dec, 0, 2);
                    } else {
                        dec[0] = (byte) (b0 << 2 & 0xfc | b1 >> 4 & 0x3);
                        dec[1] = (byte) (b1 << 4 & 0xf0 | b2 >> 2 & 0xf);
                        dec[2] = (byte) (b2 << 6 & 0xc0 | b3 & 0x3f);
                        out.write(dec, 0, 3);
                    }
                    posChunk = 0;
                }
            } else {
                throw new IllegalArgumentException("specified data is not base64 encoded");
            }
        }
    }
}





Class encodes the bytes written to the OutPutStream to a Base64 encoded string.

   
/*
 * Copyright 2005 Ralf Joachim
 *
 * 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.
 */
/**
 * Class encodes the bytes written to the OutPutStream to a Base64 encoded string.
 * The encoded string can be retrieved by as a whole by the toString() method or
 * splited into lines of 72 characters by the toStringArray() method.
 *
 * @author 
 * @version $Revision: 6907 $ $Date: 2005-08-05 13:58:36 -0600 (Fri, 05 Aug 2005) $
 * @since 0.9.9
 */
 public final class Base64Encoder {
    /** Third octets in buffer. */
    private static final int    OCTET_3 = 3;
    
    /** Mask buffer to or with first octet. */
    private static final int    OCTET_1_MASK = 0x00FFFF;
    
    /** Mask buffer to or with second octet. */
    private static final int    OCTET_2_MASK = 0xFF00FF;
    /** Mask buffer to or with third octet. */
    private static final int    OCTET_3_MASK = 0xFFFF00;
    
    /** Mask an octet. */
    private static final int    OCTET_MASK = 0xFF;
    
    /** Number of bits to shift for one octet. */
    private static final int    SHIFT_1_OCTET = 8;
    
    /** Number of bits to shift for two octet. */
    private static final int    SHIFT_2_OCTET = 16;
    
    /** Mask a sextet. */
    private static final int    SEXTET_MASK = 0x3F;
    
    /** Number of bits to shift for one sextet. */
    private static final int    SHIFT_1_SEXTET = 6;
    
    /** Number of bits to shift for two sextet. */
    private static final int    SHIFT_2_SEXTET = 12;
    
    /** Number of bits to shift for three sextet. */
    private static final int    SHIFT_3_SEXTET = 18;
    
    /** Array to convert sextet byte values into base64 characters. */
    private static final char[] MAP = {
            "A", "B", "C", "D", "E", "F", "G", "H",     // 00-07
            "I", "J", "K", "L", "M", "N", "O", "P",     // 08-15
            "Q", "R", "S", "T", "U", "V", "W", "X",     // 16-23
            "Y", "Z", "a", "b", "c", "d", "e", "f",     // 24-31
            "g", "h", "i", "j", "k", "l", "m", "n",     // 32-39
            "o", "p", "q", "r", "s", "t", "u", "v",     // 40-47
            "w", "x", "y", "z", "0", "1", "2", "3",     // 48-55
            "4", "5", "6", "7", "8", "9", "+", "/",     // 56-63
        };
    /** 24-bit buffer to translate 3 octets into 4 sextets. */
    private int     _buffer = 0;
    /** Number of octets in buffer. */
    private int     _octets = 0;
    /** Stream buffer for encoded characters waiting to be read. */
    private StringBuffer    _stream = new StringBuffer();
    /**
     * Encode given byte array into a encoded character array.
     * 
     * @param bytes The byte array to be encoded.
     * @return Base64 encoded characters as an array.
     */
    public static char[] encode(final byte[] bytes) {
        Base64Encoder enc = new Base64Encoder();
        enc.translate(bytes);
        return enc.getCharArray();
    }
    /**
     * Construct a Base64Encoder.
     */
    public Base64Encoder() { }
    /**
     * Reset Base64Encoder to its initial state. Take care using this method as it
     * throws all previously written bytes away.
     */
    public void reset() {
        _buffer = 0;
        _octets = 0;
        _stream = new StringBuffer();
    }
    /**
     * Translate all bytes of given array by appending each to octet buffer. If
     * buffer contains 3 octets its content will be encoded to 4 sextet byte values
     * which are converted to a base64 character each. All characters are appended
     * to a StringBuffer.
     * 
     * @param bytes The byte array to be encoded.
     */
    public void translate(final byte[] bytes) {
        for (int i = 0; i < bytes.length; i++) {
            byte b = bytes[i];
            if (_octets == 0) {
                _buffer = (_buffer & OCTET_1_MASK) | ((b & OCTET_MASK) << SHIFT_2_OCTET);
            } else if (_octets == 1) {
                _buffer = (_buffer & OCTET_2_MASK) | ((b & OCTET_MASK) << SHIFT_1_OCTET);
            } else {
                _buffer = (_buffer & OCTET_3_MASK) | (b & OCTET_MASK);
            }
            if ((++_octets) == OCTET_3) { encode(); }
        }
    }
    /**
     * Encode 4 sextets from buffer and add them to StringBuffer.
     */
    private void encode() {
        _stream.append(MAP[SEXTET_MASK & (_buffer >> SHIFT_3_SEXTET)]);   // sextet 1
        _stream.append(MAP[SEXTET_MASK & (_buffer >> SHIFT_2_SEXTET)]);   // sextet 2
        _stream.append(MAP[SEXTET_MASK & (_buffer >> SHIFT_1_SEXTET)]);   // sextet 3
        _stream.append(MAP[SEXTET_MASK & _buffer]);                       // sextet 4
        _buffer = 0;
        _octets = 0;
    }
    /**
     * Encode the remaining sextets from buffer and add them to to StringBuffer.
     */
    private void encodeWithPadding() {
        _stream.append(MAP[SEXTET_MASK & (_buffer >> SHIFT_3_SEXTET)]);   // sextet 1
        _stream.append(MAP[SEXTET_MASK & (_buffer >> SHIFT_2_SEXTET)]);   // sextet 2
        if (_octets <= 1) {                                               // sextet 3
            _stream.append("=");
        } else {
            _stream.append(MAP[SEXTET_MASK & (_buffer >> SHIFT_1_SEXTET)]);
        }
        if (_octets <= 2) {                                               // sextet 4
            _stream.append("=");
        } else {
            _stream.append(MAP[SEXTET_MASK & _buffer]);
        }
        _buffer = 0;
        _octets = 0;
    }
    /**
     * Get Base64 encoded characters as an array.
     * 
     * @return Base64 encoded characters as an array.
     */
    public char[] getCharArray() {
        if (_octets > 0) { encodeWithPadding(); }
        char[] chars = new char[_stream.length()];
        if (_stream.length() > 0) { _stream.getChars(0, _stream.length(), chars, 0); }
        return chars;
    }
}





Decode a BASE64 encoded input stream to some output stream

   
/*
 * Copyright � World Wide Web Consortium, (Massachusetts Institute of Technology, 
 * Institut National de Recherche en Informatique et en Automatique, Keio University).
 * All Rights Reserved. http://www.w3.org/Consortium/Legal/
 */
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
/**
 * Decode a BASE64 encoded input stream to some output stream.
 * This class implements BASE64 decoding, as specified in the
 * .
 * @see org.w3c.tools.codec.Base64Encoder
 */
public class Base64Decoder
{
  private static final int BUFFER_SIZE = 1024;
  InputStream in = null;
  OutputStream out = null;
  boolean stringp = false;
  private void printHex(int x)
  {
    int h = (x & 0xf0) >> 4;
    int l = (x & 0x0f);
    System.out.print(
      (new Character((char) ((h > 9) ? "A" + h - 10 : "0" + h)))
        .toString()
        + (new Character((char) ((l > 9) ? "A" + l - 10 : "0" + l)))
          .toString());
  }
  private void printHex(byte buf[], int off, int len)
  {
    while (off < len)
    {
      printHex(buf[off++]);
      System.out.print(" ");
    }
    System.out.println("");
  }
  private void printHex(String s)
  {
    byte bytes[];
    try
    {
      bytes = s.getBytes("ISO-8859-1");
    }
    catch (UnsupportedEncodingException ex)
    {
      throw new RuntimeException(
        this.getClass().getName()
          + "[printHex] Unable to convert"
          + "properly char to bytes");
    }
    printHex(bytes, 0, bytes.length);
  }
  private final int get1(byte buf[], int off)
  {
    return ((buf[off] & 0x3f) << 2) | ((buf[off + 1] & 0x30) >>> 4);
  }
  private final int get2(byte buf[], int off)
  {
    return ((buf[off + 1] & 0x0f) << 4) | ((buf[off + 2] & 0x3c) >>> 2);
  }
  private final int get3(byte buf[], int off)
  {
    return ((buf[off + 2] & 0x03) << 6) | (buf[off + 3] & 0x3f);
  }
  private final int check(int ch)
  {
    if ((ch >= "A") && (ch <= "Z"))
    {
      return ch - "A";
    }
    else if ((ch >= "a") && (ch <= "z"))
    {
      return ch - "a" + 26;
    }
    else if ((ch >= "0") && (ch <= "9"))
    {
      return ch - "0" + 52;
    }
    else
    {
      switch (ch)
      {
        case "=" :
          return 65;
        case "+" :
          return 62;
        case "/" :
          return 63;
        default :
          return -1;
      }
    }
  }
  /**
   * Do the actual decoding.
   * Process the input stream by decoding it and emiting the resulting bytes
   * into the output stream.
   * @exception IOException If the input or output stream accesses failed.
   * @exception Base64FormatException If the input stream is not compliant
   *    with the BASE64 specification.
   */
  public void process() throws IOException, Exception
  {
    byte buffer[] = new byte[BUFFER_SIZE];
    byte chunk[] = new byte[4];
    int got = -1;
    int ready = 0;
    fill : while ((got = in.read(buffer)) > 0)
    {
      int skiped = 0;
      while (skiped < got)
      {
        // Check for un-understood characters:
        while (ready < 4)
        {
          if (skiped >= got)
            continue fill;
          int ch = check(buffer[skiped++]);
          if (ch >= 0)
            chunk[ready++] = (byte) ch;
        }
        if (chunk[2] == 65)
        {
          out.write(get1(chunk, 0));
          return;
        }
        else if (chunk[3] == 65)
        {
          out.write(get1(chunk, 0));
          out.write(get2(chunk, 0));
          return;
        }
        else
        {
          out.write(get1(chunk, 0));
          out.write(get2(chunk, 0));
          out.write(get3(chunk, 0));
        }
        ready = 0;
      }
    }
    if (ready != 0)
      throw new Exception("Invalid length.");
    out.flush();
  }
  /**
   * Do the decoding, and return a String.
   * This methods should be called when the decoder is used in
   * <em>String</em> mode. It decodes the input string to an output string
   * that is returned.
   * @exception RuntimeException If the object wasn"t constructed to
   *    decode a String.
   * @exception Base64FormatException If the input string is not compliant 
   *     with the BASE64 specification.
   */
  public String processString() throws Exception
  {
    if (!stringp)
      throw new RuntimeException(
        this.getClass().getName()
          + "[processString]"
          + "invalid call (not a String)");
    try
    {
      process();
    }
    catch (IOException e)
    {
    }
    String s;
    try
    {
      s = ((ByteArrayOutputStream) out).toString("ISO-8859-1");
    }
    catch (UnsupportedEncodingException ex)
    {
      throw new RuntimeException(
        this.getClass().getName()
          + "[processString] Unable to convert"
          + "properly char to bytes");
    }
    return s;
  }
  /**
   * Create a decoder to decode a String.
   * @param input The string to be decoded.
   */
  public Base64Decoder(String input)
  {
    byte bytes[];
    try
    {
      bytes = input.getBytes("ISO-8859-1");
    }
    catch (UnsupportedEncodingException ex)
    {
      throw new RuntimeException(
        this.getClass().getName()
          + "[Constructor] Unable to convert"
          + "properly char to bytes");
    }
    this.stringp = true;
    this.in = new ByteArrayInputStream(bytes);
    this.out = new ByteArrayOutputStream();
  }
  /**
   * Create a decoder to decode a stream.
   * @param in The input stream (to be decoded).
   * @param out The output stream, to write decoded data to.
   */
  public Base64Decoder(InputStream in, OutputStream out)
  {
    this.in = in;
    this.out = out;
    this.stringp = false;
  }
  /**
   * Test the decoder.
   * Run it with one argument: the string to be decoded, it will print out
   * the decoded value.
   */
  public static void main(String args[])
  {
    if (args.length == 1)
    {
      try
      {
        Base64Decoder b = new Base64Decoder(args[0]);
        System.out.println("[" + b.processString() + "]");
      }
      catch (Exception e)
      {
        System.out.println("Invalid Base64 format !");
        System.exit(1);
      }
    }
    else if ((args.length == 2) && (args[0].equals("-f")))
    {
      try
      {
        FileInputStream in = new FileInputStream(args[1]);
        Base64Decoder b = new Base64Decoder(in, System.out);
        b.process();
      }
      catch (Exception ex)
      {
        System.out.println("error: " + ex.getMessage());
        System.exit(1);
      }
    }
    else
    {
      System.out.println("Base64Decoder [strong] [-f file]");
    }
    System.exit(0);
  }
}





Dumps data in hexadecimal format

    
/*
 * 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.IOException;
import java.io.OutputStream;
/**
 * Dumps data in hexadecimal format.
 * <p>
 * Provides a single function to take an array of bytes and display it
 * in hexadecimal form.
 * <p>
 * Origin of code: POI.
 *
 * @author Scott Sanders
 * @author Marc Johnson
 * @version $Id: HexDump.java 596667 2007-11-20 13:50:14Z niallp $
 */
public class HexDump {
    /**
     * Instances should NOT be constructed in standard programming.
     */
    public HexDump() {
        super();
    }
    /**
     * Dump an array of bytes to an OutputStream.
     *
     * @param data  the byte array to be dumped
     * @param offset  its offset, whatever that might mean
     * @param stream  the OutputStream to which the data is to be
     *               written
     * @param index initial index into the byte array
     *
     * @throws IOException is thrown if anything goes wrong writing
     *         the data to stream
     * @throws ArrayIndexOutOfBoundsException if the index is
     *         outside the data array"s bounds
     * @throws IllegalArgumentException if the output stream is null
     */
    public static void dump(byte[] data, long offset,
                            OutputStream stream, int index)
            throws IOException, ArrayIndexOutOfBoundsException,
            IllegalArgumentException {
        
        if ((index < 0) || (index >= data.length)) {
            throw new ArrayIndexOutOfBoundsException(
                    "illegal index: " + index + " into array of length "
                    + data.length);
        }
        if (stream == null) {
            throw new IllegalArgumentException("cannot write to nullstream");
        }
        long display_offset = offset + index;
        StringBuffer buffer = new StringBuffer(74);
        for (int j = index; j < data.length; j += 16) {
            int chars_read = data.length - j;
            if (chars_read > 16) {
                chars_read = 16;
            }
            dump(buffer, display_offset).append(" ");
            for (int k = 0; k < 16; k++) {
                if (k < chars_read) {
                    dump(buffer, data[k + j]);
                } else {
                    buffer.append("  ");
                }
                buffer.append(" ");
            }
            for (int k = 0; k < chars_read; k++) {
                if ((data[k + j] >= " ") && (data[k + j] < 127)) {
                    buffer.append((char) data[k + j]);
                } else {
                    buffer.append(".");
                }
            }
            buffer.append(EOL);
            stream.write(buffer.toString().getBytes());
            stream.flush();
            buffer.setLength(0);
            display_offset += chars_read;
        }
    }
    /**
     * The line-separator (initializes to "line.separator" system property.
     */
    public static final String EOL =
            System.getProperty("line.separator");
    private static final char[] _hexcodes =
            {
                "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
                "A", "B", "C", "D", "E", "F"
            };
    private static final int[] _shifts =
            {
                28, 24, 20, 16, 12, 8, 4, 0
            };
    /**
     * Dump a long value into a StringBuffer.
     *
     * @param _lbuffer the StringBuffer to dump the value in
     * @param value  the long value to be dumped
     * @return StringBuffer containing the dumped value.
     */
    private static StringBuffer dump(StringBuffer _lbuffer, long value) {
        for (int j = 0; j < 8; j++) {
            _lbuffer
                    .append(_hexcodes[((int) (value >> _shifts[j])) & 15]);
        }
        return _lbuffer;
    }
    /**
     * Dump a byte value into a StringBuffer.
     *
     * @param _cbuffer the StringBuffer to dump the value in
     * @param value  the byte value to be dumped
     * @return StringBuffer containing the dumped value.
     */
    private static StringBuffer dump(StringBuffer _cbuffer, byte value) {
        for (int j = 0; j < 2; j++) {
            _cbuffer.append(_hexcodes[(value >> _shifts[j + 6]) & 15]);
        }
        return _cbuffer;
    }
}





Hex dump

    
/* jcifs smb client library in Java
 * Copyright (C) 2000  "Michael B. Allen" <jcifs at samba dot org>
 *                     "Christopher R. Hertel" <jcifs at samba dot org>
 * 
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

import java.io.OutputStream;
import java.io.PrintStream;
/**
 */
public class Hexdump {
    private static final String NL = System.getProperty( "line.separator" );
    private static final int NL_LENGTH = NL.length();
    private static final char[] SPACE_CHARS = {
        " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ",
        " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ",
        " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "
    };
    public static final char[] HEX_DIGITS = { 
        "0", "1", "2", "3", "4", "5",
        "6", "7", "8", "9", "A", "B",
        "C", "D", "E", "F"
    };
/** 
 * Generate "hexdump" output of the buffer at src like the following:
 *
 * <p><blockquote><pre>
 * 00000: 04 d2 29 00 00 01 00 00 00 00 00 01 20 45 47 46  |..)......... EGF|
 * 00010: 43 45 46 45 45 43 41 43 41 43 41 43 41 43 41 43  |CEFEECACACACACAC|
 * 00020: 41 43 41 43 41 43 41 43 41 43 41 41 44 00 00 20  |ACACACACACAAD.. |
 * 00030: 00 01 c0 0c 00 20 00 01 00 00 00 00 00 06 20 00  |..... ........ .|
 * 00040: ac 22 22 e1                                      |."".            |
 * </blockquote></pre>
 */
    public static void hexdump( PrintStream ps, byte[] src, int srcIndex, int length ) {
        if( length == 0 ) {
            return;
        }
        int s = length % 16;
        int r = ( s == 0 ) ? length / 16 : length / 16 + 1;
        char[] c = new char[r * (74 + NL_LENGTH)];
        char[] d = new char[16];
        int i;
        int si = 0;
        int ci = 0;
        do {
            toHexChars( si, c, ci, 5 );
            ci += 5;
            c[ci++] = ":";
            do {
                if( si == length ) {
                    int n = 16 - s;
                    System.arraycopy( SPACE_CHARS, 0, c, ci, n * 3 );
                    ci += n * 3;
                    System.arraycopy( SPACE_CHARS, 0, d, s, n );
                    break;
                }
                c[ci++] = " ";
                i = src[srcIndex + si] & 0xFF;
                toHexChars( i, c, ci, 2 );
                ci += 2; 
                if( i < 0 || Character.isISOControl( (char)i )) {
                    d[si % 16] = ".";
                } else {
                    d[si % 16] = (char)i;
                }
            } while(( ++si % 16 ) != 0 );
            c[ci++] = " ";
            c[ci++] = " ";
            c[ci++] = "|";
            System.arraycopy( d, 0, c, ci, 16 );
            ci += 16;
            c[ci++] = "|";
            NL.getChars( 0, NL_LENGTH, c, ci );
            ci += NL_LENGTH;
        } while( si < length );
        ps.println( c );
    }
/** 
 * This is an alternative to the <code>java.lang.Integer.toHexString</cod>
 * method. It is an efficient relative that also will pad the left side so
 * that the result is <code>size</code> digits.
 */ 
    public static String toHexString( int val, int size ) {
        char[] c = new char[size];
        toHexChars( val, c, 0, size );
        return new String( c );
    }
    public static String toHexString( long val, int size ) {
        char[] c = new char[size];
        toHexChars( val, c, 0, size );
        return new String( c );
    }
    public static String toHexString( byte[] src, int srcIndex, int size ) {
        char[] c = new char[size];
        size = ( size % 2 == 0 ) ? size / 2 : size / 2 + 1;
        for( int i = 0, j = 0; i < size; i++ ) {
            c[j++] = HEX_DIGITS[(src[i] >> 4 ) & 0x0F];
            if( j == c.length ) {
                break;
            }
            c[j++] = HEX_DIGITS[src[i] & 0x0F];
        }
        return new String( c );
    }
/** 
 * This is the same as {@link jcifs.util.Hexdump#toHexString(int val, int
 * size)} but provides a more practical form when trying to avoid {@link
 * java.lang.String} concatenation and {@link java.lang.StringBuffer}.
 */ 
    public static void toHexChars( int val, char dst[], int dstIndex, int size ) {
        while( size > 0 ) {
            int i = dstIndex + size - 1;
            if( i < dst.length ) {
                dst[i] = HEX_DIGITS[val & 0x000F];
            }
            if( val != 0 ) {
                val >>>= 4;
            }
            size--;
        }
    }
    public static void toHexChars( long val, char dst[], int dstIndex, int size ) {
        while( size > 0 ) {
            dst[dstIndex + size - 1] = HEX_DIGITS[(int)( val & 0x000FL )];
            if( val != 0 ) {
                val >>>= 4;
            }
            size--;
        }
    }
}





Performs Base-64 decoding on an underlying stream.

   
/****************************************************************
 * 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.InputStream;
import java.io.OutputStream;
import java.nio.BufferUnderflowException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;

/**
 * Performs Base-64 decoding on an underlying stream.
 */
public class Base64InputStream extends InputStream {
    private static final int ENCODED_BUFFER_SIZE = 1536;
    private static final int[] BASE64_DECODE = new int[256];
    static {
        for (int i = 0; i < 256; i++)
            BASE64_DECODE[i] = -1;
        for (int i = 0; i < Base64OutputStream.BASE64_TABLE.length; i++)
            BASE64_DECODE[Base64OutputStream.BASE64_TABLE[i] & 0xff] = i;
    }
    private static final byte BASE64_PAD = "=";
    private static final int EOF = -1;
    private final byte[] singleByte = new byte[1];
    private boolean strict;
    private final InputStream in;
    private boolean closed = false;
    private final byte[] encoded = new byte[ENCODED_BUFFER_SIZE];
    private int position = 0; // current index into encoded buffer
    private int size = 0; // current size of encoded buffer
    private final ByteQueue q = new ByteQueue();
    private boolean eof; // end of file or pad character reached
    public Base64InputStream(InputStream in) {
        this(in, false);
    }
    public Base64InputStream(InputStream in, boolean strict) {
        if (in == null)
            throw new IllegalArgumentException();
        this.in = in;
        this.strict = strict;
    }
    @Override
    public int read() throws IOException {
        if (closed)
            throw new IOException("Base64InputStream has been closed");
        while (true) {
            int bytes = read0(singleByte, 0, 1);
            if (bytes == EOF)
                return EOF;
            if (bytes == 1)
                return singleByte[0] & 0xff;
        }
    }
    @Override
    public int read(byte[] buffer) throws IOException {
        if (closed)
            throw new IOException("Base64InputStream has been closed");
        if (buffer == null)
            throw new NullPointerException();
        if (buffer.length == 0)
            return 0;
        return read0(buffer, 0, buffer.length);
    }
    @Override
    public int read(byte[] buffer, int offset, int length) throws IOException {
        if (closed)
            throw new IOException("Base64InputStream 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 0;
        return read0(buffer, offset, offset + length);
    }
    @Override
    public void close() throws IOException {
        if (closed)
            return;
        closed = true;
    }
    private int read0(final byte[] buffer, final int from, final int to)
            throws IOException {
        int index = from; // index into given buffer
        // check if a previous invocation left decoded bytes in the queue
        int qCount = q.count();
        while (qCount-- > 0 && index < to) {
            buffer[index++] = q.dequeue();
        }
        // eof or pad reached?
        if (eof)
            return index == from ? EOF : index - from;
        // decode into given buffer
        int data = 0; // holds decoded data; up to four sextets
        int sextets = 0; // number of sextets
        while (index < to) {
            // make sure buffer not empty
            while (position == size) {
                int n = in.read(encoded, 0, encoded.length);
                if (n == EOF) {
                    eof = true;
                    if (sextets != 0) {
                        // error in encoded data
                        handleUnexpectedEof(sextets);
                    }
                    return index == from ? EOF : index - from;
                } else if (n > 0) {
                    position = 0;
                    size = n;
                } else {
                    assert n == 0;
                }
            }
            // decode buffer
            while (position < size && index < to) {
                int value = encoded[position++] & 0xff;
                if (value == BASE64_PAD) {
                    index = decodePad(data, sextets, buffer, index, to);
                    return index - from;
                }
                int decoded = BASE64_DECODE[value];
                if (decoded < 0) // -1: not a base64 char
                    continue;
                data = (data << 6) | decoded;
                sextets++;
                if (sextets == 4) {
                    sextets = 0;
                    byte b1 = (byte) (data >>> 16);
                    byte b2 = (byte) (data >>> 8);
                    byte b3 = (byte) data;
                    if (index < to - 2) {
                        buffer[index++] = b1;
                        buffer[index++] = b2;
                        buffer[index++] = b3;
                    } else {
                        if (index < to - 1) {
                            buffer[index++] = b1;
                            buffer[index++] = b2;
                            q.enqueue(b3);
                        } else if (index < to) {
                            buffer[index++] = b1;
                            q.enqueue(b2);
                            q.enqueue(b3);
                        } else {
                            q.enqueue(b1);
                            q.enqueue(b2);
                            q.enqueue(b3);
                        }
                        assert index == to;
                        return to - from;
                    }
                }
            }
        }
        assert sextets == 0;
        assert index == to;
        return to - from;
    }
    private int decodePad(int data, int sextets, final byte[] buffer,
            int index, final int end) throws IOException {
        eof = true;
        if (sextets == 2) {
            // one byte encoded as "XY=="
            byte b = (byte) (data >>> 4);
            if (index < end) {
                buffer[index++] = b;
            } else {
                q.enqueue(b);
            }
        } else if (sextets == 3) {
            // two bytes encoded as "XYZ="
            byte b1 = (byte) (data >>> 10);
            byte b2 = (byte) ((data >>> 2) & 0xFF);
            if (index < end - 1) {
                buffer[index++] = b1;
                buffer[index++] = b2;
            } else if (index < end) {
                buffer[index++] = b1;
                q.enqueue(b2);
            } else {
                q.enqueue(b1);
                q.enqueue(b2);
            }
        } else {
            // error in encoded data
            handleUnexpecedPad(sextets);
        }
        return index;
    }
    private void handleUnexpectedEof(int sextets) throws IOException {
        if (strict)
            throw new IOException("unexpected end of file");
    }
    private void handleUnexpecedPad(int sextets) throws IOException {
        if (strict)
            throw new IOException("unexpected padding character");
    }
}
class ByteQueue implements Iterable<Byte> {
  private UnboundedFifoByteBuffer buf;
  private int initialCapacity = -1;
  public ByteQueue() {
      buf = new UnboundedFifoByteBuffer();
  }
  public ByteQueue(int initialCapacity) {
      buf = new UnboundedFifoByteBuffer(initialCapacity);
      this.initialCapacity = initialCapacity;
  }
  public void enqueue(byte b) {
      buf.add(b);
  }
  public byte dequeue() {
      return buf.remove();
  }
  public int count() {
      return buf.size();
  }
  public void clear() {
      if (initialCapacity != -1)
          buf = new UnboundedFifoByteBuffer(initialCapacity);
      else
          buf = new UnboundedFifoByteBuffer();
  }
  public Iterator<Byte> iterator() {
      return buf.iterator();
  }

}

/**
 * UnboundedFifoByteBuffer is a very efficient buffer implementation.
 * According to performance testing, it exhibits a constant access time, but it
 * also outperforms ArrayList when used for the same purpose.
 * <p>
 * The removal order of an <code>UnboundedFifoByteBuffer</code> is based on the insertion
 * order; elements are removed in the same order in which they were added.
 * The iteration order is the same as the removal order.
 * <p>
 * The {@link #remove()} and {@link #get()} operations perform in constant time.
 * The {@link #add(Object)} operation performs in amortized constant time.  All
 * other operations perform in linear time or worse.
 * <p>
 * Note that this implementation is not synchronized.  The following can be
 * used to provide synchronized access to your <code>UnboundedFifoByteBuffer</code>:
 * <pre>
 *   Buffer fifo = BufferUtils.synchronizedBuffer(new UnboundedFifoByteBuffer());
 * </pre>
 * <p>
 * This buffer prevents null objects from being added.
 *
 * @since Commons Collections 3.0 (previously in main package v2.1)
 */
class UnboundedFifoByteBuffer {
    protected byte[] buffer;
    protected int head;
    protected int tail;
    /**
     * Constructs an UnboundedFifoByteBuffer with the default number of elements.
     * It is exactly the same as performing the following:
     *
     * <pre>
     *   new UnboundedFifoByteBuffer(32);
     * </pre>
     */
    public UnboundedFifoByteBuffer() {
        this(32);
    }
    /**
     * Constructs an UnboundedFifoByteBuffer with the specified number of elements.
     * The integer must be a positive integer.
     *
     * @param initialSize  the initial size of the buffer
     * @throws IllegalArgumentException  if the size is less than 1
     */
    public UnboundedFifoByteBuffer(int initialSize) {
        if (initialSize <= 0) {
            throw new IllegalArgumentException("The size must be greater than 0");
        }
        buffer = new byte[initialSize + 1];
        head = 0;
        tail = 0;
    }
    /**
     * Returns the number of elements stored in the buffer.
     *
     * @return this buffer"s size
     */
    public int size() {
        int size = 0;
        if (tail < head) {
            size = buffer.length - head + tail;
        } else {
            size = tail - head;
        }
        return size;
    }
    /**
     * Returns true if this buffer is empty; false otherwise.
     *
     * @return true if this buffer is empty
     */
    public boolean isEmpty() {
        return (size() == 0);
    }
    /**
     * Adds the given element to this buffer.
     *
     * @param b  the byte to add
     * @return true, always
     */
    public boolean add(final byte b) {
        if (size() + 1 >= buffer.length) {
            byte[] tmp = new byte[((buffer.length - 1) * 2) + 1];
            int j = 0;
            for (int i = head; i != tail;) {
                tmp[j] = buffer[i];
                buffer[i] = 0;
                j++;
                i++;
                if (i == buffer.length) {
                    i = 0;
                }
            }
            buffer = tmp;
            head = 0;
            tail = j;
        }
        buffer[tail] = b;
        tail++;
        if (tail >= buffer.length) {
            tail = 0;
        }
        return true;
    }
    /**
     * Returns the next object in the buffer.
     *
     * @return the next object in the buffer
     * @throws BufferUnderflowException  if this buffer is empty
     */
    public byte get() {
        if (isEmpty()) {
            throw new IllegalStateException("The buffer is already empty");
        }
        return buffer[head];
    }
    /**
     * Removes the next object from the buffer
     *
     * @return the removed object
     * @throws BufferUnderflowException  if this buffer is empty
     */
    public byte remove() {
        if (isEmpty()) {
            throw new IllegalStateException("The buffer is already empty");
        }
        byte element = buffer[head];
        head++;
        if (head >= buffer.length) {
            head = 0;
        }
        return element;
    }
    /**
     * Increments the internal index.
     *
     * @param index  the index to increment
     * @return the updated index
     */
    private int increment(int index) {
        index++;
        if (index >= buffer.length) {
            index = 0;
        }
        return index;
    }
    /**
     * Decrements the internal index.
     *
     * @param index  the index to decrement
     * @return the updated index
     */
    private int decrement(int index) {
        index--;
        if (index < 0) {
            index = buffer.length - 1;
        }
        return index;
    }
    /**
     * Returns an iterator over this buffer"s elements.
     *
     * @return an iterator over this buffer"s elements
     */
    public Iterator<Byte> iterator() {
        return new Iterator<Byte>() {
            private int index = head;
            private int lastReturnedIndex = -1;
            public boolean hasNext() {
                return index != tail;
            }
            public Byte next() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }
                lastReturnedIndex = index;
                index = increment(index);
                return new Byte(buffer[lastReturnedIndex]);
            }
            public void remove() {
                if (lastReturnedIndex == -1) {
                    throw new IllegalStateException();
                }
                // First element can be removed quickly
                if (lastReturnedIndex == head) {
                    UnboundedFifoByteBuffer.this.remove();
                    lastReturnedIndex = -1;
                    return;
                }
                // Other elements require us to shift the subsequent elements
                int i = lastReturnedIndex + 1;
                while (i != tail) {
                    if (i >= buffer.length) {
                        buffer[i - 1] = buffer[0];
                        i = 0;
                    } else {
                        buffer[i - 1] = buffer[i];
                        i++;
                    }
                }
                lastReturnedIndex = -1;
                tail = decrement(tail);
                buffer[tail] = 0;
                index = decrement(index);
            }
        };
    }
}
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) + """);
          }
      }
  }
}