You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ni...@apache.org on 2014/03/03 02:50:50 UTC
git commit: CAMEL-7253 Fixed the NPE of PGPDataFormat if decryptor
gets body with invalid format with thanks to Franz
Repository: camel
Updated Branches:
refs/heads/master cc54cb49d -> 9ea05aae6
CAMEL-7253 Fixed the NPE of PGPDataFormat if decryptor gets body with invalid format 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/9ea05aae
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/9ea05aae
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/9ea05aae
Branch: refs/heads/master
Commit: 9ea05aae64256ad652235865ad6c0f31a00e682a
Parents: cc54cb4
Author: Willem Jiang <wi...@gmail.com>
Authored: Mon Mar 3 09:50:35 2014 +0800
Committer: Willem Jiang <wi...@gmail.com>
Committed: Mon Mar 3 09:50:35 2014 +0800
----------------------------------------------------------------------
.../crypto/PGPKeyAccessDataFormat.java | 47 +++-
.../converter/crypto/PGPDataFormatTest.java | 279 +++++++++++++++++--
2 files changed, 299 insertions(+), 27 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/camel/blob/9ea05aae/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 a0762c8..92663c4 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
@@ -309,20 +309,33 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat
}
InputStream in = PGPUtil.getDecoderStream(encryptedStream);
PGPObjectFactory pgpFactory = new PGPObjectFactory(in);
- Object o = pgpFactory.nextObject();
+ Object firstObject = pgpFactory.nextObject();
// the first object might be a PGP marker packet
PGPEncryptedDataList enc;
- if (o instanceof PGPEncryptedDataList) {
- enc = (PGPEncryptedDataList) o;
+ if (firstObject instanceof PGPEncryptedDataList) {
+ enc = (PGPEncryptedDataList) firstObject;
} else {
- enc = (PGPEncryptedDataList) pgpFactory.nextObject();
+ Object secondObject = pgpFactory.nextObject();
+ if (secondObject instanceof PGPEncryptedDataList) {
+ enc = (PGPEncryptedDataList)secondObject;
+ } else {
+ enc = null;
+ }
+ }
+
+ if (enc == null) {
+ throw getFormatException();
}
PGPPublicKeyEncryptedData pbe = null;
PGPPrivateKey key = null;
// find encrypted data for which a private key exists in the secret key ring
for (int i = 0; i < enc.size() && key == null; i++) {
- pbe = (PGPPublicKeyEncryptedData) enc.get(i);
+ Object encryptedData = enc.get(i);
+ if (!(encryptedData instanceof PGPPublicKeyEncryptedData)) {
+ throw getFormatException();
+ }
+ pbe = (PGPPublicKeyEncryptedData) encryptedData;
key = secretKeyAccessor.getPrivateKey(exchange, pbe.getKeyID());
if (key != null) {
// take the first key
@@ -330,12 +343,16 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat
}
}
if (key == null) {
- throw new PGPException("Provided input is encrypted with unknown pair of keys.");
+ throw new PGPException("Message is encrypted with a key which could not be found in the Secret Key Ring.");
}
InputStream encData = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider(getProvider()).build(key));
pgpFactory = new PGPObjectFactory(encData);
- PGPCompressedData comData = (PGPCompressedData) pgpFactory.nextObject();
+ Object compObj = pgpFactory.nextObject();
+ if (!(compObj instanceof PGPCompressedData)) {
+ throw getFormatException();
+ }
+ PGPCompressedData comData = (PGPCompressedData)compObj;
pgpFactory = new PGPObjectFactory(comData.getDataStream());
Object object = pgpFactory.nextObject();
@@ -347,7 +364,12 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat
signature = null;
}
- PGPLiteralData ld = (PGPLiteralData) object;
+ PGPLiteralData ld;
+ if (object instanceof PGPLiteralData) {
+ ld = (PGPLiteralData) object;
+ } else {
+ throw getFormatException();
+ }
InputStream litData = ld.getInputStream();
// enable streaming via OutputStreamCache
@@ -392,6 +414,13 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat
}
}
+ private IllegalArgumentException getFormatException() {
+ 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 ...,) "
+ + "Literal Data, (Signature ...,)");
+ }
+
protected PGPSignature getSignatureWithKeyId(long keyID, PGPSignatureList sigList) {
for (int i = 0; i < sigList.size(); i++) {
PGPSignature signature = sigList.get(i);
@@ -419,7 +448,7 @@ public class PGPKeyAccessDataFormat extends ServiceSupport implements DataFormat
if (signatureList.isEmpty()) {
return null;
} else {
- throw new IllegalArgumentException("No public key found fitting to the signature key Id; cannot verify the signature");
+ throw new IllegalArgumentException("No public key found fitting to the signature key Id; cannot verify the signature.");
}
}
http://git-wip-us.apache.org/repos/asf/camel/blob/9ea05aae/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 f99746b..5237710 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
@@ -16,13 +16,21 @@
*/
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;
@@ -31,14 +39,36 @@ 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.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.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";
@@ -143,10 +173,7 @@ public class PGPDataFormatTest extends AbstractPGPDataFormatTest {
template.sendBodyAndHeaders("direct:verify_exception_sig_userids", payload, headers);
assertMockEndpointsSatisfied();
- //check exception text
- Exception e = (Exception) exception.getExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT);
- assertNotNull("Expected excpetion missing", e);
- assertTrue(e.getMessage().contains("No public key found fitting to the signature key Id"));
+ checkThrownException(exception, IllegalArgumentException.class, null, "No public key found fitting to the signature key Id");
}
@@ -163,11 +190,7 @@ public class PGPDataFormatTest extends AbstractPGPDataFormatTest {
template.sendBodyAndHeaders("direct:several-signer-keys", payload, headers);
assertMockEndpointsSatisfied();
- //check exception text
- Exception e = (Exception) exception.getExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT);
- assertNotNull("Expected excpetion missing", e);
- assertTrue(e.getMessage().contains("No passphrase specified for signature key user ID"));
-
+ checkThrownException(exception, IllegalArgumentException.class, null, "No passphrase specified for signature key user ID");
}
/**
@@ -195,7 +218,7 @@ 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
@@ -204,10 +227,11 @@ public class PGPDataFormatTest extends AbstractPGPDataFormatTest {
* {@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 {
+ 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");
@@ -216,11 +240,190 @@ public class PGPDataFormatTest extends AbstractPGPDataFormatTest {
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 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.");
+ }
+
+ @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,
+ "Message is encrypted with a key which could not be found in the Secret Key Ring");
+ }
+
+ 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(),
+ PGPUtil.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));
+
+ @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));
+
+ @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.");
+ }
+
protected RouteBuilder[] createRouteBuilders() {
return new RouteBuilder[] {new RouteBuilder() {
public void configure() throws Exception {
- onException(IllegalArgumentException.class).handled(true).to("mock:exception");
+ onException(Exception.class).handled(true).to("mock:exception");
// START SNIPPET: pgp-format
// Public Key FileName
@@ -404,27 +607,30 @@ public class PGPDataFormatTest extends AbstractPGPDataFormatTest {
}, new RouteBuilder() {
public void configure() throws Exception {
+
+ onException(Exception.class).handled(true).to("mock:exception");
// keyflag test
PGPDataFormat pgpKeyFlag = new PGPDataFormat();
// the following keyring contains a primary key with KeyFlag "Certify" and a subkey for signing and a subkey for encryption
- pgpKeyFlag.setKeyFileName("org/apache/camel/component/crypto/pubringSubKeys.gpg");
+ pgpKeyFlag.setKeyFileName(PUB_KEY_RING_SUBKEYS_FILE_NAME);
pgpKeyFlag.setSignatureKeyFileName("org/apache/camel/component/crypto/secringSubKeys.gpg");
pgpKeyFlag.setSignaturePassword("Abcd1234");
pgpKeyFlag.setKeyUserid("keyflag");
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.setSignatureKeyFileName(PUB_KEY_RING_SUBKEYS_FILE_NAME);
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");
+ from("direct:subkey").marshal(pgpKeyFlag).to("mock:encrypted").unmarshal(pgpDecryptVerifySubkey).to("mock:unencrypted");
+
+ from("direct:subkeyUnmarshal").unmarshal(pgpDecryptVerifySubkey).to("mock:unencrypted");
}
}, new RouteBuilder() {
public void configure() throws Exception {
@@ -473,4 +679,41 @@ public class PGPDataFormatTest extends AbstractPGPDataFormatTest {
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;
+ }
+
}