Repository: camel
Updated Branches:
  refs/heads/master 4be7f3630 -> e8c9f630b


CAMEL-7283: PGP Data Format: Signature Verification Options. Thanks to Franz 
Forsthofer for the patch.


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

Branch: refs/heads/master
Commit: e8c9f630b482f4687274cd2f184a0e16238ed94c
Parents: 4be7f36
Author: Claus Ibsen <davscl...@apache.org>
Authored: Thu Mar 13 10:42:03 2014 +0100
Committer: Claus Ibsen <davscl...@apache.org>
Committed: Thu Mar 13 10:42:03 2014 +0100

----------------------------------------------------------------------
 .../converter/crypto/PGPDataFormatUtil.java     | 148 ++++++-------
 .../crypto/PGPKeyAccessDataFormat.java          | 222 +++++++++++++------
 .../converter/crypto/PGPDataFormatTest.java     |  93 ++++++--
 3 files changed, 303 insertions(+), 160 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/e8c9f630/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPDataFormatUtil.java
----------------------------------------------------------------------
diff --git 
a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPDataFormatUtil.java
 
b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPDataFormatUtil.java
index b8849db..8713c3e 100644
--- 
a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPDataFormatUtil.java
+++ 
b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPDataFormatUtil.java
@@ -24,10 +24,8 @@ import java.security.NoSuchProviderException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.util.IOHelper;
@@ -112,7 +110,7 @@ public final class PGPDataFormatUtil {
         }
         return pubKey;
     }
-    
+
     public static PGPPublicKeyRingCollection 
