Repository: camel
Updated Branches:
  refs/heads/camel-2.12.x 0018b70e6 -> 88780f154


CAMEL-7253 Fixed the NPE of PGPDataFormat if decryptor gets body with invalid 
format with thanks to Franz


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/88780f15
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/88780f15
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/88780f15

Branch: refs/heads/camel-2.12.x
Commit: 88780f154e22aaf4885d466113a13f91f5968454
Parents: 0018b70
Author: Willem Jiang <willem.ji...@gmail.com>
Authored: Mon Mar 3 16:26:56 2014 +0800
Committer: Willem Jiang <willem.ji...@gmail.com>
Committed: Mon Mar 3 16:26:56 2014 +0800

----------------------------------------------------------------------
 .../camel/converter/crypto/PGPDataFormat.java   |  44 ++-
 .../converter/crypto/PGPDataFormatTest.java     | 267 ++++++++++++++++++-
 2 files changed, 293 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/88780f15/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPDataFormat.java
----------------------------------------------------------------------
diff --git 
a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPDataFormat.java
 
b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPDataFormat.java
index d7e92b2..5074cfe 100644
--- 
a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPDataFormat.java
+++ 
b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPDataFormat.java
@@ -401,19 +401,32 @@ public class PGPDataFormat extends ServiceSupport 
implements DataFormat {
         }
         InputStream in = PGPUtil.getDecoderStream(encryptedStream);
         PGPObjectFactory pgpFactory = new PGPObjectFactory(in);
-        Object o = pgpFactory.nextObject();
+        Object firstObject = pgpFactory.nextObject();
         // the first object might be a PGP marker packet 
         PGPEncryptedDataList enc;
