001    /*
002     * Copyright 2007-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2016 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.util;
022    
023    
024    
025    import java.io.IOException;
026    import java.text.ParseException;
027    
028    import static com.unboundid.util.UtilityMessages.*;
029    import static com.unboundid.util.Validator.*;
030    
031    
032    
033    /**
034     * This class provides methods for encoding and decoding data in base64 as
035     * defined in <A HREF="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</A>.  It
036     * provides a relatively compact way of representing binary data using only
037     * printable characters.  It uses a six-bit encoding mechanism in which every
038     * three bytes of raw data is converted to four bytes of base64-encoded data,
039     * which means that it only requires about a 33% increase in size (as compared
040     * with a hexadecimal representation, which requires a 100% increase in size).
041     * <BR><BR>
042     * Base64 encoding is used in LDIF processing as per
043     * <A HREF="http://www.ietf.org/rfc/rfc2849.txt">RFC 2849</A> to represent data
044     * that contains special characters or might otherwise be ambiguous.  It is also
045     * used in a number of other areas (e.g., for the ASCII representation of
046     * certificates) where it is desirable to deal with a string containing only
047     * printable characters but the raw data may contain other characters outside of
048     * that range.
049     * <BR><BR>
050     * This class also provides support for the URL-safe variant (called base64url)
051     * as described in RFC 4648 section 5.  This is nearly the same as base64,
052     * except that the '+' and '/' characters are replaced with '-' and '_',
053     * respectively.  The padding may be omitted if the context makes the data size
054     * clear, but if padding is to be used then the URL-encoded "%3d" will be used
055     * instead of "=".
056     * <BR><BR>
057     * <H2>Example</H2>
058     * The following examples demonstrate the process for base64-encoding raw data,
059     * and for decoding a string containing base64-encoded data back to the raw
060     * data used to create it:
061     * <PRE>
062     * // Base64-encode some raw data:
063     * String base64String = Base64.encode(rawDataBytes);
064     *
065     * // Decode a base64 string back to raw data:
066     * byte[] decodedRawDataBytes;
067     * try
068     * {
069     *   decodedRawDataBytes = Base64.decode(base64String);
070     * }
071     * catch (ParseException pe)
072     * {
073     *   // The string did not represent a valid base64 encoding.
074     *   decodedRawDataBytes = null;
075     * }
076     * </PRE>
077     */
078    public final class Base64
079    {
080      /**
081       * The set of characters in the base64 alphabet.
082       */
083      private static final char[] BASE64_ALPHABET =
084           ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
085            "0123456789+/").toCharArray();
086    
087    
088    
089      /**
090       * The set of characters in the base64url alphabet.
091       */
092      private static final char[] BASE64URL_ALPHABET =
093           ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
094            "0123456789-_").toCharArray();
095    
096    
097    
098      /**
099       * Prevent this class from being instantiated.
100       */
101      private Base64()
102      {
103        // No implementation is required.
104      }
105    
106    
107    
108      /**
109       * Encodes the UTF-8 representation of the provided string in base64 format.
110       *
111       * @param  data  The raw data to be encoded.  It must not be {@code null}.
112       *
113       * @return  The base64-encoded representation of the provided data.
114       */
115      public static String encode(final String data)
116      {
117        ensureNotNull(data);
118    
119        return encode(StaticUtils.getBytes(data));
120      }
121    
122    
123    
124      /**
125       * Encodes the provided data in base64 format.
126       *
127       * @param  data  The raw data to be encoded.  It must not be {@code null}.
128       *
129       * @return  The base64-encoded representation of the provided data.
130       */
131      public static String encode(final byte[] data)
132      {
133        ensureNotNull(data);
134    
135        final StringBuilder buffer = new StringBuilder(4*data.length/3+1);
136        encode(BASE64_ALPHABET, data, 0, data.length, buffer, "=");
137        return buffer.toString();
138      }
139    
140    
141    
142      /**
143       * Appends a base64-encoded version of the contents of the provided buffer
144       * (using a UTF-8 representation) to the given buffer.
145       *
146       * @param  data    The raw data to be encoded.  It must not be {@code null}.
147       * @param  buffer  The buffer to which the base64-encoded data is to be
148       *                 written.
149       */
150      public static void encode(final String data, final StringBuilder buffer)
151      {
152        ensureNotNull(data);
153    
154        encode(StaticUtils.getBytes(data), buffer);
155      }
156    
157    
158    
159      /**
160       * Appends a base64-encoded version of the contents of the provided buffer
161       * (using a UTF-8 representation) to the given buffer.
162       *
163       * @param  data    The raw data to be encoded.  It must not be {@code null}.
164       * @param  buffer  The buffer to which the base64-encoded data is to be
165       *                 written.
166       */
167      public static void encode(final String data, final ByteStringBuffer buffer)
168      {
169        ensureNotNull(data);
170    
171        encode(StaticUtils.getBytes(data), buffer);
172      }
173    
174    
175    
176      /**
177       * Appends a base64-encoded representation of the provided data to the given
178       * buffer.
179       *
180       * @param  data    The raw data to be encoded.  It must not be {@code null}.
181       * @param  buffer  The buffer to which the base64-encoded data is to be
182       *                 written.
183       */
184      public static void encode(final byte[] data, final StringBuilder buffer)
185      {
186        encode(BASE64_ALPHABET, data, 0, data.length, buffer, "=");
187      }
188    
189    
190    
191      /**
192       * Appends a base64-encoded representation of the provided data to the given
193       * buffer.
194       *
195       * @param  data    The array containing the raw data to be encoded.  It must
196       *                 not be {@code null}.
197       * @param  off     The offset in the array at which the data to encode begins.
198       * @param  length  The number of bytes to be encoded.
199       * @param  buffer  The buffer to which the base64-encoded data is to be
200       *                 written.
201       */
202      public static void encode(final byte[] data, final int off, final int length,
203                                final StringBuilder buffer)
204      {
205        encode(BASE64_ALPHABET, data, off, length, buffer, "=");
206      }
207    
208    
209    
210      /**
211       * Appends a base64-encoded representation of the provided data to the given
212       * buffer.
213       *
214       * @param  data    The raw data to be encoded.  It must not be {@code null}.
215       * @param  buffer  The buffer to which the base64-encoded data is to be
216       *                 written.
217       */
218      public static void encode(final byte[] data, final ByteStringBuffer buffer)
219      {
220        encode(BASE64_ALPHABET, data, 0, data.length, buffer, "=");
221      }
222    
223    
224    
225      /**
226       * Appends a base64-encoded representation of the provided data to the given
227       * buffer.
228       *
229       * @param  data    The raw data to be encoded.  It must not be {@code null}.
230       * @param  off     The offset in the array at which the data to encode begins.
231       * @param  length  The number of bytes to be encoded.
232       * @param  buffer  The buffer to which the base64-encoded data is to be
233       *                 written.
234       */
235      public static void encode(final byte[] data, final int off, final int length,
236                                final ByteStringBuffer buffer)
237      {
238        encode(BASE64_ALPHABET, data, off, length, buffer, "=");
239      }
240    
241    
242    
243      /**
244       * Retrieves a base64url-encoded representation of the provided data to the
245       * given buffer.
246       *
247       * @param  data  The raw data to be encoded.  It must not be {@code null}.
248       * @param  pad   Indicates whether to pad the URL if necessary.  Padding will
249       *               use "%3d", as the URL-escaped representation of the equal
250       *               sign.
251       *
252       * @return  A base64url-encoded representation of the provided data to the
253       *          given buffer.
254       */
255      public static String urlEncode(final String data, final boolean pad)
256      {
257        return urlEncode(StaticUtils.getBytes(data), pad);
258      }
259    
260    
261    
262      /**
263       * Retrieves a base64url-encoded representation of the provided data to the
264       * given buffer.
265       *
266       * @param  data    The raw data to be encoded.  It must not be {@code null}.
267       * @param  buffer  The buffer to which the base64-encoded data is to be
268       *                 written.
269       * @param  pad     Indicates whether to pad the URL if necessary.  Padding
270       *                 will use "%3d", as the URL-escaped representation of the
271       *                 equal sign.
272       */
273      public static void urlEncode(final String data, final StringBuilder buffer,
274                                   final boolean pad)
275      {
276        final byte[] dataBytes = StaticUtils.getBytes(data);
277        encode(BASE64_ALPHABET, dataBytes, 0, dataBytes.length, buffer,
278             (pad ? "%3d" : null));
279      }
280    
281    
282    
283      /**
284       * Retrieves a base64url-encoded representation of the provided data to the
285       * given buffer.
286       *
287       * @param  data    The raw data to be encoded.  It must not be {@code null}.
288       * @param  buffer  The buffer to which the base64-encoded data is to be
289       *                 written.
290       * @param  pad     Indicates whether to pad the URL if necessary.  Padding
291       *                 will use "%3d", as the URL-escaped representation of the
292       *                 equal sign.
293       */
294      public static void urlEncode(final String data, final ByteStringBuffer buffer,
295                                   final boolean pad)
296      {
297        final byte[] dataBytes = StaticUtils.getBytes(data);
298        encode(BASE64_ALPHABET, dataBytes, 0, dataBytes.length, buffer,
299             (pad ? "%3d" : null));
300      }
301    
302    
303    
304      /**
305       * Retrieves a base64url-encoded representation of the provided data to the
306       * given buffer.
307       *
308       * @param  data  The raw data to be encoded.  It must not be {@code null}.
309       * @param  pad   Indicates whether to pad the URL if necessary.  Padding will
310       *               use "%3d", as the URL-escaped representation of the equal
311       *               sign.
312       *
313       * @return  A base64url-encoded representation of the provided data to the
314       *          given buffer.
315       */
316      public static String urlEncode(final byte[] data, final boolean pad)
317      {
318        final StringBuilder buffer = new StringBuilder(4*data.length/3+6);
319        encode(BASE64URL_ALPHABET, data, 0, data.length, buffer,
320             (pad ? "%3d" : null));
321        return buffer.toString();
322      }
323    
324    
325    
326      /**
327       * Appends a base64url-encoded representation of the provided data to the
328       * given buffer.
329       *
330       * @param  data    The raw data to be encoded.  It must not be {@code null}.
331       * @param  off     The offset in the array at which the data to encode begins.
332       * @param  length  The number of bytes to be encoded.
333       * @param  buffer  The buffer to which the base64-encoded data is to be
334       *                 written.
335       * @param  pad     Indicates whether to pad the URL if necessary.  Padding
336       *                 will use "%3d", as the URL-escaped representation of the
337       *                 equal sign.
338       */
339      public static void urlEncode(final byte[] data, final int off,
340                                   final int length, final StringBuilder buffer,
341                                   final boolean pad)
342      {
343        encode(BASE64URL_ALPHABET, data, off, length, buffer, (pad ? "%3d" : null));
344      }
345    
346    
347    
348      /**
349       * Appends a base64url-encoded representation of the provided data to the
350       * given buffer.
351       *
352       * @param  data    The raw data to be encoded.  It must not be {@code null}.
353       * @param  off     The offset in the array at which the data to encode begins.
354       * @param  length  The number of bytes to be encoded.
355       * @param  buffer  The buffer to which the base64-encoded data is to be
356       *                 written.
357       * @param  pad     Indicates whether to pad the URL if necessary.  Padding
358       *                 will use "%3d", as the URL-escaped representation of the
359       *                 equal sign.
360       */
361      public static void urlEncode(final byte[] data, final int off,
362                                   final int length, final ByteStringBuffer buffer,
363                                   final boolean pad)
364      {
365        encode(BASE64URL_ALPHABET, data, off, length, buffer, (pad ? "%3d" : null));
366      }
367    
368    
369    
370      /**
371       * Appends a base64-encoded representation of the provided data to the given
372       * buffer.
373       *
374       * @param  alphabet  The alphabet of base64 characters to use for the
375       *                   encoding.
376       * @param  data      The raw data to be encoded.  It must not be {@code null}.
377       * @param  off       The offset in the array at which the data to encode
378       *                   begins.
379       * @param  length    The number of bytes to be encoded.
380       * @param  buffer    The buffer to which the base64-encoded data is to be
381       *                   written.
382       * @param  padStr    The string to use for padding.  It may be {@code null} if
383       *                   no padding should be applied.
384       */
385      private static void encode(final char[] alphabet, final byte[] data,
386                                 final int off, final int length,
387                                 final Appendable buffer, final String padStr)
388      {
389        ensureNotNull(data);
390        ensureTrue(data.length >= off);
391        ensureTrue(data.length >= (off+length));
392    
393        if (length == 0)
394        {
395          return;
396        }
397    
398        try
399        {
400          int pos = off;
401          for (int i=0; i < (length / 3); i++)
402          {
403            final int intValue = ((data[pos++] & 0xFF) << 16) |
404                 ((data[pos++] & 0xFF) << 8) |
405                 (data[pos++] & 0xFF);
406    
407            buffer.append(alphabet[(intValue >> 18) & 0x3F]);
408            buffer.append(alphabet[(intValue >> 12) & 0x3F]);
409            buffer.append(alphabet[(intValue >> 6) & 0x3F]);
410            buffer.append(alphabet[intValue & 0x3F]);
411          }
412    
413          switch ((off+length) - pos)
414          {
415            case 1:
416              int intValue = (data[pos] & 0xFF) << 16;
417              buffer.append(alphabet[(intValue >> 18) & 0x3F]);
418              buffer.append(alphabet[(intValue >> 12) & 0x3F]);
419              if (padStr != null)
420              {
421                buffer.append(padStr);
422                buffer.append(padStr);
423              }
424              return;
425    
426            case 2:
427              intValue = ((data[pos++] & 0xFF) << 16) | ((data[pos] & 0xFF) << 8);
428              buffer.append(alphabet[(intValue >> 18) & 0x3F]);
429              buffer.append(alphabet[(intValue >> 12) & 0x3F]);
430              buffer.append(alphabet[(intValue >> 6) & 0x3F]);
431              if (padStr != null)
432              {
433                buffer.append(padStr);
434              }
435              return;
436          }
437        }
438        catch (final IOException ioe)
439        {
440          Debug.debugException(ioe);
441    
442          // This should never happen.
443          throw new RuntimeException(ioe.getMessage(), ioe);
444        }
445      }
446    
447    
448    
449      /**
450       * Decodes the contents of the provided base64-encoded string.
451       *
452       * @param  data  The base64-encoded string to decode.  It must not be
453       *               {@code null}.
454       *
455       * @return  A byte array containing the decoded data.
456       *
457       * @throws  ParseException  If the contents of the provided string cannot be
458       *                          parsed as base64-encoded data.
459       */
460      public static byte[] decode(final String data)
461             throws ParseException
462      {
463        ensureNotNull(data);
464    
465        final int length = data.length();
466        if (length == 0)
467        {
468          return new byte[0];
469        }
470    
471        if ((length % 4) != 0)
472        {
473          throw new ParseException(ERR_BASE64_DECODE_INVALID_LENGTH.get(), length);
474        }
475    
476        int numBytes = 3 * (length / 4);
477        if (data.charAt(length-2) == '=')
478        {
479          numBytes -= 2;
480        }
481        else if (data.charAt(length-1) == '=')
482        {
483          numBytes--;
484        }
485    
486        final byte[] b = new byte[numBytes];
487    
488        int stringPos = 0;
489        int arrayPos  = 0;
490        while (stringPos < length)
491        {
492          int intValue = 0x00;
493          for (int i=0; i < 4; i++)
494          {
495            intValue <<= 6;
496            switch (data.charAt(stringPos++))
497            {
498              case 'A':
499                intValue |= 0x00;
500                break;
501              case 'B':
502                intValue |= 0x01;
503                break;
504              case 'C':
505                intValue |= 0x02;
506                break;
507              case 'D':
508                intValue |= 0x03;
509                break;
510              case 'E':
511                intValue |= 0x04;
512                break;
513              case 'F':
514                intValue |= 0x05;
515                break;
516              case 'G':
517                intValue |= 0x06;
518                break;
519              case 'H':
520                intValue |= 0x07;
521                break;
522              case 'I':
523                intValue |= 0x08;
524                break;
525              case 'J':
526                intValue |= 0x09;
527                break;
528              case 'K':
529                intValue |= 0x0A;
530                break;
531              case 'L':
532                intValue |= 0x0B;
533                break;
534              case 'M':
535                intValue |= 0x0C;
536                break;
537              case 'N':
538                intValue |= 0x0D;
539                break;
540              case 'O':
541                intValue |= 0x0E;
542                break;
543              case 'P':
544                intValue |= 0x0F;
545                break;
546              case 'Q':
547                intValue |= 0x10;
548                break;
549              case 'R':
550                intValue |= 0x11;
551                break;
552              case 'S':
553                intValue |= 0x12;
554                break;
555              case 'T':
556                intValue |= 0x13;
557                break;
558              case 'U':
559                intValue |= 0x14;
560                break;
561              case 'V':
562                intValue |= 0x15;
563                break;
564              case 'W':
565                intValue |= 0x16;
566                break;
567              case 'X':
568                intValue |= 0x17;
569                break;
570              case 'Y':
571                intValue |= 0x18;
572                break;
573              case 'Z':
574                intValue |= 0x19;
575                break;
576              case 'a':
577                intValue |= 0x1A;
578                break;
579              case 'b':
580                intValue |= 0x1B;
581                break;
582              case 'c':
583                intValue |= 0x1C;
584                break;
585              case 'd':
586                intValue |= 0x1D;
587                break;
588              case 'e':
589                intValue |= 0x1E;
590                break;
591              case 'f':
592                intValue |= 0x1F;
593                break;
594              case 'g':
595                intValue |= 0x20;
596                break;
597              case 'h':
598                intValue |= 0x21;
599                break;
600              case 'i':
601                intValue |= 0x22;
602                break;
603              case 'j':
604                intValue |= 0x23;
605                break;
606              case 'k':
607                intValue |= 0x24;
608                break;
609              case 'l':
610                intValue |= 0x25;
611                break;
612              case 'm':
613                intValue |= 0x26;
614                break;
615              case 'n':
616                intValue |= 0x27;
617                break;
618              case 'o':
619                intValue |= 0x28;
620                break;
621              case 'p':
622                intValue |= 0x29;
623                break;
624              case 'q':
625                intValue |= 0x2A;
626                break;
627              case 'r':
628                intValue |= 0x2B;
629                break;
630              case 's':
631                intValue |= 0x2C;
632                break;
633              case 't':
634                intValue |= 0x2D;
635                break;
636              case 'u':
637                intValue |= 0x2E;
638                break;
639              case 'v':
640                intValue |= 0x2F;
641                break;
642              case 'w':
643                intValue |= 0x30;
644                break;
645              case 'x':
646                intValue |= 0x31;
647                break;
648              case 'y':
649                intValue |= 0x32;
650                break;
651              case 'z':
652                intValue |= 0x33;
653                break;
654              case '0':
655                intValue |= 0x34;
656                break;
657              case '1':
658                intValue |= 0x35;
659                break;
660              case '2':
661                intValue |= 0x36;
662                break;
663              case '3':
664                intValue |= 0x37;
665                break;
666              case '4':
667                intValue |= 0x38;
668                break;
669              case '5':
670                intValue |= 0x39;
671                break;
672              case '6':
673                intValue |= 0x3A;
674                break;
675              case '7':
676                intValue |= 0x3B;
677                break;
678              case '8':
679                intValue |= 0x3C;
680                break;
681              case '9':
682                intValue |= 0x3D;
683                break;
684              case '+':
685                intValue |= 0x3E;
686                break;
687              case '/':
688                intValue |= 0x3F;
689                break;
690    
691              case '=':
692                switch (length - stringPos)
693                {
694                  case 0:
695                    // The string ended with a single equal sign, so there are only
696                    // two bytes left.  Shift the value eight bits to the right and
697                    // read those two bytes.
698                    intValue >>= 8;
699                    b[arrayPos++] = (byte) ((intValue >> 8) & 0xFF);
700                    b[arrayPos]   = (byte) (intValue & 0xFF);
701                    return b;
702    
703                  case 1:
704                    // The string ended with two equal signs, so there is only one
705                    // byte left.  Shift the value ten bits to the right and read
706                    // that single byte.
707                    intValue >>= 10;
708                    b[arrayPos] = (byte) (intValue & 0xFF);
709                    return b;
710    
711                  default:
712                    throw new ParseException(ERR_BASE64_DECODE_UNEXPECTED_EQUAL.get(
713                                                  (stringPos-1)),
714                                             (stringPos-1));
715                }
716    
717              default:
718                throw new ParseException(ERR_BASE64_DECODE_UNEXPECTED_CHAR.get(
719                                              data.charAt(stringPos-1)),
720                                         (stringPos-1));
721            }
722          }
723    
724          b[arrayPos++] = (byte) ((intValue >> 16) & 0xFF);
725          b[arrayPos++] = (byte) ((intValue >> 8) & 0xFF);
726          b[arrayPos++] = (byte) (intValue & 0xFF);
727        }
728    
729        return b;
730      }
731    
732    
733    
734      /**
735       * Decodes the contents of the provided base64-encoded string to a string
736       * containing the raw data using the UTF-8 encoding.
737       *
738       * @param  data  The base64-encoded string to decode.  It must not be
739       *               {@code null}.
740       *
741       * @return  A string containing the decoded data.
742       *
743       * @throws  ParseException  If the contents of the provided string cannot be
744       *                          parsed as base64-encoded data using the UTF-8
745       *                          encoding.
746       */
747      public static String decodeToString(final String data)
748             throws ParseException
749      {
750        ensureNotNull(data);
751    
752        final byte[] decodedBytes = decode(data);
753        return StaticUtils.toUTF8String(decodedBytes);
754      }
755    
756    
757    
758      /**
759       * Decodes the contents of the provided base64url-encoded string.
760       *
761       * @param  data  The base64url-encoded string to decode.  It must not be
762       *               {@code null}.
763       *
764       * @return  A byte array containing the decoded data.
765       *
766       * @throws  ParseException  If the contents of the provided string cannot be
767       *                          parsed as base64url-encoded data.
768       */
769      public static byte[] urlDecode(final String data)
770             throws ParseException
771      {
772        ensureNotNull(data);
773    
774        final int length = data.length();
775        if (length == 0)
776        {
777          return new byte[0];
778        }
779    
780        int stringPos = 0;
781        final ByteStringBuffer buffer = new ByteStringBuffer(length);
782    decodeLoop:
783        while (stringPos < length)
784        {
785          int intValue = 0x00;
786          for (int i=0; i < 4; i++)
787          {
788            // Since the value may not be padded, then we need to handle the
789            // possibility of missing characters.
790            final char c;
791            if (stringPos >= length)
792            {
793              c = '=';
794              stringPos++;
795            }
796            else
797            {
798              c = data.charAt(stringPos++);
799            }
800    
801            intValue <<= 6;
802            switch (c)
803            {
804              case 'A':
805                intValue |= 0x00;
806                break;
807              case 'B':
808                intValue |= 0x01;
809                break;
810              case 'C':
811                intValue |= 0x02;
812                break;
813              case 'D':
814                intValue |= 0x03;
815                break;
816              case 'E':
817                intValue |= 0x04;
818                break;
819              case 'F':
820                intValue |= 0x05;
821                break;
822              case 'G':
823                intValue |= 0x06;
824                break;
825              case 'H':
826                intValue |= 0x07;
827                break;
828              case 'I':
829                intValue |= 0x08;
830                break;
831              case 'J':
832                intValue |= 0x09;
833                break;
834              case 'K':
835                intValue |= 0x0A;
836                break;
837              case 'L':
838                intValue |= 0x0B;
839                break;
840              case 'M':
841                intValue |= 0x0C;
842                break;
843              case 'N':
844                intValue |= 0x0D;
845                break;
846              case 'O':
847                intValue |= 0x0E;
848                break;
849              case 'P':
850                intValue |= 0x0F;
851                break;
852              case 'Q':
853                intValue |= 0x10;
854                break;
855              case 'R':
856                intValue |= 0x11;
857                break;
858              case 'S':
859                intValue |= 0x12;
860                break;
861              case 'T':
862                intValue |= 0x13;
863                break;
864              case 'U':
865                intValue |= 0x14;
866                break;
867              case 'V':
868                intValue |= 0x15;
869                break;
870              case 'W':
871                intValue |= 0x16;
872                break;
873              case 'X':
874                intValue |= 0x17;
875                break;
876              case 'Y':
877                intValue |= 0x18;
878                break;
879              case 'Z':
880                intValue |= 0x19;
881                break;
882              case 'a':
883                intValue |= 0x1A;
884                break;
885              case 'b':
886                intValue |= 0x1B;
887                break;
888              case 'c':
889                intValue |= 0x1C;
890                break;
891              case 'd':
892                intValue |= 0x1D;
893                break;
894              case 'e':
895                intValue |= 0x1E;
896                break;
897              case 'f':
898                intValue |= 0x1F;
899                break;
900              case 'g':
901                intValue |= 0x20;
902                break;
903              case 'h':
904                intValue |= 0x21;
905                break;
906              case 'i':
907                intValue |= 0x22;
908                break;
909              case 'j':
910                intValue |= 0x23;
911                break;
912              case 'k':
913                intValue |= 0x24;
914                break;
915              case 'l':
916                intValue |= 0x25;
917                break;
918              case 'm':
919                intValue |= 0x26;
920                break;
921              case 'n':
922                intValue |= 0x27;
923                break;
924              case 'o':
925                intValue |= 0x28;
926                break;
927              case 'p':
928                intValue |= 0x29;
929                break;
930              case 'q':
931                intValue |= 0x2A;
932                break;
933              case 'r':
934                intValue |= 0x2B;
935                break;
936              case 's':
937                intValue |= 0x2C;
938                break;
939              case 't':
940                intValue |= 0x2D;
941                break;
942              case 'u':
943                intValue |= 0x2E;
944                break;
945              case 'v':
946                intValue |= 0x2F;
947                break;
948              case 'w':
949                intValue |= 0x30;
950                break;
951              case 'x':
952                intValue |= 0x31;
953                break;
954              case 'y':
955                intValue |= 0x32;
956                break;
957              case 'z':
958                intValue |= 0x33;
959                break;
960              case '0':
961                intValue |= 0x34;
962                break;
963              case '1':
964                intValue |= 0x35;
965                break;
966              case '2':
967                intValue |= 0x36;
968                break;
969              case '3':
970                intValue |= 0x37;
971                break;
972              case '4':
973                intValue |= 0x38;
974                break;
975              case '5':
976                intValue |= 0x39;
977                break;
978              case '6':
979                intValue |= 0x3A;
980                break;
981              case '7':
982                intValue |= 0x3B;
983                break;
984              case '8':
985                intValue |= 0x3C;
986                break;
987              case '9':
988                intValue |= 0x3D;
989                break;
990              case '-':
991                intValue |= 0x3E;
992                break;
993              case '_':
994                intValue |= 0x3F;
995                break;
996              case '=':
997              case '%':
998                switch ((stringPos-1) % 4)
999                {
1000                  case 2:
1001                    // The string should have two padding tokens, so only a single
1002                    // byte of data remains.  Shift the value ten bits to the right
1003                    // and read that single byte.
1004                    intValue >>= 10;
1005                    buffer.append((byte) (intValue & 0xFF));
1006                    break decodeLoop;
1007                  case 3:
1008                    // The string should have a single padding token, so two bytes
1009                    // of data remain.  Shift the value eight bits to the right and
1010                    // read those two bytes.
1011                    intValue >>= 8;
1012                    buffer.append((byte) ((intValue >> 8) & 0xFF));
1013                    buffer.append((byte) (intValue & 0xFF));
1014                    break decodeLoop;
1015                }
1016    
1017                // If we've gotten here, then that must mean the string had padding
1018                // when none was needed, or it had an invalid length.  That's an
1019                // error.
1020                throw new ParseException(ERR_BASE64_URLDECODE_INVALID_LENGTH.get(),
1021                     (stringPos-1));
1022    
1023              default:
1024                throw new ParseException(
1025                     ERR_BASE64_DECODE_UNEXPECTED_CHAR.get(
1026                          data.charAt(stringPos-1)),
1027                     (stringPos-1));
1028            }
1029          }
1030    
1031          buffer.append((byte) ((intValue >> 16) & 0xFF));
1032          buffer.append((byte) ((intValue >> 8) & 0xFF));
1033          buffer.append((byte) (intValue & 0xFF));
1034        }
1035    
1036        return buffer.toByteArray();
1037      }
1038    
1039    
1040    
1041      /**
1042       * Decodes the contents of the provided base64-encoded string to a string
1043       * containing the raw data using the UTF-8 encoding.
1044       *
1045       * @param  data  The base64-encoded string to decode.  It must not be
1046       *               {@code null}.
1047       *
1048       * @return  A string containing the decoded data.
1049       *
1050       * @throws  ParseException  If the contents of the provided string cannot be
1051       *                          parsed as base64-encoded data using the UTF-8
1052       *                          encoding.
1053       */
1054      public static String urlDecodeToString(final String data)
1055             throws ParseException
1056      {
1057        ensureNotNull(data);
1058    
1059        final byte[] decodedBytes = urlDecode(data);
1060        return StaticUtils.toUTF8String(decodedBytes);
1061      }
1062    }