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 }