/** * 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); }
@Test public void testCompressDecompress() throws Exception { // Compress the data and write out a compressed data OpenPGP message. byte[] data; try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { PGPCompressedDataGenerator kompressor = new PGPCompressedDataGenerator(ZIP); try (OutputStream output2 = kompressor.open(output)) { output2.write(FALL_OF_HYPERION_A_DREAM.getBytes(UTF_8)); } data = output.toByteArray(); } logger.info("Compressed data: " + dumpHex(data)); // Decompress the data. try (ByteArrayInputStream input = new ByteArrayInputStream(data)) { PGPObjectFactory pgpFact = new BcPGPObjectFactory(input); PGPCompressedData object = (PGPCompressedData) pgpFact.nextObject(); InputStream original = object.getDataStream(); // Closing this would close input. assertThat(CharStreams.toString(new InputStreamReader(original, UTF_8))) .isEqualTo(FALL_OF_HYPERION_A_DREAM); assertThat(pgpFact.nextObject()).isNull(); } }
private List<PGPPublicKeyRing> readKeysToAdd(GpgKeysInput input, Set<Fingerprint> toRemove) throws BadRequestException, IOException { if (input.add == null || input.add.isEmpty()) { return ImmutableList.of(); } List<PGPPublicKeyRing> keyRings = new ArrayList<>(input.add.size()); for (String armored : input.add) { try (InputStream in = new ByteArrayInputStream(armored.getBytes(UTF_8)); ArmoredInputStream ain = new ArmoredInputStream(in)) { @SuppressWarnings("unchecked") List<Object> objs = Lists.newArrayList(new BcPGPObjectFactory(ain)); if (objs.size() != 1 || !(objs.get(0) instanceof PGPPublicKeyRing)) { throw new BadRequestException("Expected exactly one PUBLIC KEY BLOCK"); } PGPPublicKeyRing keyRing = (PGPPublicKeyRing) objs.get(0); if (toRemove.contains(new Fingerprint(keyRing.getPublicKey().getFingerprint()))) { throw new BadRequestException( "Cannot both add and delete key: " + keyToString(keyRing.getPublicKey())); } keyRings.add(keyRing); } catch (PGPRuntimeOperationException e) { throw new BadRequestException("Failed to parse GPG keys", e); } } return keyRings; }
public OpenPGPSecretKey(String keyId, InputStream secretKeyRing, char[] password) throws IOException { PGPObjectFactory pgpObjectFactory = new BcPGPObjectFactory(PGPUtil.getDecoderStream(secretKeyRing)); for (Object it = pgpObjectFactory.nextObject(); it != null; it = pgpObjectFactory.nextObject()) { PGPSecretKeyRing pgpSecretKeyRing = (PGPSecretKeyRing) it; PGPSecretKey pgpSecretKey = pgpSecretKeyRing.getSecretKey(); if (keyId == null || keyId.isEmpty() || Long.valueOf(keyId, 16) == (pgpSecretKey.getKeyID() & MASK)) { this.secretKey = pgpSecretKey; break; } } // sanity check if (secretKey == null) { throw new IllegalArgumentException("Secret key " + keyId + " not found"); } this.password = password; }
@Test public void testPackageSignature() throws IOException, PackagingException, PGPException, SignatureException, org.bouncycastle.openpgp.PGPException, NoSuchProviderException { final File debFile = createPackage(ImmutableList.<Resource>of( new StringResource("hello world", true, "/tmp/test.txt", USER, USER, TarEntry.DEFAULT_FILE_MODE) )); final File packageDir = temporaryFolder.newFolder(); ArchiveUtils.extractAr(debFile, packageDir); final File pgpSignatureFile = new File(packageDir, "_gpgorigin"); assertTrue(pgpSignatureFile.exists()); try (final InputStream keyringIn = PGPUtil.getDecoderStream(PackageBuilderTest.class.getResourceAsStream("public.asc"))) { try (final InputStream signatureIn = PGPUtil.getDecoderStream(new FileInputStream(pgpSignatureFile))) { final PGPPublicKey publicKey = ((PGPPublicKeyRing) new BcPGPPublicKeyRingCollection(keyringIn).getKeyRings().next()).getPublicKey(); final PGPSignature signature = ((PGPSignatureList) new BcPGPObjectFactory(signatureIn).nextObject()).get(0); signature.init(new BcPGPContentVerifierBuilderProvider(), publicKey); signature.update(Files.asByteSource(new File(packageDir, "debian-binary")).read()); signature.update(Files.asByteSource(new File(packageDir, "control.tar.gz")).read()); signature.update(Files.asByteSource(new File(packageDir, "data.tar.gz")).read()); assertTrue(signature.verify()); } } }
/** * Separates stream into PGP packets. * @see PGPObjectFactory */ protected Iterator parse(InputStream stream) throws IOException, PGPException { // before BCPG v1.55 // PGPObjectFactory.iterator() doesn't work for decryption // because its next() method prematurely calls nextObject() // for the next next object, which puts the BCPGInputStream parser // into a broken state, resulting in errors like // "unknown object in stream: 27" //return new BcPGPObjectFactory(stream).iterator(); final BcPGPObjectFactory factory = new BcPGPObjectFactory(stream); return new Iterator() { boolean checkedNext = false; Object nextElement = null; public boolean hasNext() { if (!checkedNext) { checkedNext = true; try { nextElement = factory.nextObject(); } catch (IOException e) { throw new RuntimeException(e); } } return nextElement != null; } public Object next() { if (!hasNext()) throw new NoSuchElementException(); checkedNext = false; return nextElement; } public void remove() { throw new UnsupportedOperationException(); } }; }
/** * Extracts a {@link PGPSignature} object from a blob of {@code .sig} data. * * @throws SignatureException if a signature object couldn't be extracted for any reason. */ private static PGPSignature pgpExtractSignature(@Tainted byte[] signature) throws SignatureException { try { ByteArrayInputStream input = new ByteArrayInputStream(signature); PGPObjectFactory decoder = new BcPGPObjectFactory(PGPUtil.getDecoderStream(input)); Object object = decoder.nextObject(); if (object == null) { throw new SignatureException(String.format( "No OpenPGP packets found in signature.\n%s", dumpHex(signature))); } if (!(object instanceof PGPSignatureList)) { throw new SignatureException(String.format( "Expected PGPSignatureList packet but got %s\n%s", object.getClass().getSimpleName(), dumpHex(signature))); } PGPSignatureList sigs = (PGPSignatureList) object; if (sigs.isEmpty()) { throw new SignatureException(String.format( "PGPSignatureList doesn't have a PGPSignature.\n%s", dumpHex(signature))); } return sigs.get(0); } catch (IOException e) { throw new SignatureException(String.format( "Failed to extract PGPSignature object from .sig blob.\n%s", dumpHex(signature)), e); } }
@Test public void testSignVerify_Detached() throws Exception { // Load the keys. PGPPublicKeyRing publicKeyRing = new BcPGPPublicKeyRing(PUBLIC_KEY); PGPSecretKeyRing privateKeyRing = new BcPGPSecretKeyRing(PRIVATE_KEY); PGPPublicKey publicKey = publicKeyRing.getPublicKey(); PGPPrivateKey privateKey = extractPrivateKey(privateKeyRing.getSecretKey()); // Sign the data and write signature data to "signatureFile". // Note: RSA_GENERAL will encrypt AND sign. RSA_SIGN and RSA_ENCRYPT are deprecated. PGPSignatureGenerator signer = new PGPSignatureGenerator( new BcPGPContentSignerBuilder(RSA_GENERAL, SHA256)); signer.init(PGPSignature.BINARY_DOCUMENT, privateKey); addUserInfoToSignature(publicKey, signer); signer.update(FALL_OF_HYPERION_A_DREAM.getBytes(UTF_8)); ByteArrayOutputStream output = new ByteArrayOutputStream(); signer.generate().encode(output); byte[] signatureFileData = output.toByteArray(); logger.info(".sig file data: " + dumpHex(signatureFileData)); // Load algorithm information and signature data from "signatureFileData". PGPSignature sig; try (ByteArrayInputStream input = new ByteArrayInputStream(signatureFileData)) { PGPObjectFactory pgpFact = new BcPGPObjectFactory(input); PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject(); assertThat(sigList.size()).isEqualTo(1); sig = sigList.get(0); } // Use "onePass" and "sig" to verify "publicKey" signed the text. sig.init(new BcPGPContentVerifierBuilderProvider(), publicKey); sig.update(FALL_OF_HYPERION_A_DREAM.getBytes(UTF_8)); assertThat(sig.verify()).isTrue(); // Verify that they DIDN'T sign the text "hello monster". sig.init(new BcPGPContentVerifierBuilderProvider(), publicKey); sig.update("hello monster".getBytes(UTF_8)); assertThat(sig.verify()).isFalse(); }
@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); } } }
private List<PGPPublicKeyRing> get(long keyId, byte[] fp) throws IOException { if (reader == null) { load(); } if (notes == null) { return Collections.emptyList(); } Note note = notes.getNote(keyObjectId(keyId)); if (note == null) { return Collections.emptyList(); } List<PGPPublicKeyRing> keys = new ArrayList<>(); try (InputStream in = reader.open(note.getData(), OBJ_BLOB).openStream()) { while (true) { @SuppressWarnings("unchecked") Iterator<Object> it = new BcPGPObjectFactory(new ArmoredInputStream(in)).iterator(); if (!it.hasNext()) { break; } Object obj = it.next(); if (obj instanceof PGPPublicKeyRing) { PGPPublicKeyRing kr = (PGPPublicKeyRing) obj; if (fp == null || Arrays.equals(fp, kr.getPublicKey().getFingerprint())) { keys.add(kr); } } checkState(!it.hasNext(), "expected one PGP object per ArmoredInputStream"); } return keys; } }
private PGPSignature readSignature(PushCertificate cert) throws IOException { ArmoredInputStream in = new ArmoredInputStream(new ByteArrayInputStream(Constants.encode(cert.getSignature()))); PGPObjectFactory factory = new BcPGPObjectFactory(in); Object obj; while ((obj = factory.nextObject()) != null) { if (obj instanceof PGPSignatureList) { PGPSignatureList sigs = (PGPSignatureList) obj; if (!sigs.isEmpty()) { return sigs.get(0); } } } return null; }
private PGPSignature getPgpSignature() { try { final InputStream decoderStream = PGPUtil.getDecoderStream(new ByteArrayInputStream(signature)); final PGPObjectFactory objectFactory = new BcPGPObjectFactory(decoderStream); final PGPSignatureList signatureList = (PGPSignatureList) objectFactory.nextObject(); if ((signatureList == null) || (signatureList.size() != 1)) { throw new IllegalArgumentException("Couldn't read PGP signature"); } return signatureList.get(0); } catch (IOException e) { throw new IllegalArgumentException(e); } }
/** * Separates stream into PGP packets. * @see PGPObjectFactory */ protected Iterator parse(InputStream stream) throws IOException, PGPException { return new BcPGPObjectFactory(stream).iterator(); }
/** * decrypt the passed in message stream */ private byte[] decryptMessage( byte[] message, Date date) throws Exception { PGPObjectFactory pgpF = new BcPGPObjectFactory(message); PGPEncryptedDataList enc = (PGPEncryptedDataList)pgpF.nextObject(); PGPPBEEncryptedData pbe = (PGPPBEEncryptedData)enc.get(0); InputStream clear = pbe.getDataStream(new BcPBEDataDecryptorFactory(pass, new BcPGPDigestCalculatorProvider())); PGPObjectFactory pgpFact = new BcPGPObjectFactory(clear); PGPLiteralData ld = (PGPLiteralData)pgpFact.nextObject(); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); if (!ld.getFileName().equals("test.txt") && !ld.getFileName().equals("_CONSOLE")) { fail("wrong filename in packet"); } if (!ld.getModificationTime().equals(date)) { fail("wrong modification time in packet: " + ld.getModificationTime().getTime() + " " + date.getTime()); } InputStream unc = ld.getInputStream(); int ch; while ((ch = unc.read()) >= 0) { bOut.write(ch); } if (pbe.isIntegrityProtected() && !pbe.verify()) { fail("integrity check failed"); } return bOut.toByteArray(); }
/** * Generated signature test * * @param sKey * @param pgpPrivKey */ public void generateTest( PGPSecretKeyRing sKey, PGPPublicKey pgpPubKey, PGPPrivateKey pgpPrivKey) throws Exception { String data = "hello world!"; ByteArrayOutputStream bOut = new ByteArrayOutputStream(); ByteArrayInputStream testIn = new ByteArrayInputStream(data.getBytes()); PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.DSA, HashAlgorithmTags.SHA1)); sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); Iterator it = sKey.getSecretKey().getPublicKey().getUserIDs(); String primaryUserID = (String)it.next(); spGen.setSignerUserID(true, primaryUserID); sGen.setHashedSubpackets(spGen.generate()); sGen.generateOnePassVersion(false).encode(bOut); PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); Date testDate = new Date((System.currentTimeMillis() / 1000) * 1000); OutputStream lOut = lGen.open( new UncloseableOutputStream(bOut), PGPLiteralData.BINARY, "_CONSOLE", data.getBytes().length, testDate); int ch; while ((ch = testIn.read()) >= 0) { lOut.write(ch); sGen.update((byte)ch); } lGen.close(); sGen.generate().encode(bOut); PGPObjectFactory pgpFact = new BcPGPObjectFactory(bOut.toByteArray()); PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject(); PGPOnePassSignature ops = p1.get(0); PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject(); if (!p2.getModificationTime().equals(testDate)) { fail("Modification time not preserved"); } InputStream dIn = p2.getInputStream(); ops.init(new BcPGPContentVerifierBuilderProvider(), pgpPubKey); while ((ch = dIn.read()) >= 0) { ops.update((byte)ch); } PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject(); if (!ops.verify(p3.get(0))) { fail("Failed generated signature check"); } }
@Test public void testSignVerify_OnePass() throws Exception { // Load the keys. PGPPublicKeyRing publicKeyRing = new BcPGPPublicKeyRing(PUBLIC_KEY); PGPSecretKeyRing privateKeyRing = new BcPGPSecretKeyRing(PRIVATE_KEY); PGPPublicKey publicKey = publicKeyRing.getPublicKey(); PGPPrivateKey privateKey = extractPrivateKey(privateKeyRing.getSecretKey()); // Sign the data and write signature data to "signatureFile". PGPSignatureGenerator signer = new PGPSignatureGenerator( new BcPGPContentSignerBuilder(RSA_GENERAL, SHA256)); signer.init(PGPSignature.BINARY_DOCUMENT, privateKey); addUserInfoToSignature(publicKey, signer); ByteArrayOutputStream output = new ByteArrayOutputStream(); signer.generateOnePassVersion(false).encode(output); signer.update(FALL_OF_HYPERION_A_DREAM.getBytes(UTF_8)); signer.generate().encode(output); byte[] signatureFileData = output.toByteArray(); logger.info(".sig file data: " + dumpHex(signatureFileData)); // Load algorithm information and signature data from "signatureFileData". PGPSignature sig; PGPOnePassSignature onePass; try (ByteArrayInputStream input = new ByteArrayInputStream(signatureFileData)) { PGPObjectFactory pgpFact = new BcPGPObjectFactory(input); PGPOnePassSignatureList onePassList = (PGPOnePassSignatureList) pgpFact.nextObject(); PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject(); assertThat(onePassList.size()).isEqualTo(1); assertThat(sigList.size()).isEqualTo(1); onePass = onePassList.get(0); sig = sigList.get(0); } // Use "onePass" and "sig" to verify "publicKey" signed the text. onePass.init(new BcPGPContentVerifierBuilderProvider(), publicKey); onePass.update(FALL_OF_HYPERION_A_DREAM.getBytes(UTF_8)); assertThat(onePass.verify(sig)).isTrue(); // Verify that they DIDN'T sign the text "hello monster". onePass.init(new BcPGPContentVerifierBuilderProvider(), publicKey); onePass.update("hello monster".getBytes(UTF_8)); assertThat(onePass.verify(sig)).isFalse(); }
/** * Opens a new {@link Decompressor} (Reading Step 2/3) * * <p>This is the second step in reading a ghostryde file. After this method, you'll want to * call {@link #openInput(Decompressor)}. * * @param input is the value returned by {@link #openDecryptor}. * @throws IOException * @throws PGPException */ @CheckReturnValue public Decompressor openDecompressor(@WillNotClose Decryptor input) throws IOException, PGPException { PGPObjectFactory fact = new BcPGPObjectFactory(checkNotNull(input, "input")); PGPCompressedData compressed = pgpCast(fact.nextObject(), PGPCompressedData.class); return new Decompressor(compressed.getDataStream()); }
/** * Opens a new {@link Input} for reading the original contents (Reading Step 3/3) * * <p>This is the final step in reading a ghostryde file. After calling this method, you should * call the read methods on the returned {@link InputStream}. * * @param input is the value returned by {@link #openDecompressor}. * @throws IOException * @throws PGPException */ @CheckReturnValue public Input openInput(@WillNotClose Decompressor input) throws IOException, PGPException { PGPObjectFactory fact = new BcPGPObjectFactory(checkNotNull(input, "input")); PGPLiteralData literal = pgpCast(fact.nextObject(), PGPLiteralData.class); DateTime modified = new DateTime(literal.getModificationTime(), UTC); return new Input(literal.getDataStream(), literal.getFileName(), modified); }