1

I am getting below mentioned exception when trying to decrypt the file. Any help in this regard will be of great benefit. Code has been added below for reference. The Encryption is working fine so do is Decryption but when we have large files then Encryption works but Decryption gives below mentioned error.

Thanks in advance

Exception in thread "Main Thread" 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 org.bouncycastle.crypto.io.CipherInputStream.nextChunk(Unknown Source)
    at org.bouncycastle.crypto.io.CipherInputStream.read(Unknown Source)
    at org.bouncycastle.bcpg.BCPGInputStream.read(Unknown Source)
    at org.bouncycastle.openpgp.PGPEncryptedData$TruncatedStream.read(Unknown Source)
    at java.io.InputStream.read(InputStream.java:151)
    at org.bouncycastle.util.io.TeeInputStream.read(Unknown Source)
    at org.bouncycastle.bcpg.BCPGInputStream.read(Unknown Source)
    at org.bouncycastle.bcpg.BCPGInputStream$PartialInputStream.read(Unknown Source)
    at org.bouncycastle.bcpg.BCPGInputStream.read(Unknown Source)
    at org.bouncycastle.openpgp.PGPCompressedData$1.fill(Unknown Source)
    at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:141)
    at org.bouncycastle.bcpg.BCPGInputStream.read(Unknown Source)
    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)

Code for Encryption/Decryption

public class PGPUtils {

private static int BUFFER_SIZE = 1 << 24;
private static final int KEY_FLAGS = 27;
private static final int[] MASTER_KEY_CERTIFICATION_TYPES = new int[] { PGPSignature.POSITIVE_CERTIFICATION,
        PGPSignature.CASUAL_CERTIFICATION, PGPSignature.NO_CERTIFICATION, PGPSignature.DEFAULT_CERTIFICATION,

};

@SuppressWarnings("unchecked")
public static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException {

    PGPPublicKeyRingCollection keyRingCollection = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(in),
            new BcKeyFingerprintCalculator());

    //
    // 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.
    //
    PGPPublicKey publicKey = null;

    //
    // iterate through the key rings.
    //
    Iterator<PGPPublicKeyRing> rIt = keyRingCollection.getKeyRings();

    while (publicKey == null && rIt.hasNext()) {
        PGPPublicKeyRing kRing = rIt.next();
        Iterator<PGPPublicKey> kIt = kRing.getPublicKeys();
        while (publicKey == null && kIt.hasNext()) {
            PGPPublicKey key = kIt.next();
            if (key.isEncryptionKey()) {
                publicKey = key;
            }
        }
    }

    if (publicKey == null) {
        throw new IllegalArgumentException("Can't find public key in the key ring.");
    }
    if (!isForEncryption(publicKey)) {
        throw new IllegalArgumentException("KeyID " + publicKey.getKeyID() + " not flagged for encryption.");
    }
    in.close();
    return publicKey;
}

@SuppressWarnings("unchecked")
public static PGPSecretKey readSecretKey(InputStream in) throws IOException, PGPException {

    PGPSecretKeyRingCollection keyRingCollection = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(in),
            new BcKeyFingerprintCalculator());

    //
    // We just loop through the collection till we find a key suitable for
    // signing.
    // In the real world you would probably want to be a bit smarter about
    // this.
    //
    PGPSecretKey secretKey = null;

    Iterator<PGPSecretKeyRing> rIt = keyRingCollection.getKeyRings();
    while (secretKey == null && rIt.hasNext()) {
        PGPSecretKeyRing keyRing = rIt.next();
        Iterator<PGPSecretKey> kIt = keyRing.getSecretKeys();
        while (secretKey == null && kIt.hasNext()) {
            PGPSecretKey key = kIt.next();
            if (key.isSigningKey()) {
                secretKey = key;
            }
        }
    }

    // Validate secret key
    if (secretKey == null) {
        throw new IllegalArgumentException("Can't find private key in the key ring.");
    }
    if (!secretKey.isSigningKey()) {
        throw new IllegalArgumentException("Private key does not allow signing.");
    }
    if (secretKey.getPublicKey().isRevoked()) {
        throw new IllegalArgumentException("Private key has been revoked.");
    }
    if (!hasKeyFlags(secretKey.getPublicKey(), KeyFlags.SIGN_DATA)) {
        throw new IllegalArgumentException("Key cannot be used for signing.");
    }
    in.close();
    return secretKey;
}

