001/* 002 * Copyright 2007-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2007-2020 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2008-2020 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.util; 037 038 039 040import java.io.IOException; 041import java.text.ParseException; 042 043import static com.unboundid.util.UtilityMessages.*; 044 045 046 047/** 048 * This class provides methods for encoding and decoding data in base64 as 049 * defined in <A HREF="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</A>. It 050 * provides a relatively compact way of representing binary data using only 051 * printable characters. It uses a six-bit encoding mechanism in which every 052 * three bytes of raw data is converted to four bytes of base64-encoded data, 053 * which means that it only requires about a 33% increase in size (as compared 054 * with a hexadecimal representation, which requires a 100% increase in size). 055 * <BR><BR> 056 * Base64 encoding is used in LDIF processing as per 057 * <A HREF="http://www.ietf.org/rfc/rfc2849.txt">RFC 2849</A> to represent data 058 * that contains special characters or might otherwise be ambiguous. It is also 059 * used in a number of other areas (e.g., for the ASCII representation of 060 * certificates) where it is desirable to deal with a string containing only 061 * printable characters but the raw data may contain other characters outside of 062 * that range. 063 * <BR><BR> 064 * This class also provides support for the URL-safe variant (called base64url) 065 * as described in RFC 4648 section 5. This is nearly the same as base64, 066 * except that the '+' and '/' characters are replaced with '-' and '_', 067 * respectively. The padding may be omitted if the context makes the data size 068 * clear, but if padding is to be used then the URL-encoded "%3d" will be used 069 * instead of "=". 070 * <BR><BR> 071 * <H2>Example</H2> 072 * The following examples demonstrate the process for base64-encoding raw data, 073 * and for decoding a string containing base64-encoded data back to the raw 074 * data used to create it: 075 * <PRE> 076 * // Base64-encode some raw data: 077 * String base64String = Base64.encode(rawDataBytes); 078 * 079 * // Decode a base64 string back to raw data: 080 * byte[] decodedRawDataBytes; 081 * try 082 * { 083 * decodedRawDataBytes = Base64.decode(base64String); 084 * } 085 * catch (ParseException pe) 086 * { 087 * // The string did not represent a valid base64 encoding. 088 * decodedRawDataBytes = null; 089 * } 090 * </PRE> 091 */ 092@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 093public final class Base64 094{ 095 /** 096 * The set of characters in the base64 alphabet. 097 */ 098 private static final char[] BASE64_ALPHABET = 099 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + 100 "0123456789+/").toCharArray(); 101 102 103 104 /** 105 * The set of characters in the base64url alphabet. 106 */ 107 private static final char[] BASE64URL_ALPHABET = 108 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + 109 "0123456789-_").toCharArray(); 110 111 112 113 /** 114 * Prevent this class from being instantiated. 115 */ 116 private Base64() 117 { 118 // No implementation is required. 119 } 120 121 122 123 /** 124 * Encodes the UTF-8 representation of the provided string in base64 format. 125 * 126 * @param data The raw data to be encoded. It must not be {@code null}. 127 * 128 * @return The base64-encoded representation of the provided data. 129 */ 130 public static String encode(final String data) 131 { 132 Validator.ensureNotNull(data); 133 134 return encode(StaticUtils.getBytes(data)); 135 } 136 137 138 139 /** 140 * Encodes the provided data in base64 format. 141 * 142 * @param data The raw data to be encoded. It must not be {@code null}. 143 * 144 * @return The base64-encoded representation of the provided data. 145 */ 146 public static String encode(final byte[] data) 147 { 148 Validator.ensureNotNull(data); 149 150 final StringBuilder buffer = new StringBuilder(4*data.length/3+1); 151 encode(BASE64_ALPHABET, data, 0, data.length, buffer, "="); 152 return buffer.toString(); 153 } 154 155 156 157 /** 158 * Appends a base64-encoded version of the contents of the provided buffer 159 * (using a UTF-8 representation) to the given buffer. 160 * 161 * @param data The raw data to be encoded. It must not be {@code null}. 162 * @param buffer The buffer to which the base64-encoded data is to be 163 * written. 164 */ 165 public static void encode(final String data, final StringBuilder buffer) 166 { 167 Validator.ensureNotNull(data); 168 169 encode(StaticUtils.getBytes(data), buffer); 170 } 171 172 173 174 /** 175 * Appends a base64-encoded version of the contents of the provided buffer 176 * (using a UTF-8 representation) to the given buffer. 177 * 178 * @param data The raw data to be encoded. It must not be {@code null}. 179 * @param buffer The buffer to which the base64-encoded data is to be 180 * written. 181 */ 182 public static void encode(final String data, final ByteStringBuffer buffer) 183 { 184 Validator.ensureNotNull(data); 185 186 encode(StaticUtils.getBytes(data), 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 raw data to be encoded. It must not be {@code null}. 196 * @param buffer The buffer to which the base64-encoded data is to be 197 * written. 198 */ 199 public static void encode(final byte[] data, final StringBuilder buffer) 200 { 201 encode(BASE64_ALPHABET, data, 0, data.length, buffer, "="); 202 } 203 204 205 206 /** 207 * Appends a base64-encoded representation of the provided data to the given 208 * buffer. 209 * 210 * @param data The array containing the raw data to be encoded. It must 211 * not be {@code null}. 212 * @param off The offset in the array at which the data to encode begins. 213 * @param length The number of bytes to be encoded. 214 * @param buffer The buffer to which the base64-encoded data is to be 215 * written. 216 */ 217 public static void encode(final byte[] data, final int off, final int length, 218 final StringBuilder buffer) 219 { 220 encode(BASE64_ALPHABET, data, off, 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 buffer The buffer to which the base64-encoded data is to be 231 * written. 232 */ 233 public static void encode(final byte[] data, final ByteStringBuffer buffer) 234 { 235 encode(BASE64_ALPHABET, data, 0, data.length, buffer, "="); 236 } 237 238 239 240 /** 241 * Appends a base64-encoded representation of the provided data to the given 242 * buffer. 243 * 244 * @param data The raw data to be encoded. It must not be {@code null}. 245 * @param off The offset in the array at which the data to encode begins. 246 * @param length The number of bytes to be encoded. 247 * @param buffer The buffer to which the base64-encoded data is to be 248 * written. 249 */ 250 public static void encode(final byte[] data, final int off, final int length, 251 final ByteStringBuffer buffer) 252 { 253 encode(BASE64_ALPHABET, data, off, length, buffer, "="); 254 } 255 256 257 258 /** 259 * Retrieves a base64url-encoded representation of the provided data to the 260 * given buffer. 261 * 262 * @param data The raw data to be encoded. It must not be {@code null}. 263 * @param pad Indicates whether to pad the URL if necessary. Padding will 264 * use "%3d", as the URL-escaped representation of the equal 265 * sign. 266 * 267 * @return A base64url-encoded representation of the provided data to the 268 * given buffer. 269 */ 270 public static String urlEncode(final String data, final boolean pad) 271 { 272 return urlEncode(StaticUtils.getBytes(data), pad); 273 } 274 275 276 277 /** 278 * Retrieves a base64url-encoded representation of the provided data to the 279 * given buffer. 280 * 281 * @param data The raw data to be encoded. It must not be {@code null}. 282 * @param buffer The buffer to which the base64-encoded data is to be 283 * written. 284 * @param pad Indicates whether to pad the URL if necessary. Padding 285 * will use "%3d", as the URL-escaped representation of the 286 * equal sign. 287 */ 288 public static void urlEncode(final String data, final StringBuilder buffer, 289 final boolean pad) 290 { 291 final byte[] dataBytes = StaticUtils.getBytes(data); 292 encode(BASE64_ALPHABET, dataBytes, 0, dataBytes.length, buffer, 293 (pad ? "%3d" : null)); 294 } 295 296 297 298 /** 299 * Retrieves a base64url-encoded representation of the provided data to the 300 * given buffer. 301 * 302 * @param data The raw data to be encoded. It must not be {@code null}. 303 * @param buffer The buffer to which the base64-encoded data is to be 304 * written. 305 * @param pad Indicates whether to pad the URL if necessary. Padding 306 * will use "%3d", as the URL-escaped representation of the 307 * equal sign. 308 */ 309 public static void urlEncode(final String data, final ByteStringBuffer buffer, 310 final boolean pad) 311 { 312 final byte[] dataBytes = StaticUtils.getBytes(data); 313 encode(BASE64_ALPHABET, dataBytes, 0, dataBytes.length, buffer, 314 (pad ? "%3d" : null)); 315 } 316 317 318 319 /** 320 * Retrieves a base64url-encoded representation of the provided data to the 321 * given buffer. 322 * 323 * @param data The raw data to be encoded. It must not be {@code null}. 324 * @param pad Indicates whether to pad the URL if necessary. Padding will 325 * use "%3d", as the URL-escaped representation of the equal 326 * sign. 327 * 328 * @return A base64url-encoded representation of the provided data to the 329 * given buffer. 330 */ 331 public static String urlEncode(final byte[] data, final boolean pad) 332 { 333 final StringBuilder buffer = new StringBuilder(4*data.length/3+6); 334 encode(BASE64URL_ALPHABET, data, 0, data.length, buffer, 335 (pad ? "%3d" : null)); 336 return buffer.toString(); 337 } 338 339 340 341 /** 342 * Appends a base64url-encoded representation of the provided data to the 343 * given buffer. 344 * 345 * @param data The raw data to be encoded. It must not be {@code null}. 346 * @param off The offset in the array at which the data to encode begins. 347 * @param length The number of bytes to be encoded. 348 * @param buffer The buffer to which the base64-encoded data is to be 349 * written. 350 * @param pad Indicates whether to pad the URL if necessary. Padding 351 * will use "%3d", as the URL-escaped representation of the 352 * equal sign. 353 */ 354 public static void urlEncode(final byte[] data, final int off, 355 final int length, final StringBuilder buffer, 356 final boolean pad) 357 { 358 encode(BASE64URL_ALPHABET, data, off, length, buffer, (pad ? "%3d" : null)); 359 } 360 361 362 363 /** 364 * Appends a base64url-encoded representation of the provided data to the 365 * given buffer. 366 * 367 * @param data The raw data to be encoded. It must not be {@code null}. 368 * @param off The offset in the array at which the data to encode begins. 369 * @param length The number of bytes to be encoded. 370 * @param buffer The buffer to which the base64-encoded data is to be 371 * written. 372 * @param pad Indicates whether to pad the URL if necessary. Padding 373 * will use "%3d", as the URL-escaped representation of the 374 * equal sign. 375 */ 376 public static void urlEncode(final byte[] data, final int off, 377 final int length, final ByteStringBuffer buffer, 378 final boolean pad) 379 { 380 encode(BASE64URL_ALPHABET, data, off, length, buffer, (pad ? "%3d" : null)); 381 } 382 383 384 385 /** 386 * Appends a base64-encoded representation of the provided data to the given 387 * buffer. 388 * 389 * @param alphabet The alphabet of base64 characters to use for the 390 * encoding. 391 * @param data The raw data to be encoded. It must not be {@code null}. 392 * @param off The offset in the array at which the data to encode 393 * begins. 394 * @param length The number of bytes to be encoded. 395 * @param buffer The buffer to which the base64-encoded data is to be 396 * written. 397 * @param padStr The string to use for padding. It may be {@code null} if 398 * no padding should be applied. 399 */ 400 private static void encode(final char[] alphabet, final byte[] data, 401 final int off, final int length, 402 final Appendable buffer, final String padStr) 403 { 404 Validator.ensureNotNull(data); 405 Validator.ensureTrue(data.length >= off); 406 Validator.ensureTrue(data.length >= (off+length)); 407 408 if (length == 0) 409 { 410 return; 411 } 412 413 try 414 { 415 int pos = off; 416 for (int i=0; i < (length / 3); i++) 417 { 418 final int intValue = ((data[pos++] & 0xFF) << 16) | 419 ((data[pos++] & 0xFF) << 8) | 420 (data[pos++] & 0xFF); 421 422 buffer.append(alphabet[(intValue >> 18) & 0x3F]); 423 buffer.append(alphabet[(intValue >> 12) & 0x3F]); 424 buffer.append(alphabet[(intValue >> 6) & 0x3F]); 425 buffer.append(alphabet[intValue & 0x3F]); 426 } 427 428 switch ((off+length) - pos) 429 { 430 case 1: 431 int intValue = (data[pos] & 0xFF) << 16; 432 buffer.append(alphabet[(intValue >> 18) & 0x3F]); 433 buffer.append(alphabet[(intValue >> 12) & 0x3F]); 434 if (padStr != null) 435 { 436 buffer.append(padStr); 437 buffer.append(padStr); 438 } 439 return; 440 441 case 2: 442 intValue = ((data[pos++] & 0xFF) << 16) | ((data[pos] & 0xFF) << 8); 443 buffer.append(alphabet[(intValue >> 18) & 0x3F]); 444 buffer.append(alphabet[(intValue >> 12) & 0x3F]); 445 buffer.append(alphabet[(intValue >> 6) & 0x3F]); 446 if (padStr != null) 447 { 448 buffer.append(padStr); 449 } 450 return; 451 } 452 } 453 catch (final IOException ioe) 454 { 455 Debug.debugException(ioe); 456 457 // This should never happen. 458 throw new RuntimeException(ioe.getMessage(), ioe); 459 } 460 } 461 462 463 464 /** 465 * Decodes the contents of the provided base64-encoded string. 466 * 467 * @param data The base64-encoded string to decode. It must not be 468 * {@code null}. 469 * 470 * @return A byte array containing the decoded data. 471 * 472 * @throws ParseException If the contents of the provided string cannot be 473 * parsed as base64-encoded data. 474 */ 475 public static byte[] decode(final String data) 476 throws ParseException 477 { 478 Validator.ensureNotNull(data); 479 480 final int length = data.length(); 481 if (length == 0) 482 { 483 return StaticUtils.NO_BYTES; 484 } 485 486 if ((length % 4) != 0) 487 { 488 throw new ParseException(ERR_BASE64_DECODE_INVALID_LENGTH.get(), length); 489 } 490 491 int numBytes = 3 * (length / 4); 492 if (data.charAt(length-2) == '=') 493 { 494 numBytes -= 2; 495 } 496 else if (data.charAt(length-1) == '=') 497 { 498 numBytes--; 499 } 500 501 final byte[] b = new byte[numBytes]; 502 503 int stringPos = 0; 504 int arrayPos = 0; 505 while (stringPos < length) 506 { 507 int intValue = 0x00; 508 for (int i=0; i < 4; i++) 509 { 510 intValue <<= 6; 511 switch (data.charAt(stringPos++)) 512 { 513 case 'A': 514 intValue |= 0x00; 515 break; 516 case 'B': 517 intValue |= 0x01; 518 break; 519 case 'C': 520 intValue |= 0x02; 521 break; 522 case 'D': 523 intValue |= 0x03; 524 break; 525 case 'E': 526 intValue |= 0x04; 527 break; 528 case 'F': 529 intValue |= 0x05; 530 break; 531 case 'G': 532 intValue |= 0x06; 533 break; 534 case 'H': 535 intValue |= 0x07; 536 break; 537 case 'I': 538 intValue |= 0x08; 539 break; 540 case 'J': 541 intValue |= 0x09; 542 break; 543 case 'K': 544 intValue |= 0x0A; 545 break; 546 case 'L': 547 intValue |= 0x0B; 548 break; 549 case 'M': 550 intValue |= 0x0C; 551 break; 552 case 'N': 553 intValue |= 0x0D; 554 break; 555 case 'O': 556 intValue |= 0x0E; 557 break; 558 case 'P': 559 intValue |= 0x0F; 560 break; 561 case 'Q': 562 intValue |= 0x10; 563 break; 564 case 'R': 565 intValue |= 0x11; 566 break; 567 case 'S': 568 intValue |= 0x12; 569 break; 570 case 'T': 571 intValue |= 0x13; 572 break; 573 case 'U': 574 intValue |= 0x14; 575 break; 576 case 'V': 577 intValue |= 0x15; 578 break; 579 case 'W': 580 intValue |= 0x16; 581 break; 582 case 'X': 583 intValue |= 0x17; 584 break; 585 case 'Y': 586 intValue |= 0x18; 587 break; 588 case 'Z': 589 intValue |= 0x19; 590 break; 591 case 'a': 592 intValue |= 0x1A; 593 break; 594 case 'b': 595 intValue |= 0x1B; 596 break; 597 case 'c': 598 intValue |= 0x1C; 599 break; 600 case 'd': 601 intValue |= 0x1D; 602 break; 603 case 'e': 604 intValue |= 0x1E; 605 break; 606 case 'f': 607 intValue |= 0x1F; 608 break; 609 case 'g': 610 intValue |= 0x20; 611 break; 612 case 'h': 613 intValue |= 0x21; 614 break; 615 case 'i': 616 intValue |= 0x22; 617 break; 618 case 'j': 619 intValue |= 0x23; 620 break; 621 case 'k': 622 intValue |= 0x24; 623 break; 624 case 'l': 625 intValue |= 0x25; 626 break; 627 case 'm': 628 intValue |= 0x26; 629 break; 630 case 'n': 631 intValue |= 0x27; 632 break; 633 case 'o': 634 intValue |= 0x28; 635 break; 636 case 'p': 637 intValue |= 0x29; 638 break; 639 case 'q': 640 intValue |= 0x2A; 641 break; 642 case 'r': 643 intValue |= 0x2B; 644 break; 645 case 's': 646 intValue |= 0x2C; 647 break; 648 case 't': 649 intValue |= 0x2D; 650 break; 651 case 'u': 652 intValue |= 0x2E; 653 break; 654 case 'v': 655 intValue |= 0x2F; 656 break; 657 case 'w': 658 intValue |= 0x30; 659 break; 660 case 'x': 661 intValue |= 0x31; 662 break; 663 case 'y': 664 intValue |= 0x32; 665 break; 666 case 'z': 667 intValue |= 0x33; 668 break; 669 case '0': 670 intValue |= 0x34; 671 break; 672 case '1': 673 intValue |= 0x35; 674 break; 675 case '2': 676 intValue |= 0x36; 677 break; 678 case '3': 679 intValue |= 0x37; 680 break; 681 case '4': 682 intValue |= 0x38; 683 break; 684 case '5': 685 intValue |= 0x39; 686 break; 687 case '6': 688 intValue |= 0x3A; 689 break; 690 case '7': 691 intValue |= 0x3B; 692 break; 693 case '8': 694 intValue |= 0x3C; 695 break; 696 case '9': 697 intValue |= 0x3D; 698 break; 699 case '+': 700 intValue |= 0x3E; 701 break; 702 case '/': 703 intValue |= 0x3F; 704 break; 705 706 case '=': 707 switch (length - stringPos) 708 { 709 case 0: 710 // The string ended with a single equal sign, so there are only 711 // two bytes left. Shift the value eight bits to the right and 712 // read those two bytes. 713 intValue >>= 8; 714 b[arrayPos++] = (byte) ((intValue >> 8) & 0xFF); 715 b[arrayPos] = (byte) (intValue & 0xFF); 716 return b; 717 718 case 1: 719 // The string ended with two equal signs, so there is only one 720 // byte left. Shift the value ten bits to the right and read 721 // that single byte. 722 intValue >>= 10; 723 b[arrayPos] = (byte) (intValue & 0xFF); 724 return b; 725 726 default: 727 throw new ParseException(ERR_BASE64_DECODE_UNEXPECTED_EQUAL.get( 728 (stringPos-1)), 729 (stringPos-1)); 730 } 731 732 default: 733 throw new ParseException(ERR_BASE64_DECODE_UNEXPECTED_CHAR.get( 734 data.charAt(stringPos-1)), 735 (stringPos-1)); 736 } 737 } 738 739 b[arrayPos++] = (byte) ((intValue >> 16) & 0xFF); 740 b[arrayPos++] = (byte) ((intValue >> 8) & 0xFF); 741 b[arrayPos++] = (byte) (intValue & 0xFF); 742 } 743 744 return b; 745 } 746 747 748 749 /** 750 * Decodes the contents of the provided base64-encoded string to a string 751 * containing the raw data using the UTF-8 encoding. 752 * 753 * @param data The base64-encoded string to decode. It must not be 754 * {@code null}. 755 * 756 * @return A string containing the decoded data. 757 * 758 * @throws ParseException If the contents of the provided string cannot be 759 * parsed as base64-encoded data using the UTF-8 760 * encoding. 761 */ 762 public static String decodeToString(final String data) 763 throws ParseException 764 { 765 Validator.ensureNotNull(data); 766 767 final byte[] decodedBytes = decode(data); 768 return StaticUtils.toUTF8String(decodedBytes); 769 } 770 771 772 773 /** 774 * Decodes the contents of the provided base64url-encoded string. 775 * 776 * @param data The base64url-encoded string to decode. It must not be 777 * {@code null}. 778 * 779 * @return A byte array containing the decoded data. 780 * 781 * @throws ParseException If the contents of the provided string cannot be 782 * parsed as base64url-encoded data. 783 */ 784 public static byte[] urlDecode(final String data) 785 throws ParseException 786 { 787 Validator.ensureNotNull(data); 788 789 final int length = data.length(); 790 if (length == 0) 791 { 792 return StaticUtils.NO_BYTES; 793 } 794 795 int stringPos = 0; 796 final ByteStringBuffer buffer = new ByteStringBuffer(length); 797decodeLoop: 798 while (stringPos < length) 799 { 800 int intValue = 0x00; 801 for (int i=0; i < 4; i++) 802 { 803 // Since the value may not be padded, then we need to handle the 804 // possibility of missing characters. 805 final char c; 806 if (stringPos >= length) 807 { 808 c = '='; 809 stringPos++; 810 } 811 else 812 { 813 c = data.charAt(stringPos++); 814 } 815 816 intValue <<= 6; 817 switch (c) 818 { 819 case 'A': 820 intValue |= 0x00; 821 break; 822 case 'B': 823 intValue |= 0x01; 824 break; 825 case 'C': 826 intValue |= 0x02; 827 break; 828 case 'D': 829 intValue |= 0x03; 830 break; 831 case 'E': 832 intValue |= 0x04; 833 break; 834 case 'F': 835 intValue |= 0x05; 836 break; 837 case 'G': 838 intValue |= 0x06; 839 break; 840 case 'H': 841 intValue |= 0x07; 842 break; 843 case 'I': 844 intValue |= 0x08; 845 break; 846 case 'J': 847 intValue |= 0x09; 848 break; 849 case 'K': 850 intValue |= 0x0A; 851 break; 852 case 'L': 853 intValue |= 0x0B; 854 break; 855 case 'M': 856 intValue |= 0x0C; 857 break; 858 case 'N': 859 intValue |= 0x0D; 860 break; 861 case 'O': 862 intValue |= 0x0E; 863 break; 864 case 'P': 865 intValue |= 0x0F; 866 break; 867 case 'Q': 868 intValue |= 0x10; 869 break; 870 case 'R': 871 intValue |= 0x11; 872 break; 873 case 'S': 874 intValue |= 0x12; 875 break; 876 case 'T': 877 intValue |= 0x13; 878 break; 879 case 'U': 880 intValue |= 0x14; 881 break; 882 case 'V': 883 intValue |= 0x15; 884 break; 885 case 'W': 886 intValue |= 0x16; 887 break; 888 case 'X': 889 intValue |= 0x17; 890 break; 891 case 'Y': 892 intValue |= 0x18; 893 break; 894 case 'Z': 895 intValue |= 0x19; 896 break; 897 case 'a': 898 intValue |= 0x1A; 899 break; 900 case 'b': 901 intValue |= 0x1B; 902 break; 903 case 'c': 904 intValue |= 0x1C; 905 break; 906 case 'd': 907 intValue |= 0x1D; 908 break; 909 case 'e': 910 intValue |= 0x1E; 911 break; 912 case 'f': 913 intValue |= 0x1F; 914 break; 915 case 'g': 916 intValue |= 0x20; 917 break; 918 case 'h': 919 intValue |= 0x21; 920 break; 921 case 'i': 922 intValue |= 0x22; 923 break; 924 case 'j': 925 intValue |= 0x23; 926 break; 927 case 'k': 928 intValue |= 0x24; 929 break; 930 case 'l': 931 intValue |= 0x25; 932 break; 933 case 'm': 934 intValue |= 0x26; 935 break; 936 case 'n': 937 intValue |= 0x27; 938 break; 939 case 'o': 940 intValue |= 0x28; 941 break; 942 case 'p': 943 intValue |= 0x29; 944 break; 945 case 'q': 946 intValue |= 0x2A; 947 break; 948 case 'r': 949 intValue |= 0x2B; 950 break; 951 case 's': 952 intValue |= 0x2C; 953 break; 954 case 't': 955 intValue |= 0x2D; 956 break; 957 case 'u': 958 intValue |= 0x2E; 959 break; 960 case 'v': 961 intValue |= 0x2F; 962 break; 963 case 'w': 964 intValue |= 0x30; 965 break; 966 case 'x': 967 intValue |= 0x31; 968 break; 969 case 'y': 970 intValue |= 0x32; 971 break; 972 case 'z': 973 intValue |= 0x33; 974 break; 975 case '0': 976 intValue |= 0x34; 977 break; 978 case '1': 979 intValue |= 0x35; 980 break; 981 case '2': 982 intValue |= 0x36; 983 break; 984 case '3': 985 intValue |= 0x37; 986 break; 987 case '4': 988 intValue |= 0x38; 989 break; 990 case '5': 991 intValue |= 0x39; 992 break; 993 case '6': 994 intValue |= 0x3A; 995 break; 996 case '7': 997 intValue |= 0x3B; 998 break; 999 case '8': 1000 intValue |= 0x3C; 1001 break; 1002 case '9': 1003 intValue |= 0x3D; 1004 break; 1005 case '-': 1006 intValue |= 0x3E; 1007 break; 1008 case '_': 1009 intValue |= 0x3F; 1010 break; 1011 case '=': 1012 case '%': 1013 switch ((stringPos-1) % 4) 1014 { 1015 case 2: 1016 // The string should have two padding tokens, so only a single 1017 // byte of data remains. Shift the value ten bits to the right 1018 // and read that single byte. 1019 intValue >>= 10; 1020 buffer.append((byte) (intValue & 0xFF)); 1021 break decodeLoop; 1022 case 3: 1023 // The string should have a single padding token, so two bytes 1024 // of data remain. Shift the value eight bits to the right and 1025 // read those two bytes. 1026 intValue >>= 8; 1027 buffer.append((byte) ((intValue >> 8) & 0xFF)); 1028 buffer.append((byte) (intValue & 0xFF)); 1029 break decodeLoop; 1030 } 1031 1032 // If we've gotten here, then that must mean the string had padding 1033 // when none was needed, or it had an invalid length. That's an 1034 // error. 1035 throw new ParseException(ERR_BASE64_URLDECODE_INVALID_LENGTH.get(), 1036 (stringPos-1)); 1037 1038 default: 1039 throw new ParseException( 1040 ERR_BASE64_DECODE_UNEXPECTED_CHAR.get( 1041 data.charAt(stringPos-1)), 1042 (stringPos-1)); 1043 } 1044 } 1045 1046 buffer.append((byte) ((intValue >> 16) & 0xFF)); 1047 buffer.append((byte) ((intValue >> 8) & 0xFF)); 1048 buffer.append((byte) (intValue & 0xFF)); 1049 } 1050 1051 return buffer.toByteArray(); 1052 } 1053 1054 1055 1056 /** 1057 * Decodes the contents of the provided base64-encoded string to a string 1058 * containing the raw data using the UTF-8 encoding. 1059 * 1060 * @param data The base64-encoded string to decode. It must not be 1061 * {@code null}. 1062 * 1063 * @return A string containing the decoded data. 1064 * 1065 * @throws ParseException If the contents of the provided string cannot be 1066 * parsed as base64-encoded data using the UTF-8 1067 * encoding. 1068 */ 1069 public static String urlDecodeToString(final String data) 1070 throws ParseException 1071 { 1072 Validator.ensureNotNull(data); 1073 1074 final byte[] decodedBytes = urlDecode(data); 1075 return StaticUtils.toUTF8String(decodedBytes); 1076 } 1077}