diff --git a/.gitignore b/.gitignore index c2debe9..5308050 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,4 @@ test/version_tmp tmp # Skip our compiled jruby-pgp.jar -lib/pgp/jruby-pgp.jar +#lib/pgp/jruby-pgp.jar diff --git a/ext/org/sgonyea/pgp/Decryptor.java b/ext/org/sgonyea/pgp/Decryptor.java index 0a863d5..f1f7901 100644 --- a/ext/org/sgonyea/pgp/Decryptor.java +++ b/ext/org/sgonyea/pgp/Decryptor.java @@ -19,6 +19,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.security.NoSuchProviderException; +import java.security.SignatureException; import java.security.SecureRandom; import java.security.Security; import java.util.Date; @@ -26,27 +27,13 @@ import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openpgp.PGPCompressedData; -import org.bouncycastle.openpgp.PGPCompressedDataGenerator; -import org.bouncycastle.openpgp.PGPEncryptedData; -import org.bouncycastle.openpgp.PGPEncryptedDataGenerator; -import org.bouncycastle.openpgp.PGPEncryptedDataList; -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPLiteralData; -import org.bouncycastle.openpgp.PGPLiteralDataGenerator; -import org.bouncycastle.openpgp.PGPObjectFactory; -import org.bouncycastle.openpgp.PGPPrivateKey; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData; -import org.bouncycastle.openpgp.PGPPublicKeyRing; -import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; -import org.bouncycastle.openpgp.PGPUtil; +import org.bouncycastle.openpgp.*; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; public class Decryptor { private PGPSecretKeyRingCollection _privateKeys; + private PGPPublicKeyRingCollection _publicKeys; private String passphrase; @@ -66,6 +53,10 @@ public void setPrivateKeys(PGPSecretKeyRingCollection privateKeys) { _privateKeys = privateKeys; } + public void setPublicKeys(PGPPublicKeyRingCollection publicKeys) { + _publicKeys = publicKeys; + } + public void setPassphrase(String passphrase) { this.passphrase = passphrase; } @@ -87,13 +78,13 @@ public PGPPrivateKey findPrivateKey(long keyID) **/ public byte[] decryptBytes(byte[] encryptedBytes) - throws IOException, PGPException, NoSuchProviderException { + throws IOException, PGPException, NoSuchProviderException, SignatureException, VerificationFailedException { InputStream stream = new ByteArrayInputStream(encryptedBytes); return decryptStream(stream); } public byte[] decryptStream(InputStream encryptedStream) - throws IOException, PGPException, NoSuchProviderException { + throws IOException, PGPException, NoSuchProviderException, SignatureException, VerificationFailedException { InputStream decoderStream = PGPUtil.getDecoderStream(encryptedStream); @@ -135,17 +126,49 @@ public byte[] decryptStream(InputStream encryptedStream) pgpFactory = new PGPObjectFactory(compressedData.getDataStream()); - literallyTheRealFuckingData = (PGPLiteralData) pgpFactory.nextObject(); + PGPOnePassSignatureList opsList = null; + PGPOnePassSignature ops = null; + PGPPublicKey signingKey = null; + Object obj = pgpFactory.nextObject(); + if (obj instanceof PGPOnePassSignatureList) { + opsList = (PGPOnePassSignatureList) obj; + ops = opsList.get(0); + if (_publicKeys != null) { + signingKey = _publicKeys.getPublicKey(ops.getKeyID()); + // TODO warn on key not found + } + // TODO warn on no public keys set + if (signingKey != null) { + ops.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"), signingKey); + } + + literallyTheRealFuckingData = (PGPLiteralData) pgpFactory.nextObject(); + } else if (obj instanceof PGPLiteralData) { + literallyTheRealFuckingData = (PGPLiteralData) obj; + } else { + throw new RuntimeException("unexpected object"); + } decryptedDataStream = literallyTheRealFuckingData.getInputStream(); int ch; - while ((ch = decryptedDataStream.read()) >= 0) + while ((ch = decryptedDataStream.read()) >= 0) { + if (signingKey != null) { + ops.update((byte)ch); + } outputStream.write(ch); + } returnBytes = outputStream.toByteArray(); outputStream.close(); + if (signingKey != null) { + PGPSignatureList sigList = (PGPSignatureList) pgpFactory.nextObject(); + if (!ops.verify(sigList.get(0))) { + throw new VerificationFailedException("Error: Signature could not be verified."); + } + } + return returnBytes; } diff --git a/jruby-pgp.gemspec b/jruby-pgp.gemspec index 0abdc9c..28b2958 100644 --- a/jruby-pgp.gemspec +++ b/jruby-pgp.gemspec @@ -13,12 +13,12 @@ Gem::Specification.new do |gem| gem.summary = %q{This is a Java+JRuby wrapper around the Bouncy Castle PGP APIs} gem.homepage = 'https://github.com/sgonyea/jruby-pgp' - gem.files = `git ls-files`.split($/) + %w[lib/pgp/jruby-pgp.jar] + gem.files = `git ls-files`.split($/) gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ['lib'] - gem.add_dependency 'jruby-openssl' + #gem.add_dependency 'jruby-openssl' gem.add_development_dependency 'rake' gem.add_development_dependency 'rspec' diff --git a/lib/pgp/decryptor.rb b/lib/pgp/decryptor.rb index 2965eac..88eb569 100644 --- a/lib/pgp/decryptor.rb +++ b/lib/pgp/decryptor.rb @@ -10,6 +10,14 @@ def add_keys_from_file(filename) self.private_keys = keyring_from_file(filename) end + def add_public_keys(key_string) + self.public_keys = public_keyring_from_string(key_string) + end + + def add_public_keys_from_file(filename) + self.public_keys = public_keyring_from_file(filename) + end + def decrypt(encrypted_data) input_stream = PGP.string_to_bais(encrypted_data) decrypted_data = decrypt_stream(input_stream) @@ -36,5 +44,20 @@ def keyring_from_stream(stream) PGPSecretKeyRingCollection.new(yafs) end + def public_keyring_from_file(filename) + file = File.open(filename) + public_keyring_from_stream(file.to_inputstream) + end + + def public_keyring_from_string(string) + input_stream = PGP.string_to_bais(string) + public_keyring_from_stream(input_stream) + end + + def public_keyring_from_stream(stream) + yafs = PGPUtil.get_decoder_stream(stream) + PGPPublicKeyRingCollection.new(yafs) + end + end end diff --git a/lib/pgp/jruby-pgp.jar b/lib/pgp/jruby-pgp.jar new file mode 100644 index 0000000..42830fa Binary files /dev/null and b/lib/pgp/jruby-pgp.jar differ diff --git a/lib/pgp/version.rb b/lib/pgp/version.rb index f90a8f7..7e1a7ca 100644 --- a/lib/pgp/version.rb +++ b/lib/pgp/version.rb @@ -1,3 +1,3 @@ module PGP - VERSION = '0.3.0' + VERSION = '0.4.0' end