/**
 * Load a secret key ring collection from keyIn and find the private key
 * corresponding to keyID if it exists.
 *
 * @param keyIn
 *            input stream representing a key ring collection.
 * @param keyID
 *            keyID we want.
 * @param pass
 *            passphrase to decrypt secret key with.
 * @return
 * @throws IOException
 * @throws PGPException
 * @throws NoSuchProviderException
 */
public static PGPPrivateKey findPrivateKey(InputStream keyIn, long keyID, char[] pass) throws IOException,
        PGPException, NoSuchProviderException {
    PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyIn),
            new BcKeyFingerprintCalculator());
    keyIn.close();
    return findPrivateKey(pgpSec.getSecretKey(keyID), pass);

}

/**
 * Load a secret key and find the private key in it
 * 
 * @param pgpSecKey
 *            The secret key
 * @param pass
 *            passphrase to decrypt secret key with
 * @return
 * @throws PGPException
 */
public static PGPPrivateKey findPrivateKey(PGPSecretKey pgpSecKey, char[] pass) throws PGPException {
    if (pgpSecKey == null)
        return null;

    PBESecretKeyDecryptor decryptor = new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider())
            .build(pass);
    return pgpSecKey.extractPrivateKey(decryptor);
}

/**
 * decrypt the passed in message stream
 */
@SuppressWarnings("unchecked")
public static void decryptFile(InputStream in, OutputStream out, InputStream keyIn, char[] passwd) throws Exception {
    getProvider();

    in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);

    PGPObjectFactory pgpF = new PGPObjectFactory(in, new BcKeyFingerprintCalculator());
    PGPEncryptedDataList enc;

    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<PGPPublicKeyEncryptedData> it = enc.getEncryptedDataObjects();
    PGPPrivateKey sKey = null;
    PGPPublicKeyEncryptedData pbe = null;

    while (sKey == null && it.hasNext()) {
        pbe = it.next();

        sKey = findPrivateKey(keyIn, pbe.getKeyID(), passwd);
    }

    if (sKey == null) {
        throw new IllegalArgumentException("Secret key for message not found.");
    }

    InputStream clear = pbe.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey));

    PGPObjectFactory plainFact = new PGPObjectFactory(clear, new BcKeyFingerprintCalculator());

    Object message = plainFact.nextObject();

    if (message instanceof PGPCompressedData) {
        PGPCompressedData cData = (PGPCompressedData) message;
        PGPObjectFactory pgpFact = new PGPObjectFactory(cData.getDataStream(), new BcKeyFingerprintCalculator());

        message = pgpFact.nextObject();
    }

    if (message instanceof PGPLiteralData) {
        PGPLiteralData ld = (PGPLiteralData) message;

        InputStream unc = ld.getInputStream();
        int ch;

        while ((ch = unc.read()) >= 0) {
            out.write(ch);
        }
    } else if (message instanceof PGPOnePassSignature) {
        System.out.println("Instance of PGPOnePassSignature");
        PGPLiteralData ld = (PGPLiteralData) message;

        InputStream unc = ld.getInputStream();
        int ch;

        while ((ch = unc.read()) >= 0) {
            out.write(ch);
        }
    }

    else if (message instanceof PGPOnePassSignatureList) {
        // throw new
        // PGPException("Encrypted message contains a signed message - not literal data.");

    } else {
        throw new PGPException("Message is not a simple encrypted file - type unknown.");
    }

    if (pbe.isIntegrityProtected()) {
        if (!pbe.verify()) {
            throw new PGPException("Message failed integrity check");
        }
    }
}

/**
 * decrypt the passed in signed message stream
 */
