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.util;
15  
16  import java.io.ByteArrayOutputStream;
17  import java.io.UnsupportedEncodingException;
18  
19  
20  /* ------------------------------------------------------------ */
21  /** Fast B64 Encoder/Decoder as described in RFC 1421.
22   * <p>Does not insert or interpret whitespace as described in RFC
23   * 1521. If you require this you must pre/post process your data.
24   * <p> Note that in a web context the usual case is to not want
25   * linebreaks or other white space in the encoded output.
26   * 
27   */
28  public class B64Code
29  {
30      // ------------------------------------------------------------------
31      static final char pad='=';
32      static final char[] rfc1421alphabet=
33              {
34                  'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
35                  'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
36                  'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
37                  'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
38              };
39  
40      static final byte[] rfc1421nibbles;
41  
42      static
43      {
44          rfc1421nibbles=new byte[256];
45          for (int i=0;i<256;i++)
46              rfc1421nibbles[i]=-1;
47          for (byte b=0;b<64;b++)
48              rfc1421nibbles[(byte)rfc1421alphabet[b]]=b;
49          rfc1421nibbles[(byte)pad]=0;
50      }
51  
52      // ------------------------------------------------------------------
53      /**
54       * Base 64 encode as described in RFC 1421.
55       * <p>Does not insert whitespace as described in RFC 1521.
56       * @param s String to encode.
57       * @return String containing the encoded form of the input.
58       */
59      static public String encode(String s)
60      {
61          try
62          {
63              return encode(s,null);
64          }
65          catch (UnsupportedEncodingException e)
66          {
67              throw new IllegalArgumentException(e.toString());
68          }
69      }
70  
71      // ------------------------------------------------------------------
72      /**
73       * Base 64 encode as described in RFC 1421.
74       * <p>Does not insert whitespace as described in RFC 1521.
75       * @param s String to encode.
76       * @param charEncoding String representing the name of
77       *        the character encoding of the provided input String.
78       * @return String containing the encoded form of the input.
79       */
80      static public String encode(String s,String charEncoding)
81              throws UnsupportedEncodingException
82      {
83          byte[] bytes;
84          if (charEncoding==null)
85              bytes=s.getBytes(StringUtil.__ISO_8859_1);
86          else
87              bytes=s.getBytes(charEncoding);
88  
89          return new String(encode(bytes));
90      }
91      
92      // ------------------------------------------------------------------
93      /**
94       * Fast Base 64 encode as described in RFC 1421.
95       * <p>Does not insert whitespace as described in RFC 1521.
96       * <p> Avoids creating extra copies of the input/output.
97       * @param b byte array to encode.
98       * @return char array containing the encoded form of the input.
99       */
100     static public char[] encode(byte[] b)
101     {
102         return encode(b,false);
103     }
104     
105     // ------------------------------------------------------------------
106     /**
107      * Fast Base 64 encode as described in RFC 1421 and RFC2045
108      * <p>Does not insert whitespace as described in RFC 1521, unless rfc2045 is passed as true.
109      * <p> Avoids creating extra copies of the input/output.
110      * @param b byte array to encode.
111      * @param rfc2045 If true, break lines at 76 characters with CRLF
112      * @return char array containing the encoded form of the input.
113      */
114     static public char[] encode(byte[] b, boolean rfc2045)
115     {
116         if (b==null)
117             return null;
118 
119         int bLen=b.length;
120         int cLen=((bLen+2)/3)*4;
121         if (rfc2045)
122             cLen+=2+2*cLen/76;
123         char c[]=new char[cLen];
124         int ci=0;
125         int bi=0;
126         byte b0, b1, b2;
127         int stop=(bLen/3)*3;
128         int l=0;
129         while (bi<stop)
130         {
131             b0=b[bi++];
132             b1=b[bi++];
133             b2=b[bi++];
134             c[ci++]=rfc1421alphabet[(b0>>>2)&0x3f];
135             c[ci++]=rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
136             c[ci++]=rfc1421alphabet[(b1<<2)&0x3f|(b2>>>6)&0x03];
137             c[ci++]=rfc1421alphabet[b2&077];
138             l+=4;
139             if (rfc2045 && l%76==0)
140             {
141                 c[ci++]=13;
142                 c[ci++]=10;
143             }
144         }
145 
146         if (bLen!=bi)
147         {
148             switch (bLen%3)
149             {
150                 case 2:
151                     b0=b[bi++];
152                     b1=b[bi++];
153                     c[ci++]=rfc1421alphabet[(b0>>>2)&0x3f];
154                     c[ci++]=rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
155                     c[ci++]=rfc1421alphabet[(b1<<2)&0x3f];
156                     c[ci++]=pad;
157                     break;
158 
159                 case 1:
160                     b0=b[bi++];
161                     c[ci++]=rfc1421alphabet[(b0>>>2)&0x3f];
162                     c[ci++]=rfc1421alphabet[(b0<<4)&0x3f];
163                     c[ci++]=pad;
164                     c[ci++]=pad;
165                     break;
166 
167                 default:
168                     break;
169             }
170         }
171 
172         if (rfc2045)
173         {
174             c[ci++]=13;
175             c[ci++]=10;
176         }
177         return c;
178     }
179 
180     // ------------------------------------------------------------------
181     /**
182      * Base 64 decode as described in RFC 2045.
183      * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
184      * @param encoded String to decode.
185      * @param charEncoding String representing the character encoding
186      *        used to map the decoded bytes into a String.
187      * @return String decoded byte array.
188      * @throws UnsupportedEncodingException if the encoding is not supported
189      * @throws IllegalArgumentException if the input is not a valid
190      *         B64 encoding.
191      */
192     static public String decode(String encoded,String charEncoding)
193             throws UnsupportedEncodingException
194     {
195         byte[] decoded=decode(encoded);
196         if (charEncoding==null)
197             return new String(decoded);
198         return new String(decoded,charEncoding);
199     }
200 
201     /* ------------------------------------------------------------ */
202     /**
203      * Fast Base 64 decode as described in RFC 1421.
204      * 
205      * <p>Unlike other decode methods, this does not attempt to 
206      * cope with extra whitespace as described in RFC 1521/2045.
207      * <p> Avoids creating extra copies of the input/output.
208      * <p> Note this code has been flattened for performance.
209      * @param b char array to decode.
210      * @return byte array containing the decoded form of the input.
211      * @throws IllegalArgumentException if the input is not a valid
212      *         B64 encoding.
213      */
214     static public byte[] decode(char[] b)
215     {
216         if (b==null)
217             return null;
218 
219         int bLen=b.length;
220         if (bLen%4!=0)
221             throw new IllegalArgumentException("Input block size is not 4");
222 
223         int li=bLen-1;
224         while (li>=0 && b[li]==(byte)pad)
225             li--;
226 
227         if (li<0)
228             return new byte[0];
229 
230         // Create result array of exact required size.
231         int rLen=((li+1)*3)/4;
232         byte r[]=new byte[rLen];
233         int ri=0;
234         int bi=0;
235         int stop=(rLen/3)*3;
236         byte b0,b1,b2,b3;
237         try
238         {
239             while (ri<stop)
240             {
241                 b0=rfc1421nibbles[b[bi++]];
242                 b1=rfc1421nibbles[b[bi++]];
243                 b2=rfc1421nibbles[b[bi++]];
244                 b3=rfc1421nibbles[b[bi++]];
245                 if (b0<0 || b1<0 || b2<0 || b3<0)
246                     throw new IllegalArgumentException("Not B64 encoded");
247 
248                 r[ri++]=(byte)(b0<<2|b1>>>4);
249                 r[ri++]=(byte)(b1<<4|b2>>>2);
250                 r[ri++]=(byte)(b2<<6|b3);
251             }
252 
253             if (rLen!=ri)
254             {
255                 switch (rLen%3)
256                 {
257                     case 2:
258                         b0=rfc1421nibbles[b[bi++]];
259                         b1=rfc1421nibbles[b[bi++]];
260                         b2=rfc1421nibbles[b[bi++]];
261                         if (b0<0 || b1<0 || b2<0)
262                             throw new IllegalArgumentException("Not B64 encoded");
263                         r[ri++]=(byte)(b0<<2|b1>>>4);
264                         r[ri++]=(byte)(b1<<4|b2>>>2);
265                         break;
266 
267                     case 1:
268                         b0=rfc1421nibbles[b[bi++]];
269                         b1=rfc1421nibbles[b[bi++]];
270                         if (b0<0 || b1<0)
271                             throw new IllegalArgumentException("Not B64 encoded");
272                         r[ri++]=(byte)(b0<<2|b1>>>4);
273                         break;
274 
275                     default:
276                         break;
277                 }
278             }
279         }
280         catch (IndexOutOfBoundsException e)
281         {
282             throw new IllegalArgumentException("char "+bi
283                     +" was not B64 encoded");
284         }
285 
286         return r;
287     }
288     
289     /* ------------------------------------------------------------ */
290     /**
291      * Base 64 decode as described in RFC 2045.
292      * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
293      * @param encoded String to decode.
294      * @return byte array containing the decoded form of the input.
295      * @throws IllegalArgumentException if the input is not a valid
296      *         B64 encoding.
297      */
298     static public byte[] decode(String encoded)
299     {
300         if (encoded==null)
301             return null;
302         
303         int ci=0;
304         byte nibbles[] = new byte[4];
305         int s=0;
306         ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);
307 
308         while (ci<encoded.length())
309         {
310             char c=encoded.charAt(ci++);
311 
312             if (c==pad)
313                 break;
314             
315             if (Character.isWhitespace(c))
316                 continue;
317 
318             byte nibble=rfc1421nibbles[c];
319             if (nibble<0)
320                 throw new IllegalArgumentException("Not B64 encoded");
321 
322             nibbles[s++]=rfc1421nibbles[c];
323 
324             switch(s)
325             {
326                 case 1:
327                     break;
328                 case 2:
329                     bout.write(nibbles[0]<<2|nibbles[1]>>>4);
330                     break;
331                 case 3:
332                     bout.write(nibbles[1]<<4|nibbles[2]>>>2);
333                     break;
334                 case 4:
335                     bout.write(nibbles[2]<<6|nibbles[3]);
336                     s=0;
337                     break;
338             }
339 
340         }
341 
342         return bout.toByteArray();
343     }
344 }