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}