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 }