You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2014/03/13 10:41:42 UTC
git commit: CAMEL-7283: PGP Data Format: Signature Verification
Options. Thanks to Franz Forsthofer for the patch.
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 <da...@apache.org>
Authored: Thu Mar 13 10:42:03 2014 +0100
Committer: Claus Ibsen <da...@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 {