Home » openejb-3.1.2-src » org.apache » openejb » util » [javadoc | source]

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

Home » openejb-3.1.2-src » org.apache » openejb » util » [javadoc | source]