blob: 565496b6f5a96b4e4cd228ad4c222ab83804eb00 [file] [log] [blame]
/**
* 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;
}
}