This is an automated email from the ASF dual-hosted git repository.
acosentino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push:
new 15e5072 CAMEL-12605 Added encrypt/decrypt logic for enveloped entities
15e5072 is described below
commit 15e507209bef47df6ccaf13e14427827952ff66b
Author: William Collins <[email protected]>
AuthorDate: Thu Aug 23 11:43:24 2018 -0400
CAMEL-12605 Added encrypt/decrypt logic for enveloped entities
---
.../camel/component/as2/api/AS2ClientManager.java | 16 +++
.../component/as2/api/AS2SignedDataGenerator.java | 1 +
...s7Mime.java => ApplicationPkcs7MimeEntity.java} | 39 ++++++--
.../component/as2/api/entity/EntityParser.java | 107 +++++++++++++++++++++
.../camel/component/as2/api/util/AS2Utils.java | 6 +-
5 files changed, 160 insertions(+), 9 deletions(-)
diff --git
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java
index e081289..4e22e7b 100644
---
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java
+++
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java
@@ -275,4 +275,20 @@ public class AS2ClientManager {
}
+ public AS2SignedDataGenerator createEncryptingGenerator(HttpCoreContext
httpContext) throws HttpException {
+
+ Certificate[] certificateChain =
httpContext.getAttribute(SIGNING_CERTIFICATE_CHAIN, Certificate[].class);
+ if (certificateChain == null) {
+ throw new HttpException("Signing certificate chain missing");
+ }
+
+ PrivateKey privateKey = httpContext.getAttribute(SIGNING_PRIVATE_KEY,
PrivateKey.class);
+ if (privateKey == null) {
+ throw new HttpException("Signing private key missing");
+ }
+
+ return SigningUtils.createSigningGenerator(certificateChain,
privateKey);
+
+ }
+
}
diff --git
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2SignedDataGenerator.java
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2SignedDataGenerator.java
index f52429a..1ab44c0 100644
---
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2SignedDataGenerator.java
+++
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2SignedDataGenerator.java
@@ -108,6 +108,7 @@ public class AS2SignedDataGenerator extends
CMSSignedDataGenerator {
/**
* Creates a <code>multipart/signed</code> content type containing the
algorithms used by this generator.
*
+ * @param boundary - boundary to use to demarcate content.
* @return A <code>multipart/signed</code> content type
*/
public ContentType createMultipartSignedContentType(String boundary) {
diff --git
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationPkcs7Mime.java
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationPkcs7MimeEntity.java
similarity index 72%
rename from
components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationPkcs7Mime.java
rename to
components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationPkcs7MimeEntity.java
index 88d70b6..b61b440 100644
---
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationPkcs7Mime.java
+++
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/ApplicationPkcs7MimeEntity.java
@@ -16,38 +16,50 @@
*/
package org.apache.camel.component.as2.api.entity;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
+import java.security.PrivateKey;
+import java.util.Collection;
+import java.util.Iterator;
import org.apache.camel.component.as2.api.AS2Charset;
import org.apache.camel.component.as2.api.AS2Header;
import org.apache.camel.component.as2.api.CanonicalOutputStream;
+import org.apache.camel.component.as2.api.io.AS2SessionInputBuffer;
+import org.apache.camel.component.as2.api.util.EntityUtils;
import org.apache.http.Header;
import org.apache.http.HeaderIterator;
import org.apache.http.HttpException;
import org.apache.http.entity.ContentType;
+import org.apache.http.impl.io.HttpTransportMetricsImpl;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.Args;
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSTypedData;
+import org.bouncycastle.cms.Recipient;
+import org.bouncycastle.cms.RecipientInformation;
+import org.bouncycastle.cms.RecipientInformationStore;
+import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.bouncycastle.operator.OutputEncryptor;
-public class ApplicationPkcs7Mime extends MimeEntity {
+public class ApplicationPkcs7MimeEntity extends MimeEntity {
private static final String CONTENT_DISPOSITION = "attachment;
filename=\"smime.p7m\"";
private byte[] encryptedData;
- public ApplicationPkcs7Mime(MimeEntity entity2Encrypt,
+ public ApplicationPkcs7MimeEntity(MimeEntity entity2Encrypt,
CMSEnvelopedDataGenerator dataGenerator,
OutputEncryptor encryptor,
String encryptedContentTransferEncoding,
boolean isMainBody)
throws HttpException {
- setContentType(ContentType.create("application/pkcs7-mime", new
BasicNameValuePair("smime-type", "enveloped-datat"),
+ setContentType(ContentType.create("application/pkcs7-mime", new
BasicNameValuePair("smime-type", "enveloped-data"),
new BasicNameValuePair("name", "smime.p7m")));
setContentTransferEncoding(encryptedContentTransferEncoding);
addHeader(AS2Header.CONTENT_DISPOSITION, CONTENT_DISPOSITION);
@@ -59,7 +71,7 @@ public class ApplicationPkcs7Mime extends MimeEntity {
}
}
- public ApplicationPkcs7Mime(byte[] encryptedData, String
encryptedContentTransferEncoding, boolean isMainBody) {
+ public ApplicationPkcs7MimeEntity(byte[] encryptedData, String
encryptedContentTransferEncoding, boolean isMainBody) {
this.encryptedData = Args.notNull(encryptedData, "encryptedData");
setContentType(ContentType.create("application/pkcs7-mime", new
BasicNameValuePair("smime-type", "enveloped-datat"),
@@ -88,15 +100,30 @@ public class ApplicationPkcs7Mime extends MimeEntity {
}
}
+ // Write out signed data.
+ String transferEncoding = getContentTransferEncoding() == null ? null
: getContentTransferEncoding().getValue();
+ try (OutputStream transferEncodedStream = EntityUtils.encode(ncos,
transferEncoding)) {
+
+ transferEncodedStream.write(encryptedData);
+ } catch (Exception e) {
+ throw new IOException("Failed to write to output stream", e);
+ }
+ }
+
+ public MimeEntity getEncryptedEntity(PrivateKey privateKey) {
+
+ return EntityParser.parseEnvelopedEntity(encryptedData, privateKey);
+
+
}
- private byte[] createEncryptedData(MimeEntity entity2Encrypt,
CMSEnvelopedDataGenerator dataGenerator, OutputEncryptor encryptor) throws
Exception {
+ private byte[] createEncryptedData(MimeEntity entity2Encrypt,
CMSEnvelopedDataGenerator envelopedDataGenerator, OutputEncryptor encryptor)
throws Exception {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
entity2Encrypt.writeTo(bos);
bos.flush();
CMSTypedData contentData = new
CMSProcessableByteArray(bos.toByteArray());
- CMSEnvelopedData envelopedData =
dataGenerator.generate(contentData, encryptor);
+ CMSEnvelopedData envelopedData =
envelopedDataGenerator.generate(contentData, encryptor);
return envelopedData.getEncoded();
} catch (Exception e) {
throw new Exception("", e);
diff --git
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/EntityParser.java
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/EntityParser.java
index 4e86d14..b2c3f46 100644
---
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/EntityParser.java
+++
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/entity/EntityParser.java
@@ -16,10 +16,15 @@
*/
package org.apache.camel.component.as2.api.entity;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
+import java.security.PrivateKey;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
import java.util.List;
import org.apache.camel.component.as2.api.AS2Charset;
@@ -44,6 +49,11 @@ import org.apache.http.message.LineParser;
import org.apache.http.message.ParserCursor;
import org.apache.http.util.Args;
import org.apache.http.util.CharArrayBuffer;
+import org.bouncycastle.cms.CMSEnvelopedData;
+import org.bouncycastle.cms.Recipient;
+import org.bouncycastle.cms.RecipientInformation;
+import org.bouncycastle.cms.RecipientInformationStore;
+import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -164,7 +174,71 @@ public final class EntityParser {
}
}
+
+ public static MimeEntity parseEnvelopedEntity(byte[] envelopedContent,
PrivateKey privateKey) {
+
+ try {
+ byte[] decryptedContent = decryptData(envelopedContent,
privateKey);
+
+ InputStream is = new ByteArrayInputStream(decryptedContent);
+ AS2SessionInputBuffer inbuffer = new AS2SessionInputBuffer(new
HttpTransportMetricsImpl(), DEFAULT_BUFFER_SIZE);
+ inbuffer.bind(is);
+
+ // Read Text Report Body Part Headers
+ Header[] headers = AbstractMessageParser.parseHeaders(inbuffer,
-1, -1, BasicLineParser.INSTANCE,
+ new ArrayList<CharArrayBuffer>());
+
+ // Get Content-Type and Content-Transfer-Encoding
+ ContentType envelopedEntityContentType = null;
+ String envelopedEntityContentTransferEncoding = null;
+ for (Header header : headers) {
+ switch (header.getName()) {
+ case AS2Header.CONTENT_TYPE:
+ envelopedEntityContentType =
ContentType.parse(header.getValue());
+ break;
+ case AS2Header.CONTENT_TRANSFER_ENCODING:
+ envelopedEntityContentTransferEncoding = header.getValue();
+ break;
+ default:
+ continue;
+ }
+ }
+ if (envelopedEntityContentType == null) {
+ throw new HttpException("Failed to find Content-Type header in
enveloped entity");
+ }
+
+ MimeEntity entity = parseEntityBody(inbuffer, null,
envelopedEntityContentType, envelopedEntityContentTransferEncoding, headers);
+ entity.removeAllHeaders();
+ entity.setHeaders(headers);
+
+ return entity;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ public static byte[] decryptData(byte[] encryptedData, PrivateKey
privateKey) throws Exception {
+ // Create enveloped data from encrypted data
+ CMSEnvelopedData cmsEnvelopedData = new
CMSEnvelopedData(encryptedData);
+
+ // Extract recipient information form enveloped data.
+ RecipientInformationStore recipientsInformationStore =
cmsEnvelopedData.getRecipientInfos();
+ Collection<RecipientInformation> recipients =
recipientsInformationStore.getRecipients();
+ Iterator<RecipientInformation> it = recipients.iterator();
+
+ // Decrypt if enveloped data contains recipient information
+ if (it.hasNext()) {
+ // Create recipient from private key.
+ Recipient recipient = new
JceKeyTransEnvelopedRecipient(privateKey);
+
+ // Extract decrypted data from recipient information
+ RecipientInformation recipientInfo = it.next();
+ return recipientInfo.getContent(recipient);
+ }
+
+ return null;
+ }
+
public static void parseMultipartSignedEntity(HttpMessage message)
throws HttpException {
MultipartSignedEntity multipartSignedEntity = null;
@@ -763,6 +837,39 @@ public final class EntityParser {
}
}
+ public static ApplicationPkcs7MimeEntity
parseApplicationPkcs7MimeEntityBody(AS2SessionInputBuffer inbuffer,
+
String boundary,
+
ContentType contentType,
+
String contentTransferEncoding)
+ throws ParseException {
+
+ CharsetDecoder previousDecoder = inbuffer.getCharsetDecoder();
+
+ try {
+ Charset charset = contentType.getCharset();
+ if (charset == null) {
+ charset = Charset.forName(AS2Charset.US_ASCII);
+ }
+ CharsetDecoder charsetDecoder = charset.newDecoder();
+
+ inbuffer.setCharsetDecoder(charsetDecoder);
+
+ String pkcs7EncryptedBodyContent = parseBodyPartText(inbuffer,
boundary);
+
+ byte[] encryptedContent =
EntityUtils.decode(pkcs7EncryptedBodyContent.getBytes(charset),
contentTransferEncoding);
+
+ ApplicationPkcs7MimeEntity applicationPkcs7MimeEntity = new
ApplicationPkcs7MimeEntity(
+ encryptedContent, contentTransferEncoding, false);
+ return applicationPkcs7MimeEntity;
+ } catch (Exception e) {
+ ParseException parseException = new ParseException("failed to
parse PKCS7 Mime entity");
+ parseException.initCause(e);
+ throw parseException;
+ } finally {
+ inbuffer.setCharsetDecoder(previousDecoder);
+ }
+ }
+
public static String parseBodyPartText(final AS2SessionInputBuffer
inbuffer,
final String boundary)
throws IOException {
diff --git
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/AS2Utils.java
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/AS2Utils.java
index d4c3161..00482e5 100644
---
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/AS2Utils.java
+++
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/AS2Utils.java
@@ -62,7 +62,7 @@ public final class AS2Utils {
* Validates if the given <code>name</code> is a valid AS2 Name
*
* @param name - the name to validate.
- * @throws InvalidAS2NameException
+ * @throws InvalidAS2NameException - If <code>name</code> is invalid.
*/
public static void validateAS2Name(String name) throws
InvalidAS2NameException {
Matcher matcher = AS_NAME_PATTERN.matcher(name);
@@ -126,7 +126,7 @@ public final class AS2Utils {
* - the stream printed to.
* @param request
* - the request printed.
- * @throws IOException
+ * @throws IOException - If failed to print request.
*/
public static void printRequest(PrintStream out, HttpRequest request)
throws IOException {
// Print request line
@@ -152,7 +152,7 @@ public final class AS2Utils {
*
* @param out - the stream printed to.
* @param message - the request printed.
- * @throws IOException
+ * @throws IOException - If failed to print message.
*/
public static void printMessage(PrintStream out, HttpMessage message)
throws IOException {
// Print request line