/*****************************************************************************
* Copyright (c) 2008 g-Eclipse Consortium
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Initial development of the original code was made for the
* g-Eclipse project founded by European Union
* project number: FP6-IST-034327 http://www.geclipse.eu/
*
* Contributors:
* Mathias Stuempert - initial API and implementation
*****************************************************************************/
package eu.geclipse.core.security;
import java.io.ByteArrayOutputStream;
/**
* A configurable Base64 codec.
*/
public class Base64 {
/**
* Encoding table as used by conventional base64 codecs.
*/
private static final byte[] DEFAULT_ENCODING_TABLE = new byte[] {
(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)'/'
};
/**
* Fill byte as used by conventional base64 codecs.
*/
private static final byte DEFAULT_FILL_BYTE = ( byte ) '=';
/**
* Encoding table used by this codec.
*/
private byte[] encodingTable;
/**
* Decoding table used by this codec.
*/
private byte[] decodingTable;
/**
* Fill byte used by this codec.
*/
private byte fillbyte;
/**
* Create a defaut Base64 codec. The codec is initialized with the default
* en-/decoding tables and the default fill byte.
*/
public Base64() {
setEncodingTable( DEFAULT_ENCODING_TABLE, true );
setFillByte( DEFAULT_FILL_BYTE);
}
/**
* Decode the specified string using the current decoding table
* and the current fill byte.
*
* @param encoded The encoded string.
* @return The decoded string.
*/
public String decode( final String encoded ) {
return new String( decode( encoded.getBytes() ) );
}
/**
* Decode the specified byte array using the current decoding table
* and the current fill byte.
*
* @param encoded The encoded byte array.
* @return The decoded byte array.
*/
public byte[] decode( final byte[] encoded ) {
int encodedLength = encoded.length;
while ( ( encoded[ encodedLength-1 ] == this.fillbyte )
&& ( encodedLength > 0 ) ) {
encodedLength--;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
for ( int i = 0 ; i < encodedLength ; i += 4 ) {
byte e1 = this.decodingTable[ encoded[ i ] ];
byte e2 = ( i+1 < encodedLength ) ? this.decodingTable[ encoded[ i+1 ] ] : -1;
byte e3 = ( i+2 < encodedLength ) ? this.decodingTable[ encoded[ i+2 ] ] : -1;
byte e4 = ( i+3 < encodedLength ) ? this.decodingTable[ encoded[ i+3 ] ] : -1;
byte d1 = ( byte ) ( ( e1 << 2 ) & 0xFF );
byte d2 = -1;
byte d3 = -1;
if ( e2 >= 0 ) {
d1 |= ( e2 >>> 4 ) & 0xFF;
d2 = ( byte ) ( ( e2 << 4 ) & 0xFF );
}
if ( e3 >= 0 ) {
d2 |= ( byte ) ( ( e3 >>> 2 ) & 0xFF );
d3 = ( byte ) ( ( e3 << 6 ) & 0xFF );
}
if ( e4 >= 0 ) {
d3 |= ( byte ) ( e4 & 0xFF );
}
out.write( d1 );
//if ( e2 >= 0 && d2 > 0 ) out.write( d2 );
//if ( e3 >= 0 && d3 > 0 ) out.write( d3 );
if ( e2 >= 0 ) out.write( d2 );
if ( e3 >= 0 ) out.write( d3 );
}
return out.toByteArray();
}
/**
* Encode the specified string using the current encoding table
* and the current fill byte.
*
* @param decoded The decoded string.
* @return The encoded string.
*/
public String encode( final String decoded ) {
return new String( encode( decoded.getBytes() ) );
}
/**
* Encode the specified byte array using the current encoding table
* and the current fill byte.
*
* @param decoded The decoded byte array.
* @return The encoded byte array.
*/
public byte[] encode( final byte[] decoded ) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
for ( int i = 0 ; i < decoded.length ; i += 3 ) {
byte d1 = decoded[i];
byte d2 = ( i+1 < decoded.length ) ? decoded[i+1] : 0;
byte d3 = ( i+2 < decoded.length ) ? decoded[i+2] : 0;
int i1 = ( d1 >>> 2 ) & 0x3F;
int i2 = ( d1 << 4) & 0x3F;
int i3 = -1;
int i4 = -1;
if ( i+1 < decoded.length ) {
i2 |= ( d2 >>> 4 ) & 0x0F;
i3 = ( d2 << 2) & 0x3F;
}
if ( i+2 < decoded.length ) {
i3 |= ( d3 >>> 6 ) & 0x03;
i4 = d3 & 0x3F;
}
out.write( this.encodingTable[ i1 ] );
out.write( this.encodingTable[ i2 ] );
out.write( i3 >= 0 ? this.encodingTable[ i3 ] : this.fillbyte );
out.write( i4 >= 0 ? this.encodingTable[ i4 ] : this.fillbyte );
}
return out.toByteArray();
}
/**
* Set the current decoding table to the specified one.
*
* @param table The new decoding table.
*/
public void setDecodingTable( final byte[] table ) {
this.decodingTable = new byte[ table.length ];
this.encodingTable = new byte[ table.length ];
for ( int i = 0 ; i < table.length ; i++ ) {
this.decodingTable[ i ] = table[ i ];
}
}
/**
* Set the current encoding table to the specified one. This method changes
* the current decoding table accordingly if changeDecoding is set to true.
*
* @param table The new encoding table.
* @param changeDecoding If true the decoding table is changed as well.
*/
public void setEncodingTable( final byte[] table, final boolean changeDecoding ) {
this.encodingTable = new byte[ table.length ];
for ( int i = 0 ; i < this.encodingTable.length ; i++ ) {
this.encodingTable[ i ] = table[ i ];
}
if ( changeDecoding ) {
this.decodingTable = new byte[ 128 ];
for ( int i = 0 ; i < this.decodingTable.length ; i++ ) {
this.decodingTable[ i ] = 0;
}
for ( int i = 0 ; i < this.encodingTable.length ; i++ ) {
this.decodingTable[ this.encodingTable[ i ] ] = ( byte ) i;
}
}
}
/**
* Set the current fill byte to the specified one.
*
* @param b The new fill byte.
*/
public void setFillByte( final byte b ) {
this.fillbyte = b;
}
}