001/* 002 * Copyright 2018-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2018-2019 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.OutputStream; 026import java.io.IOException; 027import java.io.InputStream; 028import java.io.Serializable; 029import java.security.GeneralSecurityException; 030import java.security.InvalidKeyException; 031import java.util.ArrayList; 032import java.util.Arrays; 033import java.util.logging.Level; 034 035import javax.crypto.Cipher; 036import javax.crypto.Mac; 037import javax.crypto.SecretKey; 038import javax.crypto.SecretKeyFactory; 039import javax.crypto.spec.IvParameterSpec; 040import javax.crypto.spec.PBEKeySpec; 041import javax.crypto.spec.SecretKeySpec; 042 043import com.unboundid.asn1.ASN1Element; 044import com.unboundid.asn1.ASN1Integer; 045import com.unboundid.asn1.ASN1OctetString; 046import com.unboundid.asn1.ASN1Sequence; 047import com.unboundid.ldap.sdk.LDAPException; 048import com.unboundid.ldap.sdk.ResultCode; 049 050import static com.unboundid.util.UtilityMessages.*; 051 052 053 054/** 055 * This class represents a data structure that will be used to hold information 056 * about the encryption performed by the {@link PassphraseEncryptedOutputStream} 057 * when writing encrypted data, and that will be used by a 058 * {@link PassphraseEncryptedInputStream} to obtain the settings needed to 059 * decrypt the encrypted data. 060 * <BR><BR> 061 * The data associated with this class is completely threadsafe. The methods 062 * used to interact with input and output streams are not threadsafe in that 063 * nothing else should be attempting to read from/write to the stream at the 064 * same time. 065 */ 066@NotMutable() 067@ThreadSafety(level=ThreadSafetyLevel.MOSTLY_THREADSAFE) 068public final class PassphraseEncryptedStreamHeader 069 implements Serializable 070{ 071 /** 072 * The BER type used for the header element that specifies the encoding 073 * version. 074 */ 075 static final byte TYPE_ENCODING_VERSION = (byte) 0x80; 076 077 078 079 /** 080 * The BER type used for the header element containing the key factory 081 * algorithm. 082 */ 083 static final byte TYPE_KEY_FACTORY_ALGORITHM = (byte) 0x81; 084 085 086 087 /** 088 * The BER type used for the header element containing the key factory 089 * iteration count. 090 */ 091 static final byte TYPE_KEY_FACTORY_ITERATION_COUNT = (byte) 0x82; 092 093 094 095 /** 096 * The BER type used for the header element containing the key factory salt. 097 */ 098 static final byte TYPE_KEY_FACTORY_SALT = (byte) 0x83; 099 100 101 102 /** 103 * The BER type used for the header element containing the key length in bits. 104 */ 105 static final byte TYPE_KEY_FACTORY_KEY_LENGTH_BITS = (byte) 0x84; 106 107 108 109 /** 110 * The BER type used for the header element containing the cipher 111 * transformation. 112 */ 113 static final byte TYPE_CIPHER_TRANSFORMATION = (byte) 0x85; 114 115 116 117 /** 118 * The BER type used for the header element containing the cipher 119 * initialization vector. 120 */ 121 static final byte TYPE_CIPHER_INITIALIZATION_VECTOR = (byte) 0x86; 122 123 124 125 /** 126 * The BER type used for the header element containing the key identifier. 127 */ 128 static final byte TYPE_KEY_IDENTIFIER = (byte) 0x87; 129 130 131 132 /** 133 * The BER type used for the header element containing the MAC algorithm name. 134 */ 135 static final byte TYPE_MAC_ALGORITHM = (byte) 0x88; 136 137 138 139 /** 140 * The BER type used for the header element containing the MAC value. 141 */ 142 static final byte TYPE_MAC_VALUE = (byte) 0x89; 143 144 145 146 /** 147 * The "magic" value that will appear at the start of the header. 148 */ 149 public static final byte[] MAGIC_BYTES = 150 { 0x50, 0x55, 0x4C, 0x53, 0x50, 0x45, 0x53, 0x48 }; 151 152 153 154 /** 155 * The encoding version for a v1 encoding. 156 */ 157 static final int ENCODING_VERSION_1 = 1; 158 159 160 161 /** 162 * The serial version UID for this serializable class. 163 */ 164 private static final long serialVersionUID = 6756983626170064762L; 165 166 167 168 // The initialization vector used when creating the cipher. 169 private final byte[] cipherInitializationVector; 170 171 // An encoded representation of this header. 172 private final byte[] encodedHeader; 173 174 // The salt used when generating the encryption key from the passphrase. 175 private final byte[] keyFactorySalt; 176 177 // A MAC of the header content. 178 private final byte[] macValue; 179 180 // The iteration count used when generating the encryption key from the 181 private final int keyFactoryIterationCount; 182 // passphrase. 183 184 // The length (in bits) of the encryption key generated from the passphrase. 185 private final int keyFactoryKeyLengthBits; 186 187 // The secret key generated from the passphrase. 188 private final SecretKey secretKey; 189 190 // The cipher transformation used for the encryption. 191 private final String cipherTransformation; 192 193 // The name of the key factory used to generate the encryption key from the 194 // passphrase. 195 private final String keyFactoryAlgorithm; 196 197 // An optional identifier that can be used to associate this header with some 198 // other encryption settings object. 199 private final String keyIdentifier; 200 201 // The algorithm used to generate a MAC of the header content. 202 private final String macAlgorithm; 203 204 205 206 /** 207 * Creates a new passphrase-encrypted stream header with the provided 208 * information. 209 * 210 * @param keyFactoryAlgorithm The key factory algorithm used to 211 * generate the encryption key from the 212 * passphrase. It must not be 213 * {@code null}. 214 * @param keyFactoryIterationCount The iteration count used to generate 215 * the encryption key from the passphrase. 216 * @param keyFactorySalt The salt used to generate the 217 * encryption key from the passphrase. 218 * It must not be {@code null}. 219 * @param keyFactoryKeyLengthBits The length (in bits) of the encryption 220 * key generated from the passphrase. 221 * @param cipherTransformation The cipher transformation used for the 222 * encryption. It must not be 223 * {@code null}. 224 * @param cipherInitializationVector The initialization vector used when 225 * creating the cipher. It must not be 226 * {@code null}. 227 * @param keyIdentifier An optional identifier that can be used 228 * to associate this passphrase-encrypted 229 * stream header with some other 230 * encryption settings object. It may 231 * optionally be {@code null}. 232 * @param secretKey The secret key generated from the 233 * passphrase. 234 * @param macAlgorithm The MAC algorithm to use when 235 * generating a MAC of the header 236 * contents. It must not be {@code null}. 237 * @param macValue A MAC of the header contents. It must 238 * not be {@code null}. 239 * @param encodedHeader An encoded representation of the 240 * header. 241 */ 242 private PassphraseEncryptedStreamHeader( 243 final String keyFactoryAlgorithm, 244 final int keyFactoryIterationCount, final byte[] keyFactorySalt, 245 final int keyFactoryKeyLengthBits, 246 final String cipherTransformation, 247 final byte[] cipherInitializationVector, 248 final String keyIdentifier, final SecretKey secretKey, 249 final String macAlgorithm, final byte[] macValue, 250 final byte[] encodedHeader) 251 { 252 this.keyFactoryAlgorithm = keyFactoryAlgorithm; 253 this.keyFactoryIterationCount = keyFactoryIterationCount; 254 this.keyFactorySalt = Arrays.copyOf(keyFactorySalt, keyFactorySalt.length); 255 this.keyFactoryKeyLengthBits = keyFactoryKeyLengthBits; 256 this.cipherTransformation = cipherTransformation; 257 this.cipherInitializationVector = Arrays.copyOf(cipherInitializationVector, 258 cipherInitializationVector.length); 259 this.keyIdentifier = keyIdentifier; 260 this.secretKey = secretKey; 261 this.macAlgorithm = macAlgorithm; 262 this.macValue = macValue; 263 this.encodedHeader = encodedHeader; 264 } 265 266 267 268 /** 269 * Creates a new passphrase-encrypted stream header with the provided 270 * information. 271 * 272 * @param passphrase The passphrase to use to generate the 273 * encryption key. It must not be 274 * {@code null}. 275 * @param keyFactoryAlgorithm The key factory algorithm used to 276 * generate the encryption key from the 277 * passphrase. It must not be 278 * {@code null}. 279 * @param keyFactoryIterationCount The iteration count used to generate 280 * the encryption key from the passphrase. 281 * @param keyFactorySalt The salt used to generate the 282 * encryption key from the passphrase. 283 * It must not be {@code null}. 284 * @param keyFactoryKeyLengthBits The length (in bits) of the encryption 285 * key generated from the passphrase. 286 * @param cipherTransformation The cipher transformation used for the 287 * encryption. It must not be 288 * {@code null}. 289 * @param cipherInitializationVector The initialization vector used when 290 * creating the cipher. It must not be 291 * {@code null}. 292 * @param keyIdentifier An optional identifier that can be used 293 * to associate this passphrase-encrypted 294 * stream header with some other 295 * encryption settings object. It may 296 * optionally be {@code null}. 297 * @param macAlgorithm The MAC algorithm to use when 298 * generating a MAC of the header 299 * contents. It must not be {@code null}. 300 * 301 * @throws GeneralSecurityException If a problem is encountered while 302 * generating the encryption key or MAC 303 * from the provided passphrase. 304 */ 305 PassphraseEncryptedStreamHeader(final char[] passphrase, 306 final String keyFactoryAlgorithm, 307 final int keyFactoryIterationCount, 308 final byte[] keyFactorySalt, 309 final int keyFactoryKeyLengthBits, 310 final String cipherTransformation, 311 final byte[] cipherInitializationVector, 312 final String keyIdentifier, 313 final String macAlgorithm) 314 throws GeneralSecurityException 315 { 316 this.keyFactoryAlgorithm = keyFactoryAlgorithm; 317 this.keyFactoryIterationCount = keyFactoryIterationCount; 318 this.keyFactorySalt = Arrays.copyOf(keyFactorySalt, keyFactorySalt.length); 319 this.keyFactoryKeyLengthBits = keyFactoryKeyLengthBits; 320 this.cipherTransformation = cipherTransformation; 321 this.cipherInitializationVector = Arrays.copyOf(cipherInitializationVector, 322 cipherInitializationVector.length); 323 this.keyIdentifier = keyIdentifier; 324 this.macAlgorithm = macAlgorithm; 325 326 secretKey = generateKeyReliably(keyFactoryAlgorithm, cipherTransformation, 327 passphrase, keyFactorySalt, keyFactoryIterationCount, 328 keyFactoryKeyLengthBits); 329 330 final ObjectPair<byte[],byte[]> headerPair = encode(keyFactoryAlgorithm, 331 keyFactoryIterationCount, this.keyFactorySalt, keyFactoryKeyLengthBits, 332 cipherTransformation, this.cipherInitializationVector, keyIdentifier, 333 secretKey, macAlgorithm); 334 encodedHeader = headerPair.getFirst(); 335 macValue = headerPair.getSecond(); 336 } 337 338 339 340 /** 341 * Generates an encoded representation of the header with the provided 342 * settings. 343 * 344 * @param keyFactoryAlgorithm The key factory algorithm used to 345 * generate the encryption key from the 346 * passphrase. It must not be 347 * {@code null}. 348 * @param keyFactoryIterationCount The iteration count used to generate 349 * the encryption key from the passphrase. 350 * @param keyFactorySalt The salt used to generate the 351 * encryption key from the passphrase. 352 * It must not be {@code null}. 353 * @param keyFactoryKeyLengthBits The length (in bits) of the encryption 354 * key generated from the passphrase. 355 * @param cipherTransformation The cipher transformation used for the 356 * encryption. It must not be 357 * {@code null}. 358 * @param cipherInitializationVector The initialization vector used when 359 * creating the cipher. It must not be 360 * {@code null}. 361 * @param keyIdentifier An optional identifier that can be used 362 * to associate this passphrase-encrypted 363 * stream header with some other 364 * encryption settings object. It may 365 * optionally be {@code null}. 366 * @param secretKey The secret key generated from the 367 * passphrase. 368 * @param macAlgorithm The MAC algorithm to use when 369 * generating a MAC of the header 370 * contents. It must not be {@code null}. 371 * 372 * @return The encoded representation of the header. 373 * 374 * @throws GeneralSecurityException If a problem is encountered while 375 * generating the MAC. 376 */ 377 private static ObjectPair<byte[],byte[]> encode( 378 final String keyFactoryAlgorithm, 379 final int keyFactoryIterationCount, 380 final byte[] keyFactorySalt, 381 final int keyFactoryKeyLengthBits, 382 final String cipherTransformation, 383 final byte[] cipherInitializationVector, 384 final String keyIdentifier, 385 final SecretKey secretKey, 386 final String macAlgorithm) 387 throws GeneralSecurityException 388 { 389 // Construct a list of all elements that will go in the header except the 390 // MAC value. 391 final ArrayList<ASN1Element> elements = new ArrayList<>(10); 392 elements.add(new ASN1Integer(TYPE_ENCODING_VERSION, ENCODING_VERSION_1)); 393 elements.add(new ASN1OctetString(TYPE_KEY_FACTORY_ALGORITHM, 394 keyFactoryAlgorithm)); 395 elements.add(new ASN1Integer(TYPE_KEY_FACTORY_ITERATION_COUNT, 396 keyFactoryIterationCount)); 397 elements.add(new ASN1OctetString(TYPE_KEY_FACTORY_SALT, keyFactorySalt)); 398 elements.add(new ASN1Integer(TYPE_KEY_FACTORY_KEY_LENGTH_BITS, 399 keyFactoryKeyLengthBits)); 400 elements.add(new ASN1OctetString(TYPE_CIPHER_TRANSFORMATION, 401 cipherTransformation)); 402 elements.add(new ASN1OctetString(TYPE_CIPHER_INITIALIZATION_VECTOR, 403 cipherInitializationVector)); 404 405 if (keyIdentifier != null) 406 { 407 elements.add(new ASN1OctetString(TYPE_KEY_IDENTIFIER, keyIdentifier)); 408 } 409 410 elements.add(new ASN1OctetString(TYPE_MAC_ALGORITHM, macAlgorithm)); 411 412 413 // Compute the MAC value and add it to the list of elements. 414 final ByteStringBuffer macBuffer = new ByteStringBuffer(); 415 for (final ASN1Element e : elements) 416 { 417 macBuffer.append(e.encode()); 418 } 419 420 final Mac mac = Mac.getInstance(macAlgorithm); 421 mac.init(secretKey); 422 423 final byte[] macValue = mac.doFinal(macBuffer.toByteArray()); 424 elements.add(new ASN1OctetString(TYPE_MAC_VALUE, macValue)); 425 426 427 // Compute and return the encoded header. 428 final byte[] elementBytes = new ASN1Sequence(elements).encode(); 429 final byte[] headerBytes = 430 new byte[MAGIC_BYTES.length + elementBytes.length]; 431 System.arraycopy(MAGIC_BYTES, 0, headerBytes, 0, MAGIC_BYTES.length); 432 System.arraycopy(elementBytes, 0, headerBytes, MAGIC_BYTES.length, 433 elementBytes.length); 434 return new ObjectPair<>(headerBytes, macValue); 435 } 436 437 438 439 /** 440 * Writes an encoded representation of this passphrase-encrypted stream header 441 * to the provided output stream. The output stream will remain open after 442 * this method completes. 443 * 444 * @param outputStream The output stream to which the header will be 445 * written. 446 * 447 * @throws IOException If a problem is encountered while trying to write to 448 * the provided output stream. 449 */ 450 public void writeTo(final OutputStream outputStream) 451 throws IOException 452 { 453 outputStream.write(encodedHeader); 454 } 455 456 457 458 /** 459 * Reads a passphrase-encrypted stream header from the provided input stream. 460 * This method will not close the provided input stream, regardless of whether 461 * it returns successfully or throws an exception. If it returns 462 * successfully, then the position then the header bytes will have been 463 * consumed, so the next data to be read should be the data encrypted with 464 * these settings. If it throws an exception, then some unknown amount of 465 * data may have been read from the stream. 466 * 467 * @param inputStream The input stream from which to read the encoded 468 * passphrase-encrypted stream header. It must not be 469 * {@code null}. 470 * @param passphrase The passphrase to use to generate the encryption key. 471 * If this is {@code null}, then the header will be 472 * read, but no attempt will be made to validate the MAC, 473 * and it will not be possible to use this header to 474 * actually perform encryption or decryption. Providing 475 * a {@code null} value is primarily useful if 476 * information in the header (especially the key 477 * identifier) is needed to determine what passphrase to 478 * use. 479 * 480 * @return The passphrase-encrypted stream header that was read from the 481 * provided input stream. 482 * 483 * @throws IOException If a problem is encountered while attempting to read 484 * data from the provided input stream. 485 * 486 * @throws LDAPException If a problem is encountered while attempting to 487 * decode the data that was read. 488 * 489 * @throws InvalidKeyException If the MAC contained in the header does not 490 * match the expected value. 491 * 492 * @throws GeneralSecurityException If a problem is encountered while trying 493 * to generate the MAC. 494 */ 495 public static PassphraseEncryptedStreamHeader 496 readFrom(final InputStream inputStream, 497 final char[] passphrase) 498 throws IOException, LDAPException, InvalidKeyException, 499 GeneralSecurityException 500 { 501 // Read the magic from the input stream. 502 for (int i=0; i < MAGIC_BYTES.length; i++) 503 { 504 final int magicByte = inputStream.read(); 505 if (magicByte < 0) 506 { 507 throw new LDAPException(ResultCode.DECODING_ERROR, 508 ERR_PW_ENCRYPTED_STREAM_HEADER_READ_END_OF_STREAM_IN_MAGIC.get()); 509 } 510 else if (magicByte != MAGIC_BYTES[i]) 511 { 512 throw new LDAPException(ResultCode.DECODING_ERROR, 513 ERR_PW_ENCRYPTED_STREAM_HEADER_READ_MAGIC_MISMATCH.get()); 514 } 515 } 516 517 518 // The remainder of the header should be an ASN.1 sequence. Read and 519 // process that sequenced. 520 try 521 { 522 final ASN1Element headerSequenceElement = 523 ASN1Element.readFrom(inputStream); 524 if (headerSequenceElement == null) 525 { 526 throw new LDAPException(ResultCode.DECODING_ERROR, 527 ERR_PW_ENCRYPTED_STREAM_HEADER_READ_END_OF_STREAM_AFTER_MAGIC.get( 528 )); 529 } 530 531 final byte[] encodedHeaderSequence = headerSequenceElement.encode(); 532 final byte[] encodedHeader = 533 new byte[MAGIC_BYTES.length + encodedHeaderSequence.length]; 534 System.arraycopy(MAGIC_BYTES, 0, encodedHeader, 0, MAGIC_BYTES.length); 535 System.arraycopy(encodedHeaderSequence, 0, encodedHeader, 536 MAGIC_BYTES.length, encodedHeaderSequence.length); 537 538 final ASN1Sequence headerSequence = 539 ASN1Sequence.decodeAsSequence(headerSequenceElement); 540 return decodeHeaderSequence(encodedHeader, headerSequence, passphrase); 541 } 542 catch (final IOException | LDAPException | GeneralSecurityException e) 543 { 544 Debug.debugException(e); 545 throw e; 546 } 547 catch (final Exception e) 548 { 549 Debug.debugException(e); 550 throw new LDAPException(ResultCode.DECODING_ERROR, 551 ERR_PW_ENCRYPTED_STREAM_HEADER_READ_ASN1_DECODE_ERROR.get( 552 StaticUtils.getExceptionMessage(e)), 553 e); 554 } 555 } 556 557 558 559 /** 560 * Decodes the contents of the provided byte array as a passphrase-encrypted 561 * stream header. The provided array must contain only the header, with no 562 * additional data before or after. 563 * 564 * @param encodedHeader The bytes that comprise the header to decode. It 565 * must not be {@code null} or empty. 566 * @param passphrase The passphrase to use to generate the encryption 567 * key. If this is {@code null}, then the header will 568 * be read, but no attempt will be made to validate the 569 * MAC, and it will not be possible to use this header 570 * to actually perform encryption or decryption. 571 * Providing a {@code null} value is primarily useful 572 * if information in the header (especially the key 573 * identifier) is needed to determine what passphrase 574 * to use. 575 * 576 * @return The passphrase-encrypted stream header that was decoded from the 577 * provided byte array. 578 * 579 * @throws LDAPException If a problem is encountered while trying to decode 580 * the data as a passphrase-encrypted stream header. 581 * 582 * @throws InvalidKeyException If the MAC contained in the header does not 583 * match the expected value. 584 * 585 * @throws GeneralSecurityException If a problem is encountered while trying 586 * to generate the MAC. 587 */ 588 public static PassphraseEncryptedStreamHeader 589 decode(final byte[] encodedHeader, final char[] passphrase) 590 throws LDAPException, InvalidKeyException, GeneralSecurityException 591 { 592 // Make sure that the array is long enough to hold a valid header. 593 if (encodedHeader.length <= MAGIC_BYTES.length) 594 { 595 throw new LDAPException(ResultCode.DECODING_ERROR, 596 ERR_PW_ENCRYPTED_STREAM_HEADER_DECODE_TOO_SHORT.get()); 597 } 598 599 600 // Make sure that the array starts with the provided magic value. 601 for (int i=0; i < MAGIC_BYTES.length; i++) 602 { 603 if (encodedHeader[i] != MAGIC_BYTES[i]) 604 { 605 throw new LDAPException(ResultCode.DECODING_ERROR, 606 ERR_PW_ENCRYPTED_STREAM_HEADER_DECODE_MAGIC_MISMATCH.get()); 607 } 608 } 609 610 611 // Decode the remainder of the array as an ASN.1 sequence. 612 final ASN1Sequence headerSequence; 613 try 614 { 615 final byte[] encodedHeaderWithoutMagic = 616 new byte[encodedHeader.length - MAGIC_BYTES.length]; 617 System.arraycopy(encodedHeader, MAGIC_BYTES.length, 618 encodedHeaderWithoutMagic, 0, encodedHeaderWithoutMagic.length); 619 headerSequence = ASN1Sequence.decodeAsSequence(encodedHeaderWithoutMagic); 620 } 621 catch (final Exception e) 622 { 623 Debug.debugException(e); 624 throw new LDAPException(ResultCode.DECODING_ERROR, 625 ERR_PW_ENCRYPTED_STREAM_HEADER_DECODE_ASN1_DECODE_ERROR.get( 626 StaticUtils.getExceptionMessage(e)), 627 e); 628 } 629 630 return decodeHeaderSequence(encodedHeader, headerSequence, passphrase); 631 } 632 633 634 635 /** 636 * Decodes the contents of the provided ASN.1 sequence as the portion of a 637 * passphrase-encrypted stream header that follows the magic bytes. 638 * 639 * @param encodedHeader The bytes that comprise the encoded header. It 640 * must not be {@code null} or empty. 641 * @param headerSequence The header sequence portion of the encoded header. 642 * @param passphrase The passphrase to use to generate the encryption 643 * key. If this is {@code null}, then the header will 644 * be read, but no attempt will be made to validate 645 * the MAC, and it will not be possible to use this 646 * header to actually perform encryption or 647 * decryption. Providing a {@code null} value is 648 * primarily useful if information in the header 649 * (especially the key identifier) is needed to 650 * determine what passphrase to use. 651 * 652 * @return The passphrase-encrypted stream header that was decoded from the 653 * provided ASN.1 sequence. 654 * 655 * @throws LDAPException If a problem is encountered while trying to decode 656 * the data as a passphrase-encrypted stream header. 657 * 658 * @throws InvalidKeyException If the MAC contained in the header does not 659 * match the expected value. 660 * 661 * @throws GeneralSecurityException If a problem is encountered while trying 662 * to generate the MAC. 663 */ 664 private static PassphraseEncryptedStreamHeader decodeHeaderSequence( 665 final byte[] encodedHeader, 666 final ASN1Sequence headerSequence, 667 final char[] passphrase) 668 throws LDAPException, InvalidKeyException, GeneralSecurityException 669 { 670 try 671 { 672 // The first element must be the encoding version, and it must be 1. 673 final ASN1Element[] headerElements = headerSequence.elements(); 674 final ASN1Integer versionElement = 675 ASN1Integer.decodeAsInteger(headerElements[0]); 676 if (versionElement.intValue() != ENCODING_VERSION_1) 677 { 678 throw new LDAPException(ResultCode.DECODING_ERROR, 679 ERR_PW_ENCRYPTED_HEADER_SEQUENCE_UNSUPPORTED_VERSION.get( 680 versionElement.intValue())); 681 } 682 683 // The second element must be the key factory algorithm. 684 final String keyFactoryAlgorithm = 685 ASN1OctetString.decodeAsOctetString(headerElements[1]).stringValue(); 686 687 // The third element must be the key factory iteration count. 688 final int keyFactoryIterationCount = 689 ASN1Integer.decodeAsInteger(headerElements[2]).intValue(); 690 691 // The fourth element must be the key factory salt. 692 final byte[] keyFactorySalt = 693 ASN1OctetString.decodeAsOctetString(headerElements[3]).getValue(); 694 695 // The fifth element must be the key length in bits. 696 final int keyFactoryKeyLengthBits = 697 ASN1Integer.decodeAsInteger(headerElements[4]).intValue(); 698 699 // The sixth element must be the cipher transformation. 700 final String cipherTransformation = 701 ASN1OctetString.decodeAsOctetString(headerElements[5]).stringValue(); 702 703 // The seventh element must be the initialization vector. 704 final byte[] cipherInitializationVector = 705 ASN1OctetString.decodeAsOctetString(headerElements[6]).getValue(); 706 707 // Look through any remaining elements and decode them as appropriate. 708 byte[] macValue = null; 709 int macValuePos = -1; 710 String keyIdentifier = null; 711 String macAlgorithm = null; 712 for (int i=7; i < headerElements.length; i++) 713 { 714 switch (headerElements[i].getType()) 715 { 716 case TYPE_KEY_IDENTIFIER: 717 keyIdentifier = ASN1OctetString.decodeAsOctetString( 718 headerElements[i]).stringValue(); 719 break; 720 case TYPE_MAC_ALGORITHM: 721 macAlgorithm = ASN1OctetString.decodeAsOctetString( 722 headerElements[i]).stringValue(); 723 break; 724 case TYPE_MAC_VALUE: 725 macValuePos = i; 726 macValue = ASN1OctetString.decodeAsOctetString( 727 headerElements[i]).getValue(); 728 break; 729 default: 730 throw new LDAPException(ResultCode.DECODING_ERROR, 731 ERR_PW_ENCRYPTED_HEADER_SEQUENCE_UNRECOGNIZED_ELEMENT_TYPE.get( 732 StaticUtils.toHex(headerElements[i].getType()))); 733 } 734 } 735 736 737 // Compute a MAC of the appropriate header elements and verify that it 738 // matches the value contained in the header. If it doesn't match, then 739 // it means the provided passphrase was invalid. 740 final SecretKey secretKey; 741 if (passphrase == null) 742 { 743 secretKey = null; 744 } 745 else 746 { 747 secretKey = generateKeyReliably(keyFactoryAlgorithm, 748 cipherTransformation, passphrase, keyFactorySalt, 749 keyFactoryIterationCount, keyFactoryKeyLengthBits); 750 751 final ByteStringBuffer macBuffer = new ByteStringBuffer(); 752 for (int i=0; i < headerElements.length; i++) 753 { 754 if (i != macValuePos) 755 { 756 macBuffer.append(headerElements[i].encode()); 757 } 758 } 759 760 final Mac mac = Mac.getInstance(macAlgorithm); 761 mac.init(secretKey); 762 final byte[] computedMacValue = mac.doFinal(macBuffer.toByteArray()); 763 if (! Arrays.equals(computedMacValue, macValue)) 764 { 765 throw new InvalidKeyException( 766 ERR_PW_ENCRYPTED_HEADER_SEQUENCE_BAD_PW.get()); 767 } 768 } 769 770 return new PassphraseEncryptedStreamHeader(keyFactoryAlgorithm, 771 keyFactoryIterationCount, keyFactorySalt, keyFactoryKeyLengthBits, 772 cipherTransformation, cipherInitializationVector, keyIdentifier, 773 secretKey, macAlgorithm, macValue, encodedHeader); 774 } 775 catch (final LDAPException | GeneralSecurityException e) 776 { 777 Debug.debugException(e); 778 throw e; 779 } 780 catch (final Exception e) 781 { 782 Debug.debugException(e); 783 throw new LDAPException(ResultCode.DECODING_ERROR, 784 ERR_PW_ENCRYPTED_HEADER_SEQUENCE_DECODE_ERROR.get( 785 StaticUtils.getExceptionMessage(e)), 786 e); 787 } 788 } 789 790 791 792 /** 793 * We have seen situations where SecretKeyFactory#generateSecret returns 794 * inconsistent results for the same parameters. This can lead to data being 795 * encrypted or decrypted incorrectly. To avoid this, this method computes the 796 * key multiple times, and only returns the key once an identical key has been 797 * generated three times in a row. 798 * 799 * @param keyFactoryAlgorithm The key factory algorithm to use to 800 * generate the encryption key from the 801 * passphrase. It must not be {@code null}. 802 * @param cipherTransformation The cipher transformation used for the 803 * encryption key. It must not be {@code 804 * null}. 805 * @param passphrase The passphrase to use to generate the 806 * encryption key. It must not be 807 * {@code null}. 808 * @param keyFactorySalt The salt to use to generate the 809 * encryption key from the passphrase. 810 * It must not be {@code null}. 811 * @param keyFactoryIterationCount The iteration count to use to generate 812 * the encryption key from the passphrase. 813 * @param keyFactoryKeyLengthBits The length (in bits) of the encryption 814 * key generated from the passphrase. 815 * 816 * @return A SecretKey that has been consistently generated from the provided 817 * parameters. 818 * 819 * @throws GeneralSecurityException If a problem is encountered while 820 * generating the encryption key including 821 * not being able to generate a consistent 822 * key. 823 */ 824 private static SecretKey generateKeyReliably( 825 final String keyFactoryAlgorithm, 826 final String cipherTransformation, 827 final char[] passphrase, 828 final byte[] keyFactorySalt, 829 final int keyFactoryIterationCount, 830 final int keyFactoryKeyLengthBits) 831 throws GeneralSecurityException 832 { 833 byte[] prev = null; 834 byte[] prev2 = null; 835 836 final int iterations = 10; 837 for (int i = 0; i < iterations; i++) 838 { 839 final SecretKeyFactory keyFactory = 840 SecretKeyFactory.getInstance(keyFactoryAlgorithm); 841 final String cipherAlgorithm = cipherTransformation.substring(0, 842 cipherTransformation.indexOf('/')); 843 final PBEKeySpec pbeKeySpec = new PBEKeySpec(passphrase, keyFactorySalt, 844 keyFactoryIterationCount, keyFactoryKeyLengthBits); 845 final SecretKey secretKey = new SecretKeySpec( 846 keyFactory.generateSecret(pbeKeySpec).getEncoded(), 847 cipherAlgorithm); 848 final byte[] encoded = secretKey.getEncoded(); 849 850 // If this encoded key is the same as the previous one, and the one before 851 // that, then it was likely computed correctly, so return it. 852 if (Arrays.equals(encoded, prev) && Arrays.equals(encoded, prev2)) 853 { 854 if (i > 2) 855 { 856 Debug.debug(Level.WARNING, DebugType.OTHER, 857 "The secret key was generated inconsistently initially, but " + 858 "after " + i + " iterations, we were able to generate a " + 859 "consistent value."); 860 } 861 return secretKey; 862 } 863 864 prev2 = prev; 865 prev = encoded; 866 } 867 868 Debug.debug(Level.SEVERE, DebugType.OTHER, 869 "Even after " + iterations + " iterations, the secret key could not " + 870 "be reliably generated."); 871 872 throw new InvalidKeyException( 873 ERR_PW_ENCRYPTED_STREAM_HEADER_CANNOT_GENERATE_KEY.get()); 874 } 875 876 877 878 /** 879 * Creates a {@code Cipher} for the specified purpose. 880 * 881 * @param mode The mode to use for the cipher. It must be one of 882 * {@code Cipher.ENCRYPT_MODE} or {@code Cipher.DECRYPT_MODE}. 883 * 884 * @return The {@code Cipher} instance that was created. 885 * 886 * @throws InvalidKeyException If no passphrase was provided when decoding 887 * this passphrase-encrypted stream header. 888 * 889 * @throws GeneralSecurityException If a problem is encountered while 890 * creating the cipher. 891 */ 892 Cipher createCipher(final int mode) 893 throws InvalidKeyException, GeneralSecurityException 894 { 895 if (secretKey == null) 896 { 897 throw new InvalidKeyException( 898 ERR_PW_ENCRYPTED_HEADER_NO_KEY_AVAILABLE.get()); 899 } 900 901 final Cipher cipher = Cipher.getInstance(cipherTransformation); 902 cipher.init(mode, secretKey, 903 new IvParameterSpec(cipherInitializationVector)); 904 905 return cipher; 906 } 907 908 909 910 /** 911 * Retrieves the key factory algorithm used to generate the encryption key 912 * from the passphrase. 913 * 914 * @return The key factory algorithm used to generate the encryption key from 915 * the passphrase. 916 */ 917 public String getKeyFactoryAlgorithm() 918 { 919 return keyFactoryAlgorithm; 920 } 921 922 923 924 /** 925 * Retrieves the iteration count used to generate the encryption key from the 926 * passphrase. 927 * 928 * @return The iteration count used to generate the encryption key from the 929 * passphrase. 930 */ 931 public int getKeyFactoryIterationCount() 932 { 933 return keyFactoryIterationCount; 934 } 935 936 937 938 /** 939 * Retrieves the salt used to generate the encryption key from the passphrase. 940 * 941 * @return The salt used to generate the encryption key from the passphrase. 942 */ 943 public byte[] getKeyFactorySalt() 944 { 945 return Arrays.copyOf(keyFactorySalt, keyFactorySalt.length); 946 } 947 948 949 950 /** 951 * Retrieves the length (in bits) of the encryption key generated from the 952 * passphrase. 953 * 954 * @return The length (in bits) of the encryption key generated from the 955 * passphrase. 956 */ 957 public int getKeyFactoryKeyLengthBits() 958 { 959 return keyFactoryKeyLengthBits; 960 } 961 962 963 964 /** 965 * Retrieves the cipher transformation used for the encryption. 966 * 967 * @return The cipher transformation used for the encryption. 968 */ 969 public String getCipherTransformation() 970 { 971 return cipherTransformation; 972 } 973 974 975 976 /** 977 * Retrieves the cipher initialization vector used for the encryption. 978 * 979 * @return The cipher initialization vector used for the encryption. 980 */ 981 public byte[] getCipherInitializationVector() 982 { 983 return Arrays.copyOf(cipherInitializationVector, 984 cipherInitializationVector.length); 985 } 986 987 988 989 /** 990 * Retrieves the key identifier used to associate this passphrase-encrypted 991 * stream header with some other encryption settings object, if defined. 992 * 993 * @return The key identifier used to associate this passphrase-encrypted 994 * stream header with some other encryption settings object, or 995 * {@code null} if none was provided. 996 */ 997 public String getKeyIdentifier() 998 { 999 return keyIdentifier; 1000 } 1001 1002 1003 1004 /** 1005 * Retrieves the algorithm used to generate a MAC of the header content. 1006 * 1007 * @return The algorithm used to generate a MAC of the header content. 1008 */ 1009 public String getMACAlgorithm() 1010 { 1011 return macAlgorithm; 1012 } 1013 1014 1015 1016 /** 1017 * Retrieves an encoded representation of this passphrase-encrypted stream 1018 * header. 1019 * 1020 * @return An encoded representation of this passphrase-encrypted stream 1021 * header. 1022 */ 1023 public byte[] getEncodedHeader() 1024 { 1025 return Arrays.copyOf(encodedHeader, encodedHeader.length); 1026 } 1027 1028 1029 1030 /** 1031 * Indicates whether this passphrase-encrypted stream header includes a secret 1032 * key. If this header was read or decoded with no passphrase provided, then 1033 * it will not have a secret key, which means the MAC will not have been 1034 * validated and it cannot be used to encrypt or decrypt data. 1035 * 1036 * @return {@code true} if this passphrase-encrypted stream header includes a 1037 * secret key and can be used to encrypt or decrypt data, or 1038 * {@code false} if not. 1039 */ 1040 public boolean isSecretKeyAvailable() 1041 { 1042 return (secretKey != null); 1043 } 1044 1045 1046 1047 /** 1048 * Retrieves a string representation of this passphrase-encrypted stream 1049 * header. 1050 * 1051 * @return A string representation of this passphrase-encrypted stream 1052 * header. 1053 */ 1054 @Override() 1055 public String toString() 1056 { 1057 final StringBuilder buffer = new StringBuilder(); 1058 toString(buffer); 1059 return buffer.toString(); 1060 } 1061 1062 1063 1064 /** 1065 * Appends a string representation of this passphrase-encrypted stream header 1066 * to the provided buffer. 1067 * 1068 * @param buffer The buffer to which the information should be appended. 1069 */ 1070 public void toString(final StringBuilder buffer) 1071 { 1072 buffer.append("PassphraseEncryptedStreamHeader(keyFactoryAlgorithm='"); 1073 buffer.append(keyFactoryAlgorithm); 1074 buffer.append("', keyFactoryIterationCount="); 1075 buffer.append(keyFactoryIterationCount); 1076 buffer.append(", keyFactorySaltLengthBytes="); 1077 buffer.append(keyFactorySalt.length); 1078 buffer.append(", keyFactoryKeyLengthBits="); 1079 buffer.append(keyFactoryKeyLengthBits); 1080 buffer.append(", cipherTransformation'="); 1081 buffer.append(cipherTransformation); 1082 buffer.append("', cipherInitializationVectorLengthBytes="); 1083 buffer.append(cipherInitializationVector.length); 1084 buffer.append('\''); 1085 1086 if (keyIdentifier != null) 1087 { 1088 buffer.append(", keyIdentifier='"); 1089 buffer.append(keyIdentifier); 1090 buffer.append('\''); 1091 } 1092 1093 buffer.append(", macAlgorithm='"); 1094 buffer.append(macAlgorithm); 1095 buffer.append("', macValueLengthBytes="); 1096 buffer.append(macValue.length); 1097 buffer.append(", secretKeyAvailable="); 1098 buffer.append(isSecretKeyAvailable()); 1099 buffer.append(", encodedHeaderLengthBytes="); 1100 buffer.append(encodedHeader.length); 1101 buffer.append(')'); 1102 } 1103}