001/*
002 * Copyright 2007-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2017 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 */
021package com.unboundid.util;
022
023
024
025import java.io.IOException;
026import java.text.ParseException;
027
028import static com.unboundid.util.UtilityMessages.*;
029import 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)
079public 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);
783decodeLoop:
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}