001/*
002 * Copyright 2018-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2018-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) 2018-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.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  @NotNull 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  @NotNull private final byte[] cipherInitializationVector;
185
186  // An encoded representation of this header.
187  @NotNull private final byte[] encodedHeader;
188
189  // The salt used when generating the encryption key from the passphrase.
190  @NotNull private final byte[] keyFactorySalt;
191
192  // A MAC of the header content.
193  @NotNull 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  @Nullable private final SecretKey secretKey;
204
205  // The cipher transformation used for the encryption.
206  @NotNull private final String cipherTransformation;
207
208  // The name of the key factory used to generate the encryption key from the
209  // passphrase.
210  @NotNull 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  @Nullable private final String keyIdentifier;
215
216  // The algorithm used to generate a MAC of the header content.
217  @NotNull 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               @NotNull final String keyFactoryAlgorithm,
259               final int keyFactoryIterationCount,
260               @NotNull final byte[] keyFactorySalt,
261               final int keyFactoryKeyLengthBits,
262               @NotNull final String cipherTransformation,
263               @NotNull final byte[] cipherInitializationVector,
264               @Nullable final String keyIdentifier,
265               @Nullable final SecretKey secretKey,
266               @NotNull final String macAlgorithm,
267               @NotNull final byte[] macValue,
268               @NotNull final byte[] encodedHeader)
269  {
270    this.keyFactoryAlgorithm = keyFactoryAlgorithm;
271    this.keyFactoryIterationCount = keyFactoryIterationCount;
272    this.keyFactorySalt = Arrays.copyOf(keyFactorySalt, keyFactorySalt.length);
273    this.keyFactoryKeyLengthBits = keyFactoryKeyLengthBits;
274    this.cipherTransformation = cipherTransformation;
275    this.cipherInitializationVector = Arrays.copyOf(cipherInitializationVector,
276         cipherInitializationVector.length);
277    this.keyIdentifier = keyIdentifier;
278    this.secretKey = secretKey;
279    this.macAlgorithm = macAlgorithm;
280    this.macValue = macValue;
281    this.encodedHeader = encodedHeader;
282  }
283
284
285
286  /**
287   * Creates a new passphrase-encrypted stream header with the provided
288   * information.
289   *
290   * @param  passphrase                  The passphrase to use to generate the
291   *                                     encryption key.  It must not be
292   *                                     {@code null}.
293   * @param  keyFactoryAlgorithm         The key factory algorithm used to
294   *                                     generate the encryption key from the
295   *                                     passphrase.  It must not be
296   *                                     {@code null}.
297   * @param  keyFactoryIterationCount    The iteration count used to generate
298   *                                     the encryption key from the passphrase.
299   * @param  keyFactorySalt              The salt used to generate the
300   *                                     encryption key from the passphrase.
301   *                                     It must not be {@code null}.
302   * @param  keyFactoryKeyLengthBits     The length (in bits) of the encryption
303   *                                     key generated from the passphrase.
304   * @param  cipherTransformation        The cipher transformation used for the
305   *                                     encryption.  It must not be
306   *                                     {@code null}.
307   * @param  cipherInitializationVector  The initialization vector used when
308   *                                     creating the cipher.  It must not be
309   *                                     {@code null}.
310   * @param  keyIdentifier               An optional identifier that can be used
311   *                                     to associate this passphrase-encrypted
312   *                                     stream header with some other
313   *                                     encryption settings object.  It may
314   *                                     optionally be {@code null}.
315   * @param  macAlgorithm                The MAC algorithm to use when
316   *                                     generating a MAC of the header
317   *                                     contents.  It must not be {@code null}.
318   *
319   * @throws  GeneralSecurityException  If a problem is encountered while
320   *                                    generating the encryption key or MAC
321   *                                    from the provided passphrase.
322   */
323  PassphraseEncryptedStreamHeader(@NotNull final char[] passphrase,
324       @NotNull final String keyFactoryAlgorithm,
325       final int keyFactoryIterationCount,
326       @NotNull final byte[] keyFactorySalt,
327       final int keyFactoryKeyLengthBits,
328       @NotNull final String cipherTransformation,
329       @NotNull final byte[] cipherInitializationVector,
330       @Nullable final String keyIdentifier,
331       @NotNull final String macAlgorithm)
332       throws GeneralSecurityException
333  {
334    this.keyFactoryAlgorithm = keyFactoryAlgorithm;
335    this.keyFactoryIterationCount = keyFactoryIterationCount;
336    this.keyFactorySalt = Arrays.copyOf(keyFactorySalt, keyFactorySalt.length);
337    this.keyFactoryKeyLengthBits = keyFactoryKeyLengthBits;
338    this.cipherTransformation = cipherTransformation;
339    this.cipherInitializationVector = Arrays.copyOf(cipherInitializationVector,
340         cipherInitializationVector.length);
341    this.keyIdentifier = keyIdentifier;
342    this.macAlgorithm = macAlgorithm;
343
344    secretKey = generateKeyReliably(keyFactoryAlgorithm, cipherTransformation,
345         passphrase, keyFactorySalt, keyFactoryIterationCount,
346         keyFactoryKeyLengthBits);
347
348    final ObjectPair<byte[],byte[]> headerPair = encode(keyFactoryAlgorithm,
349         keyFactoryIterationCount, this.keyFactorySalt, keyFactoryKeyLengthBits,
350         cipherTransformation, this.cipherInitializationVector, keyIdentifier,
351         secretKey, macAlgorithm);
352    encodedHeader = headerPair.getFirst();
353    macValue = headerPair.getSecond();
354  }
355
356
357
358  /**
359   * Generates an encoded representation of the header with the provided
360   * settings.
361   *
362   * @param  keyFactoryAlgorithm         The key factory algorithm used to
363   *                                     generate the encryption key from the
364   *                                     passphrase.  It must not be
365   *                                     {@code null}.
366   * @param  keyFactoryIterationCount    The iteration count used to generate
367   *                                     the encryption key from the passphrase.
368   * @param  keyFactorySalt              The salt used to generate the
369   *                                     encryption key from the passphrase.
370   *                                     It must not be {@code null}.
371   * @param  keyFactoryKeyLengthBits     The length (in bits) of the encryption
372   *                                     key generated from the passphrase.
373   * @param  cipherTransformation        The cipher transformation used for the
374   *                                     encryption.  It must not be
375   *                                     {@code null}.
376   * @param  cipherInitializationVector  The initialization vector used when
377   *                                     creating the cipher.  It must not be
378   *                                     {@code null}.
379   * @param  keyIdentifier               An optional identifier that can be used
380   *                                     to associate this passphrase-encrypted
381   *                                     stream header with some other
382   *                                     encryption settings object.  It may
383   *                                     optionally be {@code null}.
384   * @param  secretKey                   The secret key generated from the
385   *                                     passphrase.
386   * @param  macAlgorithm                The MAC algorithm to use when
387   *                                     generating a MAC of the header
388   *                                     contents.  It must not be {@code null}.
389     *
390   * @return  The encoded representation of the header.
391   *
392   * @throws  GeneralSecurityException  If a problem is encountered while
393   *                                    generating the MAC.
394   */
395  @NotNull()
396  private static ObjectPair<byte[],byte[]> encode(
397                      @NotNull final String keyFactoryAlgorithm,
398                      final int keyFactoryIterationCount,
399                      @NotNull final byte[] keyFactorySalt,
400                      final int keyFactoryKeyLengthBits,
401                      @NotNull final String cipherTransformation,
402                      @NotNull final byte[] cipherInitializationVector,
403                      @Nullable final String keyIdentifier,
404                      @Nullable final SecretKey secretKey,
405                      @NotNull final String macAlgorithm)
406          throws GeneralSecurityException
407  {
408    // Construct a list of all elements that will go in the header except the
409    // MAC value.
410    final ArrayList<ASN1Element> elements = new ArrayList<>(10);
411    elements.add(new ASN1Integer(TYPE_ENCODING_VERSION, ENCODING_VERSION_1));
412    elements.add(new ASN1OctetString(TYPE_KEY_FACTORY_ALGORITHM,
413         keyFactoryAlgorithm));
414    elements.add(new ASN1Integer(TYPE_KEY_FACTORY_ITERATION_COUNT,
415         keyFactoryIterationCount));
416    elements.add(new ASN1OctetString(TYPE_KEY_FACTORY_SALT, keyFactorySalt));
417    elements.add(new ASN1Integer(TYPE_KEY_FACTORY_KEY_LENGTH_BITS,
418         keyFactoryKeyLengthBits));
419    elements.add(new ASN1OctetString(TYPE_CIPHER_TRANSFORMATION,
420         cipherTransformation));
421    elements.add(new ASN1OctetString(TYPE_CIPHER_INITIALIZATION_VECTOR,
422         cipherInitializationVector));
423
424    if (keyIdentifier != null)
425    {
426      elements.add(new ASN1OctetString(TYPE_KEY_IDENTIFIER, keyIdentifier));
427    }
428
429    elements.add(new ASN1OctetString(TYPE_MAC_ALGORITHM, macAlgorithm));
430
431
432    // Compute the MAC value and add it to the list of elements.
433    final ByteStringBuffer macBuffer = new ByteStringBuffer();
434    for (final ASN1Element e : elements)
435    {
436      macBuffer.append(e.encode());
437    }
438
439    final Mac mac = CryptoHelper.getMAC(macAlgorithm);
440    mac.init(secretKey);
441
442    final byte[] macValue = mac.doFinal(macBuffer.toByteArray());
443    elements.add(new ASN1OctetString(TYPE_MAC_VALUE, macValue));
444
445
446    // Compute and return the encoded header.
447    final byte[] elementBytes = new ASN1Sequence(elements).encode();
448    final byte[] headerBytes =
449         new byte[MAGIC_BYTES.length + elementBytes.length];
450    System.arraycopy(MAGIC_BYTES, 0, headerBytes, 0, MAGIC_BYTES.length);
451    System.arraycopy(elementBytes, 0, headerBytes, MAGIC_BYTES.length,
452         elementBytes.length);
453    return new ObjectPair<>(headerBytes, macValue);
454  }
455
456
457
458  /**
459   * Writes an encoded representation of this passphrase-encrypted stream header
460   * to the provided output stream.  The output stream will remain open after
461   * this method completes.
462   *
463   * @param  outputStream  The output stream to which the header will be
464   *                       written.
465   *
466   * @throws  IOException  If a problem is encountered while trying to write to
467   *                       the provided output stream.
468   */
469  public void writeTo(@NotNull final OutputStream outputStream)
470         throws IOException
471  {
472    outputStream.write(encodedHeader);
473  }
474
475
476
477  /**
478   * Reads a passphrase-encrypted stream header from the provided input stream.
479   * This method will not close the provided input stream, regardless of whether
480   * it returns successfully or throws an exception.  If it returns
481   * successfully, then the position then the header bytes will have been
482   * consumed, so the next data to be read should be the data encrypted with
483   * these settings.  If it throws an exception, then some unknown amount of
484   * data may have been read from the stream.
485   *
486   * @param  inputStream  The input stream from which to read the encoded
487   *                      passphrase-encrypted stream header.  It must not be
488   *                      {@code null}.
489   * @param  passphrase   The passphrase to use to generate the encryption key.
490   *                      If this is {@code null}, then the header will be
491   *                      read, but no attempt will be made to validate the MAC,
492   *                      and it will not be possible to use this header to
493   *                      actually perform encryption or decryption.  Providing
494   *                      a {@code null} value is primarily useful if
495   *                      information in the header (especially the key
496   *                      identifier) is needed to determine what passphrase to
497   *                      use.
498   *
499   * @return  The passphrase-encrypted stream header that was read from the
500   *          provided input stream.
501   *
502   * @throws  IOException  If a problem is encountered while attempting to read
503   *                       data from the provided input stream.
504   *
505   * @throws  LDAPException  If a problem is encountered while attempting to
506   *                         decode the data that was read.
507   *
508   * @throws  InvalidKeyException  If the MAC contained in the header does not
509   *                               match the expected value.
510   *
511   * @throws  GeneralSecurityException  If a problem is encountered while trying
512   *                                    to generate the MAC.
513   */
514  @NotNull()
515  public static PassphraseEncryptedStreamHeader
516                     readFrom(@NotNull final InputStream inputStream,
517                              @Nullable final char[] passphrase)
518         throws IOException, LDAPException, InvalidKeyException,
519                GeneralSecurityException
520  {
521    // Read the magic from the input stream.
522    for (int i=0; i < MAGIC_BYTES.length; i++)
523    {
524      final int magicByte = inputStream.read();
525      if (magicByte < 0)
526      {
527        throw new LDAPException(ResultCode.DECODING_ERROR,
528             ERR_PW_ENCRYPTED_STREAM_HEADER_READ_END_OF_STREAM_IN_MAGIC.get());
529      }
530      else if (magicByte != MAGIC_BYTES[i])
531      {
532        throw new LDAPException(ResultCode.DECODING_ERROR,
533             ERR_PW_ENCRYPTED_STREAM_HEADER_READ_MAGIC_MISMATCH.get());
534      }
535    }
536
537
538    // The remainder of the header should be an ASN.1 sequence.  Read and
539    // process that sequenced.
540    try
541    {
542      final ASN1Element headerSequenceElement =
543           ASN1Element.readFrom(inputStream);
544      if (headerSequenceElement == null)
545      {
546        throw new LDAPException(ResultCode.DECODING_ERROR,
547             ERR_PW_ENCRYPTED_STREAM_HEADER_READ_END_OF_STREAM_AFTER_MAGIC.get(
548                  ));
549      }
550
551      final byte[] encodedHeaderSequence = headerSequenceElement.encode();
552      final byte[] encodedHeader =
553           new byte[MAGIC_BYTES.length + encodedHeaderSequence.length];
554      System.arraycopy(MAGIC_BYTES, 0, encodedHeader, 0, MAGIC_BYTES.length);
555      System.arraycopy(encodedHeaderSequence, 0, encodedHeader,
556           MAGIC_BYTES.length, encodedHeaderSequence.length);
557
558      final ASN1Sequence headerSequence =
559           ASN1Sequence.decodeAsSequence(headerSequenceElement);
560      return decodeHeaderSequence(encodedHeader, headerSequence, passphrase);
561    }
562    catch (final IOException | LDAPException | GeneralSecurityException e)
563    {
564      Debug.debugException(e);
565      throw e;
566    }
567    catch (final Exception e)
568    {
569      Debug.debugException(e);
570      throw new LDAPException(ResultCode.DECODING_ERROR,
571           ERR_PW_ENCRYPTED_STREAM_HEADER_READ_ASN1_DECODE_ERROR.get(
572                StaticUtils.getExceptionMessage(e)),
573           e);
574    }
575  }
576
577
578
579  /**
580   * Decodes the contents of the provided byte array as a passphrase-encrypted
581   * stream header.  The provided array must contain only the header, with no
582   * additional data before or after.
583   *
584   * @param  encodedHeader  The bytes that comprise the header to decode.  It
585   *                        must not be {@code null} or empty.
586   * @param  passphrase     The passphrase to use to generate the encryption
587   *                        key.  If this is {@code null}, then the header will
588   *                        be read, but no attempt will be made to validate the
589   *                        MAC, and it will not be possible to use this header
590   *                        to actually perform encryption or decryption.
591   *                        Providing a {@code null} value is primarily useful
592   *                        if information in the header (especially the key
593   *                        identifier) is needed to determine what passphrase
594   *                        to use.
595   *
596   * @return  The passphrase-encrypted stream header that was decoded from the
597   *          provided byte array.
598   *
599   * @throws  LDAPException  If a problem is encountered while trying to decode
600   *                         the data as a passphrase-encrypted stream header.
601   *
602   * @throws  InvalidKeyException  If the MAC contained in the header does not
603   *                               match the expected value.
604   *
605   * @throws  GeneralSecurityException  If a problem is encountered while trying
606   *                                    to generate the MAC.
607   */
608  @NotNull()
609  public static PassphraseEncryptedStreamHeader decode(
610                     @NotNull final byte[] encodedHeader,
611                     @Nullable final char[] passphrase)
612         throws LDAPException, InvalidKeyException, GeneralSecurityException
613  {
614    // Make sure that the array is long enough to hold a valid header.
615    if (encodedHeader.length <= MAGIC_BYTES.length)
616    {
617      throw new LDAPException(ResultCode.DECODING_ERROR,
618           ERR_PW_ENCRYPTED_STREAM_HEADER_DECODE_TOO_SHORT.get());
619    }
620
621
622    // Make sure that the array starts with the provided magic value.
623    for (int i=0; i < MAGIC_BYTES.length; i++)
624    {
625      if (encodedHeader[i] != MAGIC_BYTES[i])
626      {
627        throw new LDAPException(ResultCode.DECODING_ERROR,
628             ERR_PW_ENCRYPTED_STREAM_HEADER_DECODE_MAGIC_MISMATCH.get());
629      }
630    }
631
632
633    // Decode the remainder of the array as an ASN.1 sequence.
634    final ASN1Sequence headerSequence;
635    try
636    {
637      final byte[] encodedHeaderWithoutMagic =
638           new byte[encodedHeader.length - MAGIC_BYTES.length];
639      System.arraycopy(encodedHeader, MAGIC_BYTES.length,
640           encodedHeaderWithoutMagic, 0, encodedHeaderWithoutMagic.length);
641      headerSequence = ASN1Sequence.decodeAsSequence(encodedHeaderWithoutMagic);
642    }
643    catch (final Exception e)
644    {
645      Debug.debugException(e);
646      throw new LDAPException(ResultCode.DECODING_ERROR,
647           ERR_PW_ENCRYPTED_STREAM_HEADER_DECODE_ASN1_DECODE_ERROR.get(
648                StaticUtils.getExceptionMessage(e)),
649           e);
650    }
651
652    return decodeHeaderSequence(encodedHeader, headerSequence, passphrase);
653  }
654
655
656
657  /**
658   * Decodes the contents of the provided ASN.1 sequence as the portion of a
659   * passphrase-encrypted stream header that follows the magic bytes.
660   *
661   * @param  encodedHeader   The bytes that comprise the encoded header.  It
662   *                         must not be {@code null} or empty.
663   * @param  headerSequence  The header sequence portion of the encoded header.
664   * @param  passphrase      The passphrase to use to generate the encryption
665   *                         key.  If this is {@code null}, then the header will
666   *                         be read, but no attempt will be made to validate
667   *                         the MAC, and it will not be possible to use this
668   *                         header to actually perform encryption or
669   *                         decryption. Providing a {@code null} value is
670   *                         primarily useful if information in the header
671   *                         (especially the key identifier) is needed to
672   *                         determine what passphrase to use.
673   *
674   * @return  The passphrase-encrypted stream header that was decoded from the
675   *          provided ASN.1 sequence.
676   *
677   * @throws  LDAPException  If a problem is encountered while trying to decode
678   *                         the data as a passphrase-encrypted stream header.
679   *
680   * @throws  InvalidKeyException  If the MAC contained in the header does not
681   *                               match the expected value.
682   *
683   * @throws  GeneralSecurityException  If a problem is encountered while trying
684   *                                    to generate the MAC.
685   */
686  @NotNull()
687  private static PassphraseEncryptedStreamHeader decodeHeaderSequence(
688                      @NotNull final byte[] encodedHeader,
689                      @NotNull final ASN1Sequence headerSequence,
690                      @Nullable final char[] passphrase)
691          throws LDAPException, InvalidKeyException, GeneralSecurityException
692  {
693    try
694    {
695      // The first element must be the encoding version, and it must be 1.
696      final ASN1Element[] headerElements = headerSequence.elements();
697      final ASN1Integer versionElement =
698           ASN1Integer.decodeAsInteger(headerElements[0]);
699      if (versionElement.intValue() != ENCODING_VERSION_1)
700      {
701        throw new LDAPException(ResultCode.DECODING_ERROR,
702             ERR_PW_ENCRYPTED_HEADER_SEQUENCE_UNSUPPORTED_VERSION.get(
703                  versionElement.intValue()));
704      }
705
706      // The second element must be the key factory algorithm.
707      final String keyFactoryAlgorithm =
708           ASN1OctetString.decodeAsOctetString(headerElements[1]).stringValue();
709
710      // The third element must be the key factory iteration count.
711      final int keyFactoryIterationCount =
712           ASN1Integer.decodeAsInteger(headerElements[2]).intValue();
713
714      // The fourth element must be the key factory salt.
715      final byte[] keyFactorySalt =
716           ASN1OctetString.decodeAsOctetString(headerElements[3]).getValue();
717
718      // The fifth element must be the key length in bits.
719      final int keyFactoryKeyLengthBits =
720           ASN1Integer.decodeAsInteger(headerElements[4]).intValue();
721
722      // The sixth element must be the cipher transformation.
723      final String cipherTransformation =
724           ASN1OctetString.decodeAsOctetString(headerElements[5]).stringValue();
725
726      // The seventh element must be the initialization vector.
727      final byte[] cipherInitializationVector =
728           ASN1OctetString.decodeAsOctetString(headerElements[6]).getValue();
729
730      // Look through any remaining elements and decode them as appropriate.
731      byte[] macValue = null;
732      int macValuePos = -1;
733      String keyIdentifier = null;
734      String macAlgorithm = null;
735      for (int i=7; i < headerElements.length; i++)
736      {
737        switch (headerElements[i].getType())
738        {
739          case TYPE_KEY_IDENTIFIER:
740            keyIdentifier = ASN1OctetString.decodeAsOctetString(
741                 headerElements[i]).stringValue();
742            break;
743          case TYPE_MAC_ALGORITHM:
744            macAlgorithm = ASN1OctetString.decodeAsOctetString(
745                 headerElements[i]).stringValue();
746            break;
747          case TYPE_MAC_VALUE:
748            macValuePos = i;
749            macValue = ASN1OctetString.decodeAsOctetString(
750                 headerElements[i]).getValue();
751            break;
752          default:
753            throw new LDAPException(ResultCode.DECODING_ERROR,
754                 ERR_PW_ENCRYPTED_HEADER_SEQUENCE_UNRECOGNIZED_ELEMENT_TYPE.get(
755                      StaticUtils.toHex(headerElements[i].getType())));
756        }
757      }
758
759
760      // Compute a MAC of the appropriate header elements and verify that it
761      // matches the value contained in the header.  If it doesn't match, then
762      // it means the provided passphrase was invalid.
763      final SecretKey secretKey;
764      if (passphrase == null)
765      {
766        secretKey = null;
767      }
768      else
769      {
770        secretKey = generateKeyReliably(keyFactoryAlgorithm,
771             cipherTransformation, passphrase, keyFactorySalt,
772             keyFactoryIterationCount, keyFactoryKeyLengthBits);
773
774        final ByteStringBuffer macBuffer = new ByteStringBuffer();
775        for (int i=0; i < headerElements.length; i++)
776        {
777          if (i != macValuePos)
778          {
779            macBuffer.append(headerElements[i].encode());
780          }
781        }
782
783        final Mac mac = CryptoHelper.getMAC(macAlgorithm);
784        mac.init(secretKey);
785        final byte[] computedMacValue = mac.doFinal(macBuffer.toByteArray());
786        if (! Arrays.equals(computedMacValue, macValue))
787        {
788          throw new InvalidKeyException(
789               ERR_PW_ENCRYPTED_HEADER_SEQUENCE_BAD_PW.get());
790        }
791      }
792
793      return new PassphraseEncryptedStreamHeader(keyFactoryAlgorithm,
794           keyFactoryIterationCount, keyFactorySalt, keyFactoryKeyLengthBits,
795           cipherTransformation, cipherInitializationVector, keyIdentifier,
796           secretKey, macAlgorithm, macValue, encodedHeader);
797    }
798    catch (final LDAPException | GeneralSecurityException e)
799    {
800      Debug.debugException(e);
801      throw e;
802    }
803    catch (final Exception e)
804    {
805      Debug.debugException(e);
806      throw new LDAPException(ResultCode.DECODING_ERROR,
807           ERR_PW_ENCRYPTED_HEADER_SEQUENCE_DECODE_ERROR.get(
808                StaticUtils.getExceptionMessage(e)),
809           e);
810    }
811  }
812
813
814
815  /**
816   * We have seen situations where SecretKeyFactory#generateSecret returns
817   * inconsistent results for the same parameters. This can lead to data being
818   * encrypted or decrypted incorrectly. To avoid this, this method computes the
819   * key multiple times, and only returns the key once an identical key has been
820   * generated three times in a row.
821   *
822   * @param  keyFactoryAlgorithm       The key factory algorithm to use to
823   *                                   generate the encryption key from the
824   *                                   passphrase.  It must not be {@code null}.
825   * @param  cipherTransformation      The cipher transformation used for the
826   *                                   encryption key.  It must not be {@code
827   *                                   null}.
828   * @param  passphrase                The passphrase to use to generate the
829   *                                   encryption key.  It must not be
830   *                                   {@code null}.
831   * @param  keyFactorySalt            The salt to use to generate the
832   *                                   encryption key from the passphrase.
833   *                                   It must not be {@code null}.
834   * @param  keyFactoryIterationCount  The iteration count to use to generate
835   *                                   the encryption key from the passphrase.
836   * @param  keyFactoryKeyLengthBits   The length (in bits) of the encryption
837   *                                   key generated from the passphrase.
838   *
839   * @return  A SecretKey that has been consistently generated from the provided
840   *          parameters.
841   *
842   * @throws  GeneralSecurityException  If a problem is encountered while
843   *                                    generating the encryption key including
844   *                                    not being able to generate a consistent
845   *                                    key.
846   */
847  @NotNull()
848  private static SecretKey generateKeyReliably(
849                      @NotNull final String keyFactoryAlgorithm,
850                      @NotNull final String cipherTransformation,
851                      @NotNull final char[] passphrase,
852                      @NotNull final byte[] keyFactorySalt,
853                      final int keyFactoryIterationCount,
854                      final int keyFactoryKeyLengthBits)
855          throws GeneralSecurityException
856  {
857    // First, see if the key is already in the cache.  If so, then we'll use it.
858    final PassphraseEncryptedStreamHeaderCachedKeyIdentifier identifier =
859         new PassphraseEncryptedStreamHeaderCachedKeyIdentifier(
860              keyFactoryAlgorithm, keyFactorySalt, keyFactoryIterationCount,
861              keyFactoryKeyLengthBits, passphrase);
862    SecretKey secretKey =
863         PassphraseEncryptedStreamHeaderSecretKeyCache.get(identifier);
864    if (secretKey != null)
865    {
866      return secretKey;
867    }
868
869
870    // Generate the key.  Because we've previously seen cases in which this can
871    // yield inconsistent results, we'll make sure that we repeatedly get the
872    // same answer.
873    byte[] prev = null;
874    byte[] prev2 = null;
875
876    final int iterations = 10;
877    for (int i = 0; i < iterations; i++)
878    {
879      final SecretKeyFactory keyFactory =
880           CryptoHelper.getSecretKeyFactory(keyFactoryAlgorithm);
881      final String cipherAlgorithm = cipherTransformation.substring(0,
882           cipherTransformation.indexOf('/'));
883      final PBEKeySpec pbeKeySpec = new PBEKeySpec(passphrase, keyFactorySalt,
884           keyFactoryIterationCount, keyFactoryKeyLengthBits);
885      final byte[] encoded = keyFactory.generateSecret(pbeKeySpec).getEncoded();
886      secretKey = new SecretKeySpec(encoded, cipherAlgorithm);
887
888      // If this encoded key is the same as the previous one, and the one before
889      // that, then it was likely computed correctly, so return it.
890      if (Arrays.equals(encoded, prev) && Arrays.equals(encoded, prev2))
891      {
892        if (i > 2)
893        {
894          Debug.debug(Level.WARNING, DebugType.OTHER,
895               "The secret key was generated inconsistently initially, but " +
896               "after " + i + " iterations, we were able to generate a " +
897               "consistent value.");
898        }
899
900
901        // Put the new key in the cache and return it.
902        PassphraseEncryptedStreamHeaderSecretKeyCache.put(identifier,
903             secretKey);
904        return secretKey;
905      }
906
907      prev2 = prev;
908      prev = encoded;
909    }
910
911    Debug.debug(Level.SEVERE, DebugType.OTHER,
912         "Even after " + iterations + " iterations, the secret key could not " +
913         "be reliably generated.");
914
915    throw new InvalidKeyException(
916         ERR_PW_ENCRYPTED_STREAM_HEADER_CANNOT_GENERATE_KEY.get());
917  }
918
919
920
921  /**
922   * Creates a {@code Cipher} for the specified purpose.
923   *
924   * @param  mode  The mode to use for the cipher.  It must be one of
925   *               {@code Cipher.ENCRYPT_MODE} or {@code Cipher.DECRYPT_MODE}.
926   *
927   * @return  The {@code Cipher} instance that was created.
928   *
929   * @throws  InvalidKeyException  If no passphrase was provided when decoding
930   *                               this passphrase-encrypted stream header.
931   *
932   * @throws  GeneralSecurityException  If a problem is encountered while
933   *                                    creating the cipher.
934   */
935  @NotNull()
936  Cipher createCipher(final int mode)
937         throws InvalidKeyException, GeneralSecurityException
938  {
939    if (secretKey == null)
940    {
941      throw new InvalidKeyException(
942           ERR_PW_ENCRYPTED_HEADER_NO_KEY_AVAILABLE.get());
943    }
944
945    final Cipher cipher = CryptoHelper.getCipher(cipherTransformation);
946    cipher.init(mode, secretKey,
947         new IvParameterSpec(cipherInitializationVector));
948
949    return cipher;
950  }
951
952
953
954  /**
955   * Retrieves the key factory algorithm used to generate the encryption key
956   * from the passphrase.
957   *
958   * @return  The key factory algorithm used to generate the encryption key from
959   *          the passphrase.
960   */
961  @NotNull()
962  public String getKeyFactoryAlgorithm()
963  {
964    return keyFactoryAlgorithm;
965  }
966
967
968
969  /**
970   * Retrieves the iteration count used to generate the encryption key from the
971   * passphrase.
972   *
973   * @return  The iteration count used to generate the encryption key from the
974   *          passphrase.
975   */
976  public int getKeyFactoryIterationCount()
977  {
978    return keyFactoryIterationCount;
979  }
980
981
982
983  /**
984   * Retrieves the salt used to generate the encryption key from the passphrase.
985   *
986   * @return  The salt used to generate the encryption key from the passphrase.
987   */
988  @NotNull()
989  public byte[] getKeyFactorySalt()
990  {
991    return Arrays.copyOf(keyFactorySalt, keyFactorySalt.length);
992  }
993
994
995
996  /**
997   * Retrieves the length (in bits) of the encryption key generated from the
998   * passphrase.
999   *
1000   * @return  The length (in bits) of the encryption key generated from the
1001   *          passphrase.
1002   */
1003  public int getKeyFactoryKeyLengthBits()
1004  {
1005    return keyFactoryKeyLengthBits;
1006  }
1007
1008
1009
1010  /**
1011   * Retrieves the cipher transformation used for the encryption.
1012   *
1013   * @return  The cipher transformation used for the encryption.
1014   */
1015  @NotNull()
1016  public String getCipherTransformation()
1017  {
1018    return cipherTransformation;
1019  }
1020
1021
1022
1023  /**
1024   * Retrieves the cipher initialization vector used for the encryption.
1025   *
1026   * @return  The cipher initialization vector used for the encryption.
1027   */
1028  @NotNull()
1029  public byte[] getCipherInitializationVector()
1030  {
1031    return Arrays.copyOf(cipherInitializationVector,
1032         cipherInitializationVector.length);
1033  }
1034
1035
1036
1037  /**
1038   * Retrieves the key identifier used to associate this passphrase-encrypted
1039   * stream header with some other encryption settings object, if defined.
1040   *
1041   * @return  The key identifier used to associate this passphrase-encrypted
1042   *          stream header with some other encryption settings object, or
1043   *          {@code null} if none was provided.
1044   */
1045  @Nullable()
1046  public String getKeyIdentifier()
1047  {
1048    return keyIdentifier;
1049  }
1050
1051
1052
1053  /**
1054   * Retrieves the algorithm used to generate a MAC of the header content.
1055   *
1056   * @return  The algorithm used to generate a MAC of the header content.
1057   */
1058  @NotNull()
1059  public String getMACAlgorithm()
1060  {
1061    return macAlgorithm;
1062  }
1063
1064
1065
1066  /**
1067   * Retrieves an encoded representation of this passphrase-encrypted stream
1068   * header.
1069   *
1070   * @return  An encoded representation of this passphrase-encrypted stream
1071   *          header.
1072   */
1073  @NotNull()
1074  public byte[] getEncodedHeader()
1075  {
1076    return Arrays.copyOf(encodedHeader, encodedHeader.length);
1077  }
1078
1079
1080
1081  /**
1082   * Indicates whether this passphrase-encrypted stream header includes a secret
1083   * key.  If this header was read or decoded with no passphrase provided, then
1084   * it will not have a secret key, which means the MAC will not have been
1085   * validated and it cannot be used to encrypt or decrypt data.
1086   *
1087   * @return  {@code true} if this passphrase-encrypted stream header includes a
1088   *          secret key and can be used to encrypt or decrypt data, or
1089   *          {@code false} if not.
1090   */
1091  public boolean isSecretKeyAvailable()
1092  {
1093    return (secretKey != null);
1094  }
1095
1096
1097
1098  /**
1099   * Creates a copy of this passphrase-encrypted stream header that use a
1100   * newly-computed initialization vector.  The new stream header can be used to
1101   * create a new passphrase-encrypted output stream that safely leverages an
1102   * already computed secret key to dramatically reduce the cost of creating a
1103   * new stream from the same underlying passphrase.
1104   *
1105   * @return  The new passphrase-encrypted stream header that was created.
1106   *
1107   * @throws  GeneralSecurityException  If a problem occurs while creating a
1108   *                                    copy of this passphrase-encrypted stream
1109   *                                    header with a new initialization vector.
1110   */
1111  @NotNull()
1112  PassphraseEncryptedStreamHeader withNewCipherInitializationVector()
1113       throws GeneralSecurityException
1114  {
1115    if (secretKey == null)
1116    {
1117      throw new InvalidKeyException(
1118           ERR_PW_ENCRYPTED_STREAM_HEADER_COPY_WITHOUT_SECRET_KEY.get());
1119    }
1120
1121    final byte[] newInitializationVector =
1122         new byte[cipherInitializationVector.length];
1123    ThreadLocalSecureRandom.get().nextBytes(newInitializationVector);
1124
1125    final ObjectPair<byte[],byte[]> headerPair = encode(keyFactoryAlgorithm,
1126         keyFactoryIterationCount, this.keyFactorySalt, keyFactoryKeyLengthBits,
1127         cipherTransformation, newInitializationVector, keyIdentifier,
1128         secretKey, macAlgorithm);
1129    final byte[] newEncodedHeader = headerPair.getFirst();
1130    final byte[] newMACValue = headerPair.getSecond();
1131
1132    return new PassphraseEncryptedStreamHeader(keyFactoryAlgorithm,
1133         keyFactoryIterationCount, keyFactorySalt, keyFactoryKeyLengthBits,
1134         cipherTransformation, newInitializationVector, keyIdentifier,
1135         secretKey, macAlgorithm, newMACValue, newEncodedHeader);
1136  }
1137
1138
1139
1140  /**
1141   * Retrieves a string representation of this passphrase-encrypted stream
1142   * header.
1143   *
1144   * @return  A string representation of this passphrase-encrypted stream
1145   *         header.
1146   */
1147  @Override()
1148  @NotNull()
1149  public String toString()
1150  {
1151    final StringBuilder buffer = new StringBuilder();
1152    toString(buffer);
1153    return buffer.toString();
1154  }
1155
1156
1157
1158  /**
1159   * Appends a string representation of this passphrase-encrypted stream header
1160   * to the provided buffer.
1161   *
1162   * @param  buffer  The buffer to which the information should be appended.
1163   */
1164  public void toString(@NotNull final StringBuilder buffer)
1165  {
1166    buffer.append("PassphraseEncryptedStreamHeader(keyFactoryAlgorithm='");
1167    buffer.append(keyFactoryAlgorithm);
1168    buffer.append("', keyFactoryIterationCount=");
1169    buffer.append(keyFactoryIterationCount);
1170    buffer.append(", keyFactorySaltLengthBytes=");
1171    buffer.append(keyFactorySalt.length);
1172    buffer.append(", keyFactoryKeyLengthBits=");
1173    buffer.append(keyFactoryKeyLengthBits);
1174    buffer.append(", cipherTransformation'=");
1175    buffer.append(cipherTransformation);
1176    buffer.append("', cipherInitializationVectorLengthBytes=");
1177    buffer.append(cipherInitializationVector.length);
1178    buffer.append('\'');
1179
1180    if (keyIdentifier != null)
1181    {
1182      buffer.append(", keyIdentifier='");
1183      buffer.append(keyIdentifier);
1184      buffer.append('\'');
1185    }
1186
1187    buffer.append(", macAlgorithm='");
1188    buffer.append(macAlgorithm);
1189    buffer.append("', macValueLengthBytes=");
1190    buffer.append(macValue.length);
1191    buffer.append(", secretKeyAvailable=");
1192    buffer.append(isSecretKeyAvailable());
1193    buffer.append(", encodedHeaderLengthBytes=");
1194    buffer.append(encodedHeader.length);
1195    buffer.append(')');
1196  }
1197}