View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2015 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 static final char[] __rfc4648urlAlphabet=
58          {
59          'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
60          'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
61          'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
62          'w','x','y','z','0','1','2','3','4','5','6','7','8','9','-','_'
63          };
64  
65      private static final byte[] __rfc4648urlNibbles;
66      static
67      {
68          __rfc4648urlNibbles=new byte[256];
69          for (int i=0;i<256;i++)
70              __rfc4648urlNibbles[i]=-1;
71          for (byte b=0;b<64;b++)
72              __rfc4648urlNibbles[(byte)__rfc4648urlAlphabet[b]]=b;
73          __rfc4648urlNibbles[(byte)__pad]=0;
74      }
75  
76      private B64Code()
77      {
78      }
79  
80      /**
81       * Base 64 encode as described in RFC 1421.
82       * <p>Does not insert whitespace as described in RFC 1521.
83       * @param s String to encode.
84       * @return String containing the encoded form of the input.
85       */
86      public static String encode(String s)
87      {
88          return encode(s, (Charset)null);
89      }
90  
91      /**
92       * Base 64 encode as described in RFC 1421.
93       * <p>Does not insert whitespace as described in RFC 1521.
94       * @param s String to encode.
95       * @param charEncoding String representing the name of
96       *        the character encoding of the provided input String.
97       * @return String containing the encoded form of the input.
98       */
99      public static String encode(String s,String charEncoding)
100     {
101         byte[] bytes;
102         if (charEncoding==null)
103             bytes=s.getBytes(StandardCharsets.ISO_8859_1);
104         else
105             bytes=s.getBytes(Charset.forName(charEncoding));
106         return new String(encode(bytes));
107     }
108 
109     /**
110      * Base 64 encode as described in RFC 1421.
111      * <p>Does not insert whitespace as described in RFC 1521.
112      * @param s String to encode.
113      * @param charEncoding The character encoding of the provided input String.
114      * @return String containing the encoded form of the input.
115      */
116     public static String encode(String s, Charset charEncoding)
117     {
118         byte[] bytes=s.getBytes(charEncoding==null ? StandardCharsets.ISO_8859_1 : charEncoding);
119         return new String(encode(bytes));
120     }
121 
122     /**
123      * Fast Base 64 encode as described in RFC 1421.
124      * <p>Does not insert whitespace as described in RFC 1521.
125      * <p> Avoids creating extra copies of the input/output.
126      * @param b byte array to encode.
127      * @return char array containing the encoded form of the input.
128      */
129     public static char[] encode(byte[] b)
130     {
131         if (b==null)
132             return null;
133 
134         int bLen=b.length;
135         int cLen=((bLen+2)/3)*4;
136         char c[]=new char[cLen];
137         int ci=0;
138         int bi=0;
139         byte b0, b1, b2;
140         int stop=(bLen/3)*3;
141         while (bi<stop)
142         {
143             b0=b[bi++];
144             b1=b[bi++];
145             b2=b[bi++];
146             c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
147             c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
148             c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f|(b2>>>6)&0x03];
149             c[ci++]=__rfc1421alphabet[b2&0x3f];
150         }
151 
152         if (bLen!=bi)
153         {
154             switch (bLen%3)
155             {
156                 case 2:
157                     b0=b[bi++];
158                     b1=b[bi++];
159                     c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
160                     c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
161                     c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f];
162                     c[ci++]=__pad;
163                     break;
164 
165                 case 1:
166                     b0=b[bi++];
167                     c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
168                     c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f];
169                     c[ci++]=__pad;
170                     c[ci++]=__pad;
171                     break;
172 
173                 default:
174                     break;
175             }
176         }
177 
178         return c;
179     }
180 
181     /**
182      * Fast Base 64 encode as described in RFC 1421 and RFC2045
183      * <p>Does not insert whitespace as described in RFC 1521, unless rfc2045 is passed as true.
184      * <p> Avoids creating extra copies of the input/output.
185      * @param b byte array to encode.
186      * @param rfc2045 If true, break lines at 76 characters with CRLF
187      * @return char array containing the encoded form of the input.
188      */
189     public static char[] encode(byte[] b, boolean rfc2045)
190     {
191         if (b==null)
192             return null;
193         if (!rfc2045)
194             return encode(b);
195 
196         int bLen=b.length;
197         int cLen=((bLen+2)/3)*4;
198         cLen+=2+2*(cLen/76);
199         char c[]=new char[cLen];
200         int ci=0;
201         int bi=0;
202         byte b0, b1, b2;
203         int stop=(bLen/3)*3;
204         int l=0;
205         while (bi<stop)
206         {
207             b0=b[bi++];
208             b1=b[bi++];
209             b2=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|(b2>>>6)&0x03];
213             c[ci++]=__rfc1421alphabet[b2&0x3f];
214             l+=4;
215             if (l%76==0)
216             {
217                 c[ci++]=13;
218                 c[ci++]=10;
219             }
220         }
221 
222         if (bLen!=bi)
223         {
224             switch (bLen%3)
225             {
226                 case 2:
227                     b0=b[bi++];
228                     b1=b[bi++];
229                     c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
230                     c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
231                     c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f];
232                     c[ci++]=__pad;
233                     break;
234 
235                 case 1:
236                     b0=b[bi++];
237                     c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
238                     c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f];
239                     c[ci++]=__pad;
240                     c[ci++]=__pad;
241                     break;
242 
243                 default:
244                     break;
245             }
246         }
247 
248         c[ci++]=13;
249         c[ci++]=10;
250         return c;
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 String representing the character encoding
258      *        used to map the decoded bytes into a String.
259      * @return String decoded byte array.
260      * @throws UnsupportedCharsetException if the encoding is not supported
261      * @throws IllegalArgumentException if the input is not a valid
262      *         B64 encoding.
263      */
264     public static String decode(String encoded,String charEncoding)
265     {
266         byte[] decoded=decode(encoded);
267         if (charEncoding==null)
268             return new String(decoded);
269         return new String(decoded,Charset.forName(charEncoding));
270     }
271 
272     /**
273      * Base 64 decode as described in RFC 2045.
274      * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
275      * @param encoded String to decode.
276      * @param charEncoding Character encoding
277      *        used to map the decoded bytes into a String.
278      * @return String decoded byte array.
279      * @throws IllegalArgumentException if the input is not a valid
280      *         B64 encoding.
281      */
282     public static String decode(String encoded, Charset charEncoding)
283     {
284         byte[] decoded=decode(encoded);
285         if (charEncoding==null)
286             return new String(decoded);
287         return new String(decoded, charEncoding);
288     }
289 
290     /**
291      * Fast Base 64 decode as described in RFC 1421.
292      *
293      * <p>Unlike other decode methods, this does not attempt to
294      * cope with extra whitespace as described in RFC 1521/2045.
295      * <p> Avoids creating extra copies of the input/output.
296      * <p> Note this code has been flattened for performance.
297      * @param b char array to decode.
298      * @return byte array containing the decoded form of the input.
299      * @throws IllegalArgumentException if the input is not a valid
300      *         B64 encoding.
301      */
302     public static byte[] decode(char[] b)
303     {
304         if (b==null)
305             return null;
306 
307         int bLen=b.length;
308         if (bLen%4!=0)
309             throw new IllegalArgumentException("Input block size is not 4");
310 
311         int li=bLen-1;
312         while (li>=0 && b[li]==(byte)__pad)
313             li--;
314 
315         if (li<0)
316             return new byte[0];
317 
318         // Create result array of exact required size.
319         int rLen=((li+1)*3)/4;
320         byte r[]=new byte[rLen];
321         int ri=0;
322         int bi=0;
323         int stop=(rLen/3)*3;
324         byte b0,b1,b2,b3;
325         try
326         {
327             while (ri<stop)
328             {
329                 b0=__rfc1421nibbles[b[bi++]];
330                 b1=__rfc1421nibbles[b[bi++]];
331                 b2=__rfc1421nibbles[b[bi++]];
332                 b3=__rfc1421nibbles[b[bi++]];
333                 if (b0<0 || b1<0 || b2<0 || b3<0)
334                     throw new IllegalArgumentException("Not B64 encoded");
335 
336                 r[ri++]=(byte)(b0<<2|b1>>>4);
337                 r[ri++]=(byte)(b1<<4|b2>>>2);
338                 r[ri++]=(byte)(b2<<6|b3);
339             }
340 
341             if (rLen!=ri)
342             {
343                 switch (rLen%3)
344                 {
345                     case 2:
346                         b0=__rfc1421nibbles[b[bi++]];
347                         b1=__rfc1421nibbles[b[bi++]];
348                         b2=__rfc1421nibbles[b[bi++]];
349                         if (b0<0 || b1<0 || b2<0)
350                             throw new IllegalArgumentException("Not B64 encoded");
351                         r[ri++]=(byte)(b0<<2|b1>>>4);
352                         r[ri++]=(byte)(b1<<4|b2>>>2);
353                         break;
354 
355                     case 1:
356                         b0=__rfc1421nibbles[b[bi++]];
357                         b1=__rfc1421nibbles[b[bi++]];
358                         if (b0<0 || b1<0)
359                             throw new IllegalArgumentException("Not B64 encoded");
360                         r[ri++]=(byte)(b0<<2|b1>>>4);
361                         break;
362 
363                     default:
364                         break;
365                 }
366             }
367         }
368         catch (IndexOutOfBoundsException e)
369         {
370             throw new IllegalArgumentException("char "+bi
371                     +" was not B64 encoded");
372         }
373 
374         return r;
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      * @return byte array containing the decoded form of the input.
382      * @throws IllegalArgumentException if the input is not a valid
383      *         B64 encoding.
384      */
385     public static byte[] decode(String encoded)
386     {
387         if (encoded==null)
388             return null;
389 
390         ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);        
391         decode(encoded, bout);
392         return bout.toByteArray();
393     }
394     
395     /* ------------------------------------------------------------ */
396     /**
397      * Base 64 decode as described in RFC 2045.
398      * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
399      * @param encoded String to decode.
400      * @param bout stream for decoded bytes
401      * @throws IllegalArgumentException if the input is not a valid
402      *         B64 encoding.
403      */
404     static public void decode (String encoded, ByteArrayOutputStream bout)
405     {
406         if (encoded==null)
407             return;
408         
409         if (bout == null)
410             throw new IllegalArgumentException("No outputstream for decoded bytes");
411         
412         int ci=0;
413         byte nibbles[] = new byte[4];
414         int s=0;
415   
416         while (ci<encoded.length())
417         {
418             char c=encoded.charAt(ci++);
419 
420             if (c==__pad)
421                 break;
422 
423             if (Character.isWhitespace(c))
424                 continue;
425 
426             byte nibble=__rfc1421nibbles[c];
427             if (nibble<0)
428                 throw new IllegalArgumentException("Not B64 encoded");
429 
430             nibbles[s++]=__rfc1421nibbles[c];
431 
432             switch(s)
433             {
434                 case 1:
435                     break;
436                 case 2:
437                     bout.write(nibbles[0]<<2|nibbles[1]>>>4);
438                     break;
439                 case 3:
440                     bout.write(nibbles[1]<<4|nibbles[2]>>>2);
441                     break;
442                 case 4:
443                     bout.write(nibbles[2]<<6|nibbles[3]);
444                     s=0;
445                     break;
446             }
447 
448         }
449 
450         return;
451     }
452     
453     /* ------------------------------------------------------------ */
454     public static byte[] decodeRFC4648URL(String encoded)
455     {
456         if (encoded==null)
457             return null;
458 
459         ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);        
460         decodeRFC4648URL(encoded, bout);
461         return bout.toByteArray();
462     }
463     
464     /* ------------------------------------------------------------ */
465     /**
466      * Base 64 decode as described in RFC 4648 URL.
467      * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
468      * @param encoded String to decode.
469      * @param bout stream for decoded bytes
470      * @throws IllegalArgumentException if the input is not a valid
471      *         B64 encoding.
472      */
473     static public void decodeRFC4648URL (String encoded, ByteArrayOutputStream bout)
474     {
475         if (encoded==null)
476             return;
477         
478         if (bout == null)
479             throw new IllegalArgumentException("No outputstream for decoded bytes");
480         
481         int ci=0;
482         byte nibbles[] = new byte[4];
483         int s=0;
484   
485         while (ci<encoded.length())
486         {
487             char c=encoded.charAt(ci++);
488 
489             if (c==__pad)
490                 break;
491 
492             if (Character.isWhitespace(c))
493                 continue;
494 
495             byte nibble=__rfc4648urlNibbles[c];
496             if (nibble<0)
497                 throw new IllegalArgumentException("Not B64 encoded");
498 
499             nibbles[s++]=__rfc4648urlNibbles[c];
500 
501             switch(s)
502             {
503                 case 1:
504                     break;
505                 case 2:
506                     bout.write(nibbles[0]<<2|nibbles[1]>>>4);
507                     break;
508                 case 3:
509                     bout.write(nibbles[1]<<4|nibbles[2]>>>2);
510                     break;
511                 case 4:
512                     bout.write(nibbles[2]<<6|nibbles[3]);
513                     s=0;
514                     break;
515             }
516 
517         }
518 
519         return;
520     }
521     
522 
523     public static void encode(int value,Appendable buf) throws IOException
524     {
525         buf.append(__rfc1421alphabet[0x3f&((0xFC000000&value)>>26)]);
526         buf.append(__rfc1421alphabet[0x3f&((0x03F00000&value)>>20)]);
527         buf.append(__rfc1421alphabet[0x3f&((0x000FC000&value)>>14)]);
528         buf.append(__rfc1421alphabet[0x3f&((0x00003F00&value)>>8)]);
529         buf.append(__rfc1421alphabet[0x3f&((0x000000FC&value)>>2)]);
530         buf.append(__rfc1421alphabet[0x3f&((0x00000003&value)<<4)]);
531         buf.append('=');
532     }
533 
534     public static void encode(long lvalue,Appendable buf) throws IOException
535     {
536         int value=(int)(0xFFFFFFFC&(lvalue>>32));
537         buf.append(__rfc1421alphabet[0x3f&((0xFC000000&value)>>26)]);
538         buf.append(__rfc1421alphabet[0x3f&((0x03F00000&value)>>20)]);
539         buf.append(__rfc1421alphabet[0x3f&((0x000FC000&value)>>14)]);
540         buf.append(__rfc1421alphabet[0x3f&((0x00003F00&value)>>8)]);
541         buf.append(__rfc1421alphabet[0x3f&((0x000000FC&value)>>2)]);
542 
543         buf.append(__rfc1421alphabet[0x3f&((0x00000003&value)<<4) + (0xf&(int)(lvalue>>28))]);
544 
545         value=0x0FFFFFFF&(int)lvalue;
546         buf.append(__rfc1421alphabet[0x3f&((0x0FC00000&value)>>22)]);
547         buf.append(__rfc1421alphabet[0x3f&((0x003F0000&value)>>16)]);
548         buf.append(__rfc1421alphabet[0x3f&((0x0000FC00&value)>>10)]);
549         buf.append(__rfc1421alphabet[0x3f&((0x000003F0&value)>>4)]);
550         buf.append(__rfc1421alphabet[0x3f&((0x0000000F&value)<<2)]);
551     }
552 }