001 /*
002 * Copyright 2007-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2016 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 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
079 public 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);
783 decodeLoop:
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 }