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