Repository: camel Updated Branches: refs/heads/master 4c20035bd -> cf7ae6936
CAMEL-7244 Fix the issue that verification with subkey restricted by User ID does not work 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/cf7ae693 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/cf7ae693 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/cf7ae693 Branch: refs/heads/master Commit: cf7ae69364a57028c55161dcdff494f7f963c208 Parents: 4c20035 Author: Willem Jiang <[email protected]> Authored: Thu Feb 27 15:17:21 2014 +0800 Committer: Willem Jiang <[email protected]> Committed: Thu Feb 27 15:17:21 2014 +0800 ---------------------------------------------------------------------- .../crypto/DefaultPGPPublicKeyAccessor.java | 7 ++- .../camel/converter/crypto/PGPDataFormat.java | 14 +++-- .../converter/crypto/PGPDataFormatUtil.java | 61 ++++++++++++++++++++ .../crypto/PGPKeyAccessDataFormat.java | 36 ++---------- .../converter/crypto/PGPPublicKeyAccessor.java | 11 +++- .../converter/crypto/PGPDataFormatTest.java | 31 ++++++++++ 6 files changed, 119 insertions(+), 41 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/cf7ae693/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/DefaultPGPPublicKeyAccessor.java ---------------------------------------------------------------------- diff --git a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/DefaultPGPPublicKeyAccessor.java b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/DefaultPGPPublicKeyAccessor.java index 55b8b72..a1911fb 100644 --- a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/DefaultPGPPublicKeyAccessor.java +++ b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/DefaultPGPPublicKeyAccessor.java @@ -16,6 +16,8 @@ */ package org.apache.camel.converter.crypto; + + import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.List; @@ -32,6 +34,7 @@ import org.bouncycastle.openpgp.PGPUtil; * */ public class DefaultPGPPublicKeyAccessor implements PGPPublicKeyAccessor { + private final PGPPublicKeyRingCollection pgpPublicKeyRing; @@ -46,8 +49,8 @@ public class DefaultPGPPublicKeyAccessor implements PGPPublicKeyAccessor { } @Override - public PGPPublicKey getPublicKey(Exchange exchange, long keyId) throws Exception { - return pgpPublicKeyRing.getPublicKey(keyId); + public PGPPublicKey getPublicKey(Exchange exchange, long keyId, List<String> userIdParts) throws Exception { + return PGPDataFormatUtil.getPublicKeyWithKeyIdAndUserID(keyId, userIdParts, pgpPublicKeyRing); } } http://git-wip-us.apache.org/repos/asf/camel/blob/cf7ae693/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 07f855f..a147a10 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 @@ -27,6 +27,7 @@ import org.apache.camel.Exchange; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; /** * <code>PGPDataFormat</code> uses the <a @@ -37,7 +38,7 @@ import org.bouncycastle.openpgp.PGPPublicKey; * */ public class PGPDataFormat extends PGPKeyAccessDataFormat implements PGPPublicKeyAccessor, PGPSecretKeyAccessor { - + public static final String KEY_FILE_NAME = "CamelPGPDataFormatKeyFileName"; public static final String ENCRYPTION_KEY_RING = "CamelPGPDataFormatEncryptionKeyRing"; public static final String KEY_PASSWORD = "CamelPGPDataFormatKeyPassword"; @@ -256,10 +257,13 @@ public class PGPDataFormat extends PGPKeyAccessDataFormat implements PGPPublicKe 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); + public PGPPublicKey getPublicKey(Exchange exchange, long keyId, List<String> userIdParts) throws Exception { + PGPPublicKeyRingCollection publicKeyringCollection = PGPDataFormatUtil.getPublicKeyRingCollection(exchange.getContext(), + findSignatureKeyFileName(exchange), findSignatureKeyRing(exchange), false); + return PGPDataFormatUtil.getPublicKeyWithKeyIdAndUserID(keyId, userIdParts, publicKeyringCollection); } @Override @@ -271,5 +275,5 @@ public class PGPDataFormat extends PGPKeyAccessDataFormat implements PGPPublicKe public void setSecretKeyAccessor(PGPSecretKeyAccessor secretKeyAccessor) { throw new UnsupportedOperationException("Use PGPKeyAccessDataFormat if you want to set the secret key access"); } - + } http://git-wip-us.apache.org/repos/asf/camel/blob/cf7ae693/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 083729c..b8849db 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 @@ -100,6 +100,7 @@ public final class PGPDataFormatUtil { } } + @Deprecated public static PGPPublicKey findPublicKeyWithKeyId(CamelContext context, String filename, byte[] keyRing, long keyid, boolean forEncryption) throws IOException, PGPException, NoSuchProviderException { InputStream is = determineKeyRingInputStream(context, filename, keyRing, forEncryption); @@ -111,6 +112,15 @@ 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 { + return new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(is)); + } finally { + IOHelper.close(is); + } + } public static PGPPrivateKey findPrivateKeyWithKeyId(CamelContext context, String filename, byte[] secretKeyRing, long keyid, String passphrase, PGPPassphraseAccessor passpraseAccessor, String provider) throws IOException, PGPException, @@ -483,4 +493,55 @@ 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. + * + * @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 + */ + @SuppressWarnings("unchecked") + public static PGPPublicKey getPublicKeyWithKeyIdAndUserID(long keyId, List<String> userIdParts, PGPPublicKeyRingCollection publicKeyringCollection) + throws PGPException { + PGPPublicKeyRing publicKeyring = publicKeyringCollection.getPublicKeyRing(keyId); + if (publicKeyring == null) { + LOG.debug("No public key found for key ID {}.", Long.toString(keyId)); + return null; + } + // publicKey can be a subkey the user IDs must therefore be provided by the primary/master key + if (isAllowedKey(userIdParts, publicKeyring.getPublicKey().getUserIDs())) { + return publicKeyring.getPublicKey(keyId); + } else { + return null; + } + } + + private static boolean isAllowedKey(List<String> allowedUserIds, Iterator<String> verifyingPublicKeyUserIds) { + + if (allowedUserIds == null || allowedUserIds.isEmpty()) { + // no restrictions specified + return true; + } + String keyUserId = null; + for (; verifyingPublicKeyUserIds.hasNext();) { + keyUserId = verifyingPublicKeyUserIds.next(); + for (String userid : allowedUserIds) { + if (keyUserId != null && keyUserId.contains(userid)) { + LOG.debug( + "Public key with user ID {} fulfills the User ID restriction.", + keyUserId, allowedUserIds); + return true; + } + } + } + LOG.warn( + "Public key with User ID {} does not fulfill the User ID restriction.", + keyUserId, allowedUserIds); + return false; + } + } http://git-wip-us.apache.org/repos/asf/camel/blob/cf7ae693/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 f0e0281..a0762c8 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 @@ -26,7 +26,6 @@ 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; @@ -409,15 +408,13 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat for (int i = 0; i < signatureList.size(); i++) { PGPOnePassSignature signature = signatureList.get(i); // Determine public key from signature keyId - PGPPublicKey sigPublicKey = publicKeyAccessor.getPublicKey(exchange, signature.getKeyID()); + PGPPublicKey sigPublicKey = publicKeyAccessor.getPublicKey(exchange, signature.getKeyID(), allowedUserIds); 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; - } + // choose that signature for which a public key exists! + signature.init(new JcaPGPContentVerifierBuilderProvider().setProvider(getProvider()), sigPublicKey); + return signature; } if (signatureList.isEmpty()) { return null; @@ -427,31 +424,6 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat } - 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). http://git-wip-us.apache.org/repos/asf/camel/blob/cf7ae693/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPPublicKeyAccessor.java ---------------------------------------------------------------------- diff --git a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPPublicKeyAccessor.java b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPPublicKeyAccessor.java index 0d55931..a114b3a 100644 --- a/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPPublicKeyAccessor.java +++ b/components/camel-crypto/src/main/java/org/apache/camel/converter/crypto/PGPPublicKeyAccessor.java @@ -37,14 +37,21 @@ public interface PGPPublicKeyAccessor { /** * Returns the public key with a certain key ID. This method is used for - * verifying the signature. + * verifying the signature. The given User IDs are provided to filter the + * public key, further. If the User ID parts list is empty, then any public + * key can be returned which has the specified key ID. If the User ID parts + * list is not empty then the returned key must have a User ID which + * contains at least one User ID part. * * @param exchange * exchange * @param keyId * key ID + * @param useridParts + * parts of User IDs, must not be <code>null</code>, but can be + * empty * @return public key or <code>null</code> if the key cannot be found */ - PGPPublicKey getPublicKey(Exchange exchange, long keyId) throws Exception; + PGPPublicKey getPublicKey(Exchange exchange, long keyId, List<String> useridParts) throws Exception; } http://git-wip-us.apache.org/repos/asf/camel/blob/cf7ae693/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 7cd9f74..f99746b 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 @@ -195,6 +195,26 @@ public class PGPDataFormatTest extends AbstractPGPDataFormatTest { assertEquals(1, inMess.getHeader(PGPDataFormat.NUMBER_OF_ENCRYPTION_KEYS)); assertEquals(1, inMess.getHeader(PGPDataFormat.NUMBER_OF_SIGNING_KEYS)); } + + /** + * You get three keys with the UserId "keyflag", a primary key and its two + * sub-keys. The sub-key with KeyFlag {@link KeyFlags#SIGN_DATA} should be + * used for signing and the sub-key with KeyFlag + * {@link KeyFlags#ENCRYPT_COMMS} or {@link KeyFlags#ENCRYPT_COMMS} or + * {@link KeyFlags#ENCRYPT_STORAGE} should be used for decryption. + * <p> + * Tests also the decryption and verifying part with the subkeys. + * @throws Exception + */ + @Test + public void testDecryptVerifyWithSubkey() throws Exception { + // do not use doRoundTripEncryptionTests("direct:subkey"); because otherwise you get an error in the dynamic test + String payload = "Test Message"; + MockEndpoint mockSubkey = getMockEndpoint("mock:unencrypted"); + mockSubkey.expectedBodiesReceived(payload); + template.sendBody("direct:subkey", payload); + assertMockEndpointsSatisfied(); + } protected RouteBuilder[] createRouteBuilders() { return new RouteBuilder[] {new RouteBuilder() { @@ -394,6 +414,17 @@ public class PGPDataFormatTest extends AbstractPGPDataFormatTest { 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("org/apache/camel/component/crypto/pubringSubKeys.gpg"); + pgpDecryptVerifySubkey.setPassword("Abcd1234"); + pgpDecryptVerifySubkey.setSignatureKeyUserid("keyflag"); + + // test that the correct subkey is selected during decrypt and verify + from("direct:subkey").marshal(pgpKeyFlag).to("mock:encrypted").unmarshal(pgpDecryptVerifySubkey) + .to("mock:unencrypted"); } }, new RouteBuilder() { public void configure() throws Exception {
