/** * 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.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.camel.Exchange; import org.apache.camel.Message; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.util.IOHelper; import org.bouncycastle.bcpg.BCPGOutputStream; import org.bouncycastle.bcpg.CompressionAlgorithmTags; import org.bouncycastle.bcpg.HashAlgorithmTags; import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; import org.bouncycastle.bcpg.sig.KeyFlags; import org.bouncycastle.openpgp.PGPCompressedDataGenerator; import org.bouncycastle.openpgp.PGPEncryptedDataGenerator; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPLiteralData; import org.bouncycastle.openpgp.PGPLiteralDataGenerator; import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignatureGenerator; import org.bouncycastle.openpgp.PGPUtil; import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; import org.junit.Before; import org.junit.Test; public class PGPDataFormatTest extends AbstractPGPDataFormatTest { private static final String PUB_KEY_RING_SUBKEYS_FILE_NAME = "org/apache/camel/component/crypto/pubringSubKeys.gpg"; private static final String SEC_KEY_RING_FILE_NAME = "org/apache/camel/component/crypto/secring.gpg"; private static final String PUB_KEY_RING_FILE_NAME = "org/apache/camel/component/crypto/pubring.gpg"; PGPDataFormat encryptor = new PGPDataFormat(); PGPDataFormat decryptor = new PGPDataFormat(); @Before public void setUpEncryptorAndDecryptor() { // the following keyring contains a primary key with KeyFlag "Certify" and a subkey for signing and a subkey for encryption encryptor.setKeyFileName(PUB_KEY_RING_SUBKEYS_FILE_NAME); encryptor.setSignatureKeyFileName("org/apache/camel/component/crypto/secringSubKeys.gpg"); encryptor.setSignaturePassword("Abcd1234"); encryptor.setKeyUserid("keyflag"); encryptor.setSignatureKeyUserid("keyflag"); encryptor.setIntegrity(false); encryptor.setFileName("fileNameABC"); // the following keyring contains a primary key with KeyFlag "Certify" and a subkey for signing and a subkey for encryption decryptor.setKeyFileName("org/apache/camel/component/crypto/secringSubKeys.gpg"); decryptor.setSignatureKeyFileName(PUB_KEY_RING_SUBKEYS_FILE_NAME); decryptor.setPassword("Abcd1234"); decryptor.setSignatureKeyUserid("keyflag"); } protected String getKeyFileName() { return PUB_KEY_RING_FILE_NAME; } protected String getKeyFileNameSec() { return SEC_KEY_RING_FILE_NAME; } protected String getKeyUserId() { return "sdude@nowhere.net"; } protected List<String> getKeyUserIds() { List<String> userids = new ArrayList<String>(2); userids.add("second"); userids.add(getKeyUserId()); return userids; } protected List<String> getSignatureKeyUserIds() { List<String> userids = new ArrayList<String>(2); userids.add("second"); userids.add(getKeyUserId()); return userids; } protected String getKeyPassword() { return "sdude"; } protected String getProvider() { return "BC"; } protected int getAlgorithm() { return SymmetricKeyAlgorithmTags.TRIPLE_DES; } protected int getHashAlgorithm() { return HashAlgorithmTags.SHA256; } protected int getCompressionAlgorithm() { return CompressionAlgorithmTags.BZIP2; } @Test public void testEncryption() throws Exception { doRoundTripEncryptionTests("direct:inline"); } @Test public void testEncryption2() throws Exception { doRoundTripEncryptionTests("direct:inline2"); } @Test public void testEncryptionArmor() throws Exception { doRoundTripEncryptionTests("direct:inline-armor"); } @Test public void testEncryptionSigned() throws Exception { doRoundTripEncryptionTests("direct:inline-sign"); } @Test public void testEncryptionKeyRingByteArray() throws Exception { doRoundTripEncryptionTests("direct:key-ring-byte-array"); } @Test public void testEncryptionSignedKeyRingByteArray() throws Exception { doRoundTripEncryptionTests("direct:sign-key-ring-byte-array"); } @Test public void testSeveralSignerKeys() throws Exception { doRoundTripEncryptionTests("direct:several-signer-keys"); } @Test public void testOneUserIdWithServeralKeys() throws Exception { doRoundTripEncryptionTests("direct:one-userid-several-keys"); } @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"); String payload = "Hi Alice, Be careful Eve is listening, signed Bob"; Map<String, Object> headers = getHeaders(); template.sendBodyAndHeaders("direct:verify_exception_sig_userids", payload, headers); assertMockEndpointsSatisfied(); checkThrownException(exception, IllegalArgumentException.class, null, "No public key found for the key ID(s)"); } @Test public void testVerifyExceptionNoPassphraseSpecifiedForSignatureKeyUserId() throws Exception { MockEndpoint exception = setupExpectations(context, 1, "mock:exception"); String payload = "Hi Alice, Be careful Eve is listening, signed Bob"; Map<String, Object> headers = new HashMap<String, Object>(); // add signature user id which does not have a passphrase headers.put(PGPKeyAccessDataFormat.SIGNATURE_KEY_USERID, "userIDWithNoPassphrase"); // the following entry is necessary for the dynamic test headers.put(PGPKeyAccessDataFormat.KEY_USERID, "second"); template.sendBodyAndHeaders("direct:several-signer-keys", payload, headers); assertMockEndpointsSatisfied(); checkThrownException(exception, IllegalArgumentException.class, null, "No passphrase specified for signature key user ID"); } /** * 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. * * @throws Exception */ @Test public void testKeyFlagSelectsCorrectKey() throws Exception { MockEndpoint mockKeyFlag = getMockEndpoint("mock:encrypted_keyflag"); mockKeyFlag.setExpectedMessageCount(1); template.sendBody("direct:keyflag", "Test Message"); assertMockEndpointsSatisfied(); List<Exchange> exchanges = mockKeyFlag.getExchanges(); assertEquals(1, exchanges.size()); Exchange exchange = exchanges.get(0); Message inMess = exchange.getIn(); assertNotNull(inMess); // must contain exactly one encryption key and one signature assertEquals(1, inMess.getHeader(PGPKeyAccessDataFormat.NUMBER_OF_ENCRYPTION_KEYS)); assertEquals(1, inMess.getHeader(PGPKeyAccessDataFormat.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(); } @Test public void testEmptyBody() throws Exception { String payload = ""; MockEndpoint mockSubkey = getMockEndpoint("mock:unencrypted"); mockSubkey.expectedBodiesReceived(payload); template.sendBody("direct:subkey", payload); assertMockEndpointsSatisfied(); } @Test public void testExceptionDecryptorIncorrectInputFormatNoPGPMessage() throws Exception { String payload = "Not Correct Format"; MockEndpoint mock = getMockEndpoint("mock:exception"); mock.expectedMessageCount(1); template.sendBody("direct:subkeyUnmarshal", payload); assertMockEndpointsSatisfied(); checkThrownException(mock, IllegalArgumentException.class, null, "The input message body has an invalid format."); } @Test public void testExceptionDecryptorIncorrectInputFormatPGPSignedData() throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); createSignature(bos); 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."); } @Test 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 public void testExceptionDecryptorNoKeyFound() throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); createEncryptedNonCompressedData(bos, PUB_KEY_RING_FILE_NAME); MockEndpoint mock = getMockEndpoint("mock:exception"); mock.expectedMessageCount(1); template.sendBody("direct:subkeyUnmarshal", bos.toByteArray()); assertMockEndpointsSatisfied(); checkThrownException(mock, PGPException.class, null, "PGP message is encrypted with a key which could not be found in the Secret Keyring"); } void createEncryptedNonCompressedData(ByteArrayOutputStream bos, String keyringPath) throws Exception, IOException, PGPException, UnsupportedEncodingException { PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5) .setSecureRandom(new SecureRandom()).setProvider(getProvider())); encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(readPublicKey(keyringPath))); OutputStream encOut = encGen.open(bos, new byte[512]); PGPLiteralDataGenerator litData = new PGPLiteralDataGenerator(); OutputStream litOut = litData.open(encOut, PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, new Date(), new byte[512]); try { litOut.write("Test Message Without Compression".getBytes("UTF-8")); litOut.flush(); } finally { IOHelper.close(litOut); IOHelper.close(encOut, bos); } } private void createSignature(OutputStream out) throws Exception { PGPSecretKey pgpSec = readSecretKey(); PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider(getProvider()).build( "sdude".toCharArray())); PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(pgpSec.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1).setProvider(getProvider())); sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey); BCPGOutputStream bOut = new BCPGOutputStream(out); InputStream fIn = new ByteArrayInputStream("Test Signature".getBytes("UTF-8")); int ch; while ((ch = fIn.read()) >= 0) { sGen.update((byte) ch); } fIn.close(); sGen.generate().encode(bOut); } static PGPSecretKey readSecretKey() throws Exception { InputStream input = new ByteArrayInputStream(getSecKeyRing()); PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(input), new BcKeyFingerprintCalculator()); @SuppressWarnings("rawtypes") Iterator keyRingIter = pgpSec.getKeyRings(); while (keyRingIter.hasNext()) { PGPSecretKeyRing keyRing = (PGPSecretKeyRing) keyRingIter.next(); @SuppressWarnings("rawtypes") Iterator keyIter = keyRing.getSecretKeys(); while (keyIter.hasNext()) { PGPSecretKey key = (PGPSecretKey) keyIter.next(); if (key.isSigningKey()) { return key; } } } throw new IllegalArgumentException("Can't find signing key in key ring."); } static PGPPublicKey readPublicKey(String keyringPath) throws Exception { InputStream input = new ByteArrayInputStream(getKeyRing(keyringPath)); PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input), new BcKeyFingerprintCalculator()); @SuppressWarnings("rawtypes") Iterator keyRingIter = pgpPub.getKeyRings(); while (keyRingIter.hasNext()) { PGPPublicKeyRing keyRing = (PGPPublicKeyRing) keyRingIter.next(); @SuppressWarnings("rawtypes") Iterator keyIter = keyRing.getPublicKeys(); while (keyIter.hasNext()) { PGPPublicKey key = (PGPPublicKey) keyIter.next(); if (key.isEncryptionKey()) { return key; } } } throw new IllegalArgumentException("Can't find encryption key in key ring."); } @Test public void testExceptionDecryptorIncorrectInputFormatSymmetricEncryptedData() throws Exception { byte[] payload = "Not Correct Format".getBytes("UTF-8"); ByteArrayOutputStream bos = new ByteArrayOutputStream(); PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5) .setSecureRandom(new SecureRandom()).setProvider(getProvider())); encGen.addMethod(new JcePBEKeyEncryptionMethodGenerator("pw".toCharArray())); OutputStream encOut = encGen.open(bos, new byte[1024]); PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP); OutputStream comOut = new BufferedOutputStream(comData.open(encOut)); PGPLiteralDataGenerator litData = new PGPLiteralDataGenerator(); OutputStream litOut = litData.open(comOut, PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, new Date(), new byte[1024]); litOut.write(payload); litOut.flush(); litOut.close(); comOut.close(); encOut.close(); 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."); } @Test public void testExceptionForSignatureVerificationOptionNoSignatureAllowed() throws Exception { decryptor.setSignatureVerificationOption(PGPKeyAccessDataFormat.SIGNATURE_VERIFICATION_OPTION_NO_SIGNATURE_ALLOWED); MockEndpoint mock = getMockEndpoint("mock:exception"); mock.expectedMessageCount(1); template.sendBody("direct:subkey", "Test Message"); assertMockEndpointsSatisfied(); checkThrownException(mock, PGPException.class, null, "PGP message contains a signature although a signature is not expected"); } @Test public void testExceptionForSignatureVerificationOptionRequired() throws Exception { encryptor.setSignatureKeyUserid(null); // no signature decryptor.setSignatureVerificationOption(PGPKeyAccessDataFormat.SIGNATURE_VERIFICATION_OPTION_REQUIRED); MockEndpoint mock = getMockEndpoint("mock:exception"); mock.expectedMessageCount(1); template.sendBody("direct:subkey", "Test Message"); assertMockEndpointsSatisfied(); checkThrownException(mock, PGPException.class, null, "PGP message does not contain any signatures although a signature is expected"); } @Test public void testSignatureVerificationOptionIgnore() throws Exception { // encryptor is sending a PGP message with signature! Decryptor is ignoreing the signature decryptor.setSignatureVerificationOption(PGPKeyAccessDataFormat.SIGNATURE_VERIFICATION_OPTION_IGNORE); decryptor.setSignatureKeyUserids(null); decryptor.setSignatureKeyFileName(null); // no public keyring! --> no signature validation possible String payload = "Test Message"; MockEndpoint mock = getMockEndpoint("mock:unencrypted"); mock.expectedBodiesReceived(payload); template.sendBody("direct:subkey", payload); assertMockEndpointsSatisfied(); } protected RouteBuilder[] createRouteBuilders() { return new RouteBuilder[] {new RouteBuilder() { public void configure() throws Exception { onException(Exception.class).handled(true).to("mock:exception"); // START SNIPPET: pgp-format // Public Key FileName String keyFileName = getKeyFileName(); // Private Key FileName String keyFileNameSec = getKeyFileNameSec(); // Keyring Userid Used to Encrypt String keyUserid = getKeyUserId(); // Private key password String keyPassword = getKeyPassword(); from("direct:inline").marshal().pgp(keyFileName, keyUserid).to("mock:encrypted").unmarshal() .pgp(keyFileNameSec, null, keyPassword).to("mock:unencrypted"); // END SNIPPET: pgp-format // START SNIPPET: pgp-format-header PGPDataFormat pgpEncrypt = new PGPDataFormat(); pgpEncrypt.setKeyFileName(keyFileName); pgpEncrypt.setKeyUserid(keyUserid); pgpEncrypt.setProvider(getProvider()); pgpEncrypt.setAlgorithm(getAlgorithm()); pgpEncrypt.setCompressionAlgorithm(getCompressionAlgorithm()); PGPDataFormat pgpDecrypt = new PGPDataFormat(); pgpDecrypt.setKeyFileName(keyFileNameSec); pgpDecrypt.setPassword(keyPassword); pgpDecrypt.setProvider(getProvider()); pgpDecrypt.setSignatureVerificationOption(PGPKeyAccessDataFormat.SIGNATURE_VERIFICATION_OPTION_NO_SIGNATURE_ALLOWED); from("direct:inline2").marshal(pgpEncrypt).to("mock:encrypted").unmarshal(pgpDecrypt).to("mock:unencrypted"); from("direct:inline-armor").marshal().pgp(keyFileName, keyUserid, null, true, true).to("mock:encrypted").unmarshal() .pgp(keyFileNameSec, null, keyPassword, true, true).to("mock:unencrypted"); // END SNIPPET: pgp-format-header // START SNIPPET: pgp-format-signature PGPDataFormat pgpSignAndEncrypt = new PGPDataFormat(); pgpSignAndEncrypt.setKeyFileName(keyFileName); pgpSignAndEncrypt.setKeyUserid(keyUserid); pgpSignAndEncrypt.setSignatureKeyFileName(keyFileNameSec); PGPPassphraseAccessor passphraseAccessor = getPassphraseAccessor(); pgpSignAndEncrypt.setSignatureKeyUserid("Super <sdude@nowhere.net>"); // must be the exact user Id because passphrase is searched in accessor pgpSignAndEncrypt.setPassphraseAccessor(passphraseAccessor); pgpSignAndEncrypt.setProvider(getProvider()); pgpSignAndEncrypt.setAlgorithm(getAlgorithm()); pgpSignAndEncrypt.setHashAlgorithm(getHashAlgorithm()); pgpSignAndEncrypt.setCompressionAlgorithm(getCompressionAlgorithm()); PGPDataFormat pgpVerifyAndDecrypt = new PGPDataFormat(); pgpVerifyAndDecrypt.setKeyFileName(keyFileNameSec); pgpVerifyAndDecrypt.setPassword(keyPassword); pgpVerifyAndDecrypt.setSignatureKeyFileName(keyFileName); pgpVerifyAndDecrypt.setProvider(getProvider()); pgpVerifyAndDecrypt.setSignatureKeyUserid(keyUserid); // restrict verification to public keys with certain User ID from("direct:inline-sign").marshal(pgpSignAndEncrypt).to("mock:encrypted").unmarshal(pgpVerifyAndDecrypt) .to("mock:unencrypted"); // END SNIPPET: pgp-format-signature // test verifying exception, no public key found corresponding to signature key userIds from("direct:verify_exception_sig_userids").marshal(pgpSignAndEncrypt).to("mock:encrypted") .setHeader(PGPKeyAccessDataFormat.SIGNATURE_KEY_USERIDS).constant(Arrays.asList(new String[] {"wrong1", "wrong2" })) .setHeader(PGPKeyAccessDataFormat.SIGNATURE_KEY_USERID).constant("wrongUserID").unmarshal(pgpVerifyAndDecrypt) .to("mock:unencrypted"); /* ---- key ring as byte array -- */ // START SNIPPET: pgp-format-key-ring-byte-array PGPDataFormat pgpEncryptByteArray = new PGPDataFormat(); pgpEncryptByteArray.setEncryptionKeyRing(getPublicKeyRing()); pgpEncryptByteArray.setKeyUserids(getKeyUserIds()); pgpEncryptByteArray.setProvider(getProvider()); pgpEncryptByteArray.setAlgorithm(SymmetricKeyAlgorithmTags.DES); pgpEncryptByteArray.setCompressionAlgorithm(CompressionAlgorithmTags.UNCOMPRESSED); PGPDataFormat pgpDecryptByteArray = new PGPDataFormat(); pgpDecryptByteArray.setEncryptionKeyRing(getSecKeyRing()); pgpDecryptByteArray.setPassphraseAccessor(passphraseAccessor); pgpDecryptByteArray.setProvider(getProvider()); from("direct:key-ring-byte-array").streamCaching().marshal(pgpEncryptByteArray).to("mock:encrypted") .unmarshal(pgpDecryptByteArray).to("mock:unencrypted"); // END SNIPPET: pgp-format-key-ring-byte-array // START SNIPPET: pgp-format-signature-key-ring-byte-array PGPDataFormat pgpSignAndEncryptByteArray = new PGPDataFormat(); pgpSignAndEncryptByteArray.setKeyUserid(keyUserid); pgpSignAndEncryptByteArray.setSignatureKeyRing(getSecKeyRing()); pgpSignAndEncryptByteArray.setSignatureKeyUserid(keyUserid); pgpSignAndEncryptByteArray.setSignaturePassword(keyPassword); pgpSignAndEncryptByteArray.setProvider(getProvider()); pgpSignAndEncryptByteArray.setAlgorithm(SymmetricKeyAlgorithmTags.BLOWFISH); pgpSignAndEncryptByteArray.setHashAlgorithm(HashAlgorithmTags.RIPEMD160); pgpSignAndEncryptByteArray.setCompressionAlgorithm(CompressionAlgorithmTags.ZLIB); PGPDataFormat pgpVerifyAndDecryptByteArray = new PGPDataFormat(); pgpVerifyAndDecryptByteArray.setPassphraseAccessor(passphraseAccessor); 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:sign-key-ring-byte-array").streamCaching() // encryption key ring can also be set as header .setHeader(PGPDataFormat.ENCRYPTION_KEY_RING).constant(getPublicKeyRing()).marshal(pgpSignAndEncryptByteArray) // 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-format-signature-key-ring-byte-array // START SNIPPET: pgp-format-several-signer-keys PGPDataFormat pgpSignAndEncryptSeveralSignerKeys = new PGPDataFormat(); pgpSignAndEncryptSeveralSignerKeys.setKeyUserid(keyUserid); pgpSignAndEncryptSeveralSignerKeys.setEncryptionKeyRing(getPublicKeyRing()); pgpSignAndEncryptSeveralSignerKeys.setSignatureKeyRing(getSecKeyRing()); List<String> signerUserIds = new ArrayList<String>(); signerUserIds.add("Third (comment third) <email@third.com>"); signerUserIds.add("Second <email@second.com>"); pgpSignAndEncryptSeveralSignerKeys.setSignatureKeyUserids(signerUserIds); Map<String, String> userId2Passphrase = new HashMap<String, String>(); userId2Passphrase.put("Third (comment third) <email@third.com>", "sdude"); userId2Passphrase.put("Second <email@second.com>", "sdude"); PGPPassphraseAccessor passphraseAccessorSeveralKeys = new DefaultPGPPassphraseAccessor(userId2Passphrase); pgpSignAndEncryptSeveralSignerKeys.setPassphraseAccessor(passphraseAccessorSeveralKeys); PGPDataFormat pgpVerifyAndDecryptSeveralSignerKeys = new PGPDataFormat(); pgpVerifyAndDecryptSeveralSignerKeys.setPassphraseAccessor(passphraseAccessor); pgpVerifyAndDecryptSeveralSignerKeys.setEncryptionKeyRing(getSecKeyRing()); pgpVerifyAndDecryptSeveralSignerKeys.setSignatureKeyRing(getPublicKeyRing()); pgpVerifyAndDecryptSeveralSignerKeys.setProvider(getProvider()); // only specify one expected signature List<String> expectedSigUserIds = new ArrayList<String>(); expectedSigUserIds.add("Second <email@second.com>"); pgpVerifyAndDecryptSeveralSignerKeys.setSignatureKeyUserids(expectedSigUserIds); from("direct:several-signer-keys").streamCaching().marshal(pgpSignAndEncryptSeveralSignerKeys).to("mock:encrypted") .unmarshal(pgpVerifyAndDecryptSeveralSignerKeys).to("mock:unencrypted"); // END SNIPPET: pgp-format-several-signer-keys // test encryption by several key and signing by serveral keys where the keys are specified by one User ID part PGPDataFormat pgpSignAndEncryptOneUserIdWithServeralKeys = new PGPDataFormat(); pgpSignAndEncryptOneUserIdWithServeralKeys.setEncryptionKeyRing(getPublicKeyRing()); pgpSignAndEncryptOneUserIdWithServeralKeys.setSignatureKeyRing(getSecKeyRing()); // the two private keys have the same password therefore we do not need a passphrase accessor pgpSignAndEncryptOneUserIdWithServeralKeys.setPassword(getKeyPassword()); PGPDataFormat pgpVerifyAndDecryptOneUserIdWithServeralKeys = new PGPDataFormat(); pgpVerifyAndDecryptOneUserIdWithServeralKeys.setPassword(getKeyPassword()); pgpVerifyAndDecryptOneUserIdWithServeralKeys.setEncryptionKeyRing(getSecKeyRing()); pgpVerifyAndDecryptOneUserIdWithServeralKeys.setSignatureKeyRing(getPublicKeyRing()); pgpVerifyAndDecryptOneUserIdWithServeralKeys.setProvider(getProvider()); pgpVerifyAndDecryptOneUserIdWithServeralKeys.setSignatureKeyUserids(expectedSigUserIds); from("direct:one-userid-several-keys") // there are two keys which have a User ID which contains the string "econd" .setHeader(PGPKeyAccessDataFormat.KEY_USERID) .constant("econd") .setHeader(PGPKeyAccessDataFormat.SIGNATURE_KEY_USERID) .constant("econd") .marshal(pgpSignAndEncryptOneUserIdWithServeralKeys) // it is recommended to remove the header immediately when it is no longer needed .removeHeader(PGPKeyAccessDataFormat.KEY_USERID) .removeHeader(PGPKeyAccessDataFormat.SIGNATURE_KEY_USERID) .to("mock:encrypted") // only specify one expected signature key, to check the first signature .setHeader(PGPKeyAccessDataFormat.SIGNATURE_KEY_USERID) .constant("Second <email@second.com>") .unmarshal(pgpVerifyAndDecryptOneUserIdWithServeralKeys) // do it again but now check the second signature key // there are two keys which have a User ID which contains the string "econd" .setHeader(PGPKeyAccessDataFormat.KEY_USERID).constant("econd").setHeader(PGPKeyAccessDataFormat.SIGNATURE_KEY_USERID) .constant("econd").marshal(pgpSignAndEncryptOneUserIdWithServeralKeys) // it is recommended to remove the header immediately when it is no longer needed .removeHeader(PGPKeyAccessDataFormat.KEY_USERID).removeHeader(PGPKeyAccessDataFormat.SIGNATURE_KEY_USERID) // only specify one expected signature key, to check the second signature .setHeader(PGPKeyAccessDataFormat.SIGNATURE_KEY_USERID).constant("Third (comment third) <email@third.com>") .unmarshal(pgpVerifyAndDecryptOneUserIdWithServeralKeys).to("mock:unencrypted"); } }, new RouteBuilder() { public void configure() throws Exception { onException(Exception.class).handled(true).to("mock:exception"); from("direct:keyflag").marshal(encryptor).to("mock:encrypted_keyflag"); // test that the correct subkey is selected during decrypt and verify from("direct:subkey").marshal(encryptor).to("mock:encrypted").unmarshal(decryptor).to("mock:unencrypted"); from("direct:subkeyUnmarshal").unmarshal(decryptor).to("mock:unencrypted"); } }, new RouteBuilder() { public void configure() throws Exception { PGPPublicKeyAccessor publicKeyAccessor = new DefaultPGPPublicKeyAccessor(getPublicKeyRing()); //password cannot be set dynamically! PGPSecretKeyAccessor secretKeyAccessor = new DefaultPGPSecretKeyAccessor(getSecKeyRing(), "sdude", getProvider()); PGPKeyAccessDataFormat dfEncryptSignKeyAccess = new PGPKeyAccessDataFormat(); dfEncryptSignKeyAccess.setPublicKeyAccessor(publicKeyAccessor); dfEncryptSignKeyAccess.setSecretKeyAccessor(secretKeyAccessor); dfEncryptSignKeyAccess.setKeyUserid(getKeyUserId()); dfEncryptSignKeyAccess.setSignatureKeyUserid(getKeyUserId()); PGPKeyAccessDataFormat dfDecryptVerifyKeyAccess = new PGPKeyAccessDataFormat(); dfDecryptVerifyKeyAccess.setPublicKeyAccessor(publicKeyAccessor); dfDecryptVerifyKeyAccess.setSecretKeyAccessor(secretKeyAccessor); dfDecryptVerifyKeyAccess.setSignatureKeyUserid(getKeyUserId()); from("direct:key_access").marshal(dfEncryptSignKeyAccess).to("mock:encrypted").unmarshal(dfDecryptVerifyKeyAccess) .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 { return getKeyRing(PUB_KEY_RING_FILE_NAME); } public static byte[] getSecKeyRing() throws Exception { return getKeyRing(SEC_KEY_RING_FILE_NAME); } private static byte[] getKeyRing(String fileName) throws IOException { InputStream is = PGPDataFormatTest.class.getClassLoader().getResourceAsStream(fileName); ByteArrayOutputStream output = new ByteArrayOutputStream(); IOHelper.copyAndCloseInput(is, output); output.close(); return output.toByteArray(); } public static PGPPassphraseAccessor getPassphraseAccessor() { Map<String, String> userId2Passphrase = Collections.singletonMap("Super <sdude@nowhere.net>", "sdude"); PGPPassphraseAccessor passphraseAccessor = new DefaultPGPPassphraseAccessor(userId2Passphrase); return passphraseAccessor; } public static void checkThrownException(MockEndpoint mock, Class<? extends Exception> cl, Class<? extends Exception> expectedCauseClass, String expectedMessagePart) throws Exception { Exception e = (Exception) mock.getExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT); assertNotNull("Expected excpetion " + cl.getName() + " missing", e); if (e.getClass() != cl) { String stackTrace = getStrackTrace(e); fail("Exception " + cl.getName() + " excpected, but was " + e.getClass().getName() + ": " + stackTrace); } if (expectedMessagePart != null) { if (e.getMessage() == null) { fail("Expected excption does not contain a message. Stack trace: " + getStrackTrace(e)); } else { if (!e.getMessage().contains(expectedMessagePart)) { fail("Expected excption message does not contain a expected message part " + expectedMessagePart + ". Stack trace: " + getStrackTrace(e)); } } } if (expectedCauseClass != null) { Throwable cause = e.getCause(); assertNotNull("Expected cause exception" + expectedCauseClass.getName() + " missing", cause); if (expectedCauseClass != cause.getClass()) { fail("Cause exception " + expectedCauseClass.getName() + " expected, but was " + cause.getClass().getName() + ": " + getStrackTrace(e)); } } } public static String getStrackTrace(Exception e) throws UnsupportedEncodingException { ByteArrayOutputStream os = new ByteArrayOutputStream(); PrintWriter w = new PrintWriter(os); e.printStackTrace(w); w.close(); String stackTrace = new String(os.toByteArray(), "UTF-8"); return stackTrace; } }