Bouncycastle PGP decrypt and verify

女生的网名这么多〃 提交于 2019-12-03 07:33:27

I was recently trying to do the same kind of thing and put together this method based on code I found in the Bouncycastle examples and on tutorials I found on the web. For my purposes, my code has a singleton crypto object that has a public/private key pair. In the example code, you could replace

    INSTANCE._secretKeyRingCollection.getSecretKey(pbe.getKeyID());

with your secret key. I've tested this method with a long-lived process that did several tens of encrypt & sign / decrypt & verify actions and didn't get the exception you're seeing.

public static void decryptAndVerify(InputStream in, OutputStream fOut, InputStream publicKeyIn) throws IOException, SignatureException, PGPException {
    in = PGPUtil.getDecoderStream(in);

    PGPObjectFactory pgpF = new PGPObjectFactory(in);
    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();
        PBESecretKeyDecryptor decryptor = new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(INSTANCE._secretKeyPass.toCharArray());
        PGPSecretKey psKey = INSTANCE._secretKeyRingCollection.getSecretKey(pbe.getKeyID());
        if (psKey != null) {
            sKey = psKey.extractPrivateKey(decryptor);
        }
    }
    if (sKey == null) {
        throw new IllegalArgumentException("Unable to find secret key to decrypt the message");
    }

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

    PGPObjectFactory plainFact = new PGPObjectFactory(clear);

    Object message;

    PGPOnePassSignatureList onePassSignatureList = null;
    PGPSignatureList signatureList = null;
    PGPCompressedData compressedData;

    message = plainFact.nextObject();
    ByteArrayOutputStream actualOutput = new ByteArrayOutputStream();

    while (message != null) {
        __l.trace(message.toString());
        if (message instanceof PGPCompressedData) {
            compressedData = (PGPCompressedData) message;
            plainFact = new PGPObjectFactory(compressedData.getDataStream());
            message = plainFact.nextObject();
        }

        if (message instanceof PGPLiteralData) {
            // have to read it and keep it somewhere.
            Streams.pipeAll(((PGPLiteralData) message).getInputStream(), actualOutput);
        } else if (message instanceof PGPOnePassSignatureList) {
            onePassSignatureList = (PGPOnePassSignatureList) message;
        } else if (message instanceof PGPSignatureList) {
            signatureList = (PGPSignatureList) message;
        } else {
            throw new PGPException("message unknown message type.");
        }
        message = plainFact.nextObject();
    }
    actualOutput.close();
    PGPPublicKey publicKey = null;
    byte[] output = actualOutput.toByteArray();
    if (onePassSignatureList == null || signatureList == null) {
        throw new PGPException("Poor PGP. Signatures not found.");
    } else {

        for (int i = 0; i < onePassSignatureList.size(); i++) {
            PGPOnePassSignature ops = onePassSignatureList.get(0);
            __l.trace("verifier : " + ops.getKeyID());
            PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(
                    PGPUtil.getDecoderStream(publicKeyIn));
            publicKey = pgpRing.getPublicKey(ops.getKeyID());
            if (publicKey != null) {
                ops.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"), publicKey);
                ops.update(output);
                PGPSignature signature = signatureList.get(i);
                if (ops.verify(signature)) {
                    Iterator<?> userIds = publicKey.getUserIDs();
                    while (userIds.hasNext()) {
                        String userId = (String) userIds.next();
                        __l.trace(String.format("Signed by {%s}", userId));
                    }
                    __l.trace("Signature verified");
                } else {
                    throw new SignatureException("Signature verification failed");
                }
            }
        }

    }

    if (pbe.isIntegrityProtected() && !pbe.verify()) {
        throw new PGPException("Data is integrity protected but integrity is lost.");
    } else if (publicKey == null) {
        throw new SignatureException("Signature not found");
    } else {
        fOut.write(output);
        fOut.flush();
        fOut.close();
    }
}

Getting Bouncy Castle to play along is not always easy. Code-snipets from Stackoverflow make it work just so, but they are mostly arcane pieces of code that none of the users really understand.

The problem with this is: In a production system copy'n'paste snippets quickly become "no go" and "do not touch" areas.

Shelling out executables can have some serious security implications, least of all is handling command line parameters (talking about filenames with spaces ... how do I know? Don't ask ...)

I had all of these (and more..) problems, and after some yak shaving I wrote a library to handle PGP with Bouncycastle.

Decrypting works like this:

final InputStream plaintextStream = BouncyGPG
               .decryptAndVerifyStream()
               .withConfig(keyringConfig)
               .andRequireSignatureFromAllKeys("sender@example.com")
               .fromEncryptedInputStream(cipherTextStream)

The library can be found at https://github.com/neuhalje/bouncy-gpg :

// in build.gradle add a dependency to bouncy castle and bouncy-gpg
//  ...
dependencies {
    compile 'org.bouncycastle:bcprov-jdk15on:1.56'
    compile 'org.bouncycastle:bcpg-jdk15on:1.56'
    //  ...
    compile 'name.neuhalfen.projects.crypto.bouncycastle.openpgp:bouncy-gpg:2.+'

You are calling:

encryptedDataGenerator.open(out, 4096)

where you probably meant:

encryptedDataGenerator.open(out, new byte[4096])

The first version is giving a size to open (which is wrong), the second version is giving a byte buffer.

(I know this is old, but came here because I had the same problem with some example code and so might others)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!