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