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