getPublicKeyRingCollection(CamelContext context, String filename, byte[] 
keyRing, boolean forEncryption) throws IOException, PGPException {
         InputStream is = determineKeyRingInputStream(context, filename, 
keyRing, forEncryption);
         try {
@@ -204,35 +202,37 @@ public final class PGPDataFormatUtil {
         return findPublicKeys(userids, forEncryption, pgpSec);
     }
 
-    
     @SuppressWarnings("unchecked")
-    public static List<PGPPublicKey> findPublicKeys(List<String> userids, 
boolean forEncryption, PGPPublicKeyRingCollection pgpPublicKeyringCollection) {
-        List<PGPPublicKey> result = new 
ArrayList<PGPPublicKey>(userids.size());
+    public static List<PGPPublicKey> findPublicKeys(List<String> useridParts, 
boolean forEncryption, PGPPublicKeyRingCollection pgpPublicKeyringCollection) {
+        List<PGPPublicKey> result = new 
ArrayList<PGPPublicKey>(useridParts.size());
         for (Iterator<PGPPublicKeyRing> keyRingIter = 
pgpPublicKeyringCollection.getKeyRings(); keyRingIter.hasNext();) {
             PGPPublicKeyRing keyRing = keyRingIter.next();
-            Set<String> keyUserIds = getUserIds(keyRing);
+            PGPPublicKey primaryKey = keyRing.getPublicKey();
+            String[] foundKeyUserIdForUserIdPart = 
findFirstKeyUserIdContainingOneOfTheParts(useridParts, primaryKey);
+            if (foundKeyUserIdForUserIdPart == null) {
+                LOG.debug("No User ID found in primary key with key ID {} 
containing one of the parts {}", primaryKey.getKeyID(),
+                        useridParts);
+                continue;
+            }
+            LOG.debug("User ID {} found in primary key with key ID {} 
containing one of the parts {}", new Object[] {
+                foundKeyUserIdForUserIdPart[0], primaryKey.getKeyID(), 
useridParts });
+            // add adequate keys to the result
             for (Iterator<PGPPublicKey> keyIter = keyRing.getPublicKeys(); 
keyIter.hasNext();) {
                 PGPPublicKey key = keyIter.next();
-                for (String useridPart : userids) {
-                    for (String keyUserId : keyUserIds) {
-                        if (keyUserId != null && 
keyUserId.contains(useridPart)) {
-                            if (forEncryption) {
-                                if (isEncryptionKey(key)) {
-                                    LOG.debug(
-                                            "Public encryption key with key 
user ID {} and key ID {} found for specified user ID part {}",
-                                            new Object[] {keyUserId, 
Long.toString(key.getKeyID()), useridPart });
-                                    result.add(key);
-                                }
-                            } else if (!forEncryption && isSignatureKey(key)) {
-                                // not used!
-                                result.add(key);
-                                LOG.debug("Public key with key user ID {} and 
key ID {} found for specified user ID part {}", new Object[] {
-                                    keyUserId, Long.toString(key.getKeyID()), 
useridPart });
-                            }
-                        }
+                if (forEncryption) {
+                    if (isEncryptionKey(key)) {
+                        LOG.debug("Public encryption key with key user ID {} 
and key ID {} added to the encryption keys",
+                                foundKeyUserIdForUserIdPart[0], 
Long.toString(key.getKeyID()));
+                        result.add(key);
                     }
+                } else if (!forEncryption && isSignatureKey(key)) {
+                    // not used!
+                    result.add(key);
+                    LOG.debug("Public key with key user ID {} and key ID {} 
added to the signing keys", foundKeyUserIdForUserIdPart[0],
+                            Long.toString(key.getKeyID()));
                 }
             }
+
         }
 
         return result;
@@ -247,7 +247,7 @@ public final class PGPDataFormatUtil {
         if (hasEncryptionKeyFlags != null && !hasEncryptionKeyFlags) {
             LOG.debug(
                     "Public key with key key ID {} found for specified user 
ID. But this key will not be used for the encryption, because its key flags are 
not encryption key flags.",
-                    new Object[] {Long.toString(key.getKeyID()) });
+                    Long.toString(key.getKeyID()));
             return false;
         } else {
             // also without keyflags (hasEncryptionKeyFlags = null), true is 
returned!
@@ -261,31 +261,18 @@ public final class PGPDataFormatUtil {
     // the user IDs of the master / primary key. The master / primary key is 
the first key in
     // the keyring, and the rest of the keys are subkeys.
     // 
http://bouncy-castle.1462172.n4.nabble.com/How-to-find-PGP-subkeys-td1465289.html
-    @SuppressWarnings("unchecked")
-    private static Set<String> getUserIds(PGPPublicKeyRing keyRing) {
-        Set<String> userIds = new LinkedHashSet<String>(3);
-        for (Iterator<PGPPublicKey> keyIter = keyRing.getPublicKeys(); 
keyIter.hasNext();) {
-            PGPPublicKey key = keyIter.next();
-            for (Iterator<String> iterator = key.getUserIDs(); 
iterator.hasNext();) {
-                userIds.add(iterator.next());
+    private static String[] 
findFirstKeyUserIdContainingOneOfTheParts(List<String> useridParts, 
PGPPublicKey primaryKey) {
+        String[] foundKeyUserIdForUserIdPart = null;
+        for (@SuppressWarnings("unchecked")
+        Iterator<String> iterator = primaryKey.getUserIDs(); 
iterator.hasNext();) {
+            String keyUserId = iterator.next();
+            for (String userIdPart : useridParts) {
+                if (keyUserId.contains(userIdPart)) {
+                    foundKeyUserIdForUserIdPart = new String[] {keyUserId, 
userIdPart };
+                }
             }
         }
-        return userIds;
-    }
-
-    // Within a secret keyring, the master / primary key has the user ID(s); 
the subkeys don't
-    // have user IDs associated directly to them, but the subkeys are 
implicitly associated with
-    // the user IDs of the master / primary key. The master / primary key is 
the first key in
-    // the keyring, and the rest of the keys are subkeys.
-    // 
http://bouncy-castle.1462172.n4.nabble.com/How-to-find-PGP-subkeys-td1465289.html
-    @SuppressWarnings("unchecked")
-    private static Set<String> getUserIds(PGPSecretKeyRing keyRing) {
-        Set<String> userIds = new LinkedHashSet<String>(3);
-        PGPSecretKey key = keyRing.getSecretKey();
-        for (Iterator<String> iterator = key.getUserIDs(); 
iterator.hasNext();) {
-            userIds.add(iterator.next());
-        }
-        return userIds;
+        return foundKeyUserIdForUserIdPart;
     }
 
     private static boolean isSignatureKey(PGPPublicKey key) {
@@ -409,31 +396,33 @@ public final class PGPDataFormatUtil {
 
     public static List<PGPSecretKeyAndPrivateKeyAndUserId> 
findSecretKeysWithPrivateKeyAndUserId(Map<String, String> sigKeyUserId2Password,
             String provider, PGPSecretKeyRingCollection pgpSec) throws 
PGPException {
-        List<PGPSecretKeyAndPrivateKeyAndUserId> result = new 
ArrayList<PGPSecretKeyAndPrivateKeyAndUserId>(
-                sigKeyUserId2Password.size());
+        List<PGPSecretKeyAndPrivateKeyAndUserId> result = new 
ArrayList<PGPSecretKeyAndPrivateKeyAndUserId>(sigKeyUserId2Password.size());
         for (Iterator<?> i = pgpSec.getKeyRings(); i.hasNext();) {
             Object data = i.next();
             if (data instanceof PGPSecretKeyRing) {
                 PGPSecretKeyRing keyring = (PGPSecretKeyRing) data;
-                Set<String> keyUserIds = getUserIds(keyring);
-
-                for (String userIdPart : sigKeyUserId2Password.keySet()) {
-                    for (String keyUserId : keyUserIds) {
-                        if (keyUserId.contains(userIdPart)) {
-                            for (@SuppressWarnings("unchecked")
-                            Iterator<PGPSecretKey> iterKey = 
keyring.getSecretKeys(); iterKey.hasNext();) {
-                                PGPSecretKey secKey = iterKey.next();
-                                if (isSigningKey(secKey)) {
-                                    PGPPrivateKey privateKey = 
secKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider(
-                                            
provider).build(sigKeyUserId2Password.get(userIdPart).toCharArray()));
-                                    if (privateKey != null) {
-                                        result.add(new 
PGPSecretKeyAndPrivateKeyAndUserId(secKey, privateKey, keyUserId));
-                                        LOG.debug("Private key with key user 
ID {} and key ID {} found for specified user ID part {}",
-                                                new Object[] {keyUserId, 
Long.toString(privateKey.getKeyID()), userIdPart });
-
-                                    }
-                                }
-                            }
+                PGPSecretKey primaryKey = keyring.getSecretKey();
+                List<String> useridParts = new 
ArrayList<String>(sigKeyUserId2Password.keySet());
+                String[] foundKeyUserIdForUserIdPart = 
findFirstKeyUserIdContainingOneOfTheParts(useridParts, 
primaryKey.getPublicKey());
+                if (foundKeyUserIdForUserIdPart == null) {
+                    LOG.debug("No User ID found in primary key with key ID {} 
containing one of the parts {}", primaryKey.getKeyID(),
+                            useridParts);
+                    continue;
+                }
+                LOG.debug("User ID {} found in primary key with key ID {} 
containing one of the parts {}", new Object[] {
+                    foundKeyUserIdForUserIdPart[0], primaryKey.getKeyID(), 
useridParts });
+                // add all signing keys
+                for (@SuppressWarnings("unchecked")
+                Iterator<PGPSecretKey> iterKey = keyring.getSecretKeys(); 
iterKey.hasNext();) {
+                    PGPSecretKey secKey = iterKey.next();
+                    if (isSigningKey(secKey)) {
+                        PGPPrivateKey privateKey = 
secKey.extractPrivateKey(new 
JcePBESecretKeyDecryptorBuilder().setProvider(provider)
+                                
.build(sigKeyUserId2Password.get(foundKeyUserIdForUserIdPart[1]).toCharArray()));
+                        if (privateKey != null) {
+                            result.add(new 
PGPSecretKeyAndPrivateKeyAndUserId(secKey, privateKey, 
foundKeyUserIdForUserIdPart[0]));
+                            LOG.debug("Private key with user ID {} and key ID 
{} added to the signing keys",
+                                    foundKeyUserIdForUserIdPart[0], 
Long.toString(privateKey.getKeyID()));
+
                         }
                     }
                 }
@@ -493,14 +482,19 @@ public final class PGPDataFormatUtil {
         }
         return null; // no key flag
     }
-    
-    
+
     /**
-     * Determines a public key from the keyring collection which has a certain 
key ID and which has a User ID which contains at least one of the User ID parts.
+     * Determines a public key from the keyring collection which has a certain
+     * key ID and which has a User ID which contains at least one of the User 
ID
+     * parts.
      * 
-     * @param keyId key ID
-     * @param userIdParts user ID parts, can be empty, than no filter on the 
User ID is executed
-     * @param publicKeyringCollection keyring collection
+     * @param keyId
+     *            key ID
+     * @param userIdParts
+     *            user ID parts, can be empty, than no filter on the User ID is
+     *            executed
+     * @param publicKeyringCollection
+     *            keyring collection
      * @return public key or <code>null</code> if no fitting key is found
      * @throws PGPException
      */
@@ -519,7 +513,7 @@ public final class PGPDataFormatUtil {
             return null;
         }
     }
-    
+
     private static boolean isAllowedKey(List<String> allowedUserIds, 
Iterator<String> verifyingPublicKeyUserIds) {
 
         if (allowedUserIds == null || allowedUserIds.isEmpty()) {
@@ -543,5 +537,5 @@ public final class PGPDataFormatUtil {
                 keyUserId, allowedUserIds);
         return false;
     }
-    
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/e8c9f630/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPKeyAccessDataFormat.java
----------------------------------------------------------------------
diff --git 
a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPKeyAccessDataFormat.java
 
b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPKeyAccessDataFormat.java
index 92663c4..179a4d1 100644
--- 
a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPKeyAccessDataFormat.java
+++ 
b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPKeyAccessDataFormat.java
@@ -18,12 +18,14 @@ package org.apache.camel.converter.crypto;
 
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.security.SecureRandom;
 import java.security.Security;
 import java.security.SignatureException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
@@ -76,6 +78,10 @@ import org.slf4j.LoggerFactory;
  * array or file, then you should use the class {@link PGPDataFormat}.
  * 
  */
+/**
+ * @author D023101
+ * 
+ */
 public class PGPKeyAccessDataFormat extends ServiceSupport implements 
DataFormat {
 
     public static final String KEY_USERID = "CamelPGPDataFormatKeyUserid";
@@ -87,10 +93,40 @@ public class PGPKeyAccessDataFormat extends ServiceSupport 
implements DataFormat
     public static final String COMPRESSION_ALGORITHM = 
"CamelPGPDataFormatCompressionAlgorithm";
 
     /**
-     * During encryption the number of asymmectirc encryption keys is set to
-     * this header parameter. The Value is of type Integer.
+     * Signature verification option "optional": Used during unmarshaling. The
+     * PGP message can or cannot contain signatures. If it does contain
+     * signatures then one of them is verified. This is the default option.
+     */
+    public static final String SIGNATURE_VERIFICATION_OPTION_OPTIONAL = 
"optional";
+
+    /**
+     * Signature verification option "required": Used during unmarshaling. It 
is
+     * checked that the PGP message does contain at least one signature. If 
this
+     * is not the case a {@link PGPException} is thrown. One of the contained 
+     * signatures is verified.
+     */
+    public static final String SIGNATURE_VERIFICATION_OPTION_REQUIRED = 
"required";
+    
+    /**
+     * Signature verification option "required": Used during unmarshaling. If 
+     * the PGP message contains signatures then they are ignored. No 
+     * verification takes place.
+     */
+    public static final String SIGNATURE_VERIFICATION_OPTION_IGNORE = "ignore";
+
+    /**
+     * Signature verification option "no signature allowed": Used during
+     * unmarshaling. It is checked that the PGP message does contain not any
+     * signatures. If this is not the case a {@link PGPException} is thrown.
+     */
+    public static final String 
SIGNATURE_VERIFICATION_OPTION_NO_SIGNATURE_ALLOWED = "no_signature_allowed";
+
+    /**
+     * During encryption the number of asymmetric encryption keys is set to 
this
+     * header parameter. The Value is of type Integer.
      */
     public static final String NUMBER_OF_ENCRYPTION_KEYS = 
"CamelPGPDataFormatNumberOfEncryptionKeys";
+
     /**
      * During signing the number of signing keys is set to this header
      * parameter. This corresponds to the number of signatures. The Value is of
@@ -99,11 +135,13 @@ public class PGPKeyAccessDataFormat extends ServiceSupport 
implements DataFormat
     public static final String NUMBER_OF_SIGNING_KEYS = 
"CamelPGPDataFormatNumberOfSigningKeys";
 
     private static final Logger LOG = 
LoggerFactory.getLogger(PGPKeyAccessDataFormat.class);
-    
+
+    private static final List<String> SIGNATURE_VERIFICATION_OPTIONS = 
Arrays.asList(new String[] {SIGNATURE_VERIFICATION_OPTION_OPTIONAL,
+        SIGNATURE_VERIFICATION_OPTION_REQUIRED, 
SIGNATURE_VERIFICATION_OPTION_IGNORE, 
SIGNATURE_VERIFICATION_OPTION_NO_SIGNATURE_ALLOWED });
 
     private static final String BC = "BC";
     private static final int BUFFER_SIZE = 16 * 1024;
-    
+
     PGPPublicKeyAccessor publicKeyAccessor;
 
     PGPSecretKeyAccessor secretKeyAccessor;
@@ -130,6 +168,8 @@ public class PGPKeyAccessDataFormat extends ServiceSupport 
implements DataFormat
 
     private int compressionAlgorithm = CompressionAlgorithmTags.ZIP; // for 
encryption
 
+    private String signatureVerificationOption = "optional";
+
     public PGPKeyAccessDataFormat() {
     }
 
@@ -308,52 +348,9 @@ public class PGPKeyAccessDataFormat extends ServiceSupport 
implements DataFormat
             return null;
         }
         InputStream in = PGPUtil.getDecoderStream(encryptedStream);
-        PGPObjectFactory pgpFactory = new PGPObjectFactory(in);
-        Object firstObject = pgpFactory.nextObject();
-        // the first object might be a PGP marker packet 
-        PGPEncryptedDataList enc;
-        if (firstObject instanceof PGPEncryptedDataList) {
-            enc = (PGPEncryptedDataList) firstObject;
-        } else {
-            Object secondObject = pgpFactory.nextObject();
-            if (secondObject instanceof PGPEncryptedDataList) {
-                enc = (PGPEncryptedDataList)secondObject;
-            } 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) encryptedData;
-            key = secretKeyAccessor.getPrivateKey(exchange, pbe.getKeyID());
-            if (key != null) {
-                // take the first key
-                break;
-            }
-        }
-        if (key == null) {
-            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);
-        Object compObj = pgpFactory.nextObject();
-        if (!(compObj instanceof PGPCompressedData)) {
-            throw getFormatException();
-        }
-        PGPCompressedData comData = (PGPCompressedData)compObj;
-        pgpFactory = new PGPObjectFactory(comData.getDataStream());
+        InputStream encData = getDecryptedData(exchange, in);
+        InputStream uncompressedData = getUncompressedData(encData);
+        PGPObjectFactory pgpFactory = new PGPObjectFactory(uncompressedData);
         Object object = pgpFactory.nextObject();
 
         PGPOnePassSignature signature;
@@ -361,7 +358,12 @@ public class PGPKeyAccessDataFormat extends ServiceSupport 
implements DataFormat
             signature = getSignature(exchange, (PGPOnePassSignatureList) 
object);
             object = pgpFactory.nextObject();
         } else {
+            // no signature contained in PGP message
             signature = null;
+            if 
(SIGNATURE_VERIFICATION_OPTION_REQUIRED.equals(getSignatureVerificationOption()))
 {
+                throw new PGPException(
+                        "PGP message does not contain any signatures although 
a signature is expected. Either send a PGP message with signature or change the 
configuration of the PGP decryptor.");
+            }
         }
 
         PGPLiteralData ld;
@@ -400,12 +402,7 @@ public class PGPKeyAccessDataFormat extends ServiceSupport 
implements DataFormat
             IOHelper.close(os, litData, encData, in);
         }
 
-        if (signature != null) {
-            PGPSignatureList sigList = (PGPSignatureList) 
pgpFactory.nextObject();
-            if (!signature.verify(getSignatureWithKeyId(signature.getKeyID(), 
sigList))) {
-                throw new SignatureException("Cannot verify PGP signature");
-            }
-        }
+        verifySignature(pgpFactory, signature);
 
         if (cos != null) {
             return cos.newStreamCache();
@@ -414,11 +411,79 @@ public class PGPKeyAccessDataFormat extends 
ServiceSupport implements DataFormat
         }
     }
 
+    private InputStream getUncompressedData(InputStream encData) throws 
IOException, PGPException {
+        PGPObjectFactory pgpFactory = new PGPObjectFactory(encData);
+        Object compObj = pgpFactory.nextObject();
+        if (!(compObj instanceof PGPCompressedData)) {
+            throw getFormatException();
+        }
+        PGPCompressedData comData = (PGPCompressedData) compObj;
+        InputStream uncompressedData = comData.getDataStream();
+        return uncompressedData;
+    }
+
+    private InputStream getDecryptedData(Exchange exchange, InputStream 
encryptedStream) throws Exception, PGPException {
+        PGPObjectFactory pgpFactory = new PGPObjectFactory(encryptedStream);
+        Object firstObject = pgpFactory.nextObject();
+        // the first object might be a PGP marker packet 
+        PGPEncryptedDataList enc = getEcryptedDataList(pgpFactory, 
firstObject);
+
+        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) encryptedData;
+            key = secretKeyAccessor.getPrivateKey(exchange, pbe.getKeyID());
+            if (key != null) {
+                // take the first key
+                break;
+            }
+        }
+        if (key == null) {
+            throw new PGPException("PGP message is encrypted with a key which 
could not be found in the Secret Keyring.");
+        }
+
+        InputStream encData = pbe.getDataStream(new 
JcePublicKeyDataDecryptorFactoryBuilder().setProvider(getProvider()).build(key));
+        return encData;
+    }
+
+    private PGPEncryptedDataList getEcryptedDataList(PGPObjectFactory 
pgpFactory, Object firstObject) throws IOException {
+        PGPEncryptedDataList enc;
+        if (firstObject instanceof PGPEncryptedDataList) {
+            enc = (PGPEncryptedDataList) firstObject;
+        } else {
+            Object secondObject = pgpFactory.nextObject();
+            if (secondObject instanceof PGPEncryptedDataList) {
+                enc = (PGPEncryptedDataList) secondObject;
+            } else {
+                enc = null;
+            }
+        }
+        return enc;
+    }
+
+    private void verifySignature(PGPObjectFactory pgpFactory, 
PGPOnePassSignature signature) throws IOException, PGPException, 
SignatureException {
+        if (signature != null) {
+            PGPSignatureList sigList = (PGPSignatureList) 
pgpFactory.nextObject();
+            if (!signature.verify(getSignatureWithKeyId(signature.getKeyID(), 
sigList))) {
+                throw new SignatureException("Verification of the PGP 
signature with the key ID " + signature.getKeyID() + " failed. The PGP message 
may have been tampered.");
+            }
+        }
+    }
+
     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 ...,)");
+        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) {
@@ -432,7 +497,13 @@ public class PGPKeyAccessDataFormat extends ServiceSupport 
implements DataFormat
     }
 
     protected PGPOnePassSignature getSignature(Exchange exchange, 
PGPOnePassSignatureList signatureList) throws Exception {
-
+        if 
(SIGNATURE_VERIFICATION_OPTION_IGNORE.equals(getSignatureVerificationOption())) 
{
+            return null;
+        }
+        if 
(SIGNATURE_VERIFICATION_OPTION_NO_SIGNATURE_ALLOWED.equals(getSignatureVerificationOption()))
 {
+            throw new PGPException(
+                    "PGP message contains a signature although a signature is 
not expected. Either change the configuration of the PGP decryptor or send a 
PGP message with no signature.");
+        }
         List<String> allowedUserIds = determineSignaturenUserIds(exchange);
         for (int i = 0; i < signatureList.size(); i++) {
             PGPOnePassSignature signature = signatureList.get(i);
@@ -448,7 +519,8 @@ public class PGPKeyAccessDataFormat extends ServiceSupport 
implements DataFormat
         if (signatureList.isEmpty()) {
             return null;
         } else {
-            throw new IllegalArgumentException("No public key found fitting to 
the signature key Id; cannot verify the signature.");
+            throw new IllegalArgumentException("Cannot verify the PGP 
signature: No public key found for the key ID(s) contained in the PGP 
signature(s). "
+                + "Either the received PGP message contains a signature from 
an unexpected sender or the Public Keyring does not contain the public key of 
the sender.");
         }
 
     }
@@ -626,6 +698,32 @@ public class PGPKeyAccessDataFormat extends ServiceSupport 
implements DataFormat
         this.secretKeyAccessor = secretKeyAccessor;
     }
 
+    public String getSignatureVerificationOption() {
+        return signatureVerificationOption;
+    }
+
+    /**
+     * Signature verification option. Controls the behavior for the signature
+     * verification during unmarshaling. Possible values are
+     * {@link #SIGNATURE_VERIFICATION_OPTION_OPTIONAL},
+     * {@link #SIGNATURE_VERIFICATION_OPTION_REQUIRED},
+     * {@link #SIGNATURE_VERIFICATION_OPTION_NO_SIGNATURE_ALLOWED}, and
+     * {@link #SIGNATURE_VERIFICATION_OPTION_IGNORE}. The default
+     * value is {@link #SIGNATURE_VERIFICATION_OPTION_OPTIONAL}
+     * 
+     * @param signatureVerificationOption
+     *            signature verification option
+     * @throws IllegalArgument
+     *             exception if an invalid value is entered
+     */
+    public void setSignatureVerificationOption(String 
signatureVerificationOption) {
+        if 
(SIGNATURE_VERIFICATION_OPTIONS.contains(signatureVerificationOption)) {
+            this.signatureVerificationOption = signatureVerificationOption;
+        } else {
+            throw new IllegalArgumentException(signatureVerificationOption + " 
is not a valid signature verification option");
+        }
+    }
+
     @Override
     protected void doStart() throws Exception {
         if (Security.getProvider(BC) == null && BC.equals(getProvider())) {

http://git-wip-us.apache.org/repos/asf/camel/blob/e8c9f630/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 5237710..98f9121 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
@@ -64,6 +64,7 @@ import 
org.bouncycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerat
 import 
org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
 import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
 import 
org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
+import org.junit.Before;
 import org.junit.Test;
 
 public class PGPDataFormatTest extends AbstractPGPDataFormatTest {
@@ -72,6 +73,26 @@ public class PGPDataFormatTest extends 
AbstractPGPDataFormatTest {
     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";
 
+    PGPDataFormat encryptor = new PGPDataFormat();
+    PGPDataFormat decryptor = new PGPDataFormat();
+
+    @Before
+    public void setUpEncryptorAndDecryptor() {
+
+        // the following keyring contains a primary key with KeyFlag "Certify" 
and a subkey for signing and a subkey for encryption
+        encryptor.setKeyFileName(PUB_KEY_RING_SUBKEYS_FILE_NAME);
+        
encryptor.setSignatureKeyFileName("org/apache/camel/component/crypto/secringSubKeys.gpg");
+        encryptor.setSignaturePassword("Abcd1234");
+        encryptor.setKeyUserid("keyflag");
+        encryptor.setSignatureKeyUserid("keyflag");
+
+        // the following keyring contains a primary key with KeyFlag "Certify" 
and a subkey for signing and a subkey for encryption
+        
decryptor.setKeyFileName("org/apache/camel/component/crypto/secringSubKeys.gpg");
+        decryptor.setSignatureKeyFileName(PUB_KEY_RING_SUBKEYS_FILE_NAME);
+        decryptor.setPassword("Abcd1234");
+        decryptor.setSignatureKeyUserid("keyflag");
+    }
+
     protected String getKeyFileName() {
         return PUB_KEY_RING_FILE_NAME;
     }
@@ -173,7 +194,7 @@ public class PGPDataFormatTest extends 
AbstractPGPDataFormatTest {
         template.sendBodyAndHeaders("direct:verify_exception_sig_userids", 
payload, headers);
         assertMockEndpointsSatisfied();
 
-        checkThrownException(exception, IllegalArgumentException.class, null, 
"No public key found fitting to the signature key Id");
+        checkThrownException(exception, IllegalArgumentException.class, null, 
"No public key found for the key ID(s)");
 
     }
 
@@ -299,7 +320,7 @@ public class PGPDataFormatTest extends 
AbstractPGPDataFormatTest {
         assertMockEndpointsSatisfied();
 
         checkThrownException(mock, PGPException.class, null,
-                "Message is encrypted with a key which could not be found in 
the Secret Key Ring");
+                "PGP message is encrypted with a key which could not be found 
in the Secret Keyring");
     }
 
     void createEncryptedNonCompressedData(ByteArrayOutputStream bos, String 
keyringPath) throws Exception, IOException, PGPException,
@@ -419,6 +440,49 @@ public class PGPDataFormatTest extends 
AbstractPGPDataFormatTest {
         checkThrownException(mock, IllegalArgumentException.class, null, "The 
input message body has an invalid format.");
     }
 
+    @Test
+    public void 
testExceptionForSignatureVerificationOptionNoSignatureAllowed() throws 
Exception {
+
+        
decryptor.setSignatureVerificationOption(PGPDataFormat.SIGNATURE_VERIFICATION_OPTION_NO_SIGNATURE_ALLOWED);
+
+        MockEndpoint mock = getMockEndpoint("mock:exception");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:subkey", "Test Message");
+        assertMockEndpointsSatisfied();
+
+        checkThrownException(mock, PGPException.class, null, "PGP message 
contains a signature although a signature is not expected");
+    }
+
+    @Test
+    public void testExceptionForSignatureVerificationOptionRequired() throws 
Exception {
+
+        encryptor.setSignatureKeyUserid(null); // no signature
+        
decryptor.setSignatureVerificationOption(PGPDataFormat.SIGNATURE_VERIFICATION_OPTION_REQUIRED);
+
+        MockEndpoint mock = getMockEndpoint("mock:exception");
+        mock.expectedMessageCount(1);
+        template.sendBody("direct:subkey", "Test Message");
+        assertMockEndpointsSatisfied();
+
+        checkThrownException(mock, PGPException.class, null, "PGP message does 
not contain any signatures although a signature is expected");
+    }
+
+    @Test
+    public void testSignatureVerificationOptionIgnore() throws Exception {
+
+        // encryptor is sending a PGP message with signature! Decryptor is 
ignoreing the signature
+        
decryptor.setSignatureVerificationOption(PGPDataFormat.SIGNATURE_VERIFICATION_OPTION_IGNORE);
+        decryptor.setSignatureKeyUserids(null);
+        decryptor.setSignatureKeyFileName(null); // no public keyring! --> no 
signature validation possible
+
+        String payload = "Test Message";
+        MockEndpoint mock = getMockEndpoint("mock:unencrypted");
+        mock.expectedBodiesReceived(payload);
+        template.sendBody("direct:subkey", payload);
+        assertMockEndpointsSatisfied();
+
+    }
+
     protected RouteBuilder[] createRouteBuilders() {
         return new RouteBuilder[] {new RouteBuilder() {
             public void configure() throws Exception {
@@ -451,6 +515,7 @@ public class PGPDataFormatTest extends 
AbstractPGPDataFormatTest {
                 pgpDecrypt.setKeyFileName(keyFileNameSec);
                 pgpDecrypt.setPassword(keyPassword);
                 pgpDecrypt.setProvider(getProvider());
+                
pgpDecrypt.setSignatureVerificationOption(PGPDataFormat.SIGNATURE_VERIFICATION_OPTION_NO_SIGNATURE_ALLOWED);
 
                 
from("direct:inline2").marshal(pgpEncrypt).to("mock:encrypted").unmarshal(pgpDecrypt).to("mock:unencrypted");
 
@@ -523,6 +588,7 @@ public class PGPDataFormatTest extends 
AbstractPGPDataFormatTest {
                 pgpVerifyAndDecryptByteArray.setProvider(getProvider());
                 // restrict verification to public keys with certain User ID
                 
pgpVerifyAndDecryptByteArray.setSignatureKeyUserids(getSignatureKeyUserIds());
+                
pgpVerifyAndDecryptByteArray.setSignatureVerificationOption(PGPDataFormat.SIGNATURE_VERIFICATION_OPTION_REQUIRED);
 
                 from("direct:sign-key-ring-byte-array").streamCaching()
                 // encryption key ring can also be set as header
@@ -609,28 +675,13 @@ public class PGPDataFormatTest extends 
AbstractPGPDataFormatTest {
             public void configure() throws Exception {
 
                 
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(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");
+
+                
from("direct:keyflag").marshal(encryptor).to("mock:encrypted_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:subkey").marshal(encryptor).to("mock:encrypted").unmarshal(decryptor).to("mock:unencrypted");
 
-                
from("direct:subkeyUnmarshal").unmarshal(pgpDecryptVerifySubkey).to("mock:unencrypted");
+                
from("direct:subkeyUnmarshal").unmarshal(decryptor).to("mock:unencrypted");
             }
         }, new RouteBuilder() {
             public void configure() throws Exception {

Reply via email to