Coverage Report - org.codehaus.plexus.util.Base64
 
Classes in this File Line Coverage Branch Coverage Complexity
Base64
0%
0/155
0%
0/68
5.3
 
 1  
 package org.codehaus.plexus.util;
 2  
 
 3  
 /*
 4  
  * Copyright The Codehaus Foundation.
 5  
  *
 6  
  * Licensed under the Apache License, Version 2.0 (the "License");
 7  
  * you may not use this file except in compliance with the License.
 8  
  * You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing, software
 13  
  * distributed under the License is distributed on an "AS IS" BASIS,
 14  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  
  * See the License for the specific language governing permissions and
 16  
  * limitations under the License.
 17  
  */
 18  
 
 19  
 /**
 20  
  * Provides Base64 encoding and decoding as defined by RFC 2045.
 21  
  * <p>
 22  
  * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose
 23  
  * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</cite> by Freed and Borenstein.
 24  
  * </p>
 25  
  *
 26  
  * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
 27  
  * @author Apache Software Foundation
 28  
  * @since 1.0-dev
 29  
  * @version $Id$
 30  
  */
 31  0
 public class Base64
 32  
 {
 33  
 
 34  
     //
 35  
     // Source Id: Base64.java 161350 2005-04-14 20:39:46Z ggregory
 36  
     //
 37  
 
 38  
     /**
 39  
      * Chunk size per RFC 2045 section 6.8.
 40  
      * <p>
 41  
      * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any
 42  
      * equal signs.
 43  
      * </p>
 44  
      *
 45  
      * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
 46  
      */
 47  
     static final int CHUNK_SIZE = 76;
 48  
 
 49  
     /**
 50  
      * Chunk separator per RFC 2045 section 2.1.
 51  
      *
 52  
      * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
 53  
      */
 54  0
     static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes();
 55  
 
 56  
     /**
 57  
      * The base length.
 58  
      */
 59  
     static final int BASELENGTH = 255;
 60  
 
 61  
     /**
 62  
      * Lookup length.
 63  
      */
 64  
     static final int LOOKUPLENGTH = 64;
 65  
 
 66  
     /**
 67  
      * Used to calculate the number of bits in a byte.
 68  
      */
 69  
     static final int EIGHTBIT = 8;
 70  
 
 71  
     /**
 72  
      * Used when encoding something which has fewer than 24 bits.
 73  
      */
 74  
     static final int SIXTEENBIT = 16;
 75  
 
 76  
     /**
 77  
      * Used to determine how many bits data contains.
 78  
      */
 79  
     static final int TWENTYFOURBITGROUP = 24;
 80  
 
 81  
     /**
 82  
      * Used to get the number of Quadruples.
 83  
      */
 84  
     static final int FOURBYTE = 4;
 85  
 
 86  
     /**
 87  
      * Used to test the sign of a byte.
 88  
      */
 89  
     static final int SIGN = -128;
 90  
 
 91  
     /**
 92  
      * Byte used to pad output.
 93  
      */
 94  
     static final byte PAD = (byte) '=';
 95  
 
 96  
     /**
 97  
      * Contains the Base64 values <code>0</code> through <code>63</code> accessed by using character encodings as
 98  
      * indices.
 99  
      * <p>
 100  
      * For example, <code>base64Alphabet['+']</code> returns <code>62</code>.
 101  
      * </p>
 102  
      * <p>
 103  
      * The value of undefined encodings is <code>-1</code>.
 104  
      * </p>
 105  
      */
 106  0
     private static byte[] base64Alphabet = new byte[BASELENGTH];
 107  
 
 108  
     /**
 109  
      * <p>
 110  
      * Contains the Base64 encodings <code>A</code> through <code>Z</code>, followed by <code>a</code> through
 111  
      * <code>z</code>, followed by <code>0</code> through <code>9</code>, followed by <code>+</code>, and
 112  
      * <code>/</code>.
 113  
      * </p>
 114  
      * <p>
 115  
      * This array is accessed by using character values as indices.
 116  
      * </p>
 117  
      * <p>
 118  
      * For example, <code>lookUpBase64Alphabet[62] </code> returns <code>'+'</code>.
 119  
      * </p>
 120  
      */
 121  0
     private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
 122  
 
 123  
     // Populating the lookup and character arrays
 124  
     static
 125  
     {
 126  0
         for ( int i = 0; i < BASELENGTH; i++ )
 127  
         {
 128  0
             base64Alphabet[i] = (byte) -1;
 129  
         }
 130  0
         for ( int i = 'Z'; i >= 'A'; i-- )
 131  
         {
 132  0
             base64Alphabet[i] = (byte) ( i - 'A' );
 133  
         }
 134  0
         for ( int i = 'z'; i >= 'a'; i-- )
 135  
         {
 136  0
             base64Alphabet[i] = (byte) ( i - 'a' + 26 );
 137  
         }
 138  0
         for ( int i = '9'; i >= '0'; i-- )
 139  
         {
 140  0
             base64Alphabet[i] = (byte) ( i - '0' + 52 );
 141  
         }
 142  
 
 143  0
         base64Alphabet['+'] = 62;
 144  0
         base64Alphabet['/'] = 63;
 145  
 
 146  0
         for ( int i = 0; i <= 25; i++ )
 147  
         {
 148  0
             lookUpBase64Alphabet[i] = (byte) ( 'A' + i );
 149  
         }
 150  
 
 151  0
         for ( int i = 26, j = 0; i <= 51; i++, j++ )
 152  
         {
 153  0
             lookUpBase64Alphabet[i] = (byte) ( 'a' + j );
 154  
         }
 155  
 
 156  0
         for ( int i = 52, j = 0; i <= 61; i++, j++ )
 157  
         {
 158  0
             lookUpBase64Alphabet[i] = (byte) ( '0' + j );
 159  
         }
 160  
 
 161  0
         lookUpBase64Alphabet[62] = (byte) '+';
 162  0
         lookUpBase64Alphabet[63] = (byte) '/';
 163  0
     }
 164  
 
 165  
     /**
 166  
      * Returns whether or not the <code>octect</code> is in the base 64 alphabet.
 167  
      *
 168  
      * @param octect The value to test
 169  
      * @return <code>true</code> if the value is defined in the the base 64 alphabet, <code>false</code> otherwise.
 170  
      */
 171  
     private static boolean isBase64( byte octect )
 172  
     {
 173  0
         if ( octect == PAD )
 174  
         {
 175  0
             return true;
 176  
         }
 177  0
         else if ( octect < 0 || base64Alphabet[octect] == -1 )
 178  
         {
 179  0
             return false;
 180  
         }
 181  
         else
 182  
         {
 183  0
             return true;
 184  
         }
 185  
     }
 186  
 
 187  
     /**
 188  
      * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet.
 189  
      *
 190  
      * @param arrayOctect byte array to test
 191  
      * @return <code>true</code> if all bytes are valid characters in the Base64 alphabet or if the byte array is empty;
 192  
      *         false, otherwise
 193  
      */
 194  
     public static boolean isArrayByteBase64( byte[] arrayOctect )
 195  
     {
 196  
 
 197  0
         arrayOctect = discardWhitespace( arrayOctect );
 198  
 
 199  0
         int length = arrayOctect.length;
 200  0
         if ( length == 0 )
 201  
         {
 202  
             // shouldn't a 0 length array be valid base64 data?
 203  
             // return false;
 204  0
             return true;
 205  
         }
 206  0
         for ( byte anArrayOctect : arrayOctect )
 207  
         {
 208  0
             if ( !isBase64( anArrayOctect ) )
 209  
             {
 210  0
                 return false;
 211  
             }
 212  
         }
 213  0
         return true;
 214  
     }
 215  
 
 216  
     /**
 217  
      * Encodes binary data using the base64 algorithm but does not chunk the output.
 218  
      *
 219  
      * @param binaryData binary data to encode
 220  
      * @return Base64 characters
 221  
      */
 222  
     public static byte[] encodeBase64( byte[] binaryData )
 223  
     {
 224  0
         return encodeBase64( binaryData, false );
 225  
     }
 226  
 
 227  
     /**
 228  
      * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks
 229  
      *
 230  
      * @param binaryData binary data to encode
 231  
      * @return Base64 characters chunked in 76 character blocks
 232  
      */
 233  
     public static byte[] encodeBase64Chunked( byte[] binaryData )
 234  
     {
 235  0
         return encodeBase64( binaryData, true );
 236  
     }
 237  
 
 238  
     /**
 239  
      * Decodes a byte[] containing containing characters in the Base64 alphabet.
 240  
      *
 241  
      * @param pArray A byte array containing Base64 character data
 242  
      * @return a byte array containing binary data
 243  
      */
 244  
     public byte[] decode( byte[] pArray )
 245  
     {
 246  0
         return decodeBase64( pArray );
 247  
     }
 248  
 
 249  
     /**
 250  
      * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
 251  
      *
 252  
      * @param binaryData Array containing binary data to encode.
 253  
      * @param isChunked if <code>true</code> this encoder will chunk the base64 output into 76 character blocks
 254  
      * @return Base64-encoded data.
 255  
      */
 256  
     public static byte[] encodeBase64( byte[] binaryData, boolean isChunked )
 257  
     {
 258  0
         int lengthDataBits = binaryData.length * EIGHTBIT;
 259  0
         int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
 260  0
         int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
 261  0
         byte encodedData[] = null;
 262  0
         int encodedDataLength = 0;
 263  0
         int nbrChunks = 0;
 264  
 
 265  0
         if ( fewerThan24bits != 0 )
 266  
         {
 267  
             // data not divisible by 24 bit
 268  0
             encodedDataLength = ( numberTriplets + 1 ) * 4;
 269  
         }
 270  
         else
 271  
         {
 272  
             // 16 or 8 bit
 273  0
             encodedDataLength = numberTriplets * 4;
 274  
         }
 275  
 
 276  
         // If the output is to be "chunked" into 76 character sections,
 277  
         // for compliance with RFC 2045 MIME, then it is important to
 278  
         // allow for extra length to account for the separator(s)
 279  0
         if ( isChunked )
 280  
         {
 281  
 
 282  0
             nbrChunks = ( CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math.ceil( (float) encodedDataLength / CHUNK_SIZE ) );
 283  0
             encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
 284  
         }
 285  
 
 286  0
         encodedData = new byte[encodedDataLength];
 287  
 
 288  0
         byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
 289  
 
 290  0
         int encodedIndex = 0;
 291  0
         int dataIndex = 0;
 292  0
         int i = 0;
 293  0
         int nextSeparatorIndex = CHUNK_SIZE;
 294  0
         int chunksSoFar = 0;
 295  
 
 296  
         // log.debug("number of triplets = " + numberTriplets);
 297  0
         for ( i = 0; i < numberTriplets; i++ )
 298  
         {
 299  0
             dataIndex = i * 3;
 300  0
             b1 = binaryData[dataIndex];
 301  0
             b2 = binaryData[dataIndex + 1];
 302  0
             b3 = binaryData[dataIndex + 2];
 303  
 
 304  
             // log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
 305  
 
 306  0
             l = (byte) ( b2 & 0x0f );
 307  0
             k = (byte) ( b1 & 0x03 );
 308  
 
 309  0
             byte val1 = ( ( b1 & SIGN ) == 0 ) ? (byte) ( b1 >> 2 ) : (byte) ( ( b1 ) >> 2 ^ 0xc0 );
 310  0
             byte val2 = ( ( b2 & SIGN ) == 0 ) ? (byte) ( b2 >> 4 ) : (byte) ( ( b2 ) >> 4 ^ 0xf0 );
 311  0
             byte val3 = ( ( b3 & SIGN ) == 0 ) ? (byte) ( b3 >> 6 ) : (byte) ( ( b3 ) >> 6 ^ 0xfc );
 312  
 
 313  0
             encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
 314  
             // log.debug( "val2 = " + val2 );
 315  
             // log.debug( "k4 = " + (k<<4) );
 316  
             // log.debug( "vak = " + (val2 | (k<<4)) );
 317  0
             encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2 | ( k << 4 )];
 318  0
             encodedData[encodedIndex + 2] = lookUpBase64Alphabet[( l << 2 ) | val3];
 319  0
             encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
 320  
 
 321  0
             encodedIndex += 4;
 322  
 
 323  
             // If we are chunking, let's put a chunk separator down.
 324  0
             if ( isChunked )
 325  
             {
 326  
                 // this assumes that CHUNK_SIZE % 4 == 0
 327  0
                 if ( encodedIndex == nextSeparatorIndex )
 328  
                 {
 329  0
                     System.arraycopy( CHUNK_SEPARATOR, 0, encodedData, encodedIndex, CHUNK_SEPARATOR.length );
 330  0
                     chunksSoFar++;
 331  0
                     nextSeparatorIndex =
 332  
                         ( CHUNK_SIZE * ( chunksSoFar + 1 ) ) + ( chunksSoFar * CHUNK_SEPARATOR.length );
 333  0
                     encodedIndex += CHUNK_SEPARATOR.length;
 334  
                 }
 335  
             }
 336  
         }
 337  
 
 338  
         // form integral number of 6-bit groups
 339  0
         dataIndex = i * 3;
 340  
 
 341  0
         if ( fewerThan24bits == EIGHTBIT )
 342  
         {
 343  0
             b1 = binaryData[dataIndex];
 344  0
             k = (byte) ( b1 & 0x03 );
 345  
             // log.debug("b1=" + b1);
 346  
             // log.debug("b1<<2 = " + (b1>>2) );
 347  0
             byte val1 = ( ( b1 & SIGN ) == 0 ) ? (byte) ( b1 >> 2 ) : (byte) ( ( b1 ) >> 2 ^ 0xc0 );
 348  0
             encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
 349  0
             encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
 350  0
             encodedData[encodedIndex + 2] = PAD;
 351  0
             encodedData[encodedIndex + 3] = PAD;
 352  0
         }
 353  0
         else if ( fewerThan24bits == SIXTEENBIT )
 354  
         {
 355  
 
 356  0
             b1 = binaryData[dataIndex];
 357  0
             b2 = binaryData[dataIndex + 1];
 358  0
             l = (byte) ( b2 & 0x0f );
 359  0
             k = (byte) ( b1 & 0x03 );
 360  
 
 361  0
             byte val1 = ( ( b1 & SIGN ) == 0 ) ? (byte) ( b1 >> 2 ) : (byte) ( ( b1 ) >> 2 ^ 0xc0 );
 362  0
             byte val2 = ( ( b2 & SIGN ) == 0 ) ? (byte) ( b2 >> 4 ) : (byte) ( ( b2 ) >> 4 ^ 0xf0 );
 363  
 
 364  0
             encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
 365  0
             encodedData[encodedIndex + 1] = lookUpBase64Alphabet[val2 | ( k << 4 )];
 366  0
             encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
 367  0
             encodedData[encodedIndex + 3] = PAD;
 368  
         }
 369  
 
 370  0
         if ( isChunked )
 371  
         {
 372  
             // we also add a separator to the end of the final chunk.
 373  0
             if ( chunksSoFar < nbrChunks )
 374  
             {
 375  0
                 System.arraycopy( CHUNK_SEPARATOR, 0, encodedData, encodedDataLength - CHUNK_SEPARATOR.length,
 376  
                                   CHUNK_SEPARATOR.length );
 377  
             }
 378  
         }
 379  
 
 380  0
         return encodedData;
 381  
     }
 382  
 
 383  
     /**
 384  
      * Decodes Base64 data into octects
 385  
      *
 386  
      * @param base64Data Byte array containing Base64 data
 387  
      * @return Array containing decoded data.
 388  
      */
 389  
     public static byte[] decodeBase64( byte[] base64Data )
 390  
     {
 391  
         // RFC 2045 requires that we discard ALL non-Base64 characters
 392  0
         base64Data = discardNonBase64( base64Data );
 393  
 
 394  
         // handle the edge case, so we don't have to worry about it later
 395  0
         if ( base64Data.length == 0 )
 396  
         {
 397  0
             return new byte[0];
 398  
         }
 399  
 
 400  0
         int numberQuadruple = base64Data.length / FOURBYTE;
 401  0
         byte decodedData[] = null;
 402  0
         byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
 403  
 
 404  
         // Throw away anything not in base64Data
 405  
 
 406  0
         int encodedIndex = 0;
 407  0
         int dataIndex = 0;
 408  
         {
 409  
             // this sizes the output array properly - rlw
 410  0
             int lastData = base64Data.length;
 411  
             // ignore the '=' padding
 412  0
             while ( base64Data[lastData - 1] == PAD )
 413  
             {
 414  0
                 if ( --lastData == 0 )
 415  
                 {
 416  0
                     return new byte[0];
 417  
                 }
 418  
             }
 419  0
             decodedData = new byte[lastData - numberQuadruple];
 420  
         }
 421  
 
 422  0
         for ( int i = 0; i < numberQuadruple; i++ )
 423  
         {
 424  0
             dataIndex = i * 4;
 425  0
             marker0 = base64Data[dataIndex + 2];
 426  0
             marker1 = base64Data[dataIndex + 3];
 427  
 
 428  0
             b1 = base64Alphabet[base64Data[dataIndex]];
 429  0
             b2 = base64Alphabet[base64Data[dataIndex + 1]];
 430  
 
 431  0
             if ( marker0 != PAD && marker1 != PAD )
 432  
             {
 433  
                 // No PAD e.g 3cQl
 434  0
                 b3 = base64Alphabet[marker0];
 435  0
                 b4 = base64Alphabet[marker1];
 436  
 
 437  0
                 decodedData[encodedIndex] = (byte) ( b1 << 2 | b2 >> 4 );
 438  0
                 decodedData[encodedIndex + 1] = (byte) ( ( ( b2 & 0xf ) << 4 ) | ( ( b3 >> 2 ) & 0xf ) );
 439  0
                 decodedData[encodedIndex + 2] = (byte) ( b3 << 6 | b4 );
 440  
             }
 441  0
             else if ( marker0 == PAD )
 442  
             {
 443  
                 // Two PAD e.g. 3c[Pad][Pad]
 444  0
                 decodedData[encodedIndex] = (byte) ( b1 << 2 | b2 >> 4 );
 445  
             }
 446  0
             else if ( marker1 == PAD )
 447  
             {
 448  
                 // One PAD e.g. 3cQ[Pad]
 449  0
                 b3 = base64Alphabet[marker0];
 450  
 
 451  0
                 decodedData[encodedIndex] = (byte) ( b1 << 2 | b2 >> 4 );
 452  0
                 decodedData[encodedIndex + 1] = (byte) ( ( ( b2 & 0xf ) << 4 ) | ( ( b3 >> 2 ) & 0xf ) );
 453  
             }
 454  0
             encodedIndex += 3;
 455  
         }
 456  0
         return decodedData;
 457  
     }
 458  
 
 459  
     /**
 460  
      * Discards any whitespace from a base-64 encoded block.
 461  
      *
 462  
      * @param data The base-64 encoded data to discard the whitespace from.
 463  
      * @return The data, less whitespace (see RFC 2045).
 464  
      */
 465  
     static byte[] discardWhitespace( byte[] data )
 466  
     {
 467  0
         byte groomedData[] = new byte[data.length];
 468  0
         int bytesCopied = 0;
 469  
 
 470  0
         for ( byte aData : data )
 471  
         {
 472  0
             switch ( aData )
 473  
             {
 474  
                 case (byte) ' ':
 475  
                 case (byte) '\n':
 476  
                 case (byte) '\r':
 477  
                 case (byte) '\t':
 478  0
                     break;
 479  
                 default:
 480  0
                     groomedData[bytesCopied++] = aData;
 481  
             }
 482  
         }
 483  
 
 484  0
         byte packedData[] = new byte[bytesCopied];
 485  
 
 486  0
         System.arraycopy( groomedData, 0, packedData, 0, bytesCopied );
 487  
 
 488  0
         return packedData;
 489  
     }
 490  
 
 491  
     /**
 492  
      * Discards any characters outside of the base64 alphabet, per the requirements on page 25 of RFC 2045 - "Any
 493  
      * characters outside of the base64 alphabet are to be ignored in base64 encoded data."
 494  
      *
 495  
      * @param data The base-64 encoded data to groom
 496  
      * @return The data, less non-base64 characters (see RFC 2045).
 497  
      */
 498  
     static byte[] discardNonBase64( byte[] data )
 499  
     {
 500  0
         byte groomedData[] = new byte[data.length];
 501  0
         int bytesCopied = 0;
 502  
 
 503  0
         for ( byte aData : data )
 504  
         {
 505  0
             if ( isBase64( aData ) )
 506  
             {
 507  0
                 groomedData[bytesCopied++] = aData;
 508  
             }
 509  
         }
 510  
 
 511  0
         byte packedData[] = new byte[bytesCopied];
 512  
 
 513  0
         System.arraycopy( groomedData, 0, packedData, 0, bytesCopied );
 514  
 
 515  0
         return packedData;
 516  
     }
 517  
 
 518  
     /**
 519  
      * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet.
 520  
      *
 521  
      * @param pArray a byte array containing binary data
 522  
      * @return A byte array containing only Base64 character data
 523  
      */
 524  
     public byte[] encode( byte[] pArray )
 525  
     {
 526  0
         return encodeBase64( pArray, false );
 527  
     }
 528  
 
 529  
 }