@SuppressWarnings("unchecked")
public static void decryptSignedFile(InputStream in, OutputStream out, InputStream keyIn, char[] passwd,
        InputStream signPublicKeyInputStream) throws Exception {
    getProvider();

    in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);

    // Reading a PGP object stream
    PGPObjectFactory pgpF = new PGPObjectFactory(in, new BcKeyFingerprintCalculator());

    PGPEncryptedDataList enc;

    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<PGPPublicKeyEncryptedData> it = enc.getEncryptedDataObjects();
    PGPPrivateKey sKey = null;
    PGPPublicKeyEncryptedData pbe = null;

    while (sKey == null && it.hasNext()) {
        pbe = it.next();

        sKey = findPrivateKey(keyIn, pbe.getKeyID(), passwd);
    }

    if (sKey == null) {
        throw new IllegalArgumentException("Secret key for message not found.");
    }
    keyIn.close();
    // A decryptor factory for handling public key decryption operations
    InputStream clear = pbe.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey));

    PGPObjectFactory plainFact = new PGPObjectFactory(clear, new BcKeyFingerprintCalculator());

    PGPObjectFactory objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(in),
            new BcKeyFingerprintCalculator());
    Object message = plainFact.nextObject();

    if (message instanceof PGPCompressedData) {
        System.out.println("PGPCompressedData");

        PGPCompressedData cData = (PGPCompressedData) message;
        objectFactory = new PGPObjectFactory(cData.getDataStream(), new BcKeyFingerprintCalculator());

        message = objectFactory.nextObject();
    }

    if (message instanceof PGPOnePassSignature) {
        System.out.println("Instance of PGPOnePassSignature");
        PGPLiteralData ld = (PGPLiteralData) message;

        InputStream unc = ld.getInputStream();
        int ch;

        while ((ch = unc.read()) >= 0) {

            out.write(ch);
        }
        unc.close();

    }

    if (message instanceof PGPOnePassSignatureList) {

        PGPOnePassSignature calculatedSignature = ((PGPOnePassSignatureList) message).get(0);

        PGPPublicKey signPublicKey = readPublicKey(signPublicKeyInputStream);

        calculatedSignature.init(new BcPGPContentVerifierBuilderProvider(), signPublicKey);
        message = objectFactory.nextObject();

    } else {
        throw new PGPException("Message is not a simple encrypted file - type unknown.");
    }

    if (message instanceof PGPLiteralData) {
        PGPLiteralData ld = (PGPLiteralData) message;

        InputStream uncPGLiteral = null;
        try {
            uncPGLiteral = ld.getDataStream();
            int ch1;

            byte[] buffDecrypt = new byte[PGPUtils.BUFFER_SIZE];
            int len;
            IOUtils.copy(uncPGLiteral, out);
            // while ((len = uncPGLiteral.read(buffDecrypt)) > 0) {
            // out.write(buffDecrypt, 0, len);
            //
            // }
        } finally {
            if (uncPGLiteral != null) {
                System.out.println("Going to close the input stream");
                uncPGLiteral.close();
                out.close();

            }
        }

    }
    if (pbe.isIntegrityProtected()) {
        if (!pbe.verify()) {
            throw new PGPException("Message failed integrity check");
        }
    }
    signPublicKeyInputStream.close();
    clear.close();
    in.close();
}

public static void encryptFile(OutputStream out, String fileName, PGPPublicKey encKey, boolean armor,
        boolean withIntegrityCheck) throws IOException, NoSuchProviderException, PGPException {
    getProvider();

    if (armor) {
        out = new ArmoredOutputStream(out);
    }

    ByteArrayOutputStream bOut = new ByteArrayOutputStream();
    PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);

    PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY, new File(fileName));

    comData.close();

    BcPGPDataEncryptorBuilder dataEncryptor = new BcPGPDataEncryptorBuilder(PGPEncryptedData.TRIPLE_DES);
    dataEncryptor.setWithIntegrityPacket(withIntegrityCheck);
    dataEncryptor.setSecureRandom(new SecureRandom());

    PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator(dataEncryptor);
    encryptedDataGenerator.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(encKey));

    byte[] bytes = bOut.toByteArray();
    System.out.println("length is>>>>>>>" + bytes.length);
    OutputStream cOut = encryptedDataGenerator.open(out, bytes.length);
    cOut.write(bytes);
    cOut.close();
    out.close();
}

