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