title: BouncyCastle PGP Encryption / Decryption: Caused by: java.io.EOFException: premature end of stream in PartialInputStream
body: <p>When attempting to encrypt then decrypt a file I run into the following exception:</p>
  
  <pre><code>com.example.common.crypto.CipherException: org.bouncycastle.openpgp.PGPException: Exception starting decryption
      at com.example.common.crypto.PGPFileCipher.decrypt(PGPFileCipher.java:151)
      at com.example.common.crypto.PGPFileCipherTest.testEncryptDecryptFile(PGPFileCipherTest.java:41)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:597)
      at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
      at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
      at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
      at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
      at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:43)
      at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
      at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
      at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
      at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
      at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
      at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
      at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
      at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
      at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
      at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
      at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)
      at org.apache.maven.surefire.Surefire.run(Surefire.java:177)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:597)
      at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:345)
      at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009)
  Caused by: org.bouncycastle.openpgp.PGPException: Exception starting decryption
      at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
      at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
      at com.example.common.crypto.PGPFileCipher.decrypt(PGPFileCipher.java:128)
      ... 28 more
  Caused by: java.io.EOFException: premature end of stream in PartialInputStream
      at org.bouncycastle.bcpg.BCPGInputStream$PartialInputStream.read(Unknown Source)
      at org.bouncycastle.bcpg.BCPGInputStream.read(Unknown Source)
      at java.io.InputStream.read(InputStream.java:82)
      at javax.crypto.CipherInputStream.a(DashoA13*..)
      at javax.crypto.CipherInputStream.read(DashoA13*..)
      at org.bouncycastle.bcpg.BCPGInputStream.read(Unknown Source)
      ... 31 more
  </code></pre>
  
  <p>My PGPFileCipher class is:</p>
  
  <pre><code>package com.example.common.crypto;
  
  public class PGPFileCipher implements FileCipher {
  
      private static final Logger logger = LoggerFactory.getLogger(PGPFileCipher.class);
  
      public File encryptFile(File inputFile, String encryptedFilePath, String publicKeyPath) throws CipherException {
          FileInputStream publicKeyStream = null;
          FileOutputStream encryptedFileOutputStream = null;
          try {
              publicKeyStream = new FileInputStream(new File(publicKeyPath));
              File encryptedFile = new File(encryptedFilePath);
              encryptedFileOutputStream = new FileOutputStream(encryptedFile);
              encryptedFileOutputStream.write(encrypt(getBytesFromFile(inputFile), publicKeyStream, ""));
              encryptedFileOutputStream.flush();
              return encryptedFile;
          } catch (Exception e) {
              throw new CipherException(e);
          } finally {
              IOUtils.closeQuietly(encryptedFileOutputStream);
              IOUtils.closeQuietly(publicKeyStream);
          }
      }
  
      private PGPPrivateKey findSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass) throws CipherException {
          try {
              PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
              if (pgpSecKey == null) {
                  return null;
              }
              return pgpSecKey.extractPrivateKey(pass, new BouncyCastleProvider());
          } catch (Exception e) {
              throw new CipherException(e);
          }
      }
  
      /**
       * decrypt the passed in message stream
       *
       * @param encrypted
       *            The message to be decrypted.
       * @param keyIn
       *            InputStream of the key
       *
       * @return Clear text as a byte array. I18N considerations are not handled
       *         by this routine
       * @exception CipherException
       */
      public byte[] decrypt(byte[] encrypted, InputStream keyIn, char[] password) throws CipherException {
          ByteArrayOutputStream out = null;
          InputStream clear = null;
          InputStream in = null;
          InputStream unc = null;
          try {
              in = new ByteArrayInputStream(encrypted);
  
              in = PGPUtil.getDecoderStream(in);
  
              PGPObjectFactory pgpF = new PGPObjectFactory(in);
              PGPEncryptedDataList enc = null;
              Object o = pgpF.nextObject();
  
              //
              // the first object might be a PGP marker packet.
              //
              if (o instanceof PGPEncryptedDataList) {
                  enc = (PGPEncryptedDataList) o;
              } else {
                  enc = (PGPEncryptedDataList) pgpF.nextObject();
              }
  
              //
              // find the secret key
              //
              Iterator it = enc.getEncryptedDataObjects();
              PGPPrivateKey sKey = null;
              PGPPublicKeyEncryptedData pbe = null;
              PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
                      PGPUtil.getDecoderStream(keyIn));
  
              while (sKey == null &amp;&amp; it.hasNext()) {
                  pbe = (PGPPublicKeyEncryptedData) it.next();
  
                  sKey = findSecretKey(pgpSec, pbe.getKeyID(), password);
              }
  
              if (sKey == null) {
                  throw new IllegalArgumentException(
                          "secret key for message not found.");
              }
  
              clear = pbe.getDataStream(sKey, new BouncyCastleProvider());
  
              PGPObjectFactory pgpFact = new PGPObjectFactory(clear);
  
              PGPCompressedData cData = (PGPCompressedData) pgpFact.nextObject();
  
              pgpFact = new PGPObjectFactory(cData.getDataStream());
  
              PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject();
  
              unc = ld.getInputStream();
  
              out = new ByteArrayOutputStream();
              int ch;
  
              while ((ch = unc.read()) &gt;= 0) {
                  out.write(ch);
  
              }
              out.flush();
              byte[] returnBytes = out.toByteArray();
              return returnBytes;
          } catch (Exception e) {
              e.printStackTrace();
              throw new CipherException(e);
          } finally {
              IOUtils.closeQuietly(out);
              IOUtils.closeQuietly(in);
              IOUtils.closeQuietly(clear);
              IOUtils.closeQuietly(unc);
          }
      }
  
      /**
       * Simple PGP encryptor between byte[].
       *
       * @param clearData
       *            The test to be encrypted
       * @param keyInputStream
       *            Input stream of the key
       * @param fileName
       *            File name. This is used in the Literal Data Packet (tag 11)
       *            which is really inly important if the data is to be related to
       *            a file to be recovered later. Because this routine does not
       *            know the source of the information, the caller can set
       *            something here for file name use that will be carried. If this
       *            routine is being used to encrypt SOAP MIME bodies, for
       *            example, use the file name from the MIME type, if applicable.
       *            Or anything else appropriate.
       *
       * @return encrypted data.
       * @exception CipherException
       */
      public byte[] encrypt(byte[] clearData, InputStream keyInputStream, String fileName)
              throws CipherException {
          OutputStream out = null;
          OutputStream cOut = null;
          PGPCompressedDataGenerator comData = null;
          PGPLiteralDataGenerator lData = null;
          ByteArrayOutputStream encOut = null;
          ByteArrayOutputStream bOut = null;
          OutputStream cos = null;
          OutputStream pOut = null;
          try {
              PGPPublicKey encKey = readPublicKey(keyInputStream);
              if (fileName == null || fileName.trim().length() &lt; 1) {
                  fileName = PGPLiteralData.CONSOLE;
              }
  
              encOut = new ByteArrayOutputStream();
  
              out = encOut;
  
              bOut = new ByteArrayOutputStream();
  
              comData = new PGPCompressedDataGenerator(
                      PGPCompressedDataGenerator.ZIP);
              cos = comData.open(bOut); // open it with the final
              // destination
              lData = new PGPLiteralDataGenerator();
  
              // we want to generate compressed data. This might be a user option
              // later,
              // in which case we would pass in bOut.
              pOut = lData.open(cos, // the compressed output stream
                      PGPLiteralData.BINARY, fileName, // "filename" to store
                      clearData.length, // length of clear data
                      new Date() // current time
              );
              pOut.write(clearData);
              pOut.flush();
              PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(
                      PGPEncryptedData.CAST5, false, new SecureRandom(),
                      new BouncyCastleProvider());
  
              cPk.addMethod(encKey);
  
              byte[] bytes = bOut.toByteArray();
  
              cOut = cPk.open(out, bytes.length);
  
              cOut.write(bytes); // obtain the actual bytes from the compressed stream
              cOut.flush();
              encOut.flush();
              return encOut.toByteArray();
          } catch (Exception e) {
              throw new CipherException(e);
          } finally {
              IOUtils.closeQuietly(out);
              IOUtils.closeQuietly(cOut);
              IOUtils.closeQuietly(encOut);
              IOUtils.closeQuietly(bOut);
              IOUtils.closeQuietly(cos);
              IOUtils.closeQuietly(pOut);
              try {
                  if (lData != null) {
                      lData.close();
                  }
                  if (comData != null) {
                      comData.close();
                  }
              } catch (IOException ignored) {}
          }
      }
  
      private PGPPublicKey readPublicKey(InputStream in)
              throws CipherException {
          try {
              in = PGPUtil.getDecoderStream(in);
  
              PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in);
  
              //
              // we just loop through the collection till we find a key suitable for
              // encryption, in the real
              // world you would probably want to be a bit smarter about this.
              //
  
              //
              // iterate through the key rings.
              //
              Iterator rIt = pgpPub.getKeyRings();
  
              while (rIt.hasNext()) {
                  PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next();
                  Iterator kIt = kRing.getPublicKeys();
  
                  while (kIt.hasNext()) {
                      PGPPublicKey k = (PGPPublicKey) kIt.next();
  
                      if (k.isEncryptionKey()) {
                          return k;
                      }
                  }
              }
              throw new IllegalArgumentException("Can't find encryption key in key ring.");
          } catch (Exception e) {
              throw new CipherException(e);
          }
      }
  
      private byte[] getBytesFromFile(File file) throws CipherException {
          InputStream is = null;
          try {
              is = new FileInputStream(file);
              // Get the size of the file
              long length = file.length();
  
              if (length &gt; Integer.MAX_VALUE) {
                  throw new CipherException("File is too large: " + file.getName());
              }
  
              return IOUtils.toByteArray(is);
          } catch (IOException e) {
              throw new CipherException(e);
          } finally {
              IOUtils.closeQuietly(is);
          }
      }
  }
  </code></pre>
  
  <p>And the test I'm attempting to run is:</p>
  
  <pre><code>package com.example.common.crypto;
  
  public class PGPFileCipherTest {
  
      private static final Logger logger = LoggerFactory.getLogger(PGPFileCipherTest.class);
  
      @Rule
      public TemporaryFolder folder = new TemporaryFolder();
  
      @Test
      public void testEncryptDecryptFile() throws IOException, CipherException {
          FileOutputStream decryptedFileOutputStream = null;
          InputStream privateKeyStream = null;
          try {
              PGPFileCipher fileCipher = new PGPFileCipher();
              Resource inputFile = new ClassPathResource("testInputFile.txt");
              Resource publicKey = new ClassPathResource("PUBLIC.asc");
              Resource privateKey = new ClassPathResource("PRIVATE.asc");
              // Encrypt file
              File encryptedFile = fileCipher.encryptFile(inputFile.getFile(), folder.newFile("testInputFile_enc.txt").getPath(), publicKey.getFile().getPath());
              // Now decrypt the file
              File decryptedFile = folder.newFile("testInputFile_dec.txt");
              decryptedFileOutputStream = new FileOutputStream(decryptedFile);
              privateKeyStream = new FileInputStream(privateKey.getFile());
              decryptedFileOutputStream.write(fileCipher.decrypt(FileUtils.readFileToByteArray(encryptedFile), privateKeyStream, "".toCharArray()));
              decryptedFileOutputStream.flush();
          } finally {
              IOUtils.closeQuietly(decryptedFileOutputStream);
              IOUtils.closeQuietly(privateKeyStream);
          }
      }
  }
  </code></pre>
  
  <p>Unfortunately I'm not all that familiar with the BouncyCastle and it would seem as if a stream is not properly being closed / flushed but I can't seem to track it down. This is a modified version of one of the BouncyCastle examples FYI. Thanks in advance for your help.</p>