You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ni...@apache.org on 2014/02/17 06:16:48 UTC
[2/3] git commit: CAMEL-7201 PGPDataFormat: allow caching of PGP keys
via key access interface with thanks to Franz
CAMEL-7201 PGPDataFormat: allow caching of PGP keys via key access interface 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/62c8b7ad
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/62c8b7ad
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/62c8b7ad
Branch: refs/heads/master
Commit: 62c8b7ad2215668e8d45f874ab06a5d04aee19b5
Parents: 54172d6
Author: Willem Jiang <wi...@gmail.com>
Authored: Mon Feb 17 13:11:11 2014 +0800
Committer: Willem Jiang <wi...@gmail.com>
Committed: Mon Feb 17 13:11:11 2014 +0800
----------------------------------------------------------------------
.../camel/converter/crypto/PGPDataFormat.java | 624 ++----------------
.../converter/crypto/PGPDataFormatUtil.java | 74 +--
.../crypto/PGPKeyAccessDataFormat.java | 642 +++++++++++++++++++
.../converter/crypto/PGPPublicKeyAccess.java | 50 ++
.../crypto/PGPPublicKeyAccessDefault.java | 53 ++
.../converter/crypto/PGPSecretKeyAccess.java | 55 ++
.../crypto/PGPSecretKeyAccessDefault.java | 100 +++
.../PGPSecretKeyAndPrivateKeyAndUserId.java | 53 ++
.../converter/crypto/PGPDataFormatTest.java | 27 +
.../crypto/PGPKeyAccessDataFormatTest.java | 41 ++
10 files changed, 1091 insertions(+), 628 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/camel/blob/62c8b7ad/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..6fcb961 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
@@ -16,134 +16,53 @@
*/
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.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.security.SignatureException;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.Date;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.camel.Exchange;
-import org.apache.camel.converter.crypto.PGPDataFormatUtil.PGPSecretKeyAndPrivateKeyAndUserId;
-import org.apache.camel.converter.stream.CachedOutputStream;
-import org.apache.camel.spi.DataFormat;
-import org.apache.camel.support.ServiceSupport;
-import org.apache.camel.util.ExchangeHelper;
-import org.apache.camel.util.IOHelper;
-import org.apache.camel.util.ObjectHelper;
-import org.bouncycastle.bcpg.ArmoredOutputStream;
-import org.bouncycastle.bcpg.CompressionAlgorithmTags;
-import org.bouncycastle.bcpg.HashAlgorithmTags;
-import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openpgp.PGPCompressedData;
-import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
-import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
-import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
-import org.bouncycastle.openpgp.PGPLiteralData;
-import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
-import org.bouncycastle.openpgp.PGPObjectFactory;
-import org.bouncycastle.openpgp.PGPOnePassSignature;
-import org.bouncycastle.openpgp.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
-import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
-import org.bouncycastle.openpgp.PGPSignature;
-import org.bouncycastle.openpgp.PGPSignatureGenerator;
-import org.bouncycastle.openpgp.PGPSignatureList;
-import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
-import org.bouncycastle.openpgp.PGPUtil;
-import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
-import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
-import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
-import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
-import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* <code>PGPDataFormat</code> uses the <a
* href="http://www.bouncycastle.org/java.htm">bouncy castle</a> libraries to
* enable encryption and decryption in the PGP format.
+ * <p>
+ * See also {@link PGPKeyAccessDataFormat}.
+ *
*/
-public class PGPDataFormat extends ServiceSupport implements DataFormat {
+public class PGPDataFormat extends PGPKeyAccessDataFormat implements PGPPublicKeyAccess, PGPSecretKeyAccess {
public static final String KEY_FILE_NAME = "CamelPGPDataFormatKeyFileName";
public static final String ENCRYPTION_KEY_RING = "CamelPGPDataFormatEncryptionKeyRing";
- public static final String KEY_USERID = "CamelPGPDataFormatKeyUserid";
- public static final String KEY_USERIDS = "CamelPGPDataFormatKeyUserids";
public static final String KEY_PASSWORD = "CamelPGPDataFormatKeyPassword";
public static final String SIGNATURE_KEY_FILE_NAME = "CamelPGPDataFormatSignatureKeyFileName";
public static final String SIGNATURE_KEY_RING = "CamelPGPDataFormatSignatureKeyRing";
- public static final String SIGNATURE_KEY_USERID = "CamelPGPDataFormatSignatureKeyUserid";
- public static final String SIGNATURE_KEY_USERIDS = "CamelPGPDataFormatSignatureKeyUserids";
public static final String SIGNATURE_KEY_PASSWORD = "CamelPGPDataFormatSignatureKeyPassword";
- public static final String ENCRYPTION_ALGORITHM = "CamelPGPDataFormatEncryptionAlgorithm";
- public static final String SIGNATURE_HASH_ALGORITHM = "CamelPGPDataFormatSignatureHashAlgorithm";
- 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.
- */
- 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
- * type Integer.
- */
- public static final String NUMBER_OF_SIGNING_KEYS = "CamelPGPDataFormatNumberOfSigningKeys";
-
- private static final Logger LOG = LoggerFactory.getLogger(PGPDataFormat.class);
-
- private static final String BC = "BC";
- private static final int BUFFER_SIZE = 16 * 1024;
-
- // Java Cryptography Extension provider, default is Bouncy Castle
- private String provider = BC;
+ //private static final Logger LOG = LoggerFactory.getLogger(PGPDataFormatChanged.class);
- // encryption / decryption key info (required)
- private String keyUserid; // only for encryption
- //in addition you can specify further User IDs, in this case the symmetric key is encrypted by several public keys corresponding to the User Ids
- private List<String> keyUserids; //only for encryption;
private String password; // only for decryption
private String keyFileName;
// alternatively to the file name you can specify the key ring as byte array
private byte[] encryptionKeyRing;
- // signature / verification key info (optional)
- private String signatureKeyUserid; // for signing and verification (optional for verification)
- //For verification you can specify further User IDs in addition
- private List<String> signatureKeyUserids; //only for signing with several keys and verifying;
private String signaturePassword; //only for signing, optional if you have several signature keys, then you should use passphaseAccessor
private String signatureKeyFileName;
// alternatively to the signature key file name you can specify the signature key ring as byte array
private byte[] signatureKeyRing;
- private boolean armored; // for encryption
- private boolean integrity = true; // for encryption
-
- private int hashAlgorithm = HashAlgorithmTags.SHA1; // for encryption
-
- private int algorithm = SymmetricKeyAlgorithmTags.CAST5; // for encryption
-
- private int compressionAlgorithm = CompressionAlgorithmTags.ZIP; // for encryption
-
private PGPPassphraseAccessor passphraseAccessor; // for signing and decryption with multiple keys
public PGPDataFormat() {
+ super();
+ publicKeyAccess = this;
+ secretKeyAccess = this;
}
protected String findKeyFileName(Exchange exchange) {
@@ -154,15 +73,6 @@ public class PGPDataFormat extends ServiceSupport implements DataFormat {
return exchange.getIn().getHeader(ENCRYPTION_KEY_RING, getEncryptionKeyRing(), byte[].class);
}
- protected String findKeyUserid(Exchange exchange) {
- return exchange.getIn().getHeader(KEY_USERID, getKeyUserid(), String.class);
- }
-
- @SuppressWarnings("unchecked")
- protected List<String> findKeyUserids(Exchange exchange) {
- return exchange.getIn().getHeader(KEY_USERIDS, getKeyUserids(), List.class);
- }
-
protected String findKeyPassword(Exchange exchange) {
return exchange.getIn().getHeader(KEY_PASSWORD, getPassword(), String.class);
// the following lines are not needed because the passphrase accessor is taken into account later in the decryption case
@@ -181,15 +91,6 @@ public class PGPDataFormat extends ServiceSupport implements DataFormat {
return exchange.getIn().getHeader(SIGNATURE_KEY_RING, getSignatureKeyRing(), byte[].class);
}
- protected String findSignatureKeyUserid(Exchange exchange) {
- return exchange.getIn().getHeader(SIGNATURE_KEY_USERID, getSignatureKeyUserid(), String.class);
- }
-
- @SuppressWarnings("unchecked")
- protected List<String> findSignatureKeyUserids(Exchange exchange) {
- return exchange.getIn().getHeader(SIGNATURE_KEY_USERIDS, getSignatureKeyUserids(), List.class);
- }
-
protected String findSignatureKeyPassword(Exchange exchange) {
String sigPassword = exchange.getIn().getHeader(SIGNATURE_KEY_PASSWORD, getSignaturePassword(), String.class);
if (sigPassword != null) {
@@ -202,162 +103,6 @@ public class PGPDataFormat extends ServiceSupport implements DataFormat {
}
}
- protected int findCompressionAlgorithm(Exchange exchange) {
- return exchange.getIn().getHeader(COMPRESSION_ALGORITHM, getCompressionAlgorithm(), Integer.class);
- }
-
- protected int findAlgorithm(Exchange exchange) {
- return exchange.getIn().getHeader(ENCRYPTION_ALGORITHM, getAlgorithm(), Integer.class);
- }
-
- protected int findHashAlgorithm(Exchange exchange) {
- return exchange.getIn().getHeader(SIGNATURE_HASH_ALGORITHM, getHashAlgorithm(), Integer.class);
- }
-
- public void marshal(Exchange exchange, Object graph, OutputStream outputStream) throws Exception {
- List<String> userids = determineEncryptionUserIds(exchange);
- List<PGPPublicKey> keys = PGPDataFormatUtil.findPublicKeys(exchange.getContext(), findKeyFileName(exchange),
- findEncryptionKeyRing(exchange), userids, true);
- if (keys.isEmpty()) {
- throw new IllegalArgumentException("Cannot PGP encrypt message. No public encryption key found for the User Ids " + userids
- + " in the public keyring. Either specify other User IDs or add correct public keys to the keyring.");
- }
- exchange.getOut().setHeader(NUMBER_OF_ENCRYPTION_KEYS, Integer.valueOf(keys.size()));
-
- InputStream input = ExchangeHelper.convertToMandatoryType(exchange, InputStream.class, graph);
-
- if (armored) {
- outputStream = new ArmoredOutputStream(outputStream);
- }
-
- PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(findAlgorithm(exchange))
- .setWithIntegrityPacket(integrity).setSecureRandom(new SecureRandom()).setProvider(getProvider()));
- // several keys can be added
- for (PGPPublicKey key : keys) {
- encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(key));
- }
- OutputStream encOut = encGen.open(outputStream, new byte[BUFFER_SIZE]);
-
- PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(findCompressionAlgorithm(exchange));
- OutputStream comOut = new BufferedOutputStream(comData.open(encOut));
-
- List<PGPSignatureGenerator> sigGens = createSignatureGenerator(exchange, comOut);
-
- PGPLiteralDataGenerator litData = new PGPLiteralDataGenerator();
- String fileName = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
- if (ObjectHelper.isEmpty(fileName)) {
- // This marks the file as For Your Eyes Only... may cause problems for the receiver if they use
- // an automated process to decrypt as the filename is appended with _CONSOLE
- fileName = PGPLiteralData.CONSOLE;
- }
- OutputStream litOut = litData.open(comOut, PGPLiteralData.BINARY, fileName, new Date(), new byte[BUFFER_SIZE]);
-
- try {
- byte[] buffer = new byte[BUFFER_SIZE];
- int bytesRead;
- while ((bytesRead = input.read(buffer)) != -1) {
- litOut.write(buffer, 0, bytesRead);
- if (sigGens != null && !sigGens.isEmpty()) {
- for (PGPSignatureGenerator sigGen : sigGens) {
- // not nested therefore it is the same for all
- // can this be improved that we only do it for one sigGen and set the result on the others?
- sigGen.update(buffer, 0, bytesRead);
- }
- }
- litOut.flush();
- }
- } finally {
- IOHelper.close(litOut);
- if (sigGens != null && !sigGens.isEmpty()) {
- // reverse order
- for (int i = sigGens.size() - 1; i > -1; i--) {
- PGPSignatureGenerator sigGen = sigGens.get(i);
- sigGen.generate().encode(comOut);
- }
- }
- IOHelper.close(comOut, encOut, outputStream, input);
- }
- }
-
- protected List<String> determineEncryptionUserIds(Exchange exchange) {
- String userid = findKeyUserid(exchange);
- List<String> userids = findKeyUserids(exchange);
- // merge them together
- List<String> result;
- if (userid != null) {
- if (userids == null || userids.isEmpty()) {
- result = Collections.singletonList(userid);
- } else {
- result = new ArrayList<String>(userids.size() + 1);
- result.add(userid);
- result.addAll(userids);
- }
- } else {
- if (userids == null || userids.isEmpty()) {
- throw new IllegalStateException("Cannot PGP encrypt message. No User ID of the public key specified.");
- }
- result = userids;
- }
- return result;
- }
-
- protected List<String> determineSignaturenUserIds(Exchange exchange) {
- String userid = findSignatureKeyUserid(exchange);
- List<String> userids = findSignatureKeyUserids(exchange);
- // merge them together
- List<String> result;
- if (userid != null) {
- if (userids == null || userids.isEmpty()) {
- result = Collections.singletonList(userid);
- } else {
- result = new ArrayList<String>(userids.size() + 1);
- result.add(userid);
- result.addAll(userids);
- }
- } else {
- // userids can be empty or null!
- result = userids;
- }
- return result;
- }
-
- protected List<PGPSignatureGenerator> createSignatureGenerator(Exchange exchange, OutputStream out) throws IOException, PGPException,
- NoSuchProviderException, NoSuchAlgorithmException {
-
- String sigKeyFileName = findSignatureKeyFileName(exchange);
- List<String> sigKeyUserids = determineSignaturenUserIds(exchange);
- String sigKeyPassword = findSignatureKeyPassword(exchange);
- byte[] sigKeyRing = findSignatureKeyRing(exchange);
-
- if ((sigKeyFileName == null && sigKeyRing == null) || sigKeyUserids == null || sigKeyUserids.isEmpty()
- || (sigKeyPassword == null && passphraseAccessor == null)) {
- return null;
- }
-
- List<PGPSecretKeyAndPrivateKeyAndUserId> sigSecretKeysWithPrivateKeyAndUserId = determineSecretKeysWithPrivateKeyAndUserId(
- exchange, sigKeyFileName, sigKeyUserids, sigKeyPassword, sigKeyRing);
-
- exchange.getOut().setHeader(NUMBER_OF_SIGNING_KEYS, Integer.valueOf(sigSecretKeysWithPrivateKeyAndUserId.size()));
-
- List<PGPSignatureGenerator> sigGens = new ArrayList<PGPSignatureGenerator>();
- for (PGPSecretKeyAndPrivateKeyAndUserId sigSecretKeyWithPrivateKeyAndUserId : sigSecretKeysWithPrivateKeyAndUserId) {
- PGPPrivateKey sigPrivateKey = sigSecretKeyWithPrivateKeyAndUserId.getPrivateKey();
-
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- spGen.setSignerUserID(false, sigSecretKeyWithPrivateKeyAndUserId.getUserId());
-
- int algorithm = sigSecretKeyWithPrivateKeyAndUserId.getSecretKey().getPublicKey().getAlgorithm();
- PGPSignatureGenerator sigGen = new PGPSignatureGenerator(
- new JcaPGPContentSignerBuilder(algorithm, findHashAlgorithm(exchange)).setProvider(getProvider()));
- sigGen.init(PGPSignature.BINARY_DOCUMENT, sigPrivateKey);
- sigGen.setHashedSubpackets(spGen.generate());
- sigGen.generateOnePassVersion(false).encode(out);
- sigGens.add(sigGen);
- }
- return sigGens;
- }
-
-
public List<PGPSecretKeyAndPrivateKeyAndUserId> determineSecretKeysWithPrivateKeyAndUserId(Exchange exchange, String sigKeyFileName,
List<String> sigKeyUserids, String sigKeyPassword, byte[] sigKeyRing) throws IOException, PGPException, NoSuchProviderException {
@@ -394,213 +139,6 @@ public class PGPDataFormat extends ServiceSupport implements DataFormat {
return sigKeyUserId2Password;
}
- @SuppressWarnings("resource")
- public Object unmarshal(Exchange exchange, InputStream encryptedStream) throws Exception {
- if (encryptedStream == null) {
- return null;
- }
- InputStream in = PGPUtil.getDecoderStream(encryptedStream);
- PGPObjectFactory pgpFactory = new PGPObjectFactory(in);
- Object o = pgpFactory.nextObject();
- // the first object might be a PGP marker packet
- PGPEncryptedDataList enc;
- if (o instanceof PGPEncryptedDataList) {
- enc = (PGPEncryptedDataList) o;
- } else {
- enc = (PGPEncryptedDataList) pgpFactory.nextObject();
- }
-
- 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++) {
- pbe = (PGPPublicKeyEncryptedData) enc.get(i);
- key = PGPDataFormatUtil.findPrivateKeyWithKeyId(exchange.getContext(), findKeyFileName(exchange),
- findEncryptionKeyRing(exchange), pbe.getKeyID(), findKeyPassword(exchange), getPassphraseAccessor(), getProvider());
- if (key != null) {
- // take the first key
- break;
- }
- }
- if (key == null) {
- throw new PGPException("Provided input is encrypted with unknown pair of keys.");
- }
-
- InputStream encData = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider(getProvider()).build(key));
- pgpFactory = new PGPObjectFactory(encData);
- PGPCompressedData comData = (PGPCompressedData) pgpFactory.nextObject();
- pgpFactory = new PGPObjectFactory(comData.getDataStream());
- Object object = pgpFactory.nextObject();
-
- PGPOnePassSignature signature;
- if (object instanceof PGPOnePassSignatureList) {
- signature = getSignature(exchange, (PGPOnePassSignatureList) object);
- object = pgpFactory.nextObject();
- } else {
- signature = null;
- }
-
- PGPLiteralData ld = (PGPLiteralData) object;
- InputStream litData = ld.getInputStream();
-
- // enable streaming via OutputStreamCache
- CachedOutputStream cos;
- ByteArrayOutputStream bos;
- OutputStream os;
- if (exchange.getContext().getStreamCachingStrategy().isEnabled()) {
- cos = new CachedOutputStream(exchange);
- bos = null;
- os = cos;
- } else {
- cos = null;
- bos = new ByteArrayOutputStream();
- os = bos;
- }
-
- try {
- byte[] buffer = new byte[BUFFER_SIZE];
- int bytesRead;
- while ((bytesRead = litData.read(buffer)) != -1) {
- os.write(buffer, 0, bytesRead);
- if (signature != null) {
- signature.update(buffer, 0, bytesRead);
- }
- os.flush();
- }
- } finally {
- 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");
- }
- }
-
- if (cos != null) {
- return cos.newStreamCache();
- } else {
- return bos.toByteArray();
- }
- }
-
- protected PGPSignature getSignatureWithKeyId(long keyID, PGPSignatureList sigList) {
- for (int i = 0; i < sigList.size(); i++) {
- PGPSignature signature = sigList.get(i);
- if (keyID == signature.getKeyID()) {
- return signature;
- }
- }
- throw new IllegalStateException("PGP signature is inconsistent");
- }
-
- protected PGPOnePassSignature getSignature(Exchange exchange, PGPOnePassSignatureList signatureList) throws IOException, PGPException,
- NoSuchProviderException {
-
- List<String> allowedUserIds = determineSignaturenUserIds(exchange);
- for (int i = 0; i < signatureList.size(); i++) {
- PGPOnePassSignature signature = signatureList.get(i);
- // Determine public key from signature keyId
- PGPPublicKey sigPublicKey = PGPDataFormatUtil.findPublicKeyWithKeyId(exchange.getContext(), findSignatureKeyFileName(exchange),
- findSignatureKeyRing(exchange), signature.getKeyID(), false);
- if (sigPublicKey == null) {
- continue;
- }
- if (isAllowedVerifyingKey(allowedUserIds, sigPublicKey)) {
- // choose that signature for which a public key exists!
- signature.init(new JcaPGPContentVerifierBuilderProvider().setProvider(getProvider()), sigPublicKey);
- return signature;
- }
- }
- if (signatureList.isEmpty()) {
- return null;
- } else {
- throw new IllegalArgumentException("No public key found fitting to the signature key Id; cannot verify the signature");
- }
-
- }
-
- public boolean isAllowedVerifyingKey(List<String> allowedUserIds, PGPPublicKey verifyingPublicKey) {
-
- if (allowedUserIds == null || allowedUserIds.isEmpty()) {
- // no restrictions specified
- return true;
- }
- String keyUserId = null;
- for (@SuppressWarnings("unchecked")
- Iterator<String> iterator = verifyingPublicKey.getUserIDs(); iterator.hasNext();) {
- keyUserId = iterator.next();
- for (String userid : allowedUserIds) {
- if (keyUserId != null && keyUserId.contains(userid)) {
- LOG.debug(
- "Public key with user ID {} fulfills the User ID restriction {}. Therefore this key will be used for the signature verification. ",
- keyUserId, allowedUserIds);
- return true;
- }
- }
- }
- LOG.warn(
- "Public key with User ID {} does not fulfill the User ID restriction {}. Therefore this key will not be used for the signature verification.",
- keyUserId, allowedUserIds);
- return false;
- }
-
- /**
- * Sets if the encrypted file should be written in ascii visible text (for
- * marshaling).
- */
- public void setArmored(boolean armored) {
- this.armored = armored;
- }
-
- public boolean getArmored() {
- return this.armored;
- }
-
- /**
- * Whether or not to add an integrity check/sign to the encrypted file for
- * marshaling.
- */
- public void setIntegrity(boolean integrity) {
- this.integrity = integrity;
- }
-
- public boolean getIntegrity() {
- return this.integrity;
- }
-
- /**
- * User ID, or more precisely user ID part, of the key used for encryption.
- * See also {@link #setKeyUserids(List<String>)}.
- */
- public void setKeyUserid(String keyUserid) {
- this.keyUserid = keyUserid;
- }
-
- public String getKeyUserid() {
- return keyUserid;
- }
-
- public List<String> getKeyUserids() {
- return keyUserids;
- }
-
- /**
- * Keys User IDs, or more precisely user ID parts, used for determining the
- * public keys for encryption. If you just have one User ID, then you can
- * also use the method {@link #setKeyUserid(String)}. The User ID specified
- * in {@link #setKeyUserid(String)} and in this method will be merged
- * together and the public keys which have a User ID which contain a value
- * of the specified User IDs the will be used for the encryption. Be aware
- * that you may get several public keys even if you specify only one User
- * Id, because there can be several public keys which have a User ID which
- * contains the specified User ID.
- */
- public void setKeyUserids(List<String> keyUserids) {
- this.keyUserids = keyUserids;
- }
-
/**
* Filename of the keyring that will be used for the encryption/decryption,
* classpathResource. Alternatively you can provide the keyring also as byte
@@ -628,57 +166,6 @@ public class PGPDataFormat extends ServiceSupport implements DataFormat {
}
/**
- * Userid, or more precisely user ID part, of the signature key used for
- * signing (marshal) and verifying (unmarshal). See also
- * {@link #setSignatureKeyUserids(List)}.
- */
- public void setSignatureKeyUserid(String signatureKeyUserid) {
- this.signatureKeyUserid = signatureKeyUserid;
- }
-
- public String getSignatureKeyUserid() {
- return signatureKeyUserid;
- }
-
- public List<String> getSignatureKeyUserids() {
- return signatureKeyUserids;
- }
-
- /**
- * User IDs, or more precisely user ID parts, used for signing and
- * verification.
- * <p>
- * In the signing case, the User IDs specify the private keys which are used
- * for signing. If the result are several private keys then several
- * signatures will be created. If you just have one signature User ID, then
- * you can also use the method {@link #setSignatureKeyUserid(String)} or
- * this method. The User ID specified in
- * {@link #setSignatureKeyUserid(String)} and in this method will be merged
- * together and the private keys which have a User Id which contain one
- * value out of the specified UserIds will be used for the signature
- * creation. Be aware that you may get several private keys even if you
- * specify only one User Id, because there can be several private keys which
- * have a User ID which contains the specified User ID.
- * <p>
- * In the verification case the User IDs restrict the set of public keys
- * which can be used for verification. The public keys used for verification
- * must contain a User ID which contain one value of the User ID list. If
- * you neither specify in this method and nor specify in the method
- * {@link #setSignatureKeyUserid(String)} any value then any public key in
- * the public key ring will be taken into consideration for the
- * verification.
- * <p>
- * If you just have one User ID, then you can also use the method
- * {@link #setSignatureKeyUserid(String)}. The User ID specified in
- * {@link #setSignatureKeyUserid(String)} and in this method will be merged
- * together and the corresponding public keys represent the potential keys
- * for the verification of the message.
- */
- public void setSignatureKeyUserids(List<String> signatureKeyUserids) {
- this.signatureKeyUserids = signatureKeyUserids;
- }
-
- /**
* Filename of the signature keyring that will be used, classpathResource.
*/
public void setSignatureKeyFileName(String signatureKeyFileName) {
@@ -726,57 +213,6 @@ public class PGPDataFormat extends ServiceSupport implements DataFormat {
this.signatureKeyRing = signatureKeyRing;
}
- public String getProvider() {
- return provider;
- }
-
- /**
- * Java Cryptography Extension (JCE) provider, default is Bouncy Castle
- * ("BC"). Alternatively you can use, for example, the IAIK JCE provider; in
- * this case the provider must be registered beforehand and the Bouncy
- * Castle provider must not be registered beforehand. The Sun JCE provider
- * does not work.
- */
- public void setProvider(String provider) {
- this.provider = provider;
- }
-
- public int getCompressionAlgorithm() {
- return compressionAlgorithm;
- }
-
- /**
- * Compression algorithm used during marshaling. Possible values are defined
- * in {@link CompressionAlgorithmTags}. Default value is ZIP.
- */
- public void setCompressionAlgorithm(int compressionAlgorithm) {
- this.compressionAlgorithm = compressionAlgorithm;
- }
-
- public int getHashAlgorithm() {
- return hashAlgorithm;
- }
-
- /**
- * Digest algorithm for signing (marshaling). Possible values are defined in
- * {@link HashAlgorithmTags}. Default value is SHA1.
- */
- public void setHashAlgorithm(int hashAlgorithm) {
- this.hashAlgorithm = hashAlgorithm;
- }
-
- public int getAlgorithm() {
- return algorithm;
- }
-
- /**
- * Symmetric key algorithm for encryption (marshaling). Possible values are
- * defined in {@link SymmetricKeyAlgorithmTags}. Default value is CAST5.
- */
- public void setAlgorithm(int algorithm) {
- this.algorithm = algorithm;
- }
-
public PGPPassphraseAccessor getPassphraseAccessor() {
return passphraseAccessor;
}
@@ -795,17 +231,45 @@ public class PGPDataFormat extends ServiceSupport implements DataFormat {
}
@Override
- protected void doStart() throws Exception {
- if (Security.getProvider(BC) == null && BC.equals(getProvider())) {
- LOG.debug("Adding BouncyCastleProvider as security provider");
- Security.addProvider(new BouncyCastleProvider());
- } else {
- LOG.debug("Using custom provider {} which is expected to be enlisted manually.", getProvider());
+ public List<PGPPublicKey> getEncryptionKeys(Exchange exchange, List<String> useridParts) throws Exception {
+ return PGPDataFormatUtil.findPublicKeys(exchange.getContext(), findKeyFileName(exchange), findEncryptionKeyRing(exchange),
+ useridParts, true);
+ }
+
+ @Override
+ public List<PGPSecretKeyAndPrivateKeyAndUserId> getSignerKeys(Exchange exchange, List<String> useridParts) throws Exception {
+ String sigKeyFileName = findSignatureKeyFileName(exchange);
+
+ String sigKeyPassword = findSignatureKeyPassword(exchange);
+ byte[] sigKeyRing = findSignatureKeyRing(exchange);
+
+ if ((sigKeyFileName == null && sigKeyRing == null) || useridParts == null || useridParts.isEmpty()
+ || (sigKeyPassword == null && passphraseAccessor == null)) {
+ return Collections.emptyList();
}
+ return determineSecretKeysWithPrivateKeyAndUserId(exchange, sigKeyFileName, useridParts, sigKeyPassword, sigKeyRing);
+ }
+
+ @Override
+ public PGPPrivateKey getPrivateKey(Exchange exchange, long keyId) throws Exception {
+ return PGPDataFormatUtil.findPrivateKeyWithKeyId(exchange.getContext(), findKeyFileName(exchange), findEncryptionKeyRing(exchange),
+ keyId, findKeyPassword(exchange), getPassphraseAccessor(), getProvider());
+ }
+
+ @Override
+ public PGPPublicKey getPublicKey(Exchange exchange, long keyId) throws Exception {
+ return PGPDataFormatUtil.findPublicKeyWithKeyId(exchange.getContext(), findSignatureKeyFileName(exchange),
+ findSignatureKeyRing(exchange), keyId, false);
+ }
+
+ @Override
+ public void setPublicKeyAccess(PGPPublicKeyAccess publicKeyAccess) {
+ throw new UnsupportedOperationException("Use PGPKeyAccessDataFormat if you want to set the public key access");
}
@Override
- protected void doStop() throws Exception {
- // noop
+ public void setSecretKeyAccess(PGPSecretKeyAccess secretKeyAccess) {
+ throw new UnsupportedOperationException("Use PGPKeyAccessDataFormat if you want to set the secret key access");
}
+
}
http://git-wip-us.apache.org/repos/asf/camel/blob/62c8b7ad/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 b09ebe8..083729c 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
@@ -16,6 +16,7 @@
*/
package org.apache.camel.converter.crypto;
+
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -56,6 +57,7 @@ import static org.bouncycastle.bcpg.PublicKeyAlgorithmTags.ELGAMAL_GENERAL;
import static org.bouncycastle.bcpg.PublicKeyAlgorithmTags.RSA_GENERAL;
import static org.bouncycastle.bcpg.PublicKeyAlgorithmTags.RSA_SIGN;
+
public final class PGPDataFormatUtil {
private static final Logger LOG = LoggerFactory.getLogger(PGPDataFormatUtil.class);
@@ -121,10 +123,14 @@ public final class PGPDataFormatUtil {
}
}
- @SuppressWarnings("unchecked")
private static PGPPrivateKey findPrivateKeyWithKeyId(InputStream keyringInput, long keyid, String passphrase,
PGPPassphraseAccessor passphraseAccessor, String provider) throws IOException, PGPException {
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyringInput));
+ return findPrivateKeyWithkeyId(keyid, passphrase, passphraseAccessor, provider, pgpSec);
+ }
+
+ public static PGPPrivateKey findPrivateKeyWithkeyId(long keyid, String passphrase, PGPPassphraseAccessor passphraseAccessor,
+ String provider, PGPSecretKeyRingCollection pgpSec) throws PGPException {
for (Iterator<?> i = pgpSec.getKeyRings(); i.hasNext();) {
Object data = i.next();
if (data instanceof PGPSecretKeyRing) {
@@ -133,6 +139,7 @@ public final class PGPDataFormatUtil {
if (secKey != null) {
if (passphrase == null && passphraseAccessor != null) {
// get passphrase from accessor // only primary/master key has user IDS
+ @SuppressWarnings("unchecked")
Iterator<String> userIDs = keyring.getSecretKey().getUserIDs();
while (passphrase == null && userIDs.hasNext()) {
passphrase = passphraseAccessor.getPassphrase(userIDs.next());
@@ -173,31 +180,25 @@ public final class PGPDataFormatUtil {
return is;
}
- @SuppressWarnings("unchecked")
private static PGPPublicKey findPublicKeyWithKeyId(InputStream input, long keyid) throws IOException, PGPException,
NoSuchProviderException {
PGPPublicKeyRingCollection pgpSec = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input));
-
- for (Iterator<PGPPublicKeyRing> keyRingIter = pgpSec.getKeyRings(); keyRingIter.hasNext();) {
- PGPPublicKeyRing keyRing = keyRingIter.next();
- PGPPublicKey key = keyRing.getPublicKey(keyid);
- if (key != null) {
- return key;
- }
-
- }
-
- return null;
+ return pgpSec.getPublicKey(keyid);
}
- @SuppressWarnings("unchecked")
private static List<PGPPublicKey> findPublicKeys(InputStream input, List<String> userids, boolean forEncryption) throws IOException,
PGPException, NoSuchProviderException {
- List<PGPPublicKey> result = new ArrayList<PGPPublicKey>(3);
PGPPublicKeyRingCollection pgpSec = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input));
- for (Iterator<PGPPublicKeyRing> keyRingIter = pgpSec.getKeyRings(); keyRingIter.hasNext();) {
+ 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());
+ for (Iterator<PGPPublicKeyRing> keyRingIter = pgpPublicKeyringCollection.getKeyRings(); keyRingIter.hasNext();) {
PGPPublicKeyRing keyRing = keyRingIter.next();
Set<String> keyUserIds = getUserIds(keyRing);
for (Iterator<PGPPublicKey> keyIter = keyRing.getPublicKeys(); keyIter.hasNext();) {
@@ -390,12 +391,16 @@ public final class PGPDataFormatUtil {
return findSecretKey(context, keychainFilename, secKeyRing, passphrase, null, provider);
}
- @SuppressWarnings("unchecked")
private static List<PGPSecretKeyAndPrivateKeyAndUserId> findSecretKeysWithPrivateKeyAndUserId(InputStream keyringInput,
Map<String, String> sigKeyUserId2Password, String provider) throws IOException, PGPException, NoSuchProviderException {
- List<PGPSecretKeyAndPrivateKeyAndUserId> result = new ArrayList<PGPDataFormatUtil.PGPSecretKeyAndPrivateKeyAndUserId>(
- sigKeyUserId2Password.size());
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyringInput));
+ return findSecretKeysWithPrivateKeyAndUserId(sigKeyUserId2Password, provider, pgpSec);
+ }
+
+ public static List<PGPSecretKeyAndPrivateKeyAndUserId> findSecretKeysWithPrivateKeyAndUserId(Map<String, String> sigKeyUserId2Password,
+ String provider, PGPSecretKeyRingCollection pgpSec) throws PGPException {
+ List<PGPSecretKeyAndPrivateKeyAndUserId> result = new ArrayList<PGPSecretKeyAndPrivateKeyAndUserId>(
+ sigKeyUserId2Password.size());
for (Iterator<?> i = pgpSec.getKeyRings(); i.hasNext();) {
Object data = i.next();
if (data instanceof PGPSecretKeyRing) {
@@ -405,7 +410,8 @@ public final class PGPDataFormatUtil {
for (String userIdPart : sigKeyUserId2Password.keySet()) {
for (String keyUserId : keyUserIds) {
if (keyUserId.contains(userIdPart)) {
- for (Iterator<PGPSecretKey> iterKey = keyring.getSecretKeys(); iterKey.hasNext();) {
+ for (@SuppressWarnings("unchecked")
+ Iterator<PGPSecretKey> iterKey = keyring.getSecretKeys(); iterKey.hasNext();) {
PGPSecretKey secKey = iterKey.next();
if (isSigningKey(secKey)) {
PGPPrivateKey privateKey = secKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider(
@@ -477,32 +483,4 @@ public final class PGPDataFormatUtil {
}
return null; // no key flag
}
-
- public static class PGPSecretKeyAndPrivateKeyAndUserId {
-
- private final PGPSecretKey secretKey;
-
- private final PGPPrivateKey privateKey;
-
- private final String userId;
-
- public PGPSecretKeyAndPrivateKeyAndUserId(PGPSecretKey secretKey, PGPPrivateKey privateKey, String userId) {
- this.secretKey = secretKey;
- this.privateKey = privateKey;
- this.userId = userId;
- }
-
- public PGPSecretKey getSecretKey() {
- return secretKey;
- }
-
- public PGPPrivateKey getPrivateKey() {
- return privateKey;
- }
-
- public String getUserId() {
- return userId;
- }
-
- }
}
http://git-wip-us.apache.org/repos/asf/camel/blob/62c8b7ad/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
new file mode 100644
index 0000000..456bb7e
--- /dev/null
+++ b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPKeyAccessDataFormat.java
@@ -0,0 +1,642 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.converter.crypto;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+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.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.converter.stream.CachedOutputStream;
+import org.apache.camel.spi.DataFormat;
+import org.apache.camel.support.ServiceSupport;
+import org.apache.camel.util.ExchangeHelper;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.ObjectHelper;
+import org.bouncycastle.bcpg.ArmoredOutputStream;
+import org.bouncycastle.bcpg.CompressionAlgorithmTags;
+import org.bouncycastle.bcpg.HashAlgorithmTags;
+import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPCompressedData;
+import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
+import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
+import org.bouncycastle.openpgp.PGPEncryptedDataList;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPLiteralData;
+import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
+import org.bouncycastle.openpgp.PGPObjectFactory;
+import org.bouncycastle.openpgp.PGPOnePassSignature;
+import org.bouncycastle.openpgp.PGPOnePassSignatureList;
+import org.bouncycastle.openpgp.PGPPrivateKey;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureGenerator;
+import org.bouncycastle.openpgp.PGPSignatureList;
+import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This PGP Data Format uses the interfaces {@link PGPPublicKeyAccess} and
+ * {@link PGPSecretKeyAccess} to access the keys for encryption/signing and
+ * decryption/signature verification. These interfaces allow caching of the keys
+ * which can improve the performance.
+ * <p>
+ * If you want to provide the key access via keyrings in the format of a byte
+ * array or file, then you should use the class {@link PGPDataFormat}.
+ *
+ */
+public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat {
+
+ public static final String KEY_USERID = "CamelPGPDataFormatKeyUserid";
+ public static final String KEY_USERIDS = "CamelPGPDataFormatKeyUserids";
+ public static final String SIGNATURE_KEY_USERID = "CamelPGPDataFormatSignatureKeyUserid";
+ public static final String SIGNATURE_KEY_USERIDS = "CamelPGPDataFormatSignatureKeyUserids";
+ public static final String ENCRYPTION_ALGORITHM = "CamelPGPDataFormatEncryptionAlgorithm";
+ public static final String SIGNATURE_HASH_ALGORITHM = "CamelPGPDataFormatSignatureHashAlgorithm";
+ 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.
+ */
+ 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
+ * type Integer.
+ */
+ public static final String NUMBER_OF_SIGNING_KEYS = "CamelPGPDataFormatNumberOfSigningKeys";
+
+ private static final Logger LOG = LoggerFactory.getLogger(PGPKeyAccessDataFormat.class);
+
+
+ private static final String BC = "BC";
+ private static final int BUFFER_SIZE = 16 * 1024;
+
+ PGPPublicKeyAccess publicKeyAccess;
+
+ PGPSecretKeyAccess secretKeyAccess;
+
+ // Java Cryptography Extension provider, default is Bouncy Castle
+ private String provider = BC;
+
+ // encryption / decryption key info (required)
+ private String keyUserid; // only for encryption
+ //in addition you can specify further User IDs, in this case the symmetric key is encrypted by several public keys corresponding to the User Ids
+ private List<String> keyUserids; //only for encryption;
+
+ // signature / verification key info (optional)
+ private String signatureKeyUserid; // for signing and verification (optional for verification)
+ //For verification you can specify further User IDs in addition
+ private List<String> signatureKeyUserids; //only for signing with several keys and verifying;
+
+ private boolean armored; // for encryption
+ private boolean integrity = true; // for encryption
+
+ private int hashAlgorithm = HashAlgorithmTags.SHA1; // for encryption
+
+ private int algorithm = SymmetricKeyAlgorithmTags.CAST5; // for encryption
+
+ private int compressionAlgorithm = CompressionAlgorithmTags.ZIP; // for encryption
+
+ public PGPKeyAccessDataFormat() {
+ }
+
+ protected String findKeyUserid(Exchange exchange) {
+ return exchange.getIn().getHeader(KEY_USERID, getKeyUserid(), String.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected List<String> findKeyUserids(Exchange exchange) {
+ return exchange.getIn().getHeader(KEY_USERIDS, getKeyUserids(), List.class);
+ }
+
+ protected String findSignatureKeyUserid(Exchange exchange) {
+ return exchange.getIn().getHeader(SIGNATURE_KEY_USERID, getSignatureKeyUserid(), String.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected List<String> findSignatureKeyUserids(Exchange exchange) {
+ return exchange.getIn().getHeader(SIGNATURE_KEY_USERIDS, getSignatureKeyUserids(), List.class);
+ }
+
+ protected int findCompressionAlgorithm(Exchange exchange) {
+ return exchange.getIn().getHeader(COMPRESSION_ALGORITHM, getCompressionAlgorithm(), Integer.class);
+ }
+
+ protected int findAlgorithm(Exchange exchange) {
+ return exchange.getIn().getHeader(ENCRYPTION_ALGORITHM, getAlgorithm(), Integer.class);
+ }
+
+ protected int findHashAlgorithm(Exchange exchange) {
+ return exchange.getIn().getHeader(SIGNATURE_HASH_ALGORITHM, getHashAlgorithm(), Integer.class);
+ }
+
+ public void marshal(Exchange exchange, Object graph, OutputStream outputStream) throws Exception {
+ List<String> userids = determineEncryptionUserIds(exchange);
+ List<PGPPublicKey> keys = publicKeyAccess.getEncryptionKeys(exchange, userids);
+ if (keys.isEmpty()) {
+ throw new IllegalArgumentException("Cannot PGP encrypt message. No public encryption key found for the User Ids " + userids
+ + " in the public keyring. Either specify other User IDs or add correct public keys to the keyring.");
+ }
+ exchange.getOut().setHeader(NUMBER_OF_ENCRYPTION_KEYS, Integer.valueOf(keys.size()));
+
+ InputStream input = ExchangeHelper.convertToMandatoryType(exchange, InputStream.class, graph);
+
+ if (armored) {
+ outputStream = new ArmoredOutputStream(outputStream);
+ }
+
+ PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(findAlgorithm(exchange))
+ .setWithIntegrityPacket(integrity).setSecureRandom(new SecureRandom()).setProvider(getProvider()));
+ // several keys can be added
+ for (PGPPublicKey key : keys) {
+ encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(key));
+ }
+ OutputStream encOut = encGen.open(outputStream, new byte[BUFFER_SIZE]);
+
+ PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(findCompressionAlgorithm(exchange));
+ OutputStream comOut = new BufferedOutputStream(comData.open(encOut));
+
+ List<PGPSignatureGenerator> sigGens = createSignatureGenerator(exchange, comOut);
+
+ PGPLiteralDataGenerator litData = new PGPLiteralDataGenerator();
+ String fileName = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
+ if (ObjectHelper.isEmpty(fileName)) {
+ // This marks the file as For Your Eyes Only... may cause problems for the receiver if they use
+ // an automated process to decrypt as the filename is appended with _CONSOLE
+ fileName = PGPLiteralData.CONSOLE;
+ }
+ OutputStream litOut = litData.open(comOut, PGPLiteralData.BINARY, fileName, new Date(), new byte[BUFFER_SIZE]);
+
+ try {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int bytesRead;
+ while ((bytesRead = input.read(buffer)) != -1) {
+ litOut.write(buffer, 0, bytesRead);
+ if (sigGens != null && !sigGens.isEmpty()) {
+ for (PGPSignatureGenerator sigGen : sigGens) {
+ // not nested therefore it is the same for all
+ // can this be improved that we only do it for one sigGen and set the result on the others?
+ sigGen.update(buffer, 0, bytesRead);
+ }
+ }
+ litOut.flush();
+ }
+ } finally {
+ IOHelper.close(litOut);
+ if (sigGens != null && !sigGens.isEmpty()) {
+ // reverse order
+ for (int i = sigGens.size() - 1; i > -1; i--) {
+ PGPSignatureGenerator sigGen = sigGens.get(i);
+ sigGen.generate().encode(comOut);
+ }
+ }
+ IOHelper.close(comOut, encOut, outputStream, input);
+ }
+ }
+
+ protected List<String> determineEncryptionUserIds(Exchange exchange) {
+ String userid = findKeyUserid(exchange);
+ List<String> userids = findKeyUserids(exchange);
+ // merge them together
+ List<String> result;
+ if (userid != null) {
+ if (userids == null || userids.isEmpty()) {
+ result = Collections.singletonList(userid);
+ } else {
+ result = new ArrayList<String>(userids.size() + 1);
+ result.add(userid);
+ result.addAll(userids);
+ }
+ } else {
+ if (userids == null || userids.isEmpty()) {
+ throw new IllegalStateException("Cannot PGP encrypt message. No User ID of the public key specified.");
+ }
+ result = userids;
+ }
+ return result;
+ }
+
+ protected List<String> determineSignaturenUserIds(Exchange exchange) {
+ String userid = findSignatureKeyUserid(exchange);
+ List<String> userids = findSignatureKeyUserids(exchange);
+ // merge them together
+ List<String> result;
+ if (userid != null) {
+ if (userids == null || userids.isEmpty()) {
+ result = Collections.singletonList(userid);
+ } else {
+ result = new ArrayList<String>(userids.size() + 1);
+ result.add(userid);
+ result.addAll(userids);
+ }
+ } else {
+ // userids can be empty or null!
+ result = userids;
+ }
+ return result;
+ }
+
+ protected List<PGPSignatureGenerator> createSignatureGenerator(Exchange exchange, OutputStream out) throws Exception {
+
+ if (secretKeyAccess == null) {
+ return null;
+ }
+
+ List<String> sigKeyUserids = determineSignaturenUserIds(exchange);
+ List<PGPSecretKeyAndPrivateKeyAndUserId> sigSecretKeysWithPrivateKeyAndUserId = secretKeyAccess.getSignerKeys(exchange,
+ sigKeyUserids);
+ if (sigSecretKeysWithPrivateKeyAndUserId.isEmpty()) {
+ return null;
+ }
+
+ exchange.getOut().setHeader(NUMBER_OF_SIGNING_KEYS, Integer.valueOf(sigSecretKeysWithPrivateKeyAndUserId.size()));
+
+ List<PGPSignatureGenerator> sigGens = new ArrayList<PGPSignatureGenerator>();
+ for (PGPSecretKeyAndPrivateKeyAndUserId sigSecretKeyWithPrivateKeyAndUserId : sigSecretKeysWithPrivateKeyAndUserId) {
+ PGPPrivateKey sigPrivateKey = sigSecretKeyWithPrivateKeyAndUserId.getPrivateKey();
+
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ spGen.setSignerUserID(false, sigSecretKeyWithPrivateKeyAndUserId.getUserId());
+
+ int algorithm = sigSecretKeyWithPrivateKeyAndUserId.getSecretKey().getPublicKey().getAlgorithm();
+ PGPSignatureGenerator sigGen = new PGPSignatureGenerator(
+ new JcaPGPContentSignerBuilder(algorithm, findHashAlgorithm(exchange)).setProvider(getProvider()));
+ sigGen.init(PGPSignature.BINARY_DOCUMENT, sigPrivateKey);
+ sigGen.setHashedSubpackets(spGen.generate());
+ sigGen.generateOnePassVersion(false).encode(out);
+ sigGens.add(sigGen);
+ }
+ return sigGens;
+ }
+
+ @SuppressWarnings("resource")
+ public Object unmarshal(Exchange exchange, InputStream encryptedStream) throws Exception {
+ if (encryptedStream == null) {
+ return null;
+ }
+ InputStream in = PGPUtil.getDecoderStream(encryptedStream);
+ PGPObjectFactory pgpFactory = new PGPObjectFactory(in);
+ Object o = pgpFactory.nextObject();
+ // the first object might be a PGP marker packet
+ PGPEncryptedDataList enc;
+ if (o instanceof PGPEncryptedDataList) {
+ enc = (PGPEncryptedDataList) o;
+ } else {
+ enc = (PGPEncryptedDataList) pgpFactory.nextObject();
+ }
+
+ 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++) {
+ pbe = (PGPPublicKeyEncryptedData) enc.get(i);
+ key = secretKeyAccess.getPrivateKey(exchange, pbe.getKeyID());
+ if (key != null) {
+ // take the first key
+ break;
+ }
+ }
+ if (key == null) {
+ throw new PGPException("Provided input is encrypted with unknown pair of keys.");
+ }
+
+ InputStream encData = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider(getProvider()).build(key));
+ pgpFactory = new PGPObjectFactory(encData);
+ PGPCompressedData comData = (PGPCompressedData) pgpFactory.nextObject();
+ pgpFactory = new PGPObjectFactory(comData.getDataStream());
+ Object object = pgpFactory.nextObject();
+
+ PGPOnePassSignature signature;
+ if (object instanceof PGPOnePassSignatureList) {
+ signature = getSignature(exchange, (PGPOnePassSignatureList) object);
+ object = pgpFactory.nextObject();
+ } else {
+ signature = null;
+ }
+
+ PGPLiteralData ld = (PGPLiteralData) object;
+ InputStream litData = ld.getInputStream();
+
+ // enable streaming via OutputStreamCache
+ CachedOutputStream cos;
+ ByteArrayOutputStream bos;
+ OutputStream os;
+ if (exchange.getContext().getStreamCachingStrategy().isEnabled()) {
+ cos = new CachedOutputStream(exchange);
+ bos = null;
+ os = cos;
+ } else {
+ cos = null;
+ bos = new ByteArrayOutputStream();
+ os = bos;
+ }
+
+ try {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int bytesRead;
+ while ((bytesRead = litData.read(buffer)) != -1) {
+ os.write(buffer, 0, bytesRead);
+ if (signature != null) {
+ signature.update(buffer, 0, bytesRead);
+ }
+ os.flush();
+ }
+ } finally {
+ 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");
+ }
+ }
+
+ if (cos != null) {
+ return cos.newStreamCache();
+ } else {
+ return bos.toByteArray();
+ }
+ }
+
+ protected PGPSignature getSignatureWithKeyId(long keyID, PGPSignatureList sigList) {
+ for (int i = 0; i < sigList.size(); i++) {
+ PGPSignature signature = sigList.get(i);
+ if (keyID == signature.getKeyID()) {
+ return signature;
+ }
+ }
+ throw new IllegalStateException("PGP signature is inconsistent");
+ }
+
+ protected PGPOnePassSignature getSignature(Exchange exchange, PGPOnePassSignatureList signatureList) throws Exception {
+
+ List<String> allowedUserIds = determineSignaturenUserIds(exchange);
+ for (int i = 0; i < signatureList.size(); i++) {
+ PGPOnePassSignature signature = signatureList.get(i);
+ // Determine public key from signature keyId
+ PGPPublicKey sigPublicKey = publicKeyAccess.getPublicKey(exchange, signature.getKeyID());
+ if (sigPublicKey == null) {
+ continue;
+ }
+ if (isAllowedVerifyingKey(allowedUserIds, sigPublicKey)) {
+ // choose that signature for which a public key exists!
+ signature.init(new JcaPGPContentVerifierBuilderProvider().setProvider(getProvider()), sigPublicKey);
+ return signature;
+ }
+ }
+ if (signatureList.isEmpty()) {
+ return null;
+ } else {
+ throw new IllegalArgumentException("No public key found fitting to the signature key Id; cannot verify the signature");
+ }
+
+ }
+
+ public boolean isAllowedVerifyingKey(List<String> allowedUserIds, PGPPublicKey verifyingPublicKey) {
+
+ if (allowedUserIds == null || allowedUserIds.isEmpty()) {
+ // no restrictions specified
+ return true;
+ }
+ String keyUserId = null;
+ for (@SuppressWarnings("unchecked")
+ Iterator<String> iterator = verifyingPublicKey.getUserIDs(); iterator.hasNext();) {
+ keyUserId = iterator.next();
+ for (String userid : allowedUserIds) {
+ if (keyUserId != null && keyUserId.contains(userid)) {
+ LOG.debug(
+ "Public key with user ID {} fulfills the User ID restriction {}. Therefore this key will be used for the signature verification. ",
+ keyUserId, allowedUserIds);
+ return true;
+ }
+ }
+ }
+ LOG.warn(
+ "Public key with User ID {} does not fulfill the User ID restriction {}. Therefore this key will not be used for the signature verification.",
+ keyUserId, allowedUserIds);
+ return false;
+ }
+
+ /**
+ * Sets if the encrypted file should be written in ascii visible text (for
+ * marshaling).
+ */
+ public void setArmored(boolean armored) {
+ this.armored = armored;
+ }
+
+ public boolean getArmored() {
+ return this.armored;
+ }
+
+ /**
+ * Whether or not to add an integrity check/sign to the encrypted file for
+ * marshaling.
+ */
+ public void setIntegrity(boolean integrity) {
+ this.integrity = integrity;
+ }
+
+ public boolean getIntegrity() {
+ return this.integrity;
+ }
+
+ /**
+ * User ID, or more precisely user ID part, of the key used for encryption.
+ * See also {@link #setKeyUserids(List<String>)}.
+ */
+ public void setKeyUserid(String keyUserid) {
+ this.keyUserid = keyUserid;
+ }
+
+ public String getKeyUserid() {
+ return keyUserid;
+ }
+
+ public List<String> getKeyUserids() {
+ return keyUserids;
+ }
+
+ /**
+ * Keys User IDs, or more precisely user ID parts, used for determining the
+ * public keys for encryption. If you just have one User ID, then you can
+ * also use the method {@link #setKeyUserid(String)}. The User ID specified
+ * in {@link #setKeyUserid(String)} and in this method will be merged
+ * together and the public keys which have a User ID which contain a value
+ * of the specified User IDs the will be used for the encryption. Be aware
+ * that you may get several public keys even if you specify only one User
+ * Id, because there can be several public keys which have a User ID which
+ * contains the specified User ID.
+ */
+ public void setKeyUserids(List<String> keyUserids) {
+ this.keyUserids = keyUserids;
+ }
+
+ /**
+ * Userid, or more precisely user ID part, of the signature key used for
+ * signing (marshal) and verifying (unmarshal). See also
+ * {@link #setSignatureKeyUserids(List)}.
+ */
+ public void setSignatureKeyUserid(String signatureKeyUserid) {
+ this.signatureKeyUserid = signatureKeyUserid;
+ }
+
+ public String getSignatureKeyUserid() {
+ return signatureKeyUserid;
+ }
+
+ public List<String> getSignatureKeyUserids() {
+ return signatureKeyUserids;
+ }
+
+ /**
+ * User IDs, or more precisely user ID parts, used for signing and
+ * verification.
+ * <p>
+ * In the signing case, the User IDs specify the private keys which are used
+ * for signing. If the result are several private keys then several
+ * signatures will be created. If you just have one signature User ID, then
+ * you can also use the method {@link #setSignatureKeyUserid(String)} or
+ * this method. The User ID specified in
+ * {@link #setSignatureKeyUserid(String)} and in this method will be merged
+ * together and the private keys which have a User Id which contain one
+ * value out of the specified UserIds will be used for the signature
+ * creation. Be aware that you may get several private keys even if you
+ * specify only one User Id, because there can be several private keys which
+ * have a User ID which contains the specified User ID.
+ * <p>
+ * In the verification case the User IDs restrict the set of public keys
+ * which can be used for verification. The public keys used for verification
+ * must contain a User ID which contain one value of the User ID list. If
+ * you neither specify in this method and nor specify in the method
+ * {@link #setSignatureKeyUserid(String)} any value then any public key in
+ * the public key ring will be taken into consideration for the
+ * verification.
+ * <p>
+ * If you just have one User ID, then you can also use the method
+ * {@link #setSignatureKeyUserid(String)}. The User ID specified in
+ * {@link #setSignatureKeyUserid(String)} and in this method will be merged
+ * together and the corresponding public keys represent the potential keys
+ * for the verification of the message.
+ */
+ public void setSignatureKeyUserids(List<String> signatureKeyUserids) {
+ this.signatureKeyUserids = signatureKeyUserids;
+ }
+
+ public String getProvider() {
+ return provider;
+ }
+
+ /**
+ * Java Cryptography Extension (JCE) provider, default is Bouncy Castle
+ * ("BC"). Alternatively you can use, for example, the IAIK JCE provider; in
+ * this case the provider must be registered beforehand and the Bouncy
+ * Castle provider must not be registered beforehand. The Sun JCE provider
+ * does not work.
+ */
+ public void setProvider(String provider) {
+ this.provider = provider;
+ }
+
+ public int getCompressionAlgorithm() {
+ return compressionAlgorithm;
+ }
+
+ /**
+ * Compression algorithm used during marshaling. Possible values are defined
+ * in {@link CompressionAlgorithmTags}. Default value is ZIP.
+ */
+ public void setCompressionAlgorithm(int compressionAlgorithm) {
+ this.compressionAlgorithm = compressionAlgorithm;
+ }
+
+ public int getHashAlgorithm() {
+ return hashAlgorithm;
+ }
+
+ /**
+ * Digest algorithm for signing (marshaling). Possible values are defined in
+ * {@link HashAlgorithmTags}. Default value is SHA1.
+ */
+ public void setHashAlgorithm(int hashAlgorithm) {
+ this.hashAlgorithm = hashAlgorithm;
+ }
+
+ public int getAlgorithm() {
+ return algorithm;
+ }
+
+ /**
+ * Symmetric key algorithm for encryption (marshaling). Possible values are
+ * defined in {@link SymmetricKeyAlgorithmTags}. Default value is CAST5.
+ */
+ public void setAlgorithm(int algorithm) {
+ this.algorithm = algorithm;
+ }
+
+ public PGPPublicKeyAccess getPublicKeyAccess() {
+ return publicKeyAccess;
+ }
+
+ public void setPublicKeyAccess(PGPPublicKeyAccess publicKeyAccess) {
+ this.publicKeyAccess = publicKeyAccess;
+ }
+
+ public PGPSecretKeyAccess getSecretKeyAccess() {
+ return secretKeyAccess;
+ }
+
+ public void setSecretKeyAccess(PGPSecretKeyAccess secretKeyAccess) {
+ this.secretKeyAccess = secretKeyAccess;
+ }
+
+ @Override
+ protected void doStart() throws Exception {
+ if (Security.getProvider(BC) == null && BC.equals(getProvider())) {
+ LOG.debug("Adding BouncyCastleProvider as security provider");
+ Security.addProvider(new BouncyCastleProvider());
+ } else {
+ LOG.debug("Using custom provider {} which is expected to be enlisted manually.", getProvider());
+ }
+ }
+
+ @Override
+ protected void doStop() throws Exception {
+ // noop
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/62c8b7ad/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPPublicKeyAccess.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPPublicKeyAccess.java b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPPublicKeyAccess.java
new file mode 100644
index 0000000..0ee9f67
--- /dev/null
+++ b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPPublicKeyAccess.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.converter.crypto;
+
+import java.util.List;
+
+import org.apache.camel.Exchange;
+import org.bouncycastle.openpgp.PGPPublicKey;
+
+public interface PGPPublicKeyAccess {
+
+ /**
+ * Returns the encryption keys for the given user ID parts. This method is
+ * used for encryption.
+ *
+ * @param exchange
+ * exchange, can be <code>null</code>
+ * @param useridParts
+ * parts of User IDs, must not be <code>null</code>
+ * @return list of public keys, must not be <code>null</code>
+ */
+ List<PGPPublicKey> getEncryptionKeys(Exchange exchange, List<String> useridParts) throws Exception;
+
+ /**
+ * Returns the public key with a certain key ID. This method is used for
+ * verifying the signature.
+ *
+ * @param exchange
+ * exchange
+ * @param keyId
+ * key ID
+ * @return public key or <code>null</code> if the key cannot be found
+ */
+ PGPPublicKey getPublicKey(Exchange exchange, long keyId) throws Exception;
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/62c8b7ad/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPPublicKeyAccessDefault.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPPublicKeyAccessDefault.java b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPPublicKeyAccessDefault.java
new file mode 100644
index 0000000..5e05d04
--- /dev/null
+++ b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPPublicKeyAccessDefault.java
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.converter.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.util.ObjectHelper;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
+import org.bouncycastle.openpgp.PGPUtil;
+
+/**
+ * Caches a public key ring.
+ *
+ */
+public class PGPPublicKeyAccessDefault implements PGPPublicKeyAccess {
+
+ private final PGPPublicKeyRingCollection pgpPublicKeyRing;
+
+ public PGPPublicKeyAccessDefault(byte[] publicKeyRing) throws IOException, PGPException {
+ ObjectHelper.notNull(publicKeyRing, "publicKeyRing");
+ pgpPublicKeyRing = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(new ByteArrayInputStream(publicKeyRing)));
+ }
+
+ @Override
+ public List<PGPPublicKey> getEncryptionKeys(Exchange exchange, List<String> useridParts) throws Exception {
+ return PGPDataFormatUtil.findPublicKeys(useridParts, true, pgpPublicKeyRing);
+ }
+
+ @Override
+ public PGPPublicKey getPublicKey(Exchange exchange, long keyId) throws Exception {
+ return pgpPublicKeyRing.getPublicKey(keyId);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/62c8b7ad/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPSecretKeyAccess.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPSecretKeyAccess.java b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPSecretKeyAccess.java
new file mode 100644
index 0000000..d70a081
--- /dev/null
+++ b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPSecretKeyAccess.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.converter.crypto;
+
+import java.util.List;
+
+import org.apache.camel.Exchange;
+import org.bouncycastle.openpgp.PGPPrivateKey;
+
+public interface PGPSecretKeyAccess {
+
+ /**
+ * Returns the signer keys for the given user ID parts. This method is used
+ * for signing.
+ *
+ * @param exchange
+ * exchange, can be <code>null</code>
+ * @param useridParts
+ * parts of User IDs, can be <code>null</code> or empty, then an
+ * empty list must be returned
+ * @return list of secret keys with their private keys and User Ids which
+ * corresponds to one of the useridParts, must not be
+ * <code>null</code>, can be empty
+ */
+ List<PGPSecretKeyAndPrivateKeyAndUserId> getSignerKeys(Exchange exchange, List<String> useridParts) throws Exception;
+
+ /**
+ * Returns the private key with a certain key ID. This method is used for
+ * decrypting.
+ *
+ * @param exchange
+ * exchange, can be <code>null</code>
+ *
+ * @param keyId
+ * key ID
+ * @return private key or <code>null</code> if the key cannot be found
+ */
+ PGPPrivateKey getPrivateKey(Exchange exchange, long keyId) throws Exception;
+
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/62c8b7ad/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPSecretKeyAccessDefault.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPSecretKeyAccessDefault.java b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPSecretKeyAccessDefault.java
new file mode 100644
index 0000000..a0c07d7
--- /dev/null
+++ b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPSecretKeyAccessDefault.java
@@ -0,0 +1,100 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.converter.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.util.ObjectHelper;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPrivateKey;
+import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
+import org.bouncycastle.openpgp.PGPUtil;
+
+/**
+ * Caches a Secret Keyring. Assumes that the password for all private keys is
+ * the same.
+ *
+ */
+public class PGPSecretKeyAccessDefault implements PGPSecretKeyAccess {
+
+ private final Map<String, List<PGPSecretKeyAndPrivateKeyAndUserId>> userIdPart2SecretKeyList = new HashMap<String, List<PGPSecretKeyAndPrivateKeyAndUserId>>(
+ 3);
+
+ private final Map<Long, PGPPrivateKey> keyId2PrivateKey = new HashMap<Long, PGPPrivateKey>(3);
+
+ private final PGPSecretKeyRingCollection pgpSecretKeyring;
+
+ private final String password;
+
+ private final String provider;
+
+ /**
+ *
+ * @param secretKeyRing
+ * secret key ring as byte array
+ * @param password
+ * password for the private keys, assuming that all private keys
+ * have the same password
+ * @param provider
+ * @throws PGPException
+ * @throws IOException
+ */
+ public PGPSecretKeyAccessDefault(byte[] secretKeyRing, String password, String provider) throws PGPException, IOException {
+ ObjectHelper.notNull(secretKeyRing, "secretKeyRing");
+ ObjectHelper.notEmpty(password, "password");
+ ObjectHelper.notEmpty(provider, "provider");
+ pgpSecretKeyring = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(new ByteArrayInputStream(secretKeyRing)));
+ this.password = password;
+ this.provider = provider;
+ }
+
+ @Override
+ public List<PGPSecretKeyAndPrivateKeyAndUserId> getSignerKeys(Exchange exchange, List<String> useridParts) throws Exception {
+ List<PGPSecretKeyAndPrivateKeyAndUserId> result = new ArrayList<PGPSecretKeyAndPrivateKeyAndUserId>(3);
+ for (String useridPart : useridParts) {
+ List<PGPSecretKeyAndPrivateKeyAndUserId> partResult = userIdPart2SecretKeyList.get(useridPart);
+ if (partResult == null) {
+ partResult = PGPDataFormatUtil.findSecretKeysWithPrivateKeyAndUserId(Collections.singletonMap(useridPart, password),
+ provider, pgpSecretKeyring);
+ userIdPart2SecretKeyList.put(useridPart, partResult);
+ }
+ result.addAll(partResult);
+ }
+ return result;
+ }
+
+ @Override
+ public PGPPrivateKey getPrivateKey(Exchange exchange, long keyId) throws Exception {
+ Long keyIdLong = Long.valueOf(keyId);
+ PGPPrivateKey result = keyId2PrivateKey.get(keyIdLong);
+ if (result == null) {
+ result = PGPDataFormatUtil.findPrivateKeyWithkeyId(keyId, password, null, provider, pgpSecretKeyring);
+ if (result != null) {
+ keyId2PrivateKey.put(keyIdLong, result);
+ }
+ }
+ return result;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/62c8b7ad/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPSecretKeyAndPrivateKeyAndUserId.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPSecretKeyAndPrivateKeyAndUserId.java b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPSecretKeyAndPrivateKeyAndUserId.java
new file mode 100644
index 0000000..7ab78a8
--- /dev/null
+++ b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPSecretKeyAndPrivateKeyAndUserId.java
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.converter.crypto;
+
+import org.bouncycastle.openpgp.PGPPrivateKey;
+import org.bouncycastle.openpgp.PGPSecretKey;
+
+/**
+ * Helper class which groups secret, private key and User ID.
+ *
+ *
+ */
+public class PGPSecretKeyAndPrivateKeyAndUserId {
+
+ private final PGPSecretKey secretKey;
+
+ private final PGPPrivateKey privateKey;
+
+ private final String userId;
+
+ public PGPSecretKeyAndPrivateKeyAndUserId(PGPSecretKey secretKey, PGPPrivateKey privateKey, String userId) {
+ this.secretKey = secretKey;
+ this.privateKey = privateKey;
+ this.userId = userId;
+ }
+
+ public PGPSecretKey getSecretKey() {
+ return secretKey;
+ }
+
+ public PGPPrivateKey getPrivateKey() {
+ return privateKey;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/62c8b7ad/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 1aa7f7f..ab4a1b1 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
@@ -129,6 +129,11 @@ public class PGPDataFormatTest extends AbstractPGPDataFormatTest {
}
@Test
+ public void testKeyAccess() throws Exception {
+ doRoundTripEncryptionTests("direct:key_access");
+ }
+
+ @Test
public void testVerifyExceptionNoPublicKeyFoundCorrespondingToSignatureUserIds() throws Exception {
setupExpectations(context, 1, "mock:encrypted");
MockEndpoint exception = setupExpectations(context, 1, "mock:exception");
@@ -390,6 +395,28 @@ public class PGPDataFormatTest extends AbstractPGPDataFormatTest {
from("direct:keyflag").marshal(pgpKeyFlag).to("mock:encrypted_keyflag");
}
+ }, new RouteBuilder() {
+ public void configure() throws Exception {
+
+ PGPPublicKeyAccess publicKeyAccess = new PGPPublicKeyAccessDefault(getPublicKeyRing());
+ //password cannot be set dynamically!
+ PGPSecretKeyAccess secretKeyAccess = new PGPSecretKeyAccessDefault(getSecKeyRing(), "sdude", getProvider());
+
+ PGPKeyAccessDataFormat dfEncryptSignKeyAccess = new PGPKeyAccessDataFormat();
+ dfEncryptSignKeyAccess.setPublicKeyAccess(publicKeyAccess);
+ dfEncryptSignKeyAccess.setSecretKeyAccess(secretKeyAccess);
+ dfEncryptSignKeyAccess.setKeyUserid(getKeyUserId());
+ dfEncryptSignKeyAccess.setSignatureKeyUserid(getKeyUserId());
+
+ PGPKeyAccessDataFormat dfDecryptVerifyKeyAccess = new PGPKeyAccessDataFormat();
+ dfDecryptVerifyKeyAccess.setPublicKeyAccess(publicKeyAccess);
+ dfDecryptVerifyKeyAccess.setSecretKeyAccess(secretKeyAccess);
+ dfDecryptVerifyKeyAccess.setSignatureKeyUserid(getKeyUserId());
+
+ from("direct:key_access").marshal(dfEncryptSignKeyAccess).to("mock:encrypted").unmarshal(dfDecryptVerifyKeyAccess)
+ .to("mock:unencrypted");
+
+ }
} };
}