@SuppressWarnings("unchecked")
public static OutputStream signEncryptFile(String fileName, PGPPublicKey publicKey, PGPSecretKey secretKey,
        String password, boolean armor, boolean withIntegrityCheck) throws Exception {
    System.out.println("fileName:::" + fileName);

    // Initialize Bouncy Castle security provider
    getProvider();
    OutputStream compressedOut = null;
    try {
        File inputFile = new File(fileName);

        String outputFileName = fileName.concat(".pgp");
        OutputStream out = new FileOutputStream(outputFileName);

        if (armor) {
            out = new ArmoredOutputStream(out);
        }

        // Bouncy Castle lightweight API to implement cryptographic
        // primitives. Constructs a new data encryptor builder for a
        // specified cipher type
        BcPGPDataEncryptorBuilder dataEncryptor = new BcPGPDataEncryptorBuilder(PGPEncryptedData.TRIPLE_DES);
        // Sets whether or not the resulting encrypted data will be
        // protected using an integrity packet i.e withIntegrityPacket -
        // true if an integrity packet is to be included, false otherwise
        dataEncryptor.setWithIntegrityPacket(withIntegrityCheck);
        dataEncryptor.setSecureRandom(new SecureRandom());

        // A PGPEncryptedDataGenerator is used by configuring one or more
        // encryption methods, and then invoking one of the open functions
        // to create an OutputStream that raw data can be supplied to for
        // encryption
        PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator(dataEncryptor);
        // Add a key encryption method to be used to encrypt the session
        // data associated with this encrypted data
        encryptedDataGenerator.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(publicKey));

        // Create an OutputStream based on the configured methods to write a
        // single encrypted object of known length
        OutputStream encryptedOut = encryptedDataGenerator.open(out, new byte[BUFFER_SIZE]);

        // Generator for producing compressed data packets. Initialize
        // compressed data generator
        PGPCompressedDataGenerator compressedDataGenerator = new PGPCompressedDataGenerator(
                PGPCompressedData.UNCOMPRESSED);
        compressedOut = compressedDataGenerator.open(encryptedOut);

        // Initialize signature generator
        PGPPrivateKey privateKey = findPrivateKey(secretKey, password.toCharArray());

        // Initialize Content Sign Builder parameters are {int keyAlgorithm,
        // int hashAlgorithm}
        PGPContentSignerBuilder signerBuilder = new BcPGPContentSignerBuilder(secretKey.getPublicKey()
                .getAlgorithm(), HashAlgorithmTags.SHA1);

        // Create a signature generator built on the passed in
        // contentSignerBuilder
        PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(signerBuilder);
        // Initialise the generator for signing
        signatureGenerator.init(PGPSignature.STAND_ALONE, privateKey);

        boolean firstTime = true;
        Iterator<String> it = secretKey.getPublicKey().getUserIDs();
        while (it.hasNext() && firstTime) {
            PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
            spGen.setSignerUserID(false, it.next());
            signatureGenerator.setHashedSubpackets(spGen.generate());
            // Exit the loop after the first iteration
            firstTime = false;
        }
        signatureGenerator.generateOnePassVersion(false).encode(compressedOut);

        // Generator for producing literal data packets.A
        // PGPLiteralDataGenerator is usually used to wrap the OutputStream
        // obtained from a PGPEncryptedDataGenerator or a
        // PGPCompressedDataGenerator. Initialize literal data generator
        PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator();
        OutputStream literalOut = literalDataGenerator.open(compressedOut, PGPLiteralData.UTF8, fileName,
                inputFile.length(), new Date());

        // Main loop - read the "in" stream, compress, encrypt and write to
        // the
        // "out" stream
        InputStream in = new FileInputStream(fileName);
        byte[] buf = new byte[BUFFER_SIZE];
        int len;
        while ((len = in.read(buf)) > 0) {
            literalOut.write(buf, 0, len);
            signatureGenerator.update(buf, 0, len);
        }

        in.close();
        literalOut.close();
        literalDataGenerator.close();
        // Generate the signature, compress, encrypt and write to the "out"
        // stream

        signatureGenerator.generate();

        compressedOut.close();
        compressedDataGenerator.close();
        encryptedOut.close();
        encryptedDataGenerator.close();

        if (armor) {
            out.close();
        }
    } catch (Exception e) {
        System.out.println("Error is::" + e.getMessage());
    }

    return compressedOut;
}

public static boolean verifyFile(InputStream in, InputStream keyIn, String extractContentFile) throws Exception {
    in = PGPUtil.getDecoderStream(in);

    PGPObjectFactory pgpFact = new PGPObjectFactory(in, new BcKeyFingerprintCalculator());
    PGPCompressedData c1 = (PGPCompressedData) pgpFact.nextObject();

    pgpFact = new PGPObjectFactory(c1.getDataStream(), new BcKeyFingerprintCalculator());

    PGPOnePassSignatureList p1 = (PGPOnePassSignatureList) pgpFact.nextObject();

    PGPOnePassSignature ops = p1.get(0);

    PGPLiteralData p2 = (PGPLiteralData) pgpFact.nextObject();

    InputStream dIn = p2.getInputStream();

    IOUtils.copy(dIn, new FileOutputStream(extractContentFile));

    int ch;
    PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(keyIn),
            new BcKeyFingerprintCalculator());

    PGPPublicKey key = pgpRing.getPublicKey(ops.getKeyID());

    FileOutputStream out = new FileOutputStream(p2.getFileName());

    ops.init(new BcPGPContentVerifierBuilderProvider(), key);

    while ((ch = dIn.read()) >= 0) {
        ops.update((byte) ch);
        out.write(ch);
    }

    out.close();

    PGPSignatureList p3 = (PGPSignatureList) pgpFact.nextObject();
    return ops.verify(p3.get(0));
}

