001/*
002 * Copyright 2018-2022 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2018-2022 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-2022 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    byte[] prev = null;
858    byte[] prev2 = null;
859
860    final int iterations = 10;
861    for (int i = 0; i < iterations; i++)
862    {
863      final SecretKeyFactory keyFactory =
864           CryptoHelper.getSecretKeyFactory(keyFactoryAlgorithm);
865      final String cipherAlgorithm = cipherTransformation.substring(0,
866           cipherTransformation.indexOf('/'));
867      final PBEKeySpec pbeKeySpec = new PBEKeySpec(passphrase, keyFactorySalt,
868           keyFactoryIterationCount, keyFactoryKeyLengthBits);
869      final SecretKey secretKey = new SecretKeySpec(
870           keyFactory.generateSecret(pbeKeySpec).getEncoded(),
871           cipherAlgorithm);
872      final byte[] encoded = secretKey.getEncoded();
873
874      // If this encoded key is the same as the previous one, and the one before
875      // that, then it was likely computed correctly, so return it.
876      if (Arrays.equals(encoded, prev) && Arrays.equals(encoded, prev2))
877      {
878        if (i > 2)
879        {
880          Debug.debug(Level.WARNING, DebugType.OTHER,
881               "The secret key was generated inconsistently initially, but " +
882               "after " + i + " iterations, we were able to generate a " +
883               "consistent value.");
884        }
885        return secretKey;
886      }
887
888      prev2 = prev;
889      prev = encoded;
890    }
891
892    Debug.debug(Level.SEVERE, DebugType.OTHER,
893         "Even after " + iterations + " iterations, the secret key could not " +
894         "be reliably generated.");
895
896    throw new InvalidKeyException(
897         ERR_PW_ENCRYPTED_STREAM_HEADER_CANNOT_GENERATE_KEY.get());
898  }
899
900
901
902  /**
903   * Creates a {@code Cipher} for the specified purpose.
904   *
905   * @param  mode  The mode to use for the cipher.  It must be one of
906   *               {@code Cipher.ENCRYPT_MODE} or {@code Cipher.DECRYPT_MODE}.
907   *
908   * @return  The {@code Cipher} instance that was created.
909   *
910   * @throws  InvalidKeyException  If no passphrase was provided when decoding
911   *                               this passphrase-encrypted stream header.
912   *
913   * @throws  GeneralSecurityException  If a problem is encountered while
914   *                                    creating the cipher.
915   */
916  @NotNull()
917  Cipher createCipher(final int mode)
918         throws InvalidKeyException, GeneralSecurityException
919  {
920    if (secretKey == null)
921    {
922      throw new InvalidKeyException(
923           ERR_PW_ENCRYPTED_HEADER_NO_KEY_AVAILABLE.get());
924    }
925
926    final Cipher cipher = CryptoHelper.getCipher(cipherTransformation);
927    cipher.init(mode, secretKey,
928         new IvParameterSpec(cipherInitializationVector));
929
930    return cipher;
931  }
932
933
934
935  /**
936   * Retrieves the key factory algorithm used to generate the encryption key
937   * from the passphrase.
938   *
939   * @return  The key factory algorithm used to generate the encryption key from
940   *          the passphrase.
941   */
942  @NotNull()
943  public String getKeyFactoryAlgorithm()
944  {
945    return keyFactoryAlgorithm;
946  }
947
948
949
950  /**
951   * Retrieves the iteration count used to generate the encryption key from the
952   * passphrase.
953   *
954   * @return  The iteration count used to generate the encryption key from the
955   *          passphrase.
956   */
957  public int getKeyFactoryIterationCount()
958  {
959    return keyFactoryIterationCount;
960  }
961
962
963
964  /**
965   * Retrieves the salt used to generate the encryption key from the passphrase.
966   *
967   * @return  The salt used to generate the encryption key from the passphrase.
968   */
969  @NotNull()
970  public byte[] getKeyFactorySalt()
971  {
972    return Arrays.copyOf(keyFactorySalt, keyFactorySalt.length);
973  }
974
975
976
977  /**
978   * Retrieves the length (in bits) of the encryption key generated from the
979   * passphrase.
980   *
981   * @return  The length (in bits) of the encryption key generated from the
982   *          passphrase.
983   */
984  public int getKeyFactoryKeyLengthBits()
985  {
986    return keyFactoryKeyLengthBits;
987  }
988
989
990
991  /**
992   * Retrieves the cipher transformation used for the encryption.
993   *
994   * @return  The cipher transformation used for the encryption.
995   */
996  @NotNull()
997  public String getCipherTransformation()
998  {
999    return cipherTransformation;
1000  }
1001
1002
1003
1004  /**
1005   * Retrieves the cipher initialization vector used for the encryption.
1006   *
1007   * @return  The cipher initialization vector used for the encryption.
1008   */
1009  @NotNull()
1010  public byte[] getCipherInitializationVector()
1011  {
1012    return Arrays.copyOf(cipherInitializationVector,
1013         cipherInitializationVector.length);
1014  }
1015
1016
1017
1018  /**
1019   * Retrieves the key identifier used to associate this passphrase-encrypted
1020   * stream header with some other encryption settings object, if defined.
1021   *
1022   * @return  The key identifier used to associate this passphrase-encrypted
1023   *          stream header with some other encryption settings object, or
1024   *          {@code null} if none was provided.
1025   */
1026  @Nullable()
1027  public String getKeyIdentifier()
1028  {
1029    return keyIdentifier;
1030  }
1031
1032
1033
1034  /**
1035   * Retrieves the algorithm used to generate a MAC of the header content.
1036   *
1037   * @return  The algorithm used to generate a MAC of the header content.
1038   */
1039  @NotNull()
1040  public String getMACAlgorithm()
1041  {
1042    return macAlgorithm;
1043  }
1044
1045
1046
1047  /**
1048   * Retrieves an encoded representation of this passphrase-encrypted stream
1049   * header.
1050   *
1051   * @return  An encoded representation of this passphrase-encrypted stream
1052   *          header.
1053   */
1054  @NotNull()
1055  public byte[] getEncodedHeader()
1056  {
1057    return Arrays.copyOf(encodedHeader, encodedHeader.length);
1058  }
1059
1060
1061
1062  /**
1063   * Indicates whether this passphrase-encrypted stream header includes a secret
1064   * key.  If this header was read or decoded with no passphrase provided, then
1065   * it will not have a secret key, which means the MAC will not have been
1066   * validated and it cannot be used to encrypt or decrypt data.
1067   *
1068   * @return  {@code true} if this passphrase-encrypted stream header includes a
1069   *          secret key and can be used to encrypt or decrypt data, or
1070   *          {@code false} if not.
1071   */
1072  public boolean isSecretKeyAvailable()
1073  {
1074    return (secretKey != null);
1075  }
1076
1077
1078
1079  /**
1080   * Retrieves a string representation of this passphrase-encrypted stream
1081   * header.
1082   *
1083   * @return  A string representation of this passphrase-encrypted stream
1084   *         header.
1085   */
1086  @Override()
1087  @NotNull()
1088  public String toString()
1089  {
1090    final StringBuilder buffer = new StringBuilder();
1091    toString(buffer);
1092    return buffer.toString();
1093  }
1094
1095
1096
1097  /**
1098   * Appends a string representation of this passphrase-encrypted stream header
1099   * to the provided buffer.
1100   *
1101   * @param  buffer  The buffer to which the information should be appended.
1102   */
1103  public void toString(@NotNull final StringBuilder buffer)
1104  {
1105    buffer.append("PassphraseEncryptedStreamHeader(keyFactoryAlgorithm='");
1106    buffer.append(keyFactoryAlgorithm);
1107    buffer.append("', keyFactoryIterationCount=");
1108    buffer.append(keyFactoryIterationCount);
1109    buffer.append(", keyFactorySaltLengthBytes=");
1110    buffer.append(keyFactorySalt.length);
1111    buffer.append(", keyFactoryKeyLengthBits=");
1112    buffer.append(keyFactoryKeyLengthBits);
1113    buffer.append(", cipherTransformation'=");
1114    buffer.append(cipherTransformation);
1115    buffer.append("', cipherInitializationVectorLengthBytes=");
1116    buffer.append(cipherInitializationVector.length);
1117    buffer.append('\'');
1118
1119    if (keyIdentifier != null)
1120    {
1121      buffer.append(", keyIdentifier='");
1122      buffer.append(keyIdentifier);
1123      buffer.append('\'');
1124    }
1125
1126    buffer.append(", macAlgorithm='");
1127    buffer.append(macAlgorithm);
1128    buffer.append("', macValueLengthBytes=");
1129    buffer.append(macValue.length);
1130    buffer.append(", secretKeyAvailable=");
1131    buffer.append(isSecretKeyAvailable());
1132    buffer.append(", encodedHeaderLengthBytes=");
1133    buffer.append(encodedHeader.length);
1134    buffer.append(')');
1135  }
1136}