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