View Javadoc

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