Bouncycastle PGP decrypt and verify

后端 未结 3 1109
忘掉有多难
忘掉有多难 2020-12-31 11:24

I\'m trying to decrypt and verify a PGP message using the java BouncyCastle libraries, but am running into issues, complaining about premature ends of PartialInputStream.

相关标签:
3条回答
  • 2020-12-31 12:03

    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)

    0 讨论(0)
  • 2020-12-31 12:05

    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();
        }
    }
    
    0 讨论(0)
  • 2020-12-31 12:18

    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.+'
    
    0 讨论(0)
提交回复
热议问题