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