View Javadoc

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