/**
 * From LockBox Lobs PGP Encryption tools.
 * http://www.lockboxlabs.org/content/downloads
 *
 * I didn't think it was worth having to import a 4meg lib for three methods
 * 
 * @param key
 * @return
 */
public static boolean isForEncryption(PGPPublicKey key) {
    if (key.getAlgorithm() == PublicKeyAlgorithmTags.RSA_SIGN || key.getAlgorithm() == PublicKeyAlgorithmTags.DSA
            || key.getAlgorithm() == PublicKeyAlgorithmTags.EC
            || key.getAlgorithm() == PublicKeyAlgorithmTags.ECDSA) {
        return false;
    }

    return hasKeyFlags(key, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
}

/**
 * From LockBox Lobs PGP Encryption tools.
 * http://www.lockboxlabs.org/content/downloads
 *
 * I didn't think it was worth having to import a 4meg lib for three methods
 * 
 * @param key
 * @return
 */
@SuppressWarnings("unchecked")
private static boolean hasKeyFlags(PGPPublicKey encKey, int keyUsage) {
    System.out.println("In hasKeyFlags+" + encKey.isMasterKey());
    if (encKey.isMasterKey()) {
        System.out.println("In Here" + PGPUtils.MASTER_KEY_CERTIFICATION_TYPES.length);
        for (int i = 0; i != PGPUtils.MASTER_KEY_CERTIFICATION_TYPES.length; i++) {
            for (Iterator<PGPSignature> eIt = encKey
                    .getSignaturesOfType(PGPUtils.MASTER_KEY_CERTIFICATION_TYPES[i]); eIt.hasNext();) {
                PGPSignature sig = eIt.next();
                if (!isMatchingUsage(sig, keyUsage)) {
                    return false;
                }
            }
        }
    } else {
        for (Iterator<PGPSignature> eIt = encKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING); eIt.hasNext();) {
            PGPSignature sig = eIt.next();
            if (!isMatchingUsage(sig, keyUsage)) {
                return false;
            }
        }
    }
    return true;
}

/**
 * From LockBox Lobs PGP Encryption tools.
 * http://www.lockboxlabs.org/content/downloads
 *
 * I didn't think it was worth having to import a 4meg lib for three methods
 * 
 * @param key
 * @return
 */
private static boolean isMatchingUsage(PGPSignature sig, int keyUsage) {
    System.out.println("isMatchingUsage::");
    if (sig.hasSubpackets()) {
        PGPSignatureSubpacketVector sv = sig.getHashedSubPackets();
        if (sv.hasSubpacket(PGPUtils.KEY_FLAGS)) {
            // code fix suggested by kzt (see comments)
            if ((sv.getKeyFlags() == 0) && (keyUsage == 0)) {
                return false;
            }
        }
    }
    return true;
}

private static Provider getProvider() {
    Provider provider = Security.getProvider("BC");
    if (provider == null) {
        provider = new BouncyCastleProvider();
        Security.addProvider(provider);
    }
    return provider;
}

}

1 Answers1

0

The code is too complicated and not complete enough to really test. However, I did notice that the order of stream closures in signEncryptFile looks wrong. If you generate bad data during the sign and encrypt operation then you can expect your decryptSignedFile method to fail.

The particular sequence in signEncryptFile that looks wrong is:

signatureGenerator.generate();
compressedDataGenerator.close();
encryptedDataGenerator.close();
compressedOut.close();
encryptedOut.close();

In particular, notice that you perform encryptedDataGenerator.close(); before you perform compressedOut.close();. This means that data in the compression buffers doesn't have a chance to be written out to the succeeding stream because you've already closed it.

I would expect that this situation would cause compressedOut.close to throw an Exception, but I'm not sure.

I recommend you re-examine this stream closures very carefully to make sure it is doing exactly what you expect.

President James K. Polk
  • 40,516
  • 21
  • 95
  • 125