I am trying to implement PGP encryption in Java using Bouncy Castle, also using some of the examples provided by them.
But if I try and decrypt the message I just encrypted, it does not work. The keys and the decryption method seem to be ok, because I can encrypt using an external tool (link) and then successfully decrypt it here.
public static void main(String[] args) throws PGPException, IOException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { Security.addProvider(new BouncyCastleProvider()); byte[] msg = ("Test blabla BLA").getBytes(); byte[] encData = encrypt(msg, publicKey); System.out.println(new String(encData)); System.err.println("DECRYPTED"); System.out.println(decrypt(IOUtils.toInputStream(new String(encData), StandardCharsets.UTF_8), privateKey, passphrase)); System.err.println("DECRYPTED external"); String testEncryptedMsg = "-----BEGIN PGP MESSAGE-----n" + "Version: BCPG C# v1.6.1.0n" + "n" + "hQIMA4p9BgtBOg+BARAAiLdmGZBupsSkrji1qdC/y6KACuaWytcjVg3LWudWRlH8n" + "Ye1U0bz+HYc6mLt21RivAZ3p+lPXPzh/h0GVLW/aI2ngJracL+iEA7eam5/H3NQVn" + "2o0CR1mjw4xOf0XwDxaLrnivdeuWniXiZyFN7Ud7lmNG+EmQ0d5ZfR7KEnTJNjSen" + "FpzwRffBG5lKB4CxF49jBp+Iupdv4A5JzHuDVBkypPMNhS/UEbuPOt5cfmddYVvWn" + "gtDM3rX+ePBhg7d/mVkiDT2KM0Cr7GSrdZ8q83fF4sat3Xp8OHMq61GWriNK4gwbn" + "ViT/vmEsY0n55TVgN5VagMZJJlEThkqWE2wpEjq4HM8pNxR4PvP5inqP5oipn8Osn" + "Nq2a5f77BS7GB9NQ+hnaLSBsywJkZVKqygN81MeHBnNrVXFQ/Hqg24BLwk49bOuHn" + "jTUzorudCnjj/p8/WiyR+PuESkQDLKpo1z9KGEE/rKjTqAwH/fBKJ73XpmAWwd4Un" + "1hv/EdF+XDZv7XwxeVjSIK2inAjdNz+way1WNY3qouo7G+gjKf1gbL14q2HVPnd/n" + "7mfiW3uWCQ7GhXlHtd1E0WeK2296TdBuSGAvLlVxI4xepsiuVw+zC4BxvAkTXB/an" + "HyJZhKPksHMRCt8ZxitMfhdvMNGjc4MDhv1MLqKijpagKhgrmDTiJRMb3ng6QLvJn" + "L2GICyuS4aIsz6XiCQvZG8ebfb0J4uogbb5HxS2fGFT+Y/VgpCnwhwj6LnLBy1Q3n" + "=D/1dn" + "-----END PGP MESSAGE-----n"; System.out.println(decrypt(IOUtils.toInputStream(testEncryptedMsg, StandardCharsets.UTF_8), privateKey, passphrase)); }
I think the problem is on the encryption side, but I cannot figure out what exactly it is. Does anyone have any ideas?
public static byte[] encrypt(byte[] bytes, String publicKey) throws IOException { ByteArrayOutputStream encOut = new ByteArrayOutputStream(); try { PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator( new JcePGPDataEncryptorBuilder(PGPEncryptedData.AES_128) .setSecureRandom(new SecureRandom()) .setWithIntegrityPacket(true) .setProvider("BC")); PGPPublicKey encryptionKey = readPublicKey(publicKey); encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encryptionKey).setProvider("BC")); ArmoredOutputStream aOut = new ArmoredOutputStream(encOut); OutputStream cOut = encGen.open(aOut, bytes.length); // write out the literal data PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); OutputStream pOut = lData.open(aOut, PGPLiteralData.UTF8, PGPLiteralData.CONSOLE, bytes.length, new Date()); pOut.write(bytes); pOut.close(); // finish the encryption cOut.close(); aOut.close(); } catch (PGPException e) { log.error("Exception encountered while encoding data.", e); } return encOut.toByteArray(); } public static String decrypt(InputStream in, String secretKey, String passphrase) throws IOException { String content = null; try { in = PGPUtil.getDecoderStream(in); JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(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<PGPEncryptedData> it = enc.getEncryptedDataObjects(); PGPPrivateKey sKey = null; PGPPublicKeyEncryptedData pbe = null; PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(new ByteArrayInputStream(secretKey.getBytes())), new JcaKeyFingerprintCalculator()); while (sKey == null && it.hasNext()) { pbe = (PGPPublicKeyEncryptedData) it.next(); sKey = findSecretKey(pgpSec, pbe.getKeyID(), passphrase.toCharArray()); } if (sKey == null) { log.error("Secret key for message not found."); throw new IllegalArgumentException("secret key for message not found."); } InputStream clear = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC").build(sKey)); JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear); Object message = plainFact.nextObject(); if (message instanceof PGPCompressedData) { PGPCompressedData cData = (PGPCompressedData) message; JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(cData.getDataStream()); message = pgpFact.nextObject(); } if (message instanceof PGPLiteralData) { PGPLiteralData ld = (PGPLiteralData) message; content = new String(ld.getInputStream().readAllBytes(), StandardCharsets.UTF_8); } 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()) { log.error("message failed integrity check"); } else { log.info("message integrity check passed"); } } else { log.warn("no message integrity check"); } } catch (PGPException e) { log.error("Exception encountered while decrypting file.", e); } return content; } private static PGPPrivateKey findSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass) throws PGPException { PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID); if (pgpSecKey == null) { return null; } return pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(pass)); } private static PGPPublicKey readPublicKey(String publicKey) throws IOException, PGPException { InputStream input = IOUtils.toInputStream(publicKey, StandardCharsets.UTF_8); PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator()); Iterator<PGPPublicKeyRing> keyRingIter = pgpPub.getKeyRings(); while (keyRingIter.hasNext()) { PGPPublicKeyRing keyRing = keyRingIter.next(); Iterator<PGPPublicKey> keyIter = keyRing.getPublicKeys(); while (keyIter.hasNext()) { PGPPublicKey key = keyIter.next(); if (key.isEncryptionKey()) { return key; } } } throw new IllegalArgumentException("Can't find encryption key in key ring."); }
Advertisement
Answer
Here is the encryption method that eventually worked for me. It helped debugging together with the examples provided by the BCFipsIn100 book.
public byte[] encrypt(byte[] bytes, String publicKey) throws IOException { try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { byte[] compressedData = compress(bytes); PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator( new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5) .setWithIntegrityPacket(true) .setSecureRandom(new SecureRandom()) .setProvider(PROVIDER)); PGPPublicKey encryptionKey = readPublicKey(publicKey); encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encryptionKey).setProvider(PROVIDER)); ArmoredOutputStream aOut = new ArmoredOutputStream(byteArrayOutputStream); OutputStream cOut = encGen.open(aOut, compressedData.length); cOut.write(compressedData); cOut.close(); aOut.close(); return byteArrayOutputStream.toByteArray(); } catch (PGPException e) { log.error("Exception encountered while encoding data.", e); } return new byte[0]; } private static byte[] compress(byte[] clearData) throws IOException { try (ByteArrayOutputStream bOut = new ByteArrayOutputStream()) { PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP); OutputStream cos = comData.open(bOut); PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator(); OutputStream pOut = lData.open(cos, PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, clearData.length, new Date()); pOut.write(clearData); pOut.close(); comData.close(); return bOut.toByteArray(); } }