001 /*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.util;
022
023
024
025 import java.io.IOException;
026 import java.text.ParseException;
027
028 import static com.unboundid.util.UtilityMessages.*;
029 import static com.unboundid.util.Validator.*;
030
031
032
033 /**
034 * This class provides methods for encoding and decoding data in base64 as
035 * defined in <A HREF="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</A>. It
036 * provides a relatively compact way of representing binary data using only
037 * printable characters. It uses a six-bit encoding mechanism in which every
038 * three bytes of raw data is converted to four bytes of base64-encoded data,
039 * which means that it only requires about a 33% increase in size (as compared
040 * with a hexadecimal representation, which requires a 100% increase in size).
041 * <BR><BR>
042 * Base64 encoding is used in LDIF processing as per
043 * <A HREF="http://www.ietf.org/rfc/rfc2849.txt">RFC 2849</A> to represent data
044 * that contains special characters or might otherwise be ambiguous. It is also
045 * used in a number of other areas (e.g., for the ASCII representation of
046 * certificates) where it is desirable to deal with a string containing only
047 * printable characters but the raw data may contain other characters outside of
048 * that range.
049 * <BR><BR>
050 * This class also provides support for the URL-safe variant (called base64url)
051 * as described in RFC 4648 section 5. This is nearly the same as base64,
052 * except that the '+' and '/' characters are replaced with '-' and '_',
053 * respectively. The padding may be omitted if the context makes the data size
054 * clear, but if padding is to be used then the URL-encoded "%3d" will be used
055 * instead of "=".
056 * <BR><BR>
057 * <H2>Example</H2>
058 * The following examples demonstrate the process for base64-encoding raw data,
059 * and for decoding a string containing base64-encoded data back to the raw
060 * data used to create it:
061 * <PRE>
062 * // Base64-encode some raw data:
063 * String base64String = Base64.encode(rawDataBytes);
064 *
065 * // Decode a base64 string back to raw data:
066 * byte[] decodedRawDataBytes;
067 * try
068 * {
069 * decodedRawDataBytes = Base64.decode(base64String);
070 * }
071 * catch (ParseException pe)
072 * {
073 * // The string did not represent a valid base64 encoding.
074 * decodedRawDataBytes = null;
075 * }
076 * </PRE>
077 */
078 public 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 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 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 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 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 ensureNotNull(data);
390 ensureTrue(data.length >= off);
391 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 ensureNotNull(data);
464
465 final int length = data.length();
466 if (length == 0)
467 {
468 return new byte[0];
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 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 ensureNotNull(data);
773
774 final int length = data.length();
775 if (length == 0)
776 {
777 return new byte[0];
778 }
779
780 int stringPos = 0;
781 final ByteStringBuffer buffer = new ByteStringBuffer(length);
782 decodeLoop:
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 ensureNotNull(data);
1058
1059 final byte[] decodedBytes = urlDecode(data);
1060 return StaticUtils.toUTF8String(decodedBytes);
1061 }
1062 }