001/* 002 * Copyright 2020-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2020-2024 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) 2020-2024 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.ldap.sdk.unboundidds; 037 038 039 040import java.io.Serializable; 041import java.security.GeneralSecurityException; 042import java.security.SecureRandom; 043import java.text.ParseException; 044import java.util.Arrays; 045 046import javax.crypto.BadPaddingException; 047import javax.crypto.Cipher; 048import javax.crypto.spec.GCMParameterSpec; 049 050import com.unboundid.util.Base64; 051import com.unboundid.util.ByteStringBuffer; 052import com.unboundid.util.CryptoHelper; 053import com.unboundid.util.Debug; 054import com.unboundid.util.NotMutable; 055import com.unboundid.util.NotNull; 056import com.unboundid.util.StaticUtils; 057import com.unboundid.util.ThreadLocalSecureRandom; 058import com.unboundid.util.ThreadSafety; 059import com.unboundid.util.ThreadSafetyLevel; 060import com.unboundid.util.Validator; 061 062import static com.unboundid.ldap.sdk.unboundidds.UnboundIDDSMessages.*; 063 064 065 066/** 067 * This class provides a mechanism that can be used to encrypt and decrypt 068 * passwords using the same mechanism that the Ping Identity Directory Server 069 * uses for the AES256 password storage scheme (for clients that know the 070 * passphrase used to generate the encryption key). 071 * <BR> 072 * <BLOCKQUOTE> 073 * <B>NOTE:</B> This class, and other classes within the 074 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 075 * supported for use against Ping Identity, UnboundID, and 076 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 077 * for proprietary functionality or for external specifications that are not 078 * considered stable or mature enough to be guaranteed to work in an 079 * interoperable way with other types of LDAP servers. 080 * </BLOCKQUOTE> 081 * <BR> 082 * Note that this class requires strong encryption support in the underlying 083 * JVM. For Java 7 JVMs, and for Java 8 JVMs prior to update 161, this requires 084 * installing unlimited strength jurisdiction policy files in the JVM. For Java 085 * 8 JVMs starting with update 161, and for all later Java versions, strong 086 * encryption should be available by default. 087 * <BR><BR> 088 * The raw representation for encoded passwords is constructed as follows: 089 * <OL> 090 * <LI> 091 * A single byte that combines the encoding version and the padding length. 092 * The least significant four bits represent a two's complement integer that 093 * indicate the number of zero bytes that will be appended to the provided 094 * password to make it a multiple of sixteen bytes. The most significant 095 * four bits represent the encoding version. At present, we only support a 096 * single encoding version in which all of those bits must be set to zero. 097 * With this encoding version, the following properties will be used: 098 * <UL> 099 * <LI>Cipher Transformation: AES/GCM/NoPadding</LI> 100 * <LI>Key Factory Algorithm: PBKDF2WithHmacSHA512</LI> 101 * <LI>Key Factory Iteration Count: 32,768</LI> 102 * <LI>Key Factory Salt Length: 128 bits (16 bytes)</LI> 103 * <LI>Key Factory Generated Key length: 256 bits (32 bytes)</LI> 104 * <LI>Initialization Vector Length: 128 bits (16 bytes)</LI> 105 * <LI>GCM Tag Length: 128 bits</LI> 106 * <LI>Padding Modulus: 16</LI> 107 * </UL> 108 * </LI> 109 * <LI> 110 * Sixteen bytes of random data generated by a secure random number 111 * generator. This represents the salt provided to the key factory for the 112 * purpose of generating the secret key. 113 * </LI> 114 * <LI> 115 * Sixteen bytes of random data generated by a secure random number 116 * generator. This represents the initialization vector that will be used 117 * to randomize the cipher output. 118 * </LI> 119 * <LI> 120 * One byte that represents a two's complement integer that indicates the 121 * number of bytes in the ID of the encryption settings definition whose 122 * passphrase is used to generate the encryption key. The value must be 123 * less than or equal to 255. For current versions of the Ping Identity 124 * Directory Server, it will typically be 32 bytes. 125 * </LI> 126 * <LI> 127 * The bytes that comprise the raw ID of the encryption settings definition 128 * whose passphrase will be used to generate the encryption key. 129 * </LI> 130 * <LI> 131 * The bytes that comprise the encrypted password using the above settings. 132 * </LI> 133 * </OL> 134 * <BR> 135 * The string representation of the encoded password is generated by appending 136 * the base64-encoded representation of the raw encoded bytes to the prefix 137 * "{AES256}". 138 * <BR><BR> 139 * When encrypting a password using this algorithm, the first step is to 140 * generate the encryption key. This is done using a key factory, which 141 * combines a passphrase (obtained from an encryption settings definition), an 142 * iteration count, and a salt. 143 * <BR><BR> 144 * The second step is to apply any necessary padding to the password. Because 145 * AES used in Galois Counter mode (GCM) behaves as a stream cipher, the size of 146 * the encrypted data can be used to determine the size of the plaintext that 147 * was encrypted. This is undesirable when encrypting passwords because it can 148 * let an attacker know how long the user's password is. Padding the password 149 * (by appending enough zero bytes to make its length a multiple of sixteen 150 * bytes) makes it impossible for an attacker to determine the size of the 151 * clear-text password. 152 * <BR><BR> 153 * The final step is to perform the encryption. A cipher is created using the 154 * generated secret key and initialization vector, and it is used to encrypt the 155 * padded password. 156 * <BR><BR> 157 * Because this encoding uses reversible encryption rather than a one-way 158 * algorithm, there are two possible ways to verify that a provided plain-text 159 * password matches an encoded representation. Both involve decoding the 160 * encoded representation of the password to extract the padding length, salt, 161 * initialization vector, and encryption settings definition ID. Then, you can 162 * either encrypt the provided plaintext password with the same settings to 163 * verify that it yields the same encoded representation, or you can decrypt 164 * the encoded password and remove any padding to verify that it yields the same 165 * plaintext representation. 166 */ 167@NotMutable() 168@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 169public final class AES256EncodedPassword 170 implements Serializable 171{ 172 /** 173 * The bitmask that will be used to indicate an encoding version of zero. 174 * Only the most significant four bits of this byte will be used. The least 175 * significant four bits of the first byte will be used to indicate the number 176 * of padding bytes that must be appended to the clear-text password to make 177 * its length a multiple of sixteen bytes. 178 */ 179 public static final byte ENCODING_VERSION_0_MASK = (byte) 0x00; 180 181 182 183 /** 184 * The integer value for encoding version 0. 185 */ 186 public static final int ENCODING_VERSION_0 = 0; 187 188 189 190 /** 191 * The GCM tag length in bits to use with an encoding version of zero. 192 */ 193 public static final int ENCODING_VERSION_0_GCM_TAG_LENGTH_BITS = 128; 194 195 196 197 /** 198 * The generated secret key length in bits to use with an encoding version of 199 * zero. 200 */ 201 public static final int ENCODING_VERSION_0_GENERATED_KEY_LENGTH_BITS = 256; 202 203 204 205 /** 206 * The size in bytes to use for the initialization vector with an encoding 207 * version of zero. 208 */ 209 public static final int ENCODING_VERSION_0_IV_LENGTH_BYTES = 16; 210 211 212 213 /** 214 * The key factory iteration count to use with an encoding version of zero. 215 */ 216 public static final int ENCODING_VERSION_0_KEY_FACTORY_ITERATION_COUNT = 217 32_768; 218 219 220 221 /** 222 * The size in bytes to use for the key factory salt with an encoding version 223 * of zero. 224 */ 225 public static final int ENCODING_VERSION_0_KEY_FACTORY_SALT_LENGTH_BYTES = 16; 226 227 228 229 /** 230 * The padding modulus to use with an encoding version of zero. 231 */ 232 public static final int ENCODING_VERSION_0_PADDING_MODULUS = 16; 233 234 235 236 /** 237 * The name of the cipher algorithm that should be used with an encoding 238 * version of zero. 239 */ 240 @NotNull public static final String ENCODING_VERSION_0_CIPHER_ALGORITHM = 241 "AES"; 242 243 244 245 /** 246 * The name of the cipher transformation that should be used with an encoding 247 * version of zero. 248 */ 249 @NotNull public static final String ENCODING_VERSION_0_CIPHER_TRANSFORMATION = 250 "AES/GCM/NoPadding"; 251 252 253 254 /** 255 * The name of the key factory algorithm should be used with an encoding 256 * version of zero. 257 */ 258 @NotNull public static final String ENCODING_VERSION_0_KEY_FACTORY_ALGORITHM = 259 "PBKDF2WithHmacSHA512"; 260 261 262 263 /** 264 * The prefix that will appear at the beginning of the string representation 265 * for an encoded password. 266 */ 267 @NotNull public static final String PASSWORD_STORAGE_SCHEME_PREFIX = 268 "{AES256}"; 269 270 271 272 /** 273 * The serial version UID for this serializable class. 274 */ 275 private static final long serialVersionUID = 8663129897722695672L; 276 277 278 279 // The bytes that comprise the complete raw encoded representation of the 280 // password. 281 @NotNull private final byte[] encodedRepresentation; 282 283 // The bytes that comprise the encrypted representation of the padded 284 // password. 285 @NotNull private final byte[] encryptedPaddedPassword; 286 287 // The bytes that comprise the raw encryption settings definition ID whose 288 // passphrase was used to generate the encoded password. 289 @NotNull private final byte[] encryptionSettingsDefinitionID; 290 291 // The initialization vector used to randomize the output of the encrypted 292 // password. 293 @NotNull private final byte[] initializationVector; 294 295 // The salt used in the course of generating the encryption key from the 296 // encryption settings definition passphrase. 297 @NotNull private final byte[] keyFactorySalt; 298 299 // The encoding version used for the password. 300 private final int encodingVersion; 301 302 // The number of zero bytes that were appended to the clear-text password 303 // before it was encrypted. 304 private final int paddingBytes; 305 306 307 308 /** 309 * Creates a new encoded password with the provided information. 310 * 311 * @param encodedRepresentation 312 * The bytes that comprise the complete raw encoded 313 * representation of the password. 314 * @param encodingVersion 315 * The encoding version for this encoded password. 316 * @param paddingBytes 317 * The number of bytes of padding that need to be appended to the 318 * clear-text password to make its length a multiple of sixteen 319 * bytes. 320 * @param keyFactorySalt 321 * The salt used to generate the encryption key from the 322 * encryption settings definition passphrase. 323 * @param initializationVector 324 * The initialization vector used to randomize the cipher output. 325 * @param encryptionSettingsDefinitionID 326 * The bytes that comprise the raw encryption settings definition 327 * ID whose passphrase was used to generate the encoded password. 328 * @param encryptedPaddedPassword 329 * The bytes that comprise the encrypted representation of the 330 * padded password. 331 */ 332 private AES256EncodedPassword( 333 @NotNull final byte[] encodedRepresentation, 334 final int encodingVersion, 335 final int paddingBytes, 336 @NotNull final byte[] keyFactorySalt, 337 @NotNull final byte[] initializationVector, 338 @NotNull final byte[] encryptionSettingsDefinitionID, 339 @NotNull final byte[] encryptedPaddedPassword) 340 { 341 this.encodedRepresentation = encodedRepresentation; 342 this.encodingVersion = encodingVersion; 343 this.paddingBytes = paddingBytes; 344 this.keyFactorySalt = keyFactorySalt; 345 this.initializationVector = initializationVector; 346 this.encryptionSettingsDefinitionID = encryptionSettingsDefinitionID; 347 this.encryptedPaddedPassword = encryptedPaddedPassword; 348 } 349 350 351 352 /** 353 * Retrieves the encoding version for this encoded password. 354 * 355 * @return The encoding version for this encoded password. 356 */ 357 public int getEncodingVersion() 358 { 359 return encodingVersion; 360 } 361 362 363 364 /** 365 * Retrieves the number of bytes of padding that need to be appended to the 366 * clear-text password to make its length a multiple of sixteen bytes. 367 * 368 * @return The number of bytes of padding that need to be appended to the 369 * clear-text password to make its length a multiple of sixteen 370 * bytes. 371 */ 372 public int getPaddingBytes() 373 { 374 return paddingBytes; 375 } 376 377 378 379 /** 380 * Retrieves the salt used to generate the encryption key from the encryption 381 * settings definition passphrase. 382 * 383 * @return The salt used to generate the encryption key from the encryption 384 * settings definition passphrase. 385 */ 386 @NotNull() 387 public byte[] getKeyFactorySalt() 388 { 389 return keyFactorySalt; 390 } 391 392 393 394 /** 395 * Retrieves the initialization vector used to randomize the cipher output. 396 * 397 * @return The initialization vector used to randomize the cipher output. 398 */ 399 @NotNull() 400 public byte[] getInitializationVector() 401 { 402 return initializationVector; 403 } 404 405 406 407 /** 408 * Retrieves the bytes that comprise the raw ID of the encryption settings 409 * definition whose passphrase is used to generate the encryption key. 410 * 411 * @return A bytes that comprise the raw ID of the encryption settings 412 * definition whose passphrase is used to generate the encryption 413 * key. 414 */ 415 @NotNull() 416 public byte[] getEncryptionSettingsDefinitionIDBytes() 417 { 418 return encryptionSettingsDefinitionID; 419 } 420 421 422 423 /** 424 * Retrieves a string representation of the ID of the encryption settings 425 * definition whose passphrase is used to generate the encryption key. 426 * 427 * @return A string representation of the ID of the encryption settings 428 * definition whose passphrase is used to generate the encryption 429 * key. 430 */ 431 @NotNull() 432 public String getEncryptionSettingsDefinitionIDString() 433 { 434 return StaticUtils.toUpperCase( 435 StaticUtils.toHex(encryptionSettingsDefinitionID)); 436 } 437 438 439 440 /** 441 * Retrieves the bytes that comprise the complete raw encoded representation 442 * of the password. 443 * 444 * @return The bytes that comprise the complete raw encoded representation of 445 * the password. 446 */ 447 @NotNull() 448 public byte[] getEncodedRepresentation() 449 { 450 return encodedRepresentation; 451 } 452 453 454 455 /** 456 * Retrieves the string representation of this AES256 password. 457 * 458 * @param includeScheme Indicates whether to include the "{AES256}" prefix 459 * at the beginning of the string representation. 460 * 461 * @return The string representation of this encoded password. 462 */ 463 @NotNull() 464 public String getStringRepresentation(final boolean includeScheme) 465 { 466 final String base64String = Base64.encode(encodedRepresentation); 467 if (includeScheme) 468 { 469 return PASSWORD_STORAGE_SCHEME_PREFIX + base64String; 470 } 471 else 472 { 473 return base64String; 474 } 475 } 476 477 478 479 /** 480 * Encodes a password using the provided information. 481 * 482 * @param encryptionSettingsDefinitionID 483 * A string with the hexadecimal representation of the 484 * encryption settings definition whose passphrase was used to 485 * generate the encoded password. It must not be 486 * {@code null} or empty, and it must represent a valid 487 * hexadecimal string whose length is an even number less than 488 * or equal to 510 bytes. 489 * @param encryptionSettingsDefinitionPassphrase 490 * The passphrase associated with the specified encryption 491 * settings definition. It must not be {@code null} or empty. 492 * @param clearTextPassword 493 * The clear-text password to encode. It must not be 494 * {@code null} or empty. 495 * 496 * @return An object with all of the encoded password details. 497 * 498 * @throws GeneralSecurityException If a problem occurs while attempting to 499 * perform any of the cryptographic 500 * processing. 501 * 502 * @throws ParseException If the provided encryption settings definition ID 503 * cannot be parsed as a valid hexadecimal string. 504 */ 505 @NotNull() 506 public static AES256EncodedPassword encode( 507 @NotNull final String encryptionSettingsDefinitionID, 508 @NotNull final String encryptionSettingsDefinitionPassphrase, 509 @NotNull final String clearTextPassword) 510 throws GeneralSecurityException, ParseException 511 { 512 final byte[] encryptionSettingsDefinitionIDBytes = 513 StaticUtils.fromHex(encryptionSettingsDefinitionID); 514 515 final char[] encryptionSettingsDefinitionPassphraseChars = 516 encryptionSettingsDefinitionPassphrase.toCharArray(); 517 final byte[] clearTextPasswordBytes = 518 StaticUtils.getBytes(clearTextPassword); 519 520 try 521 { 522 return encode(encryptionSettingsDefinitionIDBytes, 523 encryptionSettingsDefinitionPassphraseChars, clearTextPasswordBytes); 524 } 525 finally 526 { 527 Arrays.fill(encryptionSettingsDefinitionPassphraseChars, '\u0000'); 528 Arrays.fill(clearTextPasswordBytes, (byte) 0x00); 529 } 530 } 531 532 533 534 /** 535 * Encodes a password using the provided information. 536 * 537 * @param encryptionSettingsDefinitionID 538 * The bytes that comprise the raw encryption settings definition 539 * ID whose passphrase was used to generate the encoded password. 540 * It must not be {@code null} or empty, and its length must be 541 * less than or equal to 255 bytes. 542 * @param encryptionSettingsDefinitionPassphrase 543 * The passphrase associated with the specified encryption 544 * settings definition. It must not be {@code null} or empty. 545 * @param clearTextPassword 546 * The bytes that comprise the clear-text password to encode. 547 * It must not be {@code null} or empty. 548 * 549 * @return An object with all of the encoded password details. 550 * 551 * @throws GeneralSecurityException If a problem occurs while attempting to 552 * perform any of the cryptographic 553 * processing. 554 */ 555 @NotNull() 556 public static AES256EncodedPassword encode( 557 @NotNull final byte[] encryptionSettingsDefinitionID, 558 @NotNull final char[] encryptionSettingsDefinitionPassphrase, 559 @NotNull final byte[] clearTextPassword) 560 throws GeneralSecurityException 561 { 562 final SecureRandom random = ThreadLocalSecureRandom.get(); 563 564 final byte[] keyFactorySalt = 565 new byte[ENCODING_VERSION_0_KEY_FACTORY_SALT_LENGTH_BYTES]; 566 random.nextBytes(keyFactorySalt); 567 568 final byte[] initializationVector = 569 new byte[ENCODING_VERSION_0_IV_LENGTH_BYTES]; 570 random.nextBytes(initializationVector); 571 572 return encode(encryptionSettingsDefinitionID, 573 encryptionSettingsDefinitionPassphrase, keyFactorySalt, 574 initializationVector, clearTextPassword); 575 } 576 577 578 579 /** 580 * Encodes a password using the provided information. 581 * 582 * @param encryptionSettingsDefinitionID 583 * The bytes that comprise the raw encryption settings definition 584 * ID whose passphrase was used to generate the encoded password. 585 * It must not be {@code null} or empty, and its length must be 586 * less than or equal to 255 bytes. 587 * @param encryptionSettingsDefinitionPassphrase 588 * The passphrase associated with the specified encryption 589 * settings definition. It must not be {@code null} or empty. 590 * @param keyFactorySalt 591 * The salt used to generate the encryption key from the 592 * encryption settings definition passphrase. It must not be 593 * {@code null} and it must have a length of exactly 16 bytes. 594 * @param initializationVector 595 * The initialization vector used to randomize the cipher output. 596 * It must not be [@code null} and it must have a length of 597 * exactly 16 bytes. 598 * @param clearTextPassword 599 * The bytes that comprise the clear-text password to encode. 600 * It must not be {@code null} or empty. 601 * 602 * @return An object with all of the encoded password details. 603 * 604 * @throws GeneralSecurityException If a problem occurs while attempting to 605 * perform any of the cryptographic 606 * processing. 607 */ 608 @NotNull() 609 public static AES256EncodedPassword encode( 610 @NotNull final byte[] encryptionSettingsDefinitionID, 611 @NotNull final char[] encryptionSettingsDefinitionPassphrase, 612 @NotNull final byte[] keyFactorySalt, 613 @NotNull final byte[] initializationVector, 614 @NotNull final byte[] clearTextPassword) 615 throws GeneralSecurityException 616 { 617 final AES256EncodedPasswordSecretKey secretKey = 618 AES256EncodedPasswordSecretKey.generate(encryptionSettingsDefinitionID, 619 encryptionSettingsDefinitionPassphrase, keyFactorySalt); 620 try 621 { 622 return encode(secretKey, initializationVector, clearTextPassword); 623 } 624 finally 625 { 626 secretKey.destroy(); 627 } 628 } 629 630 631 632 /** 633 * Encodes a password using the provided information. 634 * 635 * @param secretKey 636 * The secret key that should be used to encrypt the password. 637 * It must not be {@code null}. The secret key can be reused 638 * when 639 * @param initializationVector 640 * The initialization vector used to randomize the cipher output. 641 * It must not be [@code null} and it must have a length of 642 * exactly 16 bytes. 643 * @param clearTextPassword 644 * The bytes that comprise the clear-text password to encode. 645 * It must not be {@code null} or empty. 646 * 647 * @return An object with all of the encoded password details. 648 * 649 * @throws GeneralSecurityException If a problem occurs while attempting to 650 * perform any of the cryptographic 651 * processing. 652 */ 653 @NotNull() 654 public static AES256EncodedPassword encode( 655 @NotNull final AES256EncodedPasswordSecretKey secretKey, 656 @NotNull final byte[] initializationVector, 657 @NotNull final byte[] clearTextPassword) 658 throws GeneralSecurityException 659 { 660 // Validate all of the provided parameters. 661 Validator.ensureNotNull(secretKey, 662 "AES256EncodedPassword.encode.secretKey must not be null."); 663 664 Validator.ensureNotNull(initializationVector, 665 "AES256EncodedPassword.encode.initializationVector must not be null."); 666 if (initializationVector.length != ENCODING_VERSION_0_IV_LENGTH_BYTES) 667 { 668 Validator.violation("AES256EncodedPassword.encode.initializationVector " + 669 "must have a length of exactly " + 670 ENCODING_VERSION_0_IV_LENGTH_BYTES + " bytes. The provided " + 671 "initialization vector had a length of " + 672 initializationVector.length + " bytes."); 673 } 674 675 Validator.ensureNotNullOrEmpty(clearTextPassword, 676 "AES256EncodedPassword.encode.clearTextPassword must not be null or " + 677 "empty."); 678 679 680 // Generate a padded representation of the password. 681 final byte[] paddedClearTextPassword; 682 final int paddingBytesNeeded; 683 final int clearTextPasswordLengthModulus = 684 clearTextPassword.length % ENCODING_VERSION_0_PADDING_MODULUS; 685 if (clearTextPasswordLengthModulus == 0) 686 { 687 paddedClearTextPassword = clearTextPassword; 688 paddingBytesNeeded = 0; 689 } 690 else 691 { 692 paddingBytesNeeded = 693 ENCODING_VERSION_0_PADDING_MODULUS - clearTextPasswordLengthModulus; 694 paddedClearTextPassword = 695 new byte[clearTextPassword.length + paddingBytesNeeded]; 696 Arrays.fill(paddedClearTextPassword, (byte) 0x00); 697 System.arraycopy(clearTextPassword, 0, paddedClearTextPassword, 0, 698 clearTextPassword.length); 699 } 700 701 702 // Create and initialize the cipher and use it to encrypt the padded 703 // password. 704 final Cipher cipher = 705 CryptoHelper.getCipher(ENCODING_VERSION_0_CIPHER_TRANSFORMATION); 706 cipher.init(Cipher.ENCRYPT_MODE, secretKey.getSecretKey(), 707 new GCMParameterSpec(ENCODING_VERSION_0_GCM_TAG_LENGTH_BITS, 708 initializationVector)); 709 final byte[] encryptedPaddedPassword = 710 cipher.doFinal(paddedClearTextPassword); 711 712 713 // Generate the raw encoded representation of the password. 714 final ByteStringBuffer buffer = new ByteStringBuffer(); 715 716 // The first byte will combine the encoding version (the upper four bits) 717 // and the number of padding bytes (the lower four bits). 718 buffer.append((byte) 719 ((ENCODING_VERSION_0_MASK & 0xF0) | (paddingBytesNeeded & 0x0F))); 720 721 // The next 16 bytes will be the salt. 722 final byte[] keyFactorySalt = secretKey.getKeyFactorySalt(); 723 buffer.append(keyFactorySalt); 724 725 // The next 16 bytes will be the initialization vector. 726 buffer.append(initializationVector); 727 728 // The next byte will be the number of bytes in the raw encryption settings 729 // definition ID, followed by the encoded ID. 730 final byte[] encryptionSettingsDefinitionID = 731 secretKey.getEncryptionSettingsDefinitionID(); 732 buffer.append((byte) (encryptionSettingsDefinitionID.length & 0xFF)); 733 buffer.append(encryptionSettingsDefinitionID); 734 735 // The remainder of the encoded representation will be the encrypted 736 // padded password. 737 buffer.append(encryptedPaddedPassword); 738 739 740 // Create and return an object with all of the encoded password details. 741 return new AES256EncodedPassword(buffer.toByteArray(), ENCODING_VERSION_0, 742 paddingBytesNeeded, keyFactorySalt, initializationVector, 743 encryptionSettingsDefinitionID, encryptedPaddedPassword); 744 } 745 746 747 748 /** 749 * Decodes the provided password into its component parts. 750 * 751 * @param encodedPassword 752 * The string representation of the encoded password to be 753 * decoded. It must not be {@code null} or empty, and it must 754 * contain the base64-encoded representation of the raw encoded 755 * password, optionally preceded by the "{AES256}" prefix. 756 * 757 * @return The decoded representation of the provided password. 758 * 759 * @throws ParseException If the provided string does not represent a valid 760 * encoded password. 761 */ 762 @NotNull() 763 public static AES256EncodedPassword decode( 764 @NotNull final String encodedPassword) 765 throws ParseException 766 { 767 Validator.ensureNotNullOrEmpty(encodedPassword, 768 "AES256EncodedPassword.decode.encodedPassword must not be null or " + 769 "empty."); 770 771 772 // If the provided string starts with a prefix, then strip it off. 773 final int base64StartPos; 774 final String base64EncodedString; 775 if (encodedPassword.startsWith(PASSWORD_STORAGE_SCHEME_PREFIX)) 776 { 777 base64StartPos = PASSWORD_STORAGE_SCHEME_PREFIX.length(); 778 base64EncodedString = encodedPassword.substring(base64StartPos); 779 } 780 else 781 { 782 base64StartPos = 0; 783 base64EncodedString = encodedPassword; 784 } 785 786 787 // Base64-decode the data. 788 final byte[] encodedPasswordBytes; 789 try 790 { 791 encodedPasswordBytes = Base64.decode(base64EncodedString); 792 } 793 catch (final ParseException e) 794 { 795 Debug.debugException(e); 796 throw new ParseException( 797 ERR_AES256_ENC_PW_DECODE_NOT_BASE64.get( 798 StaticUtils.getExceptionMessage(e)), 799 base64StartPos); 800 } 801 802 803 return decode(encodedPasswordBytes); 804 } 805 806 807 808 /** 809 * Decodes the provided password into its component parts. 810 * 811 * @param encodedPassword 812 * The bytes that comprise the complete raw encoded 813 * representation of the password. It must not be {@code null} 814 * or empty. 815 * 816 * @return The decoded representation of the provided password. 817 * 818 * @throws ParseException If the provided string does not represent a valid 819 * encoded password. 820 */ 821 @NotNull() 822 public static AES256EncodedPassword decode( 823 @NotNull final byte[] encodedPassword) 824 throws ParseException 825 { 826 Validator.ensureNotNullOrEmpty(encodedPassword, 827 "AES256EncodedPassword.decode.encodedPassword must not be null or " + 828 "empty."); 829 830 831 // Make sure that the length is at least 36 bytes long. This isn't long 832 // enough for a valid encoded password, but it's long enough to let us get a 833 // good starting point. 834 if (encodedPassword.length < 36) 835 { 836 throw new ParseException( 837 ERR_AES256_ENC_PW_DECODE_TOO_SHORT_INITIAL.get( 838 encodedPassword.length), 839 0); 840 } 841 842 843 // The first byte must contain the encoding version and the number of bytes 844 // of padding. 845 final byte encodingVersionAndPaddingByte = encodedPassword[0]; 846 final int encodingVersion = (encodingVersionAndPaddingByte >> 4) & 0x0F; 847 if (encodingVersion != ENCODING_VERSION_0) 848 { 849 throw new ParseException( 850 ERR_AES256_ENC_PW_DECODE_UNSUPPORTED_ENCODING_VERSION.get( 851 encodingVersion, ENCODING_VERSION_0), 852 0); 853 } 854 855 final int paddingBytes = (encodingVersionAndPaddingByte & 0x0F); 856 857 858 // The next 16 bytes must contain the salt. 859 final byte[] keyFactorySalt = 860 new byte[ENCODING_VERSION_0_KEY_FACTORY_SALT_LENGTH_BYTES]; 861 System.arraycopy(encodedPassword, 1, keyFactorySalt, 0, 862 keyFactorySalt.length); 863 864 865 // The next 16 bytes must contain the initialization vector. 866 final byte[] initializationVector = 867 new byte[ENCODING_VERSION_0_IV_LENGTH_BYTES]; 868 System.arraycopy(encodedPassword, 1 + keyFactorySalt.length, 869 initializationVector, 0, initializationVector.length); 870 871 872 // The next byte must indicate how many bytes are in the encryption settings 873 // definition ID. That should then be followed by the specified number of 874 // bytes of the encryption settings definition ID. 875 final int esdIDLengthPos = 876 1 + keyFactorySalt.length + initializationVector.length; 877 final int esdIDLength = encodedPassword[esdIDLengthPos] & 0xFF; 878 if (encodedPassword.length < (esdIDLengthPos + 2 + esdIDLength)) 879 { 880 throw new ParseException( 881 ERR_AES256_ENC_PW_DECODE_TOO_SHORT_FOR_ESD_ID.get( 882 encodedPassword.length, esdIDLength), 883 esdIDLengthPos); 884 } 885 886 final byte[] encryptionSettingsDefinitionID = new byte[esdIDLength]; 887 System.arraycopy(encodedPassword, esdIDLengthPos + 1, 888 encryptionSettingsDefinitionID, 0, esdIDLength); 889 890 891 // The remainder of the encoded password must be the encrypted padded 892 // password. 893 final int encryptedPaddedPasswordPos = 894 esdIDLengthPos + 1 + esdIDLength; 895 final int encryptedPaddedPasswordLength = encodedPassword.length - 896 encryptedPaddedPasswordPos; 897 final byte[] encryptedPaddedPassword = 898 new byte[encryptedPaddedPasswordLength]; 899 System.arraycopy(encodedPassword, encryptedPaddedPasswordPos, 900 encryptedPaddedPassword, 0, encryptedPaddedPasswordLength); 901 902 return new AES256EncodedPassword(encodedPassword, encodingVersion, 903 paddingBytes, keyFactorySalt, initializationVector, 904 encryptionSettingsDefinitionID, encryptedPaddedPassword); 905 } 906 907 908 909 /** 910 * Decrypts this encoded password to obtain the original clear-text password 911 * used to generate it. 912 * 913 * @param encryptionSettingsDefinitionPassphrase 914 * The passphrase associated with the encryption settings 915 * definition used to encrypt the password. It must not be 916 * {@code null} or empty. 917 * 918 * @return The original clear-txt password used to generate this encoded 919 * representation. 920 * 921 * @throws GeneralSecurityException If an error occurs while attempting to 922 * decrypt the password using the 923 * provided encryption settings ID 924 * passphrase. 925 */ 926 @NotNull() 927 public byte[] decrypt( 928 @NotNull final String encryptionSettingsDefinitionPassphrase) 929 throws GeneralSecurityException 930 { 931 final char[] passphraseChars = 932 encryptionSettingsDefinitionPassphrase.toCharArray(); 933 934 try 935 { 936 return decrypt(passphraseChars); 937 } 938 finally 939 { 940 Arrays.fill(passphraseChars, '\u0000'); 941 } 942 } 943 944 945 946 /** 947 * Decrypts this encoded password to obtain the original clear-text password 948 * used to generate it. 949 * 950 * @param encryptionSettingsDefinitionPassphrase 951 * The passphrase associated with the encryption settings 952 * definition used to encrypt the password. It must not be 953 * {@code null} or empty. 954 * 955 * @return The original clear-txt password used to generate this encoded 956 * representation. 957 * 958 * @throws GeneralSecurityException If an error occurs while attempting to 959 * decrypt the password using the 960 * provided encryption settings ID 961 * passphrase. 962 */ 963 @NotNull() 964 public byte[] decrypt( 965 @NotNull final char[] encryptionSettingsDefinitionPassphrase) 966 throws GeneralSecurityException 967 { 968 final AES256EncodedPasswordSecretKey secretKey = 969 AES256EncodedPasswordSecretKey.generate(encryptionSettingsDefinitionID, 970 encryptionSettingsDefinitionPassphrase, keyFactorySalt); 971 972 try 973 { 974 return decrypt(secretKey); 975 } 976 finally 977 { 978 secretKey.destroy(); 979 } 980 } 981 982 983 984 /** 985 * Decrypts this encoded password to obtain the original clear-text password 986 * used to generate it. 987 * 988 * @param secretKey 989 * The that will be used to decrypt the password. It must not 990 * be {@code null}. 991 * 992 * @return The original clear-txt password used to generate this encoded 993 * representation. 994 * 995 * @throws GeneralSecurityException If an error occurs while attempting to 996 * decrypt the password using the 997 * provided encryption settings ID 998 * passphrase. 999 */ 1000 @NotNull() 1001 public byte[] decrypt(@NotNull final AES256EncodedPasswordSecretKey secretKey) 1002 throws GeneralSecurityException 1003 { 1004 Validator.ensureNotNull(secretKey, 1005 "AES256EncodedPassword.decrypt.secretKey must not be null."); 1006 1007 1008 // Create and initialize the cipher and use it to decrypt the padded 1009 // password. 1010 final Cipher cipher = 1011 CryptoHelper.getCipher(ENCODING_VERSION_0_CIPHER_TRANSFORMATION); 1012 cipher.init(Cipher.DECRYPT_MODE, secretKey.getSecretKey(), 1013 new GCMParameterSpec(ENCODING_VERSION_0_GCM_TAG_LENGTH_BITS, 1014 initializationVector)); 1015 final byte[] decryptedPaddedPassword = 1016 cipher.doFinal(encryptedPaddedPassword); 1017 1018 1019 // Strip off any padding and return the resulting password. 1020 final byte[] decryptedPassword; 1021 if (paddingBytes > 0) 1022 { 1023 try 1024 { 1025 decryptedPassword = 1026 new byte[decryptedPaddedPassword.length - paddingBytes]; 1027 for (int i=0; i < decryptedPaddedPassword.length; i++) 1028 { 1029 if (i < decryptedPassword.length) 1030 { 1031 // This byte is not padding. 1032 decryptedPassword[i] = decryptedPaddedPassword[i]; 1033 } 1034 else 1035 { 1036 // This byte is considered padding. Make sure that it's 0x00. 1037 if (decryptedPaddedPassword[i] != 0x00) 1038 { 1039 throw new BadPaddingException( 1040 ERR_AES256_ENC_PW_DECRYPT_NONZERO_PADDING.get(paddingBytes)); 1041 } 1042 } 1043 } 1044 1045 System.arraycopy(decryptedPaddedPassword, 0, decryptedPassword, 0, 1046 decryptedPassword.length); 1047 } 1048 finally 1049 { 1050 Arrays.fill(decryptedPaddedPassword, (byte) 0x00); 1051 } 1052 } 1053 else 1054 { 1055 decryptedPassword = decryptedPaddedPassword; 1056 } 1057 1058 return decryptedPassword; 1059 } 1060 1061 1062 1063 /** 1064 * Retrieves a string representation of this AES256 password. 1065 * 1066 * @return A string representation of this encoded password. 1067 */ 1068 @Override() 1069 @NotNull() 1070 public String toString() 1071 { 1072 final StringBuilder buffer = new StringBuilder(); 1073 toString(buffer); 1074 return buffer.toString(); 1075 } 1076 1077 1078 1079 /** 1080 * Appends a string representation of this AES256 encoded password to the 1081 * provided buffer. 1082 * 1083 * @param buffer The buffer to which the information should be appended. 1084 */ 1085 public void toString(@NotNull final StringBuilder buffer) 1086 { 1087 buffer.append("AES256EncodedPassword(stringRepresentation='"); 1088 buffer.append(getStringRepresentation(true)); 1089 buffer.append("', encodingVersion="); 1090 buffer.append(encodingVersion); 1091 buffer.append(", paddingBytes="); 1092 buffer.append(paddingBytes); 1093 buffer.append(", encryptionSettingsDefinitionIDHex='"); 1094 StaticUtils.toHex(encryptionSettingsDefinitionID, buffer); 1095 buffer.append("', keyFactorySaltBytesHex='"); 1096 StaticUtils.toHex(keyFactorySalt, buffer); 1097 buffer.append("', initializationVectorBytesHex='"); 1098 StaticUtils.toHex(keyFactorySalt, buffer); 1099 buffer.append("')"); 1100 } 1101}