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