View Javadoc

    1   // ========================================================================
    2   // Copyright 1999-2005 Mort Bay Consulting Pty. Ltd.
    3   // ------------------------------------------------------------------------
    4   // Licensed under the Apache License, Version 2.0 (the "License");
    5   // you may not use this file except in compliance with the License.
    6   // You may obtain a copy of the License at
    7   // http://www.apache.org/licenses/LICENSE-2.0
    8   // Unless required by applicable law or agreed to in writing, software
    9   // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  // ========================================================================
    14
    15  package org.mortbay.jetty.security;
    16
    17  import java.io.UnsupportedEncodingException;
    18
    19  import org.mortbay.util.StringUtil;
    20
    21  /* ------------------------------------------------------------ */
    22
    23  /** Fast B64 Encoder/Decoder as described in RFC 1421.
    24   * <p>Does not insert or interpret whitespace as described in RFC
    25   * 1521. If you require this you must pre/post process your data.
    26   * <p> Note that in a web context the usual case is to not want
    27   * linebreaks or other white space in the encoded output.
    28   *
    29   * @author Brett Sealey (bretts)
    30   * @author Greg Wilkins (gregw)
    31   */
    32  public class B64Code
    33  {
    34      // ------------------------------------------------------------------
    35      static final char pad='=';
    36      static final char[] nibble2code=
    37              {
    38                  'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
    39                  'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
    40                  'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
    41                  'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
    42              };
    43
    44      static byte[] code2nibble=null;
    45
    46      static
    47      {
    48          code2nibble=new byte[256];
    49          for (int i=0;i<256;i++)
    50              code2nibble[i]=-1;
    51          for (byte b=0;b<64;b++)
    52              code2nibble[(byte)nibble2code[b]]=b;
    53          code2nibble[(byte)pad]=0;
    54      }
    55
    56      // ------------------------------------------------------------------
    57      /**
    58       * Base 64 encode as described in RFC 1421.
    59       * <p>Does not insert whitespace as described in RFC 1521.
    60       * @param s String to encode.
    61       * @return String containing the encoded form of the input.
    62       */
    63      static public String encode(String s)
    64      {
    65          try
    66          {
    67              return encode(s,null);
    68          }
    69          catch (UnsupportedEncodingException e)
    70          {
    71              throw new IllegalArgumentException(e.toString());
    72          }
    73      }
    74
    75      // ------------------------------------------------------------------
    76      /**
    77       * Base 64 encode as described in RFC 1421.
    78       * <p>Does not insert whitespace as described in RFC 1521.
    79       * @param s String to encode.
    80       * @param charEncoding String representing the name of
    81       *        the character encoding of the provided input String.
    82       * @return String containing the encoded form of the input.
    83       */
    84      static public String encode(String s,String charEncoding)
    85              throws UnsupportedEncodingException
    86      {
    87          byte[] bytes;
    88          if (charEncoding==null)
    89              bytes=s.getBytes(StringUtil.__ISO_8859_1);
    90          else
    91              bytes=s.getBytes(charEncoding);
    92
    93          return new String(encode(bytes));
    94      }
    95
    96      // ------------------------------------------------------------------
    97      /**
    98       * Fast Base 64 encode as described in RFC 1421.
    99       * <p>Does not insert whitespace as described in RFC 1521.
    100      * <p> Avoids creating extra copies of the input/output.
    101      * @param b byte array to encode.
    102      * @return char array containing the encoded form of the input.
    103      */
    104     static public char[] encode(byte[] b)
    105     {
    106         if (b==null)
    107             return null;
    108
    109         int bLen=b.length;
    110         char r[]=new char[((bLen+2)/3)*4];
    111         int ri=0;
    112         int bi=0;
    113         byte b0, b1, b2;
    114         int stop=(bLen/3)*3;
    115         while (bi<stop)
    116         {
    117             b0=b[bi++];
    118             b1=b[bi++];
    119             b2=b[bi++];
    120             r[ri++]=nibble2code[(b0>>>2)&0x3f];
    121             r[ri++]=nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f];
    122             r[ri++]=nibble2code[(b1<<2)&0x3f|(b2>>>6)&0x03];
    123             r[ri++]=nibble2code[b2&077];
    124         }
    125
    126         if (bLen!=bi)
    127         {
    128             switch (bLen%3)
    129             {
    130                 case 2:
    131                     b0=b[bi++];
    132                     b1=b[bi++];
    133                     r[ri++]=nibble2code[(b0>>>2)&0x3f];
    134                     r[ri++]=nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f];
    135                     r[ri++]=nibble2code[(b1<<2)&0x3f];
    136                     r[ri++]=pad;
    137                     break;
    138
    139                 case 1:
    140                     b0=b[bi++];
    141                     r[ri++]=nibble2code[(b0>>>2)&0x3f];
    142                     r[ri++]=nibble2code[(b0<<4)&0x3f];
    143                     r[ri++]=pad;
    144                     r[ri++]=pad;
    145                     break;
    146
    147                 default:
    148                     break;
    149             }
    150         }
    151
    152         return r;
    153     }
    154
    155     // ------------------------------------------------------------------
    156     /**
    157      * Base 64 decode as described in RFC 1421.
    158      * <p>Does not attempt to cope with extra whitespace
    159      * as described in RFC 1521.
    160      * @param s String to decode
    161      * @return String decoded byte array.
    162      */
    163     static public String decode(String s)
    164     {
    165         try
    166         {
    167             return decode(s,StringUtil.__ISO_8859_1);
    168         }
    169         catch (UnsupportedEncodingException e)
    170         {
    171             throw new IllegalArgumentException(e.toString());
    172         }
    173     }
    174
    175     // ------------------------------------------------------------------
    176     /**
    177      * Base 64 decode as described in RFC 1421.
    178      * <p>Does not attempt to cope with extra whitespace
    179      * as described in RFC 1521.
    180      * @param s String to decode
    181      * @param charEncoding String representing the character encoding
    182      *        used to map the decoded bytes into a String.
    183      * @return String decoded byte array.
    184      */
    185     static public String decode(String s,String charEncoding)
    186             throws UnsupportedEncodingException
    187     {
    188         byte[] decoded=decode(s.toCharArray());
    189
    190         if (charEncoding==null)
    191             return new String(decoded);
    192         return new String(decoded,charEncoding);
    193     }
    194
    195     /* ------------------------------------------------------------ */
    196     /**
    197      * Fast Base 64 decode as described in RFC 1421.
    198      * <p>Does not attempt to cope with extra whitespace
    199      * as described in RFC 1521.
    200      * <p> Avoids creating extra copies of the input/output.
    201      * <p> Note this code has been flattened for performance.
    202      * @param b char array to decode.
    203      * @return byte array containing the decoded form of the input.
    204      * @throws IllegalArgumentException if the input is not a valid
    205      *         B64 encoding.
    206      */
    207     static public byte[] decode(char[] b)
    208     {
    209         if (b==null)
    210             return null;
    211
    212         int bLen=b.length;
    213         if (bLen%4!=0)
    214             throw new IllegalArgumentException("Input block size is not 4");
    215
    216         int li=bLen-1;
    217         while (li>=0 && b[li]==(byte)pad)
    218             li--;
    219
    220         if (li<0)
    221             return new byte[0];
    222
    223         // Create result array of exact required size.
    224         int rLen=((li+1)*3)/4;
    225         byte r[]=new byte[rLen];
    226         int ri=0;
    227         int bi=0;
    228         int stop=(rLen/3)*3;
    229         byte b0,b1,b2,b3;
    230         try
    231         {
    232             while (ri<stop)
    233             {
    234                 b0=code2nibble[b[bi++]];
    235                 b1=code2nibble[b[bi++]];
    236                 b2=code2nibble[b[bi++]];
    237                 b3=code2nibble[b[bi++]];
    238                 if (b0<0 || b1<0 || b2<0 || b3<0)
    239                     throw new IllegalArgumentException("Not B64 encoded");
    240
    241                 r[ri++]=(byte)(b0<<2|b1>>>4);
    242                 r[ri++]=(byte)(b1<<4|b2>>>2);
    243                 r[ri++]=(byte)(b2<<6|b3);
    244             }
    245
    246             if (rLen!=ri)
    247             {
    248                 switch (rLen%3)
    249                 {
    250                     case 2:
    251                         b0=code2nibble[b[bi++]];
    252                         b1=code2nibble[b[bi++]];
    253                         b2=code2nibble[b[bi++]];
    254                         if (b0<0 || b1<0 || b2<0)
    255                             throw new IllegalArgumentException("Not B64 encoded");
    256                         r[ri++]=(byte)(b0<<2|b1>>>4);
    257                         r[ri++]=(byte)(b1<<4|b2>>>2);
    258                         break;
    259
    260                     case 1:
    261                         b0=code2nibble[b[bi++]];
    262                         b1=code2nibble[b[bi++]];
    263                         if (b0<0 || b1<0)
    264                             throw new IllegalArgumentException("Not B64 encoded");
    265                         r[ri++]=(byte)(b0<<2|b1>>>4);
    266                         break;
    267
    268                     default:
    269                         break;
    270                 }
    271             }
    272         }
    273         catch (IndexOutOfBoundsException e)
    274         {
    275             throw new IllegalArgumentException("char "+bi
    276                     +" was not B64 encoded");
    277         }
    278
    279         return r;
    280     }
    281 }