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