/** * Opens a new {@link Decryptor} (Reading Step 1/3) * * <p>This is the first step in opening a ghostryde file. After this method, you'll want to * call {@link #openDecompressor(Decryptor)}. * * @param input is an {@link InputStream} of the ghostryde file data. * @param privateKey is the private encryption key of the recipient (which is us!) * @throws IOException * @throws PGPException */ @CheckReturnValue public Decryptor openDecryptor(@WillNotClose InputStream input, PGPPrivateKey privateKey) throws IOException, PGPException { checkNotNull(privateKey, "privateKey"); PGPObjectFactory fact = new BcPGPObjectFactory(checkNotNull(input, "input")); PGPEncryptedDataList crypts = pgpCast(fact.nextObject(), PGPEncryptedDataList.class); checkState(crypts.size() > 0); if (crypts.size() > 1) { logger.warningfmt("crypts.size() is %d (should be 1)", crypts.size()); } PGPPublicKeyEncryptedData crypt = pgpCast(crypts.get(0), PGPPublicKeyEncryptedData.class); if (crypt.getKeyID() != privateKey.getKeyID()) { throw new PGPException(String.format( "Message was encrypted for keyid %x but ours is %x", crypt.getKeyID(), privateKey.getKeyID())); } return new Decryptor( crypt.getDataStream(new BcPublicKeyDataDecryptorFactory(privateKey)), crypt); }
/** * Builds a symmetric-encryption decryptor for the specified passphrase. */ protected PublicKeyDataDecryptorFactory buildPublicKeyDecryptor( Subkey subkey) throws PGPException { PGPPrivateKey privateKey = subkey.getPrivateKey(); if (privateKey == null) throw new PGPException("no private key for " + subkey); return new BcPublicKeyDataDecryptorFactory(privateKey); }
@Test public void testEncryptDecrypt_ExplicitStyle() throws Exception { int bufferSize = 64 * 1024; // Alice loads Bob's "publicKey" into memory. PGPPublicKeyRing publicKeyRing = new BcPGPPublicKeyRing(PUBLIC_KEY); PGPPublicKey publicKey = publicKeyRing.getPublicKey(); // Alice encrypts the secret message for Bob using his "publicKey". PGPEncryptedDataGenerator encryptor = new PGPEncryptedDataGenerator( new BcPGPDataEncryptorBuilder(AES_128)); encryptor.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(publicKey)); byte[] encryptedData; try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { try (OutputStream output2 = encryptor.open(output, new byte[bufferSize])) { output2.write(FALL_OF_HYPERION_A_DREAM.getBytes(UTF_8)); } encryptedData = output.toByteArray(); } logger.info("Encrypted data: " + dumpHex(encryptedData)); // Bob loads his "privateKey" into memory. PGPSecretKeyRing privateKeyRing = new BcPGPSecretKeyRing(PRIVATE_KEY); PGPPrivateKey privateKey = extractPrivateKey(privateKeyRing.getSecretKey()); // Bob decrypt's the OpenPGP message (w/ ciphertext) using his "privateKey". try (ByteArrayInputStream input = new ByteArrayInputStream(encryptedData)) { PGPObjectFactory pgpFact = new BcPGPObjectFactory(input); PGPEncryptedDataList encDataList = (PGPEncryptedDataList) pgpFact.nextObject(); assertThat(encDataList.size()).isEqualTo(1); PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) encDataList.get(0); assertThat(encData.getKeyID()).isEqualTo(publicKey.getKeyID()); assertThat(encData.getKeyID()).isEqualTo(privateKey.getKeyID()); try (InputStream original = encData.getDataStream(new BcPublicKeyDataDecryptorFactory(privateKey))) { assertThat(CharStreams.toString(new InputStreamReader(original, UTF_8))) .isEqualTo(FALL_OF_HYPERION_A_DREAM); } } }
/** * Decrypts the message and verifies its signature and integrity packet. * * @param encrypted the encrypted message body * @return the decrypted message body * @throws CryptographicException if any error occurs while processing the message. This should * be taken as an indicator that the message has been tampered * with or is invalid, and that retrying the operation would be * pointless. */ public byte[] read(InputStream encrypted) throws CryptographicException { try { final PGPPublicKeyEncryptedData encryptedData = getEncryptedData(encrypted); final InputStream decryptedData = encryptedData.getDataStream(new BcPublicKeyDataDecryptorFactory(recipient.getUnlockedSubKey().getPrivateKey())); final InputStream decompressedData = getCompressedData(decryptedData); final PGPObjectFactory factory = getFactory(decompressedData); final PGPOnePassSignature signature = getOnePassSignature(signer, factory); signature.init(new BcPGPContentVerifierBuilderProvider(), signer.getMasterKey().getPublicKey()); final InputStream body = getLiteralData(factory); final ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] b = new byte[BUFFER_SIZE]; int r; while ((r = body.read(b)) >= 0) { output.write(b, 0, r); signature.update(b, 0, r); } if (!signature.verify(getSignature(signer, factory))) { throw new CryptographicException("Invalid signature"); } if (!encryptedData.verify()) { throw new CryptographicException("Integrity check failed"); } return output.toByteArray(); } catch (IOException | ClassCastException | GeneralSecurityException | PGPException e) { throw new CryptographicException(e); } }
private PGPPublicKeyEncryptedData getEncryptedData(InputStream input) throws IOException, CryptographicException, IllegalArgumentException, NoSuchProviderException, PGPException { final PGPObjectFactory factory = getFactory(input); final PGPEncryptedDataList encryptedDataList = (PGPEncryptedDataList) factory.nextObject(); for (int i = 0, size = encryptedDataList.size(); i < size; i++) { final PGPEncryptedData encryptedData = (PGPEncryptedData) encryptedDataList.get(i); if (encryptedData instanceof PGPPublicKeyEncryptedData) { final PGPPublicKeyEncryptedData pkEncryptedData = (PGPPublicKeyEncryptedData) encryptedData; if (pkEncryptedData.getKeyID() == recipient.getSubKey().getKeyID()) { final SymmetricAlgorithm symmetricAlgorithm = Flags.fromInt( SymmetricAlgorithm.class, pkEncryptedData.getSymmetricAlgorithm(new BcPublicKeyDataDecryptorFactory(recipient.getUnlockedSubKey().getPrivateKey())) ); if (!SymmetricAlgorithm.ACCEPTABLE_ALGORITHMS.contains(symmetricAlgorithm)) { throw new CryptographicException("Data is encrypted with " + symmetricAlgorithm + " which is unacceptable"); } if (!pkEncryptedData.isIntegrityProtected()) { throw new CryptographicException("Missing integrity packet"); } return pkEncryptedData; } } } throw new CryptographicException("No encrypted data for " + recipient + " found"); }
@Test public void encryptAndDecrypt() throws Exception { // both keys have property encryptionKey==true final String[] keyIds = { "d7a92a24aa97ddbd", // master-key "a58da7d810b74edf" // sub-key }; for (final String keyId : keyIds) { final PGPDataEncryptorBuilder encryptorBuilder = new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.TWOFISH); final PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator(encryptorBuilder); final PGPKeyEncryptionMethodGenerator keyEncryptionMethodGenerator = new BcPublicKeyKeyEncryptionMethodGenerator( getPgpPublicKeyOrFail(bytesToLong(decodeHexStr(keyId)))); encryptedDataGenerator.addMethod(keyEncryptionMethodGenerator); final byte[] plain = new byte[1 + random.nextInt(1024 * 1024)]; random.nextBytes(plain); final File encryptedFile = File.createTempFile("encrypted_", ".tmp"); try (final OutputStream encryptedOut = new FileOutputStream(encryptedFile);) { try (final OutputStream plainOut = encryptedDataGenerator.open(encryptedOut, new byte[1024 * 16]);) { plainOut.write(plain); } } final byte[] decrypted; try (InputStream in = new FileInputStream(encryptedFile)) { final PGPEncryptedDataList encryptedDataList = new PGPEncryptedDataList(new BCPGInputStream(in)); final Iterator<?> encryptedDataObjects = encryptedDataList.getEncryptedDataObjects(); assertThat(encryptedDataObjects.hasNext()).isTrue(); final PGPPublicKeyEncryptedData encryptedData = (PGPPublicKeyEncryptedData) encryptedDataObjects.next(); assertThat(encryptedDataObjects.hasNext()).isFalse(); final PublicKeyDataDecryptorFactory dataDecryptorFactory = new BcPublicKeyDataDecryptorFactory( getPgpPrivateKeyOrFail(encryptedData.getKeyID(), "test12345".toCharArray())); try (InputStream plainIn = encryptedData.getDataStream(dataDecryptorFactory);) { final ByteArrayOutputStream out = new ByteArrayOutputStream(); transferStreamData(plainIn, out); decrypted = out.toByteArray(); } } assertThat(decrypted).isEqualTo(plain); encryptedFile.delete(); // delete it, if this test did not fail } }