View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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.IOException;
23  import java.io.UnsupportedEncodingException;
24  
25  
26  /* ------------------------------------------------------------ */
27  /** Fast B64 Encoder/Decoder as described in RFC 1421.
28   * <p>Does not insert or interpret whitespace as described in RFC
29   * 1521. If you require this you must pre/post process your data.
30   * <p> Note that in a web context the usual case is to not want
31   * linebreaks or other white space in the encoded output.
32   * 
33   */
34  public class B64Code
35  {
36      // ------------------------------------------------------------------
37      static final char __pad='=';
38      static final char[] __rfc1421alphabet=
39              {
40                  'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
41                  'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
42                  'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
43                  'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
44              };
45  
46      static final byte[] __rfc1421nibbles;
47  
48      static
49      {
50          __rfc1421nibbles=new byte[256];
51          for (int i=0;i<256;i++)
52              __rfc1421nibbles[i]=-1;
53          for (byte b=0;b<64;b++)
54              __rfc1421nibbles[(byte)__rfc1421alphabet[b]]=b;
55          __rfc1421nibbles[(byte)__pad]=0;
56      }
57  
58      // ------------------------------------------------------------------
59      /**
60       * Base 64 encode as described in RFC 1421.
61       * <p>Does not insert whitespace as described in RFC 1521.
62       * @param s String to encode.
63       * @return String containing the encoded form of the input.
64       */
65      static public String encode(String s)
66      {
67          try
68          {
69              return encode(s,null);
70          }
71          catch (UnsupportedEncodingException e)
72          {
73              throw new IllegalArgumentException(e.toString());
74          }
75      }
76  
77      // ------------------------------------------------------------------
78      /**
79       * Base 64 encode as described in RFC 1421.
80       * <p>Does not insert whitespace as described in RFC 1521.
81       * @param s String to encode.
82       * @param charEncoding String representing the name of
83       *        the character encoding of the provided input String.
84       * @return String containing the encoded form of the input.
85       */
86      static public String encode(String s,String charEncoding)
87              throws UnsupportedEncodingException
88      {
89          byte[] bytes;
90          if (charEncoding==null)
91              bytes=s.getBytes(StringUtil.__ISO_8859_1);
92          else
93              bytes=s.getBytes(charEncoding);
94  
95          return new String(encode(bytes));
96      }
97      
98      // ------------------------------------------------------------------
99      /**
100      * Fast Base 64 encode as described in RFC 1421.
101      * <p>Does not insert whitespace as described in RFC 1521.
102      * <p> Avoids creating extra copies of the input/output.
103      * @param b byte array to encode.
104      * @return char array containing the encoded form of the input.
105      */
106     static public char[] encode(byte[] b)
107     {
108         if (b==null)
109             return null;
110 
111         int bLen=b.length;
112         int cLen=((bLen+2)/3)*4;
113         char c[]=new char[cLen];
114         int ci=0;
115         int bi=0;
116         byte b0, b1, b2;
117         int stop=(bLen/3)*3;
118         while (bi<stop)
119         {
120             b0=b[bi++];
121             b1=b[bi++];
122             b2=b[bi++];
123             c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
124             c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
125             c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f|(b2>>>6)&0x03];
126             c[ci++]=__rfc1421alphabet[b2&077];
127         }
128 
129         if (bLen!=bi)
130         {
131             switch (bLen%3)
132             {
133                 case 2:
134                     b0=b[bi++];
135                     b1=b[bi++];
136                     c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
137                     c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
138                     c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f];
139                     c[ci++]=__pad;
140                     break;
141 
142                 case 1:
143                     b0=b[bi++];
144                     c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
145                     c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f];
146                     c[ci++]=__pad;
147                     c[ci++]=__pad;
148                     break;
149 
150                 default:
151                     break;
152             }
153         }
154 
155         return c;
156     }
157     
158     // ------------------------------------------------------------------
159     /**
160      * Fast Base 64 encode as described in RFC 1421 and RFC2045
161      * <p>Does not insert whitespace as described in RFC 1521, unless rfc2045 is passed as true.
162      * <p> Avoids creating extra copies of the input/output.
163      * @param b byte array to encode.
164      * @param rfc2045 If true, break lines at 76 characters with CRLF
165      * @return char array containing the encoded form of the input.
166      */
167     static public char[] encode(byte[] b, boolean rfc2045)
168     {
169         if (b==null)
170             return null;
171         if (!rfc2045)
172             return encode(b);
173 
174         int bLen=b.length;
175         int cLen=((bLen+2)/3)*4;
176         cLen+=2+2*(cLen/76);
177         char c[]=new char[cLen];
178         int ci=0;
179         int bi=0;
180         byte b0, b1, b2;
181         int stop=(bLen/3)*3;
182         int l=0;
183         while (bi<stop)
184         {
185             b0=b[bi++];
186             b1=b[bi++];
187             b2=b[bi++];
188             c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
189             c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
190             c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f|(b2>>>6)&0x03];
191             c[ci++]=__rfc1421alphabet[b2&077];
192             l+=4;
193             if (l%76==0)
194             {
195                 c[ci++]=13;
196                 c[ci++]=10;
197             }
198         }
199 
200         if (bLen!=bi)
201         {
202             switch (bLen%3)
203             {
204                 case 2:
205                     b0=b[bi++];
206                     b1=b[bi++];
207                     c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
208                     c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
209                     c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f];
210                     c[ci++]=__pad;
211                     break;
212 
213                 case 1:
214                     b0=b[bi++];
215                     c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
216                     c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f];
217                     c[ci++]=__pad;
218                     c[ci++]=__pad;
219                     break;
220 
221                 default:
222                     break;
223             }
224         }
225 
226         c[ci++]=13;
227         c[ci++]=10;
228         return c;
229     }
230 
231     // ------------------------------------------------------------------
232     /**
233      * Base 64 decode as described in RFC 2045.
234      * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
235      * @param encoded String to decode.
236      * @param charEncoding String representing the character encoding
237      *        used to map the decoded bytes into a String.
238      * @return String decoded byte array.
239      * @throws UnsupportedEncodingException if the encoding is not supported
240      * @throws IllegalArgumentException if the input is not a valid
241      *         B64 encoding.
242      */
243     static public String decode(String encoded,String charEncoding)
244             throws UnsupportedEncodingException
245     {
246         byte[] decoded=decode(encoded);
247         if (charEncoding==null)
248             return new String(decoded);
249         return new String(decoded,charEncoding);
250     }
251 
252     /* ------------------------------------------------------------ */
253     /**
254      * Fast Base 64 decode as described in RFC 1421.
255      * 
256      * <p>Unlike other decode methods, this does not attempt to 
257      * cope with extra whitespace as described in RFC 1521/2045.
258      * <p> Avoids creating extra copies of the input/output.
259      * <p> Note this code has been flattened for performance.
260      * @param b char array to decode.
261      * @return byte array containing the decoded form of the input.
262      * @throws IllegalArgumentException if the input is not a valid
263      *         B64 encoding.
264      */
265     static public byte[] decode(char[] b)
266     {
267         if (b==null)
268             return null;
269 
270         int bLen=b.length;
271         if (bLen%4!=0)
272             throw new IllegalArgumentException("Input block size is not 4");
273 
274         int li=bLen-1;
275         while (li>=0 && b[li]==(byte)__pad)
276             li--;
277 
278         if (li<0)
279             return new byte[0];
280 
281         // Create result array of exact required size.
282         int rLen=((li+1)*3)/4;
283         byte r[]=new byte[rLen];
284         int ri=0;
285         int bi=0;
286         int stop=(rLen/3)*3;
287         byte b0,b1,b2,b3;
288         try
289         {
290             while (ri<stop)
291             {
292                 b0=__rfc1421nibbles[b[bi++]];
293                 b1=__rfc1421nibbles[b[bi++]];
294                 b2=__rfc1421nibbles[b[bi++]];
295                 b3=__rfc1421nibbles[b[bi++]];
296                 if (b0<0 || b1<0 || b2<0 || b3<0)
297                     throw new IllegalArgumentException("Not B64 encoded");
298 
299                 r[ri++]=(byte)(b0<<2|b1>>>4);
300                 r[ri++]=(byte)(b1<<4|b2>>>2);
301                 r[ri++]=(byte)(b2<<6|b3);
302             }
303 
304             if (rLen!=ri)
305             {
306                 switch (rLen%3)
307                 {
308                     case 2:
309                         b0=__rfc1421nibbles[b[bi++]];
310                         b1=__rfc1421nibbles[b[bi++]];
311                         b2=__rfc1421nibbles[b[bi++]];
312                         if (b0<0 || b1<0 || b2<0)
313                             throw new IllegalArgumentException("Not B64 encoded");
314                         r[ri++]=(byte)(b0<<2|b1>>>4);
315                         r[ri++]=(byte)(b1<<4|b2>>>2);
316                         break;
317 
318                     case 1:
319                         b0=__rfc1421nibbles[b[bi++]];
320                         b1=__rfc1421nibbles[b[bi++]];
321                         if (b0<0 || b1<0)
322                             throw new IllegalArgumentException("Not B64 encoded");
323                         r[ri++]=(byte)(b0<<2|b1>>>4);
324                         break;
325 
326                     default:
327                         break;
328                 }
329             }
330         }
331         catch (IndexOutOfBoundsException e)
332         {
333             throw new IllegalArgumentException("char "+bi
334                     +" was not B64 encoded");
335         }
336 
337         return r;
338     }
339     
340     /* ------------------------------------------------------------ */
341     /**
342      * Base 64 decode as described in RFC 2045.
343      * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
344      * @param encoded String to decode.
345      * @return byte array containing the decoded form of the input.
346      * @throws IllegalArgumentException if the input is not a valid
347      *         B64 encoding.
348      */
349     static public byte[] decode(String encoded)
350     {
351         if (encoded==null)
352             return null;
353 
354         ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);        
355         decode(encoded, bout);
356         return bout.toByteArray();
357     }
358     
359     /* ------------------------------------------------------------ */
360     /**
361      * Base 64 decode as described in RFC 2045.
362      * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
363      * @param encoded String to decode.
364      * @param output stream for decoded bytes
365      * @return byte array containing the decoded form of the input.
366      * @throws IllegalArgumentException if the input is not a valid
367      *         B64 encoding.
368      */
369     static public void decode (String encoded, ByteArrayOutputStream bout)
370     {
371         if (encoded==null)
372             return;
373         
374         if (bout == null)
375             throw new IllegalArgumentException("No outputstream for decoded bytes");
376         
377         int ci=0;
378         byte nibbles[] = new byte[4];
379         int s=0;
380   
381         while (ci<encoded.length())
382         {
383             char c=encoded.charAt(ci++);
384 
385             if (c==__pad)
386                 break;
387             
388             if (Character.isWhitespace(c))
389                 continue;
390 
391             byte nibble=__rfc1421nibbles[c];
392             if (nibble<0)
393                 throw new IllegalArgumentException("Not B64 encoded");
394 
395             nibbles[s++]=__rfc1421nibbles[c];
396 
397             switch(s)
398             {
399                 case 1:
400                     break;
401                 case 2:
402                     bout.write(nibbles[0]<<2|nibbles[1]>>>4);
403                     break;
404                 case 3:
405                     bout.write(nibbles[1]<<4|nibbles[2]>>>2);
406                     break;
407                 case 4:
408                     bout.write(nibbles[2]<<6|nibbles[3]);
409                     s=0;
410                     break;
411             }
412 
413         }
414 
415         return;
416     }
417     
418     
419     /* ------------------------------------------------------------ */
420     public static void encode(int value,Appendable buf) throws IOException
421     {
422         buf.append(__rfc1421alphabet[0x3f&((0xFC000000&value)>>26)]);
423         buf.append(__rfc1421alphabet[0x3f&((0x03F00000&value)>>20)]);
424         buf.append(__rfc1421alphabet[0x3f&((0x000FC000&value)>>14)]);
425         buf.append(__rfc1421alphabet[0x3f&((0x00003F00&value)>>8)]);
426         buf.append(__rfc1421alphabet[0x3f&((0x000000FC&value)>>2)]);
427         buf.append(__rfc1421alphabet[0x3f&((0x00000003&value)<<4)]);
428         buf.append('=');
429     }
430     
431     /* ------------------------------------------------------------ */
432     public static void encode(long lvalue,Appendable buf) throws IOException
433     {
434         int value=(int)(0xFFFFFFFC&(lvalue>>32));
435         buf.append(__rfc1421alphabet[0x3f&((0xFC000000&value)>>26)]);
436         buf.append(__rfc1421alphabet[0x3f&((0x03F00000&value)>>20)]);
437         buf.append(__rfc1421alphabet[0x3f&((0x000FC000&value)>>14)]);
438         buf.append(__rfc1421alphabet[0x3f&((0x00003F00&value)>>8)]);
439         buf.append(__rfc1421alphabet[0x3f&((0x000000FC&value)>>2)]);
440         
441         buf.append(__rfc1421alphabet[0x3f&((0x00000003&value)<<4) + (0xf&(int)(lvalue>>28))]);
442         
443         value=0x0FFFFFFF&(int)lvalue;
444         buf.append(__rfc1421alphabet[0x3f&((0x0FC00000&value)>>22)]);
445         buf.append(__rfc1421alphabet[0x3f&((0x003F0000&value)>>16)]);
446         buf.append(__rfc1421alphabet[0x3f&((0x0000FC00&value)>>10)]);
447         buf.append(__rfc1421alphabet[0x3f&((0x000003F0&value)>>4)]);
448         buf.append(__rfc1421alphabet[0x3f&((0x0000000F&value)<<2)]);
449     }
450 }