-        if (o instanceof PGPEncryptedDataList) {
-            enc = (PGPEncryptedDataList) o;
+        if (firstObject instanceof PGPEncryptedDataList) {
+            enc = (PGPEncryptedDataList) firstObject;
         } else {
-            enc = (PGPEncryptedDataList) pgpFactory.nextObject();
+            Object secondObject = pgpFactory.nextObject();
+            if (secondObject instanceof PGPEncryptedDataList) {
+                enc = (PGPEncryptedDataList) pgpFactory.nextObject();
+            } else {
+                enc = null;
+            }
+        }
+
+        if (enc == null) {
+            throw getFormatException();
         }
 
         PGPPublicKeyEncryptedData pbe = null;
         PGPPrivateKey key = null;
         // find encrypted data for which a private key exists in the secret 
key ring
         for (int i = 0; i < enc.size() && key == null; i++) {
+            Object encryptedData = enc.get(i);
+            if (!(encryptedData instanceof PGPPublicKeyEncryptedData)) {
+                throw getFormatException();
+            }
             pbe = (PGPPublicKeyEncryptedData) enc.get(i);
             key = 
PGPDataFormatUtil.findPrivateKeyWithKeyId(exchange.getContext(), 
findKeyFileName(exchange),
                     findEncryptionKeyRing(exchange), pbe.getKeyID(), 
findKeyPassword(exchange), getPassphraseAccessor(), getProvider());
@@ -423,12 +436,16 @@ public class PGPDataFormat extends ServiceSupport 
implements DataFormat {
             }
         }
         if (key == null) {
-            throw new PGPException("Provided input is encrypted with unknown 
pair of keys.");
+            throw new PGPException("Message is encrypted with a key which 
could not be found in the Secret Key Ring.");
         }
 
         InputStream encData = pbe.getDataStream(new 
JcePublicKeyDataDecryptorFactoryBuilder().setProvider(getProvider()).build(key));
         pgpFactory = new PGPObjectFactory(encData);
-        PGPCompressedData comData = (PGPCompressedData) 
pgpFactory.nextObject();
+        Object compObj = pgpFactory.nextObject();
+        if (!(compObj instanceof PGPCompressedData)) {
+            throw getFormatException();
+        }
+        PGPCompressedData comData = (PGPCompressedData)compObj;
         pgpFactory = new PGPObjectFactory(comData.getDataStream());
         Object object = pgpFactory.nextObject();
 
@@ -440,7 +457,13 @@ public class PGPDataFormat extends ServiceSupport 
implements DataFormat {
             signature = null;
         }
 
-        PGPLiteralData ld = (PGPLiteralData) object;
+        PGPLiteralData ld;
+        if (object instanceof PGPLiteralData) {
+            ld = (PGPLiteralData) object;
+        } else {
+            throw getFormatException();
+        }
+
         InputStream litData = ld.getInputStream();
 
         // enable streaming via OutputStreamCache
@@ -485,6 +508,13 @@ public class PGPDataFormat extends ServiceSupport 
implements DataFormat {
         }
     }
 
+    private IllegalArgumentException getFormatException() {
+        return new IllegalArgumentException("The input message body has an 
invalid format. The PGP decryption/verification processor expects a sequence of 
PGP packets of the form "
+                + "(entries in brackets are optional and ellipses indicate 
repetition, comma represents  sequential composition, and vertical bar 
separates alternatives): "
+                + "Public Key Encrypted Session Key ..., Symmetrically 
Encrypted Data | Sym. Encrypted and Integrity Protected Data, Compressed Data, 
(One Pass Signature ...,) "
+                + "Literal Data, (Signature ...,)");
+    }
+
     protected PGPSignature getSignatureWithKeyId(long keyID, PGPSignatureList 
sigList) {
         for (int i = 0; i < sigList.size(); i++) {
             PGPSignature signature = sigList.get(i);

http://git-wip-us.apache.org/repos/asf/camel/blob/88780f15/components/camel-crypto/src/test/java/org/apache/camel/converter/crypto/PGPDataFormatTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-crypto/src/test/java/org/apache/camel/converter/crypto/PGPDataFormatTest.java
 
b/components/camel-crypto/src/test/java/org/apache/camel/converter/crypto/PGPDataFormatTest.java
index b017a86..4231e43 100644
--- 
a/components/camel-crypto/src/test/java/org/apache/camel/converter/crypto/PGPDataFormatTest.java
+++ 
b/components/camel-crypto/src/test/java/org/apache/camel/converter/crypto/PGPDataFormatTest.java
@@ -16,13 +16,21 @@
  */
 package org.apache.camel.converter.crypto;
 
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -31,14 +39,36 @@ import org.apache.camel.Message;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.util.IOHelper;
+import org.bouncycastle.bcpg.BCPGOutputStream;
 import org.bouncycastle.bcpg.CompressionAlgorithmTags;
 import org.bouncycastle.bcpg.HashAlgorithmTags;
 import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
 import org.bouncycastle.bcpg.sig.KeyFlags;
+import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
+import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPLiteralData;
+import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
+import org.bouncycastle.openpgp.PGPPrivateKey;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
+import org.bouncycastle.openpgp.PGPSecretKey;
+import org.bouncycastle.openpgp.PGPSecretKeyRing;
+import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureGenerator;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import 
org.bouncycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
+import 
org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
+import 
org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
 import org.junit.Test;
 
 public class PGPDataFormatTest extends AbstractPGPDataFormatTest {
 
+    private static final String PUB_KEY_RING_SUBKEYS_FILE_NAME = 
"org/apache/camel/component/crypto/pubringSubKeys.gpg";
     private static final String SEC_KEY_RING_FILE_NAME = 
"org/apache/camel/component/crypto/secring.gpg";
     private static final String PUB_KEY_RING_FILE_NAME = 
"org/apache/camel/component/crypto/pubring.gpg";
 
@@ -138,10 +168,7 @@ public class PGPDataFormatTest extends 
AbstractPGPDataFormatTest {
         template.sendBodyAndHeaders("direct:verify_exception_sig_userids", 
payload, headers);
         assertMockEndpointsSatisfied();
 
-        //check exception text
-        Exception e = (Exception) 
exception.getExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT);
-        assertNotNull("Expected excpetion  missing", e);
-        assertTrue(e.getMessage().contains("No public key found fitting to the 
signature key Id"));
+        checkThrownException(exception, IllegalArgumentException.class, null, 
"No public key found fitting to the signature key Id");
 
     }
 
@@ -158,10 +185,7 @@ public class PGPDataFormatTest extends 
AbstractPGPDataFormatTest {
         template.sendBodyAndHeaders("direct:several-signer-keys", payload, 
headers);
         assertMockEndpointsSatisfied();
 
-        //check exception text
-        Exception e = (Exception) 
exception.getExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT);
-        assertNotNull("Expected excpetion  missing", e);
-        assertTrue(e.getMessage().contains("No passphrase specified for 
signature key user ID"));
+        checkThrownException(exception, IllegalArgumentException.class, null, 
"No passphrase specified for signature key user ID");
 
     }
 
@@ -191,11 +215,182 @@ public class PGPDataFormatTest extends 
AbstractPGPDataFormatTest {
         assertEquals(1, 
inMess.getHeader(PGPDataFormat.NUMBER_OF_SIGNING_KEYS));
     }
 
+
+    @Test
+    public void testExceptionDecryptorIncorrectInputFormatNoPGPMessage() 
throws Exception {
+        String payload = "Not Correct Format";
+        MockEndpoint mock = getMockEndpoint("mock:exception");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:subkeyUnmarshal", payload);
+        assertMockEndpointsSatisfied();
+
+        checkThrownException(mock, IllegalArgumentException.class, null, "The 
input message body has an invalid format.");
+    }
+
+    @Test
+    public void testExceptionDecryptorIncorrectInputFormatPGPSignedData() 
throws Exception {
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        createSignature(bos);
+        MockEndpoint mock = getMockEndpoint("mock:exception");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:subkeyUnmarshal", bos.toByteArray());
+        assertMockEndpointsSatisfied();
+
+        checkThrownException(mock, IllegalArgumentException.class, null, "The 
input message body has an invalid format.");
+    }
+
+    @Test
+    public void testExceptionDecryptorIncorrectInputNoCompression() throws 
Exception {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        createEncryptedNonCompressedData(bos, PUB_KEY_RING_SUBKEYS_FILE_NAME);
+
+        MockEndpoint mock = getMockEndpoint("mock:exception");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:subkeyUnmarshal", bos.toByteArray());
+        assertMockEndpointsSatisfied();
+
+        checkThrownException(mock, IllegalArgumentException.class, null, "The 
input message body has an invalid format.");
+    }
+
+    @Test
+    public void testExceptionDecryptorNoKeyFound() throws Exception {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        createEncryptedNonCompressedData(bos, PUB_KEY_RING_FILE_NAME);
+
+        MockEndpoint mock = getMockEndpoint("mock:exception");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:subkeyUnmarshal", bos.toByteArray());
+        assertMockEndpointsSatisfied();
+
+        checkThrownException(mock, PGPException.class, null,
+                "Message is encrypted with a key which could not be found in 
the Secret Key Ring");
+    }
+
+    void createEncryptedNonCompressedData(ByteArrayOutputStream bos, String 
keyringPath) throws Exception, IOException, PGPException,
+            UnsupportedEncodingException {
+        PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new 
JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5)
+                .setSecureRandom(new 
SecureRandom()).setProvider(getProvider()));
+        encGen.addMethod(new 
JcePublicKeyKeyEncryptionMethodGenerator(readPublicKey(keyringPath)));
+        OutputStream encOut = encGen.open(bos, new byte[512]);
+        PGPLiteralDataGenerator litData = new PGPLiteralDataGenerator();
+        OutputStream litOut = litData.open(encOut, PGPLiteralData.BINARY, 
PGPLiteralData.CONSOLE, new Date(), new byte[512]);
+
+        try {
+            litOut.write("Test Message Without Compression".getBytes("UTF-8"));
+            litOut.flush();
+        } finally {
+            IOHelper.close(litOut);
+            IOHelper.close(encOut, bos);
+        }
+    }
+
+    private void createSignature(OutputStream out) throws Exception {
+        PGPSecretKey pgpSec = readSecretKey();
+        PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(new 
JcePBESecretKeyDecryptorBuilder().setProvider(getProvider()).build(
+                "sdude".toCharArray()));
+        PGPSignatureGenerator sGen = new PGPSignatureGenerator(new 
JcaPGPContentSignerBuilder(pgpSec.getPublicKey().getAlgorithm(),
+                PGPUtil.SHA1).setProvider(getProvider()));
+
+        sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey);
+
+        BCPGOutputStream bOut = new BCPGOutputStream(out);
+
+        InputStream fIn = new ByteArrayInputStream("Test 
Signature".getBytes("UTF-8"));
+
+        int ch;
+        while ((ch = fIn.read()) >= 0) {
+            sGen.update((byte) ch);
+        }
+
+        fIn.close();
+
+        sGen.generate().encode(bOut);
+
+    }
+
+    static PGPSecretKey readSecretKey() throws Exception {
+        InputStream input = new ByteArrayInputStream(getSecKeyRing());
+        PGPSecretKeyRingCollection pgpSec = new 
PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(input));
+
+        @SuppressWarnings("rawtypes")
+        Iterator keyRingIter = pgpSec.getKeyRings();
+        while (keyRingIter.hasNext()) {
+            PGPSecretKeyRing keyRing = (PGPSecretKeyRing) keyRingIter.next();
+
+            @SuppressWarnings("rawtypes")
+            Iterator keyIter = keyRing.getSecretKeys();
+            while (keyIter.hasNext()) {
+                PGPSecretKey key = (PGPSecretKey) keyIter.next();
+
+                if (key.isSigningKey()) {
+                    return key;
+                }
+            }
+        }
+
+        throw new IllegalArgumentException("Can't find signing key in key 
ring.");
+    }
+
+    static PGPPublicKey readPublicKey(String keyringPath) throws Exception {
+        InputStream input = new ByteArrayInputStream(getKeyRing(keyringPath));
+        PGPPublicKeyRingCollection pgpPub = new 
PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input));
+
+        @SuppressWarnings("rawtypes")
+        Iterator keyRingIter = pgpPub.getKeyRings();
+        while (keyRingIter.hasNext()) {
+            PGPPublicKeyRing keyRing = (PGPPublicKeyRing) keyRingIter.next();
+
+            @SuppressWarnings("rawtypes")
+            Iterator keyIter = keyRing.getPublicKeys();
+            while (keyIter.hasNext()) {
+                PGPPublicKey key = (PGPPublicKey) keyIter.next();
+
+                if (key.isEncryptionKey()) {
+                    return key;
+                }
+            }
+        }
+
+        throw new IllegalArgumentException("Can't find encryption key in key 
ring.");
+    }
+
+    @Test
+    public void 
testExceptionDecryptorIncorrectInputFormatSymmetricEncryptedData() throws 
Exception {
+
+        byte[] payload = "Not Correct Format".getBytes("UTF-8");
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new 
JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5)
+                .setSecureRandom(new 
SecureRandom()).setProvider(getProvider()));
+
+        encGen.addMethod(new 
JcePBEKeyEncryptionMethodGenerator("pw".toCharArray()));
+
+        OutputStream encOut = encGen.open(bos, new byte[1024]);
+        PGPCompressedDataGenerator comData = new 
PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
+        OutputStream comOut = new BufferedOutputStream(comData.open(encOut));
+        PGPLiteralDataGenerator litData = new PGPLiteralDataGenerator();
+        OutputStream litOut = litData.open(comOut, PGPLiteralData.BINARY, 
PGPLiteralData.CONSOLE, new Date(), new byte[1024]);
+        litOut.write(payload);
+        litOut.flush();
+        litOut.close();
+        comOut.close();
+        encOut.close();
+        MockEndpoint mock = getMockEndpoint("mock:exception");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:subkeyUnmarshal", bos.toByteArray());
+        assertMockEndpointsSatisfied();
+
+        checkThrownException(mock, IllegalArgumentException.class, null, "The 
input message body has an invalid format.");
+    }
+
     protected RouteBuilder[] createRouteBuilders() {
         return new RouteBuilder[] {new RouteBuilder() {
             public void configure() throws Exception {
 
-                
onException(IllegalArgumentException.class).handled(true).to("mock:exception");
+                
onException(Exception.class).handled(true).to("mock:exception");
 
                 // START SNIPPET: pgp-format
                 // Public Key FileName
@@ -379,16 +574,29 @@ public class PGPDataFormatTest extends 
AbstractPGPDataFormatTest {
 
         }, new RouteBuilder() {
             public void configure() throws Exception {
-                // keyflag test
+                
onException(Exception.class).handled(true).to("mock:exception");
+                 // keyflag test
                 PGPDataFormat pgpKeyFlag = new PGPDataFormat();
                 // the following keyring contains a primary key with KeyFlag 
"Certify" and a subkey for signing and a subkey for encryption
-                
pgpKeyFlag.setKeyFileName("org/apache/camel/component/crypto/pubringSubKeys.gpg");
+                pgpKeyFlag.setKeyFileName(PUB_KEY_RING_SUBKEYS_FILE_NAME);
                 
pgpKeyFlag.setSignatureKeyFileName("org/apache/camel/component/crypto/secringSubKeys.gpg");
                 pgpKeyFlag.setSignaturePassword("Abcd1234");
                 pgpKeyFlag.setKeyUserid("keyflag");
                 pgpKeyFlag.setSignatureKeyUserid("keyflag");
 
                 
from("direct:keyflag").marshal(pgpKeyFlag).to("mock:encrypted_keyflag");
+
+                PGPDataFormat pgpDecryptVerifySubkey = new PGPDataFormat();
+                // the following keyring contains a primary key with KeyFlag 
"Certify" and a subkey for signing and a subkey for encryption
+                
pgpDecryptVerifySubkey.setKeyFileName("org/apache/camel/component/crypto/secringSubKeys.gpg");
+                
pgpDecryptVerifySubkey.setSignatureKeyFileName(PUB_KEY_RING_SUBKEYS_FILE_NAME);
+                pgpDecryptVerifySubkey.setPassword("Abcd1234");
+                pgpDecryptVerifySubkey.setSignatureKeyUserid("keyflag");
+
+                // test that the correct subkey is selected during decrypt and 
verify
+                
//from("direct:subkey").marshal(pgpKeyFlag).to("mock:encrypted").unmarshal(pgpDecryptVerifySubkey).to("mock:unencrypted");
+
+                
from("direct:subkeyUnmarshal").unmarshal(pgpDecryptVerifySubkey).to("mock:unencrypted");
             }
         } };
     }
@@ -415,4 +623,41 @@ public class PGPDataFormatTest extends 
AbstractPGPDataFormatTest {
         return passphraseAccessor;
     }
 
+    public static void checkThrownException(MockEndpoint mock, Class<? extends 
Exception> cl,
+                                            Class<? extends Exception> 
expectedCauseClass, String expectedMessagePart) throws Exception {
+        Exception e = (Exception) 
mock.getExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT);
+        assertNotNull("Expected excpetion " + cl.getName() + " missing", e);
+        if (e.getClass() != cl) {
+            String stackTrace = getStrackTrace(e);
+            fail("Exception  " + cl.getName() + " excpected, but was " + 
e.getClass().getName() + ": " + stackTrace);
+        }
+        if (expectedMessagePart != null) {
+            if (e.getMessage() == null) {
+                fail("Expected excption does not contain a message. Stack 
trace: " + getStrackTrace(e));
+            } else {
+                if (!e.getMessage().contains(expectedMessagePart)) {
+                    fail("Expected excption message does not contain a 
expected message part " + expectedMessagePart + ".  Stack trace: "
+                            + getStrackTrace(e));
+                }
+            }
+        }
+        if (expectedCauseClass != null) {
+            Throwable cause = e.getCause();
+            assertNotNull("Expected cause exception" + 
expectedCauseClass.getName() + " missing", cause);
+            if (expectedCauseClass != cause.getClass()) {
+                fail("Cause exception " + expectedCauseClass.getName() + " 
expected, but was " + cause.getClass().getName() + ": "
+                        + getStrackTrace(e));
+            }
+        }
+    }
+
+    public static String getStrackTrace(Exception e) throws 
UnsupportedEncodingException {
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        PrintWriter w = new PrintWriter(os);
+        e.printStackTrace(w);
+        w.close();
+        String stackTrace = new String(os.toByteArray(), "UTF-8");
+        return stackTrace;
+    }
+
 }

Reply via email to