Repository: camel Updated Branches: refs/for/master [created] ecbbeb74f
CAMEL-9163: PGP Data Format: Support PGP Messages without Compressed Data packet Change-Id: I6818cb2ee4777a7069a0760cae22c72074c63042 Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/ecbbeb74 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/ecbbeb74 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/ecbbeb74 Branch: refs/for/master Commit: ecbbeb74f753ce81995171fb5d25f58930c22d26 Parents: 92c4a81 Author: Franz Forsthofer <franz.forstho...@sap.com> Authored: Thu Sep 24 16:28:27 2015 +0200 Committer: Franz Forsthofer <franz.forstho...@sap.com> Committed: Thu Sep 24 16:30:32 2015 +0200 ---------------------------------------------------------------------- .../converter/crypto/PGPDataFormatUtil.java | 4 +- .../crypto/PGPKeyAccessDataFormat.java | 63 +++++++++++++------- .../converter/crypto/PGPDataFormatTest.java | 60 +++++++++++++++---- 3 files changed, 89 insertions(+), 38 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/ecbbeb74/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 864337c..6f8f815 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 @@ -207,7 +207,6 @@ public final class PGPDataFormatUtil { return findPublicKeys(userids, forEncryption, pgpSec); } - @SuppressWarnings("unchecked") public static List<PGPPublicKey> findPublicKeys(List<String> useridParts, boolean forEncryption, PGPPublicKeyRingCollection pgpPublicKeyringCollection) { List<PGPPublicKey> result = new ArrayList<PGPPublicKey>(useridParts.size()); for (Iterator<PGPPublicKeyRing> keyRingIter = pgpPublicKeyringCollection.getKeyRings(); keyRingIter.hasNext();) { @@ -422,8 +421,7 @@ public final class PGPDataFormatUtil { LOG.debug("User ID {} found in primary key with key ID {} containing one of the parts {}", new Object[] { foundKeyUserIdForUserIdPart[0], primaryKey.getKeyID(), useridParts }); // add all signing keys - for (@SuppressWarnings("unchecked") - Iterator<PGPSecretKey> iterKey = keyring.getSecretKeys(); iterKey.hasNext();) { + for (Iterator<PGPSecretKey> iterKey = keyring.getSecretKeys(); iterKey.hasNext();) { PGPSecretKey secKey = iterKey.next(); if (isSigningKey(secKey)) { PGPPrivateKey privateKey = secKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider(provider) http://git-wip-us.apache.org/repos/asf/camel/blob/ecbbeb74/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 88c0261..0d521a8 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 @@ -80,6 +80,8 @@ import org.slf4j.LoggerFactory; */ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat { + private static final Logger log = LoggerFactory.getLogger(PGPKeyAccessDataFormat.class); + public static final String KEY_USERID = "CamelPGPDataFormatKeyUserid"; public static final String KEY_USERIDS = "CamelPGPDataFormatKeyUserids"; public static final String SIGNATURE_KEY_USERID = "CamelPGPDataFormatSignatureKeyUserid"; @@ -163,6 +165,8 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat private int algorithm = SymmetricKeyAlgorithmTags.CAST5; // for encryption private int compressionAlgorithm = CompressionAlgorithmTags.ZIP; // for encryption + + private boolean withCompressedDataPacket = true; // for encryption private String signatureVerificationOption = "optional"; @@ -210,7 +214,7 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat return exchange.getIn().getHeader(Exchange.FILE_NAME, getFileName(), String.class); } - public void marshal(Exchange exchange, Object graph, OutputStream outputStream) throws Exception { + public void marshal(Exchange exchange, Object graph, OutputStream outputStream) throws Exception { //NOPMD List<String> userids = determineEncryptionUserIds(exchange); List<PGPPublicKey> keys = publicKeyAccessor.getEncryptionKeys(exchange, userids); if (keys.isEmpty()) { @@ -233,9 +237,15 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat } OutputStream encOut = encGen.open(outputStream, new byte[BUFFER_SIZE]); - PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(findCompressionAlgorithm(exchange)); - OutputStream comOut = new BufferedOutputStream(comData.open(encOut)); - + OutputStream comOut; + if (withCompressedDataPacket) { + PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(findCompressionAlgorithm(exchange)); + comOut = new BufferedOutputStream(comData.open(encOut)); + } else { + comOut = encOut; + LOG.debug("No Compressed Data packet is added"); + } + List<PGPSignatureGenerator> sigGens = createSignatureGenerator(exchange, comOut); PGPLiteralDataGenerator litData = new PGPLiteralDataGenerator(); @@ -311,7 +321,7 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat return result; } - protected List<PGPSignatureGenerator> createSignatureGenerator(Exchange exchange, OutputStream out) throws Exception { + protected List<PGPSignatureGenerator> createSignatureGenerator(Exchange exchange, OutputStream out) throws Exception { //NOPMD if (secretKeyAccessor == null) { return null; @@ -345,7 +355,7 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat } @SuppressWarnings("resource") - public Object unmarshal(Exchange exchange, InputStream encryptedStream) throws Exception { + public Object unmarshal(Exchange exchange, InputStream encryptedStream) throws Exception { //NOPMD if (encryptedStream == null) { return null; } @@ -361,10 +371,16 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat try { in = PGPUtil.getDecoderStream(encryptedStream); encData = getDecryptedData(exchange, in); - uncompressedData = getUncompressedData(encData); - PGPObjectFactory pgpFactory = new PGPObjectFactory(uncompressedData, new BcKeyFingerprintCalculator()); + PGPObjectFactory pgpFactory = new PGPObjectFactory(encData, new BcKeyFingerprintCalculator()); Object object = pgpFactory.nextObject(); - + if (object instanceof PGPCompressedData) { + PGPCompressedData comData = (PGPCompressedData) object; + uncompressedData = comData.getDataStream(); + pgpFactory = new PGPObjectFactory(uncompressedData, new BcKeyFingerprintCalculator()); + object = pgpFactory.nextObject(); + } else { + log.debug("PGP Message does not contain a Compressed Data Packet"); + } PGPOnePassSignature signature; if (object instanceof PGPOnePassSignatureList) { signature = getSignature(exchange, (PGPOnePassSignatureList) object); @@ -418,17 +434,6 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat } } - private InputStream getUncompressedData(InputStream encData) throws IOException, PGPException { - PGPObjectFactory pgpFactory = new PGPObjectFactory(encData, new BcKeyFingerprintCalculator()); - Object compObj = pgpFactory.nextObject(); - if (!(compObj instanceof PGPCompressedData)) { - throw getFormatException(); - } - PGPCompressedData comData = (PGPCompressedData) compObj; - InputStream uncompressedData = comData.getDataStream(); - return uncompressedData; - } - private InputStream getDecryptedData(Exchange exchange, InputStream encryptedStream) throws Exception, PGPException { PGPObjectFactory pgpFactory = new PGPObjectFactory(encryptedStream, new BcKeyFingerprintCalculator()); Object firstObject = pgpFactory.nextObject(); @@ -489,7 +494,7 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat return new IllegalArgumentException( "The input message body has an invalid format. The PGP decryption/verification processor expects a sequence of PGP packets of the form " + "(entries in brackets are optional and ellipses indicate repetition, comma represents sequential composition, and vertical bar separates alternatives): " - + "Public Key Encrypted Session Key ..., Symmetrically Encrypted Data | Sym. Encrypted and Integrity Protected Data, Compressed Data, (One Pass Signature ...,) " + + "Public Key Encrypted Session Key ..., Symmetrically Encrypted Data | Sym. Encrypted and Integrity Protected Data, (Compressed Data,) (One Pass Signature ...,) " + "Literal Data, (Signature ...,)"); } @@ -709,6 +714,18 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat return signatureVerificationOption; } + public boolean isWithCompressedDataPacket() { + return withCompressedDataPacket; + } + + /** Indicator that Compressed Data packet shall be added during encryption. + * The default value is true. + * If <tt>false</tt> then the compression algorithm (see {@link #setCompressionAlgorithm(int)} is ignored. + */ + public void setWithCompressedDataPacket(boolean withCompressedDataPacket) { + this.withCompressedDataPacket = withCompressedDataPacket; + } + /** * Signature verification option. Controls the behavior for the signature * verification during unmarshaling. Possible values are @@ -762,7 +779,7 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat } @Override - protected void doStart() throws Exception { + protected void doStart() throws Exception { //NOPMD if (Security.getProvider(BC) == null && BC.equals(getProvider())) { LOG.debug("Adding BouncyCastleProvider as security provider"); Security.addProvider(new BouncyCastleProvider()); @@ -772,7 +789,7 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat } @Override - protected void doStop() throws Exception { + protected void doStop() throws Exception { //NOPMD // noop } } http://git-wip-us.apache.org/repos/asf/camel/blob/ecbbeb74/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 f24d40e..565496b 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 @@ -298,17 +298,19 @@ public class PGPDataFormatTest extends AbstractPGPDataFormatTest { } @Test - public void testExceptionDecryptorIncorrectInputNoCompression() throws Exception { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - createEncryptedNonCompressedData(bos, PUB_KEY_RING_SUBKEYS_FILE_NAME); - - MockEndpoint mock = getMockEndpoint("mock:exception"); - mock.expectedMessageCount(1); - template.sendBody("direct:subkeyUnmarshal", bos.toByteArray()); - assertMockEndpointsSatisfied(); - - checkThrownException(mock, IllegalArgumentException.class, null, "The input message body has an invalid format."); + public void testEncryptSignWithoutCompressedDataPacket() throws Exception { + + doRoundTripEncryptionTests("direct:encrypt-sign-without-compressed-data-packet"); +// ByteArrayOutputStream bos = new ByteArrayOutputStream(); +// +//// createEncryptedNonCompressedData(bos, PUB_KEY_RING_SUBKEYS_FILE_NAME); +// +// MockEndpoint mock = getMockEndpoint("mock:exception"); +// mock.expectedMessageCount(1); +// template.sendBody("direct:encrypt-sign-without-compressed-data-packet", bos.toByteArray()); +// assertMockEndpointsSatisfied(); +// +// //checkThrownException(mock, IllegalArgumentException.class, null, "The input message body has an invalid format."); } @Test @@ -710,7 +712,41 @@ public class PGPDataFormatTest extends AbstractPGPDataFormatTest { .to("mock:unencrypted"); } - } }; + }, new RouteBuilder() { + public void configure() throws Exception { + + // START SNIPPET: pgp-encrypt-sign-without-compressed-data-packet + PGPDataFormat pgpEncryptSign = new PGPDataFormat(); + pgpEncryptSign.setKeyUserid(getKeyUserId()); + pgpEncryptSign.setSignatureKeyRing(getSecKeyRing()); + pgpEncryptSign.setSignatureKeyUserid(getKeyUserId()); + pgpEncryptSign.setSignaturePassword(getKeyPassword()); + pgpEncryptSign.setProvider(getProvider()); + pgpEncryptSign.setAlgorithm(SymmetricKeyAlgorithmTags.BLOWFISH); + pgpEncryptSign.setHashAlgorithm(HashAlgorithmTags.RIPEMD160); + // without compressed data packet + pgpEncryptSign.setWithCompressedDataPacket(false); + + PGPDataFormat pgpVerifyAndDecryptByteArray = new PGPDataFormat(); + pgpVerifyAndDecryptByteArray.setPassphraseAccessor(getPassphraseAccessor()); + pgpVerifyAndDecryptByteArray.setEncryptionKeyRing(getSecKeyRing()); + pgpVerifyAndDecryptByteArray.setProvider(getProvider()); + // restrict verification to public keys with certain User ID + pgpVerifyAndDecryptByteArray.setSignatureKeyUserids(getSignatureKeyUserIds()); + pgpVerifyAndDecryptByteArray.setSignatureVerificationOption(PGPKeyAccessDataFormat.SIGNATURE_VERIFICATION_OPTION_REQUIRED); + + from("direct:encrypt-sign-without-compressed-data-packet").streamCaching() + // encryption key ring can also be set as header + .setHeader(PGPDataFormat.ENCRYPTION_KEY_RING).constant(getPublicKeyRing()).marshal(pgpEncryptSign) + // it is recommended to remove the header immediately when it is no longer needed + .removeHeader(PGPDataFormat.ENCRYPTION_KEY_RING).to("mock:encrypted") + // signature key ring can also be set as header + .setHeader(PGPDataFormat.SIGNATURE_KEY_RING).constant(getPublicKeyRing()).unmarshal(pgpVerifyAndDecryptByteArray) + // it is recommended to remove the header immediately when it is no longer needed + .removeHeader(PGPDataFormat.SIGNATURE_KEY_RING).to("mock:unencrypted"); + // END SNIPPET: pgp-encrypt-sign-without-compressed-data-packet + } + }}; } public static byte[] getPublicKeyRing() throws Exception {