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