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.IOException;
041import java.io.OutputStream;
042import java.security.SecureRandom;
043import java.security.GeneralSecurityException;
044import java.util.concurrent.atomic.AtomicReference;
045import javax.crypto.Cipher;
046import javax.crypto.CipherOutputStream;
047
048
049
050/**
051 * This class provides an {@code OutputStream} implementation that will encrypt
052 * all data written to it with a key generated from a passphrase.  Details about
053 * the encryption will be encapsulated in a
054 * {@link PassphraseEncryptedStreamHeader}, which will typically be written to
055 * the underlying stream before any of the encrypted data, so that the
056 * {@link PassphraseEncryptedInputStream} can read it to determine how to
057 * decrypt that data when provided with the same passphrase.  However, it is
058 * also possible to store the encryption header elsewhere and provide it to the
059 * {@code PassphraseEncryptedInputStream} constructor so that that the
060 * underlying stream will only include encrypted data.
061 * <BR><BR>
062 * The specific details of the encryption performed may change over time, but
063 * the information in the header should ensure that data encrypted with
064 * different settings can still be decrypted (as long as the JVM provides the
065 * necessary support for that encryption).  The current implementation uses a
066 * baseline of 128-bit AES/CBC/PKCS5Padding using a key generated from the
067 * provided passphrase using the PBKDF2WithHmacSHA1 key factory algorithm
068 * (unfortunately, PBKDF2WithHmacSHA256 isn't available on Java 7, which is
069 * still a supported Java version for the LDAP SDK) with 16,384 iterations and a
070 * 128-bit (16-byte) salt.  However, if the  output stream is configured to use
071 * strong encryption, then it will attempt to use 256-bit AES/CBC/PKCS5Padding
072 * with a PBKDF2WithHmacSHA512 key factory algorithm with 131,072 iterations and
073 * a 128-bit salt.  If the JVM does not support this level of encryption, then
074 * it will fall back to a key size of 128 bits and a key factory algorithm of
075 * PBKDF2WithHmacSHA1.
076 * <BR><BR>
077 * Note that the use of strong encryption may require special configuration for
078 * some versions of the JVM (for example, installation of JCE unlimited strength
079 * jurisdiction policy files).  If data encrypted on one system may need to be
080 * decrypted on another system, then you should make sure that all systems will
081 * support the stronger encryption option before choosing to use it over the
082 * baseline encryption option.
083 */
084@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
085public final class PassphraseEncryptedOutputStream
086     extends OutputStream
087{
088  /**
089   * An atomic reference that indicates whether the JVM supports the stronger
090   * encryption settings.  It will be {@code null} until an attempt is made to
091   * use stronger encryption, at which point the determination will be made and
092   * a value assigned.  The cached value will be used for subsequent attempts to
093   * use the strong encryption.
094   */
095  private static final AtomicReference<Boolean> SUPPORTS_STRONG_ENCRYPTION =
096       new AtomicReference<>();
097
098
099
100  /**
101   * The length (in bytes) of the initialization vector that will be generated
102   * for the cipher.
103   */
104  private static final int CIPHER_INITIALIZATION_VECTOR_LENGTH_BYTES = 16;
105
106
107
108  /**
109   * The length (in bits) for the encryption key to generate from the password
110   * when using the baseline encryption strength.
111   */
112  private static final int BASELINE_KEY_FACTORY_KEY_LENGTH_BITS = 128;
113
114
115
116  /**
117   * The length (in bits) for the encryption key to generate from the password
118   * when using strong encryption.
119   */
120  private static final int STRONG_KEY_FACTORY_KEY_LENGTH_BITS = 256;
121
122
123
124  /**
125   * The key factory iteration count that will be used when generating the
126   * encryption key from the passphrase when using the baseline encryption
127   * strength.
128   */
129  private static final int BASELINE_KEY_FACTORY_ITERATION_COUNT = 16_384;
130
131
132
133  /**
134   * The key factory iteration count that will be used when generating the
135   * encryption key from the passphrase when using the strong encryption.
136   */
137  private static final int STRONG_KEY_FACTORY_ITERATION_COUNT = 131_072;
138
139
140
141  /**
142   * The length (in bytes) of the key factory salt that will be used when
143   * generating the encryption key from the passphrase.
144   */
145  private static final int KEY_FACTORY_SALT_LENGTH_BYTES = 16;
146
147
148
149  /**
150   * The cipher transformation that will be used for the encryption.
151   */
152  private static final String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";
153
154
155
156  /**
157   * The key factory algorithm that will be used when generating the encryption
158   * key from the passphrase when using the baseline encryption strength.
159   */
160  private static final String BASELINE_KEY_FACTORY_ALGORITHM =
161       "PBKDF2WithHmacSHA1";
162
163
164
165  /**
166   * The key factory algorithm that will be used when generating the encryption
167   * key from the passphrase when using strong encryption.
168   */
169  private static final String STRONG_KEY_FACTORY_ALGORITHM =
170       "PBKDF2WithHmacSHA512";
171
172
173
174  /**
175   * The algorithm that will be used when generating a MAC of the header
176   * contents when using the baseline encryption strength.
177   */
178  private static final String BASELINE_MAC_ALGORITHM = "HmacSHA256";
179
180
181
182  /**
183   * The algorithm that will be used when generating a MAC of the header
184   * contents when using strong encryption.
185   */
186  private static final String STRONG_MAC_ALGORITHM = "HmacSHA512";
187
188
189
190  // The cipher output stream that will be used to actually write the
191  // encrypted output.
192  private final CipherOutputStream cipherOutputStream;
193
194  // A header containing the encoded encryption details.
195  private final PassphraseEncryptedStreamHeader encryptionHeader;
196
197
198
199  /**
200   * Creates a new passphrase-encrypted output stream with the provided
201   * information.  It will not use a key identifier, will use the baseline
202   * encryption strength rather than attempting to use strong encryption, and it
203   * will write the generated {@link PassphraseEncryptedStreamHeader} to the
204   * underlying stream before writing any encrypted data.
205   *
206   * @param  passphrase
207   *              The passphrase that will be used to generate the encryption
208   *              key.  It must not be {@code null}.
209   * @param  wrappedOutputStream
210   *              The output stream to which the encrypted data (optionally
211   *              preceded by a header with details about the encryption) will
212   *              be written.  It must not be {@code null}.
213   *
214   * @throws  GeneralSecurityException  If a problem is encountered while
215   *                                    initializing the encryption.
216   *
217   * @throws  IOException  If a problem is encountered while writing the
218   *                       encryption header to the underlying output stream.
219   */
220  public PassphraseEncryptedOutputStream(final String passphrase,
221                                         final OutputStream wrappedOutputStream)
222         throws GeneralSecurityException, IOException
223  {
224    this(passphrase.toCharArray(), wrappedOutputStream);
225  }
226
227
228
229  /**
230   * Creates a new passphrase-encrypted output stream with the provided
231   * information.  It will not use a key identifier, will use the baseline
232   * encryption strength rather than attempting to use strong encryption, and it
233   * will write the generated {@link PassphraseEncryptedStreamHeader} to the
234   * underlying stream before writing any encrypted data.
235   *
236   * @param  passphrase
237   *              The passphrase that will be used to generate the encryption
238   *              key.  It must not be {@code null}.
239   * @param  wrappedOutputStream
240   *              The output stream to which the encrypted data (optionally
241   *              preceded by a header with details about the encryption) will
242   *              be written.  It must not be {@code null}.
243   *
244   * @throws  GeneralSecurityException  If a problem is encountered while
245   *                                    initializing the encryption.
246   *
247   * @throws  IOException  If a problem is encountered while writing the
248   *                       encryption header to the underlying output stream.
249   */
250  public PassphraseEncryptedOutputStream(final char[] passphrase,
251                                         final OutputStream wrappedOutputStream)
252         throws GeneralSecurityException, IOException
253  {
254    this(passphrase, wrappedOutputStream, null, false, true);
255  }
256
257
258
259  /**
260   * Creates a new passphrase-encrypted output stream with the provided
261   * information.
262   *
263   * @param  passphrase
264   *              The passphrase that will be used to generate the encryption
265   *              key.  It must not be {@code null}.
266   * @param  wrappedOutputStream
267   *              The output stream to which the encrypted data (optionally
268   *              preceded by a header with details about the encryption) will
269   *              be written.  It must not be {@code null}.
270   * @param  keyIdentifier
271   *              An optional identifier that may be used to associate the
272   *              encryption details with information in another system.  This
273   *              is primarily intended for use in conjunction with
274   *              UnboundID/Ping Identity products, but may be useful in other
275   *              systems.  It may be {@code null} if no key identifier is
276   *              needed.
277   * @param  useStrongEncryption
278   *              Indicates whether to attempt to use strong encryption, if it
279   *              is available.  If this is {@code true} and the JVM supports
280   *              the stronger level of encryption, then that encryption will be
281   *              used.  If this is {@code false}, or if the JVM does not
282   *              support the attempted stronger level of encryption, then the
283   *              baseline configuration will be used.
284   * @param  writeHeaderToStream
285   *              Indicates whether to write the generated
286   *              {@link PassphraseEncryptedStreamHeader} to the provided
287   *              {@code wrappedOutputStream} before any encrypted data so that
288   *              a {@link PassphraseEncryptedInputStream} can read it to obtain
289   *              information necessary for decrypting the data.  If this is
290   *              {@code false}, then the {@link #getEncryptionHeader()} method
291   *              must be used to obtain the encryption header so that it can be
292   *              stored elsewhere and provided to the
293   *              {@code PassphraseEncryptedInputStream} constructor.
294   *
295   * @throws  GeneralSecurityException  If a problem is encountered while
296   *                                    initializing the encryption.
297   *
298   * @throws  IOException  If a problem is encountered while writing the
299   *                       encryption header to the underlying output stream.
300   */
301  public PassphraseEncryptedOutputStream(final String passphrase,
302                                         final OutputStream wrappedOutputStream,
303                                         final String keyIdentifier,
304                                         final boolean useStrongEncryption,
305                                         final boolean writeHeaderToStream)
306         throws GeneralSecurityException, IOException
307  {
308    this(passphrase.toCharArray(), wrappedOutputStream, keyIdentifier,
309         useStrongEncryption, writeHeaderToStream);
310  }
311
312
313
314  /**
315   * Creates a new passphrase-encrypted output stream with the provided
316   * information.
317   *
318   * @param  passphrase
319   *              The passphrase that will be used to generate the encryption
320   *              key.  It must not be {@code null}.
321   * @param  wrappedOutputStream
322   *              The output stream to which the encrypted data (optionally
323   *              preceded by a header with details about the encryption) will
324   *              be written.  It must not be {@code null}.
325   * @param  keyIdentifier
326   *              An optional identifier that may be used to associate the
327   *              encryption details with information in another system.  This
328   *              is primarily intended for use in conjunction with
329   *              UnboundID/Ping Identity products, but may be useful in other
330   *              systems.  It may be {@code null} if no key identifier is
331   *              needed.
332   * @param  useStrongEncryption
333   *              Indicates whether to attempt to use strong encryption, if it
334   *              is available.  If this is {@code true} and the JVM supports
335   *              the stronger level of encryption, then that encryption will be
336   *              used.  If this is {@code false}, or if the JVM does not
337   *              support the attempted stronger level of encryption, then the
338   *              baseline configuration will be used.
339   * @param  writeHeaderToStream
340   *              Indicates whether to write the generated
341   *              {@link PassphraseEncryptedStreamHeader} to the provided
342   *              {@code wrappedOutputStream} before any encrypted data so that
343   *              a {@link PassphraseEncryptedInputStream} can read it to obtain
344   *              information necessary for decrypting the data.  If this is
345   *              {@code false}, then the {@link #getEncryptionHeader()} method
346   *              must be used to obtain the encryption header so that it can be
347   *              stored elsewhere and provided to the
348   *              {@code PassphraseEncryptedInputStream} constructor.
349   *
350   * @throws  GeneralSecurityException  If a problem is encountered while
351   *                                    initializing the encryption.
352   *
353   * @throws  IOException  If a problem is encountered while writing the
354   *                       encryption header to the underlying output stream.
355   */
356  public PassphraseEncryptedOutputStream(final char[] passphrase,
357                                         final OutputStream wrappedOutputStream,
358                                         final String keyIdentifier,
359                                         final boolean useStrongEncryption,
360                                         final boolean writeHeaderToStream)
361         throws GeneralSecurityException, IOException
362  {
363    this(passphrase, wrappedOutputStream, keyIdentifier, useStrongEncryption,
364         (useStrongEncryption
365              ? STRONG_KEY_FACTORY_ITERATION_COUNT
366              : BASELINE_KEY_FACTORY_ITERATION_COUNT),
367         writeHeaderToStream);
368  }
369
370
371
372  /**
373   * Creates a new passphrase-encrypted output stream with the provided
374   * information.
375   *
376   * @param  passphrase
377   *              The passphrase that will be used to generate the encryption
378   *              key.  It must not be {@code null}.
379   * @param  wrappedOutputStream
380   *              The output stream to which the encrypted data (optionally
381   *              preceded by a header with details about the encryption) will
382   *              be written.  It must not be {@code null}.
383   * @param  keyIdentifier
384   *              An optional identifier that may be used to associate the
385   *              encryption details with information in another system.  This
386   *              is primarily intended for use in conjunction with
387   *              UnboundID/Ping Identity products, but may be useful in other
388   *              systems.  It may be {@code null} if no key identifier is
389   *              needed.
390   * @param  useStrongEncryption
391   *              Indicates whether to attempt to use strong encryption, if it
392   *              is available.  If this is {@code true} and the JVM supports
393   *              the stronger level of encryption, then that encryption will be
394   *              used.  If this is {@code false}, or if the JVM does not
395   *              support the attempted stronger level of encryption, then the
396   *              baseline configuration will be used.
397   * @param  keyFactoryIterationCount
398   *              The iteration count to use when generating the encryption key
399   *              from the provided passphrase.
400   * @param  writeHeaderToStream
401   *              Indicates whether to write the generated
402   *              {@link PassphraseEncryptedStreamHeader} to the provided
403   *              {@code wrappedOutputStream} before any encrypted data so that
404   *              a {@link PassphraseEncryptedInputStream} can read it to obtain
405   *              information necessary for decrypting the data.  If this is
406   *              {@code false}, then the {@link #getEncryptionHeader()} method
407   *              must be used to obtain the encryption header so that it can be
408   *              stored elsewhere and provided to the
409   *              {@code PassphraseEncryptedInputStream} constructor.
410   *
411   * @throws  GeneralSecurityException  If a problem is encountered while
412   *                                    initializing the encryption.
413   *
414   * @throws  IOException  If a problem is encountered while writing the
415   *                       encryption header to the underlying output stream.
416   */
417  public PassphraseEncryptedOutputStream(final String passphrase,
418                                         final OutputStream wrappedOutputStream,
419                                         final String keyIdentifier,
420                                         final boolean useStrongEncryption,
421                                         final int keyFactoryIterationCount,
422                                         final boolean writeHeaderToStream)
423         throws GeneralSecurityException, IOException
424  {
425    this(passphrase.toCharArray(), wrappedOutputStream, keyIdentifier,
426         useStrongEncryption, keyFactoryIterationCount, writeHeaderToStream);
427  }
428
429
430
431  /**
432   * Creates a new passphrase-encrypted output stream with the provided
433   * information.
434   *
435   * @param  passphrase
436   *              The passphrase that will be used to generate the encryption
437   *              key.  It must not be {@code null}.
438   * @param  wrappedOutputStream
439   *              The output stream to which the encrypted data (optionally
440   *              preceded by a header with details about the encryption) will
441   *              be written.  It must not be {@code null}.
442   * @param  keyIdentifier
443   *              An optional identifier that may be used to associate the
444   *              encryption details with information in another system.  This
445   *              is primarily intended for use in conjunction with
446   *              UnboundID/Ping Identity products, but may be useful in other
447   *              systems.  It may be {@code null} if no key identifier is
448   *              needed.
449   * @param  useStrongEncryption
450   *              Indicates whether to attempt to use strong encryption, if it
451   *              is available.  If this is {@code true} and the JVM supports
452   *              the stronger level of encryption, then that encryption will be
453   *              used.  If this is {@code false}, or if the JVM does not
454   *              support the attempted stronger level of encryption, then the
455   *              baseline configuration will be used.
456   * @param  keyFactoryIterationCount
457   *              The iteration count to use when generating the encryption key
458   *              from the provided passphrase.
459   * @param  writeHeaderToStream
460   *              Indicates whether to write the generated
461   *              {@link PassphraseEncryptedStreamHeader} to the provided
462   *              {@code wrappedOutputStream} before any encrypted data so that
463   *              a {@link PassphraseEncryptedInputStream} can read it to obtain
464   *              information necessary for decrypting the data.  If this is
465   *              {@code false}, then the {@link #getEncryptionHeader()} method
466   *              must be used to obtain the encryption header so that it can be
467   *              stored elsewhere and provided to the
468   *              {@code PassphraseEncryptedInputStream} constructor.
469   *
470   * @throws  GeneralSecurityException  If a problem is encountered while
471   *                                    initializing the encryption.
472   *
473   * @throws  IOException  If a problem is encountered while writing the
474   *                       encryption header to the underlying output stream.
475   */
476  public PassphraseEncryptedOutputStream(final char[] passphrase,
477                                         final OutputStream wrappedOutputStream,
478                                         final String keyIdentifier,
479                                         final boolean useStrongEncryption,
480                                         final int keyFactoryIterationCount,
481                                         final boolean writeHeaderToStream)
482         throws GeneralSecurityException, IOException
483  {
484    final SecureRandom random = new SecureRandom();
485
486    final byte[] keyFactorySalt = new byte[KEY_FACTORY_SALT_LENGTH_BYTES];
487    random.nextBytes(keyFactorySalt);
488
489    final byte[] cipherInitializationVector =
490         new byte[CIPHER_INITIALIZATION_VECTOR_LENGTH_BYTES];
491    random.nextBytes(cipherInitializationVector);
492
493    final String macAlgorithm;
494    PassphraseEncryptedStreamHeader header = null;
495    CipherOutputStream cipherStream = null;
496    if (useStrongEncryption)
497    {
498      macAlgorithm = STRONG_MAC_ALGORITHM;
499
500      final Boolean supportsStrongEncryption = SUPPORTS_STRONG_ENCRYPTION.get();
501      if ((supportsStrongEncryption == null) ||
502           Boolean.TRUE.equals(supportsStrongEncryption))
503      {
504        try
505        {
506          header = new PassphraseEncryptedStreamHeader(passphrase,
507               STRONG_KEY_FACTORY_ALGORITHM, keyFactoryIterationCount,
508               keyFactorySalt, STRONG_KEY_FACTORY_KEY_LENGTH_BITS,
509               CIPHER_TRANSFORMATION, cipherInitializationVector,
510               keyIdentifier, macAlgorithm);
511
512          final Cipher cipher = header.createCipher(Cipher.ENCRYPT_MODE);
513          if (writeHeaderToStream)
514          {
515            header.writeTo(wrappedOutputStream);
516          }
517
518          cipherStream = new CipherOutputStream(wrappedOutputStream, cipher);
519          SUPPORTS_STRONG_ENCRYPTION.compareAndSet(null, Boolean.TRUE);
520        }
521        catch (final Exception e)
522        {
523          Debug.debugException(e);
524          SUPPORTS_STRONG_ENCRYPTION.set(Boolean.FALSE);
525        }
526      }
527    }
528    else
529    {
530      macAlgorithm = BASELINE_MAC_ALGORITHM;
531    }
532
533    if (cipherStream == null)
534    {
535      header = new PassphraseEncryptedStreamHeader(passphrase,
536           BASELINE_KEY_FACTORY_ALGORITHM, keyFactoryIterationCount,
537           keyFactorySalt, BASELINE_KEY_FACTORY_KEY_LENGTH_BITS,
538           CIPHER_TRANSFORMATION, cipherInitializationVector, keyIdentifier,
539           macAlgorithm);
540
541      final Cipher cipher = header.createCipher(Cipher.ENCRYPT_MODE);
542      if (writeHeaderToStream)
543      {
544        header.writeTo(wrappedOutputStream);
545      }
546
547      cipherStream = new CipherOutputStream(wrappedOutputStream, cipher);
548    }
549
550    encryptionHeader = header;
551    cipherOutputStream = cipherStream;
552  }
553
554
555
556  /**
557   * Writes an encrypted representation of the provided byte to the underlying
558   * output stream.
559   *
560   * @param  b  The byte of data to be written.  Only the least significant 8
561   *            bits of the value will be used, and the most significant 24 bits
562   *            will be ignored.
563   *
564   * @throws  IOException  If a problem is encountered while encrypting the data
565   *                       or writing to the underlying output stream.
566   */
567  @Override()
568  public void write(final int b)
569         throws IOException
570  {
571    cipherOutputStream.write(b);
572  }
573
574
575
576  /**
577   * Writes an encrypted representation of the contents of the provided byte
578   * array to the underlying output stream.
579   *
580   * @param  b  The array containing the data to be written.  It must not be
581   *            {@code null}.  All bytes in the array will be written.
582   *
583   * @throws  IOException  If a problem is encountered while encrypting the data
584   *                       or writing to the underlying output stream.
585   */
586  @Override()
587  public void write(final byte[] b)
588         throws IOException
589  {
590    cipherOutputStream.write(b);
591  }
592
593
594
595  /**
596   * Writes an encrypted representation of the specified portion of the provided
597   * byte array to the underlying output stream.
598   *
599   * @param  b       The array containing the data to be written.  It must not
600   *                 be {@code null}.
601   * @param  offset  The index in the array of the first byte to be written.
602   *                 It must be greater than or equal to zero, and less than the
603   *                 length of the provided array.
604   * @param  length  The number of bytes to be written.  It must be greater than
605   *                 or equal to zero, and the sum of the {@code offset} and
606   *                 {@code length} values must be less than or equal to the
607   *                 length of the provided array.
608   *
609   * @throws  IOException  If a problem is encountered while encrypting the data
610   *                       or writing to the underlying output stream.
611   */
612  @Override()
613  public void write(final byte[] b, final int offset, final int length)
614         throws IOException
615  {
616    cipherOutputStream.write(b, offset, length);
617  }
618
619
620
621  /**
622   * Flushes the underlying output stream so that any buffered encrypted output
623   * will be written to the underlying output stream, and also flushes the
624   * underlying output stream.  Note that this call may not flush any data that
625   * has yet to be encrypted (for example, because the encryption uses a block
626   * cipher and the associated block is not yet full).
627   *
628   * @throws  IOException  If a problem is encountered while flushing data to
629   *                       the underlying output stream.
630   */
631  @Override()
632  public void flush()
633         throws IOException
634  {
635    cipherOutputStream.flush();
636  }
637
638
639
640  /**
641   * Closes this output stream, along with the underlying output stream.  Any
642   * remaining buffered data will be processed (including generating any
643   * necessary padding) and flushed to the underlying output stream before the
644   * streams are closed.
645   *
646   * @throws  IOException  If a problem is encountered while closing the stream.
647   */
648  @Override()
649  public void close()
650         throws IOException
651  {
652    cipherOutputStream.close();
653  }
654
655
656
657  /**
658   * Retrieves an encryption header with details about the encryption being
659   * used.  If this header was not automatically written to the beginning of the
660   * underlying output stream before any encrypted data, then it must be stored
661   * somewhere else so that it can be provided to the
662   * {@link PassphraseEncryptedInputStream} constructor.
663   *
664   * @return  An encryption header with details about the encryption being used.
665   */
666  public PassphraseEncryptedStreamHeader getEncryptionHeader()
667  {
668    return encryptionHeader;
669  }
670}