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