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  @NotNull private static final AtomicReference<Boolean>
096       SUPPORTS_STRONG_ENCRYPTION = 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  @NotNull private static final String CIPHER_TRANSFORMATION =
153       "AES/CBC/PKCS5Padding";
154
155
156
157  /**
158   * The key factory algorithm that will be used when generating the encryption
159   * key from the passphrase when using the baseline encryption strength.
160   */
161  @NotNull private static final String BASELINE_KEY_FACTORY_ALGORITHM =
162       "PBKDF2WithHmacSHA1";
163
164
165
166  /**
167   * The key factory algorithm that will be used when generating the encryption
168   * key from the passphrase when using strong encryption.
169   */
170  @NotNull private static final String STRONG_KEY_FACTORY_ALGORITHM =
171       "PBKDF2WithHmacSHA512";
172
173
174
175  /**
176   * The algorithm that will be used when generating a MAC of the header
177   * contents when using the baseline encryption strength.
178   */
179  @NotNull private static final String BASELINE_MAC_ALGORITHM = "HmacSHA256";
180
181
182
183  /**
184   * The algorithm that will be used when generating a MAC of the header
185   * contents when using strong encryption.
186   */
187  @NotNull private static final String STRONG_MAC_ALGORITHM = "HmacSHA512";
188
189
190
191  // The cipher output stream that will be used to actually write the
192  // encrypted output.
193  @NotNull private final CipherOutputStream cipherOutputStream;
194
195  // A header containing the encoded encryption details.
196  @NotNull private final PassphraseEncryptedStreamHeader encryptionHeader;
197
198
199
200  /**
201   * Creates a new passphrase-encrypted output stream with the provided
202   * information.  It will not use a key identifier, will use the baseline
203   * encryption strength rather than attempting to use strong encryption, and it
204   * will write the generated {@link PassphraseEncryptedStreamHeader} to the
205   * underlying stream before writing any encrypted data.
206   *
207   * @param  passphrase
208   *              The passphrase that will be used to generate the encryption
209   *              key.  It must not be {@code null}.
210   * @param  wrappedOutputStream
211   *              The output stream to which the encrypted data (optionally
212   *              preceded by a header with details about the encryption) will
213   *              be written.  It must not be {@code null}.
214   *
215   * @throws  GeneralSecurityException  If a problem is encountered while
216   *                                    initializing the encryption.
217   *
218   * @throws  IOException  If a problem is encountered while writing the
219   *                       encryption header to the underlying output stream.
220   */
221  public PassphraseEncryptedOutputStream(@NotNull final String passphrase,
222              @NotNull final OutputStream wrappedOutputStream)
223         throws GeneralSecurityException, IOException
224  {
225    this(passphrase.toCharArray(), wrappedOutputStream);
226  }
227
228
229
230  /**
231   * Creates a new passphrase-encrypted output stream with the provided
232   * information.  It will not use a key identifier, will use the baseline
233   * encryption strength rather than attempting to use strong encryption, and it
234   * will write the generated {@link PassphraseEncryptedStreamHeader} to the
235   * underlying stream before writing any encrypted data.
236   *
237   * @param  passphrase
238   *              The passphrase that will be used to generate the encryption
239   *              key.  It must not be {@code null}.
240   * @param  wrappedOutputStream
241   *              The output stream to which the encrypted data (optionally
242   *              preceded by a header with details about the encryption) will
243   *              be written.  It must not be {@code null}.
244   *
245   * @throws  GeneralSecurityException  If a problem is encountered while
246   *                                    initializing the encryption.
247   *
248   * @throws  IOException  If a problem is encountered while writing the
249   *                       encryption header to the underlying output stream.
250   */
251  public PassphraseEncryptedOutputStream(@NotNull final char[] passphrase,
252              @NotNull final OutputStream wrappedOutputStream)
253         throws GeneralSecurityException, IOException
254  {
255    this(passphrase, wrappedOutputStream, null, false, true);
256  }
257
258
259
260  /**
261   * Creates a new passphrase-encrypted output stream with the provided
262   * information.
263   *
264   * @param  passphrase
265   *              The passphrase that will be used to generate the encryption
266   *              key.  It must not be {@code null}.
267   * @param  wrappedOutputStream
268   *              The output stream to which the encrypted data (optionally
269   *              preceded by a header with details about the encryption) will
270   *              be written.  It must not be {@code null}.
271   * @param  keyIdentifier
272   *              An optional identifier that may be used to associate the
273   *              encryption details with information in another system.  This
274   *              is primarily intended for use in conjunction with
275   *              UnboundID/Ping Identity products, but may be useful in other
276   *              systems.  It may be {@code null} if no key identifier is
277   *              needed.
278   * @param  useStrongEncryption
279   *              Indicates whether to attempt to use strong encryption, if it
280   *              is available.  If this is {@code true} and the JVM supports
281   *              the stronger level of encryption, then that encryption will be
282   *              used.  If this is {@code false}, or if the JVM does not
283   *              support the attempted stronger level of encryption, then the
284   *              baseline configuration will be used.
285   * @param  writeHeaderToStream
286   *              Indicates whether to write the generated
287   *              {@link PassphraseEncryptedStreamHeader} to the provided
288   *              {@code wrappedOutputStream} before any encrypted data so that
289   *              a {@link PassphraseEncryptedInputStream} can read it to obtain
290   *              information necessary for decrypting the data.  If this is
291   *              {@code false}, then the {@link #getEncryptionHeader()} method
292   *              must be used to obtain the encryption header so that it can be
293   *              stored elsewhere and provided to the
294   *              {@code PassphraseEncryptedInputStream} constructor.
295   *
296   * @throws  GeneralSecurityException  If a problem is encountered while
297   *                                    initializing the encryption.
298   *
299   * @throws  IOException  If a problem is encountered while writing the
300   *                       encryption header to the underlying output stream.
301   */
302  public PassphraseEncryptedOutputStream(@NotNull final String passphrase,
303              @NotNull final OutputStream wrappedOutputStream,
304              @Nullable final String keyIdentifier,
305              final boolean useStrongEncryption,
306              final boolean writeHeaderToStream)
307         throws GeneralSecurityException, IOException
308  {
309    this(passphrase.toCharArray(), wrappedOutputStream, keyIdentifier,
310         useStrongEncryption, writeHeaderToStream);
311  }
312
313
314
315  /**
316   * Creates a new passphrase-encrypted output stream with the provided
317   * information.
318   *
319   * @param  passphrase
320   *              The passphrase that will be used to generate the encryption
321   *              key.  It must not be {@code null}.
322   * @param  wrappedOutputStream
323   *              The output stream to which the encrypted data (optionally
324   *              preceded by a header with details about the encryption) will
325   *              be written.  It must not be {@code null}.
326   * @param  keyIdentifier
327   *              An optional identifier that may be used to associate the
328   *              encryption details with information in another system.  This
329   *              is primarily intended for use in conjunction with
330   *              UnboundID/Ping Identity products, but may be useful in other
331   *              systems.  It may be {@code null} if no key identifier is
332   *              needed.
333   * @param  useStrongEncryption
334   *              Indicates whether to attempt to use strong encryption, if it
335   *              is available.  If this is {@code true} and the JVM supports
336   *              the stronger level of encryption, then that encryption will be
337   *              used.  If this is {@code false}, or if the JVM does not
338   *              support the attempted stronger level of encryption, then the
339   *              baseline configuration will be used.
340   * @param  writeHeaderToStream
341   *              Indicates whether to write the generated
342   *              {@link PassphraseEncryptedStreamHeader} to the provided
343   *              {@code wrappedOutputStream} before any encrypted data so that
344   *              a {@link PassphraseEncryptedInputStream} can read it to obtain
345   *              information necessary for decrypting the data.  If this is
346   *              {@code false}, then the {@link #getEncryptionHeader()} method
347   *              must be used to obtain the encryption header so that it can be
348   *              stored elsewhere and provided to the
349   *              {@code PassphraseEncryptedInputStream} constructor.
350   *
351   * @throws  GeneralSecurityException  If a problem is encountered while
352   *                                    initializing the encryption.
353   *
354   * @throws  IOException  If a problem is encountered while writing the
355   *                       encryption header to the underlying output stream.
356   */
357  public PassphraseEncryptedOutputStream(@NotNull final char[] passphrase,
358              @NotNull final OutputStream wrappedOutputStream,
359              @Nullable final String keyIdentifier,
360              final boolean useStrongEncryption,
361              final boolean writeHeaderToStream)
362         throws GeneralSecurityException, IOException
363  {
364    this(passphrase, wrappedOutputStream, keyIdentifier, useStrongEncryption,
365         (useStrongEncryption
366              ? STRONG_KEY_FACTORY_ITERATION_COUNT
367              : BASELINE_KEY_FACTORY_ITERATION_COUNT),
368         writeHeaderToStream);
369  }
370
371
372
373  /**
374   * Creates a new passphrase-encrypted output stream with the provided
375   * information.
376   *
377   * @param  passphrase
378   *              The passphrase that will be used to generate the encryption
379   *              key.  It must not be {@code null}.
380   * @param  wrappedOutputStream
381   *              The output stream to which the encrypted data (optionally
382   *              preceded by a header with details about the encryption) will
383   *              be written.  It must not be {@code null}.
384   * @param  keyIdentifier
385   *              An optional identifier that may be used to associate the
386   *              encryption details with information in another system.  This
387   *              is primarily intended for use in conjunction with
388   *              UnboundID/Ping Identity products, but may be useful in other
389   *              systems.  It may be {@code null} if no key identifier is
390   *              needed.
391   * @param  useStrongEncryption
392   *              Indicates whether to attempt to use strong encryption, if it
393   *              is available.  If this is {@code true} and the JVM supports
394   *              the stronger level of encryption, then that encryption will be
395   *              used.  If this is {@code false}, or if the JVM does not
396   *              support the attempted stronger level of encryption, then the
397   *              baseline configuration will be used.
398   * @param  keyFactoryIterationCount
399   *              The iteration count to use when generating the encryption key
400   *              from the provided passphrase.
401   * @param  writeHeaderToStream
402   *              Indicates whether to write the generated
403   *              {@link PassphraseEncryptedStreamHeader} to the provided
404   *              {@code wrappedOutputStream} before any encrypted data so that
405   *              a {@link PassphraseEncryptedInputStream} can read it to obtain
406   *              information necessary for decrypting the data.  If this is
407   *              {@code false}, then the {@link #getEncryptionHeader()} method
408   *              must be used to obtain the encryption header so that it can be
409   *              stored elsewhere and provided to the
410   *              {@code PassphraseEncryptedInputStream} constructor.
411   *
412   * @throws  GeneralSecurityException  If a problem is encountered while
413   *                                    initializing the encryption.
414   *
415   * @throws  IOException  If a problem is encountered while writing the
416   *                       encryption header to the underlying output stream.
417   */
418  public PassphraseEncryptedOutputStream(@NotNull final String passphrase,
419              @NotNull final OutputStream wrappedOutputStream,
420              @Nullable final String keyIdentifier,
421              final boolean useStrongEncryption,
422              final int keyFactoryIterationCount,
423              final boolean writeHeaderToStream)
424         throws GeneralSecurityException, IOException
425  {
426    this(passphrase.toCharArray(), wrappedOutputStream, keyIdentifier,
427         useStrongEncryption, keyFactoryIterationCount, writeHeaderToStream);
428  }
429
430
431
432  /**
433   * Creates a new passphrase-encrypted output stream with the provided
434   * information.
435   *
436   * @param  passphrase
437   *              The passphrase that will be used to generate the encryption
438   *              key.  It must not be {@code null}.
439   * @param  wrappedOutputStream
440   *              The output stream to which the encrypted data (optionally
441   *              preceded by a header with details about the encryption) will
442   *              be written.  It must not be {@code null}.
443   * @param  keyIdentifier
444   *              An optional identifier that may be used to associate the
445   *              encryption details with information in another system.  This
446   *              is primarily intended for use in conjunction with
447   *              UnboundID/Ping Identity products, but may be useful in other
448   *              systems.  It may be {@code null} if no key identifier is
449   *              needed.
450   * @param  useStrongEncryption
451   *              Indicates whether to attempt to use strong encryption, if it
452   *              is available.  If this is {@code true} and the JVM supports
453   *              the stronger level of encryption, then that encryption will be
454   *              used.  If this is {@code false}, or if the JVM does not
455   *              support the attempted stronger level of encryption, then the
456   *              baseline configuration will be used.
457   * @param  keyFactoryIterationCount
458   *              The iteration count to use when generating the encryption key
459   *              from the provided passphrase.
460   * @param  writeHeaderToStream
461   *              Indicates whether to write the generated
462   *              {@link PassphraseEncryptedStreamHeader} to the provided
463   *              {@code wrappedOutputStream} before any encrypted data so that
464   *              a {@link PassphraseEncryptedInputStream} can read it to obtain
465   *              information necessary for decrypting the data.  If this is
466   *              {@code false}, then the {@link #getEncryptionHeader()} method
467   *              must be used to obtain the encryption header so that it can be
468   *              stored elsewhere and provided to the
469   *              {@code PassphraseEncryptedInputStream} constructor.
470   *
471   * @throws  GeneralSecurityException  If a problem is encountered while
472   *                                    initializing the encryption.
473   *
474   * @throws  IOException  If a problem is encountered while writing the
475   *                       encryption header to the underlying output stream.
476   */
477  public PassphraseEncryptedOutputStream(@NotNull final char[] passphrase,
478              @NotNull final OutputStream wrappedOutputStream,
479              @Nullable final String keyIdentifier,
480              final boolean useStrongEncryption,
481              final int keyFactoryIterationCount,
482              final boolean writeHeaderToStream)
483         throws GeneralSecurityException, IOException
484  {
485    final SecureRandom random = new SecureRandom();
486
487    final byte[] keyFactorySalt = new byte[KEY_FACTORY_SALT_LENGTH_BYTES];
488    random.nextBytes(keyFactorySalt);
489
490    final byte[] cipherInitializationVector =
491         new byte[CIPHER_INITIALIZATION_VECTOR_LENGTH_BYTES];
492    random.nextBytes(cipherInitializationVector);
493
494    final String macAlgorithm;
495    PassphraseEncryptedStreamHeader header = null;
496    CipherOutputStream cipherStream = null;
497    if (useStrongEncryption)
498    {
499      macAlgorithm = STRONG_MAC_ALGORITHM;
500
501      final Boolean supportsStrongEncryption = SUPPORTS_STRONG_ENCRYPTION.get();
502      if ((supportsStrongEncryption == null) ||
503           Boolean.TRUE.equals(supportsStrongEncryption))
504      {
505        try
506        {
507          header = new PassphraseEncryptedStreamHeader(passphrase,
508               STRONG_KEY_FACTORY_ALGORITHM, keyFactoryIterationCount,
509               keyFactorySalt, STRONG_KEY_FACTORY_KEY_LENGTH_BITS,
510               CIPHER_TRANSFORMATION, cipherInitializationVector,
511               keyIdentifier, macAlgorithm);
512
513          final Cipher cipher = header.createCipher(Cipher.ENCRYPT_MODE);
514          if (writeHeaderToStream)
515          {
516            header.writeTo(wrappedOutputStream);
517          }
518
519          cipherStream = new CipherOutputStream(wrappedOutputStream, cipher);
520          SUPPORTS_STRONG_ENCRYPTION.compareAndSet(null, Boolean.TRUE);
521        }
522        catch (final Exception e)
523        {
524          Debug.debugException(e);
525          SUPPORTS_STRONG_ENCRYPTION.set(Boolean.FALSE);
526        }
527      }
528    }
529    else
530    {
531      macAlgorithm = BASELINE_MAC_ALGORITHM;
532    }
533
534    if (cipherStream == null)
535    {
536      header = new PassphraseEncryptedStreamHeader(passphrase,
537           BASELINE_KEY_FACTORY_ALGORITHM, keyFactoryIterationCount,
538           keyFactorySalt, BASELINE_KEY_FACTORY_KEY_LENGTH_BITS,
539           CIPHER_TRANSFORMATION, cipherInitializationVector, keyIdentifier,
540           macAlgorithm);
541
542      final Cipher cipher = header.createCipher(Cipher.ENCRYPT_MODE);
543      if (writeHeaderToStream)
544      {
545        header.writeTo(wrappedOutputStream);
546      }
547
548      cipherStream = new CipherOutputStream(wrappedOutputStream, cipher);
549    }
550
551    encryptionHeader = header;
552    cipherOutputStream = cipherStream;
553  }
554
555
556
557  /**
558   * Writes an encrypted representation of the provided byte to the underlying
559   * output stream.
560   *
561   * @param  b  The byte of data to be written.  Only the least significant 8
562   *            bits of the value will be used, and the most significant 24 bits
563   *            will be ignored.
564   *
565   * @throws  IOException  If a problem is encountered while encrypting the data
566   *                       or writing to the underlying output stream.
567   */
568  @Override()
569  public void write(final int b)
570         throws IOException
571  {
572    cipherOutputStream.write(b);
573  }
574
575
576
577  /**
578   * Writes an encrypted representation of the contents of the provided byte
579   * array to the underlying output stream.
580   *
581   * @param  b  The array containing the data to be written.  It must not be
582   *            {@code null}.  All bytes in the array will be written.
583   *
584   * @throws  IOException  If a problem is encountered while encrypting the data
585   *                       or writing to the underlying output stream.
586   */
587  @Override()
588  public void write(@NotNull final byte[] b)
589         throws IOException
590  {
591    cipherOutputStream.write(b);
592  }
593
594
595
596  /**
597   * Writes an encrypted representation of the specified portion of the provided
598   * byte array to the underlying output stream.
599   *
600   * @param  b       The array containing the data to be written.  It must not
601   *                 be {@code null}.
602   * @param  offset  The index in the array of the first byte to be written.
603   *                 It must be greater than or equal to zero, and less than the
604   *                 length of the provided array.
605   * @param  length  The number of bytes to be written.  It must be greater than
606   *                 or equal to zero, and the sum of the {@code offset} and
607   *                 {@code length} values must be less than or equal to the
608   *                 length of the provided array.
609   *
610   * @throws  IOException  If a problem is encountered while encrypting the data
611   *                       or writing to the underlying output stream.
612   */
613  @Override()
614  public void write(@NotNull final byte[] b, final int offset, final int length)
615         throws IOException
616  {
617    cipherOutputStream.write(b, offset, length);
618  }
619
620
621
622  /**
623   * Flushes the underlying output stream so that any buffered encrypted output
624   * will be written to the underlying output stream, and also flushes the
625   * underlying output stream.  Note that this call may not flush any data that
626   * has yet to be encrypted (for example, because the encryption uses a block
627   * cipher and the associated block is not yet full).
628   *
629   * @throws  IOException  If a problem is encountered while flushing data to
630   *                       the underlying output stream.
631   */
632  @Override()
633  public void flush()
634         throws IOException
635  {
636    cipherOutputStream.flush();
637  }
638
639
640
641  /**
642   * Closes this output stream, along with the underlying output stream.  Any
643   * remaining buffered data will be processed (including generating any
644   * necessary padding) and flushed to the underlying output stream before the
645   * streams are closed.
646   *
647   * @throws  IOException  If a problem is encountered while closing the stream.
648   */
649  @Override()
650  public void close()
651         throws IOException
652  {
653    cipherOutputStream.close();
654  }
655
656
657
658  /**
659   * Retrieves an encryption header with details about the encryption being
660   * used.  If this header was not automatically written to the beginning of the
661   * underlying output stream before any encrypted data, then it must be stored
662   * somewhere else so that it can be provided to the
663   * {@link PassphraseEncryptedInputStream} constructor.
664   *
665   * @return  An encryption header with details about the encryption being used.
666   */
667  @NotNull()
668  public PassphraseEncryptedStreamHeader getEncryptionHeader()
669  {
670    return encryptionHeader;
671  }
672}