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 */ 021package com.unboundid.util; 022 023 024 025import java.io.IOException; 026import java.text.ParseException; 027 028import static com.unboundid.util.UtilityMessages.*; 029import 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) 079public 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); 783decodeLoop: 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}