You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2018/08/24 06:47:37 UTC
[camel] branch master updated: CAMEL-12605 Added encrypt/decrypt
logic for enveloped entities
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 <pu...@gmail.com>
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