java.io.EOFException:PartialInputStream中流的过早结束 - bouncy Castle

时间:2016-10-10 08:19:00

标签: java bouncycastle

我在尝试解密文件时遇到下面提到的异常。在这方面的任何帮助都将是非常有益的。代码已在下面添加以供参考。加密工作正常,因此解密,但是当我们有大文件时,加密工作但解密会给出下面提到的错误。

提前致谢

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)

加密/解密代码

公共类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 个答案:

答案 0 :(得分:0)

代码太复杂,不够完整,无法真正测试。但是,我注意到signEncryptFile中流关闭的顺序看起来不对。如果在签名和加密操作期间生成错误数据,则可能会导致decryptSignedFile方法失败。

signEncryptFile中看起来不对的特定序列是:

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

特别注意,在执行encryptedDataGenerator.close();之前执行compressedOut.close(); 。这意味着压缩缓冲区中的数据没有机会写入后续流,因为您已经关闭它。

我希望这种情况会导致compressedOut.close抛出异常,但我不确定。

我建议你仔细检查这个流闭包 ,以确保它完全符合你的期望。