You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by fo...@apache.org on 2017/07/25 08:20:05 UTC
[3/4] camel git commit: CAMEL-11438 new component crypto-cms
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/EnvelopedDataEncryptor.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/EnvelopedDataEncryptor.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/EnvelopedDataEncryptor.java
new file mode 100644
index 0000000..147d819
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/EnvelopedDataEncryptor.java
@@ -0,0 +1,122 @@
+/**
+ * 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.component.crypto.cms.crypt;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.cert.X509Certificate;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.component.crypto.cms.common.AttributesGeneratorProvider;
+import org.apache.camel.component.crypto.cms.common.CryptoCmsMarshallerAbstract;
+import org.apache.camel.component.crypto.cms.common.OriginatorInformationProvider;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsException;
+import org.apache.camel.util.IOHelper;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cms.CMSEnvelopedDataStreamGenerator;
+import org.bouncycastle.cms.OriginatorInformation;
+import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
+import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.OutputEncryptor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Processor for creating an enveloped data object.
+ */
+public class EnvelopedDataEncryptor extends CryptoCmsMarshallerAbstract {
+ private static final Logger LOG = LoggerFactory.getLogger(EnvelopedDataEncryptor.class);
+
+ private final EnvelopedDataEncryptorConfiguration conf;
+
+ public EnvelopedDataEncryptor(EnvelopedDataEncryptorConfiguration conf) {
+ super(conf);
+ this.conf = conf;
+ }
+
+ @Override
+ protected void marshalInternal(InputStream is, OutputStream os, Exchange exchange) throws Exception {
+
+ LOG.debug("Content encryption algorithm: {}", conf.getAlgorithmID());
+ LOG.debug("Parameter secretKeyLength: {}", conf.getSecretKeyLength());
+
+ OutputStream encryptingStream = null;
+ try {
+ CMSEnvelopedDataStreamGenerator gen = new CMSEnvelopedDataStreamGenerator();
+ OriginatorInformationProvider originatorInformationProvider = conf.getOriginatorInformationProvider();
+ if (originatorInformationProvider != null) {
+ LOG.debug("originatorInformationProvider found");
+ OriginatorInformation originatorInformation = originatorInformationProvider.getOriginatorInformation(exchange);
+ if (originatorInformation != null) {
+ LOG.debug("originatorInformation found");
+ gen.setOriginatorInfo(originatorInformation);
+ }
+ }
+ AttributesGeneratorProvider attributeGeneratorProvider = conf.getUnprotectedAttributesGeneratorProvider();
+ if (attributeGeneratorProvider != null) {
+ LOG.debug("attributeGeneratorProvider found");
+ gen.setUnprotectedAttributeGenerator(attributeGeneratorProvider.getAttributesGenerator(exchange));
+ }
+
+ if (conf.getRecipient().isEmpty()) {
+ throw new CryptoCmsException("No recipient configured.");
+ }
+
+ for (RecipientInfo recipientInfo : conf.getRecipient()) {
+ // currently we only support key transport alternative, in
+ // future there maybe others
+ TransRecipientInfo keyTransrecipientInfo = (TransRecipientInfo)recipientInfo;
+ LOG.debug("Recipient info: {}", keyTransrecipientInfo);
+ X509Certificate encryptCert = keyTransrecipientInfo.getCertificate(exchange);
+ LOG.debug("Encryption certificate for recipient with '{}' : {}", keyTransrecipientInfo, encryptCert);
+
+ AlgorithmIdentifier keyEncryptionAlgorithm = determineKeyEncryptionAlgorithmIdentifier(keyTransrecipientInfo.getKeyEncryptionAlgorithm(exchange),
+ keyTransrecipientInfo);
+ JceKeyTransRecipientInfoGenerator keyTransRecipeintInfoGen = new JceKeyTransRecipientInfoGenerator(encryptCert, keyEncryptionAlgorithm);
+
+ keyTransRecipeintInfoGen.setProvider(BouncyCastleProvider.PROVIDER_NAME);
+ gen.addRecipientInfoGenerator(keyTransRecipeintInfoGen);
+ }
+
+ OutputEncryptor encryptor = new JceCMSContentEncryptorBuilder(conf.getAlgorithmID()).setProvider(BouncyCastleProvider.PROVIDER_NAME).build();
+
+ encryptingStream = gen.open(os, encryptor);
+
+ IOHelper.copy(is, encryptingStream);
+
+ LOG.debug("CMS Enveloped Data creation successful");
+
+ } finally {
+ IOHelper.close(is);
+ IOHelper.close(encryptingStream);
+ }
+
+ }
+
+ private AlgorithmIdentifier determineKeyEncryptionAlgorithmIdentifier(String keyEncryptionAlgorithm, TransRecipientInfo keyTransRecipient) throws CryptoCmsException {
+
+ if (keyEncryptionAlgorithm == null) {
+ throw new CryptoCmsException("Key encryption algorithm of recipient info '" + keyTransRecipient + "' is missing");
+ }
+ if ("RSA".equals(keyEncryptionAlgorithm)) {
+ return new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption);
+ }
+ throw new CryptoCmsException("Key encryption algorithm '" + keyEncryptionAlgorithm + "' of recipient info '" + keyTransRecipient + "' is not supported");
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/EnvelopedDataEncryptorConfiguration.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/EnvelopedDataEncryptorConfiguration.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/EnvelopedDataEncryptorConfiguration.java
new file mode 100644
index 0000000..50f3fc8
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/EnvelopedDataEncryptorConfiguration.java
@@ -0,0 +1,283 @@
+/**
+ * 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.component.crypto.cms.crypt;
+
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.crypto.Cipher;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.crypto.cms.common.AttributesGeneratorProvider;
+import org.apache.camel.component.crypto.cms.common.CryptoCmsMarshallerConfiguration;
+import org.apache.camel.component.crypto.cms.common.OriginatorInformationProvider;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsException;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.cms.CMSAlgorithm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@UriParams
+public class EnvelopedDataEncryptorConfiguration extends CryptoCmsMarshallerConfiguration {
+
+ private static final String CAST5_CBC_PKCS5_PADDING = "CAST5/CBC/PKCS5Padding";
+
+ private static final String RC2_CBC_PKCS5_PADDING = "RC2/CBC/PKCS5Padding";
+
+ private static final String CAMELLIA_CBC_PKCS5_PADDING = "Camellia/CBC/PKCS5Padding";
+
+ private static final String AES_CBC_PKCS5_PADDING = "AES/CBC/PKCS5Padding";
+
+ private static final String DES_CBC_PKCS5_PADDING = "DES/CBC/PKCS5Padding";
+
+ private static final String DESEDE_CBC_PKCS5_PADDING = "DESede/CBC/PKCS5Padding";
+
+ private static final Logger LOG = LoggerFactory.getLogger(EnvelopedDataEncryptorConfiguration.class);
+
+ private static final Map<String, List<Integer>> SUPPORTED_ENCRYPTION_ALGORITHMS = new HashMap<String, List<Integer>>(7);
+
+ static {
+
+ List<Integer> allowedKeyLengthForAESandCamellia;
+ if (isLimitedEncryptionStrength()) {
+ allowedKeyLengthForAESandCamellia = Arrays.asList(new Integer[] {128});
+ } else {
+ allowedKeyLengthForAESandCamellia = Arrays.asList(new Integer[] {256, 192, 128});
+ }
+
+ SUPPORTED_ENCRYPTION_ALGORITHMS.put(DESEDE_CBC_PKCS5_PADDING, Arrays.asList(new Integer[] {192, 128}));
+ SUPPORTED_ENCRYPTION_ALGORITHMS.put(DES_CBC_PKCS5_PADDING, Arrays.asList(new Integer[] {64, 56}));
+ SUPPORTED_ENCRYPTION_ALGORITHMS.put(AES_CBC_PKCS5_PADDING, allowedKeyLengthForAESandCamellia);
+ SUPPORTED_ENCRYPTION_ALGORITHMS.put(CAMELLIA_CBC_PKCS5_PADDING, allowedKeyLengthForAESandCamellia);
+ SUPPORTED_ENCRYPTION_ALGORITHMS.put(RC2_CBC_PKCS5_PADDING, Arrays.asList(new Integer[] {128, 120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40}));
+ SUPPORTED_ENCRYPTION_ALGORITHMS.put(CAST5_CBC_PKCS5_PADDING, Arrays.asList(new Integer[] {128, 120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40}));
+ }
+
+ @UriParam(label = "encrypt", multiValue = true, description = "Recipient Info: reference to a bean which implements the interface org.apache.camel.component.crypto.cms.api.TransRecipientInfo")
+ private final List<RecipientInfo> recipient = new ArrayList<RecipientInfo>(3);
+
+ @UriParam(label = "encrypt", enums = "AES/CBC/PKCS5Padding,DESede/CBC/PKCS5Padding,Camellia/CBC/PKCS5Padding,CAST5/CBC/PKCS5Padding")
+ private String contentEncryptionAlgorithm;
+
+ @UriParam(label = "encrypt")
+ private int secretKeyLength;
+
+ @UriParam(label = "encrypt", defaultValue = "null")
+ private AttributesGeneratorProvider unprotectedAttributesGeneratorProvider;
+
+ @UriParam(label = "encrypt", defaultValue = "null")
+ private OriginatorInformationProvider originatorInformationProvider;
+
+ // calculated parameters
+ private ASN1ObjectIdentifier algorithmId;
+
+ public EnvelopedDataEncryptorConfiguration(CamelContext context) {
+ super(context);
+ }
+
+ private static boolean isLimitedEncryptionStrength() {
+ // limited encryption strength
+ boolean limitedEncryptionStrength;
+ try {
+ limitedEncryptionStrength = Cipher.getMaxAllowedKeyLength("AES") < 256;
+ } catch (NoSuchAlgorithmException e) {
+ // should never occur
+ throw new IllegalStateException(e);
+ }
+ return limitedEncryptionStrength;
+ }
+
+ public List<RecipientInfo> getRecipient() {
+ return recipient;
+ }
+
+ public void setRecipient(RecipientInfo recipient) {
+ this.recipient.add(recipient);
+ }
+
+ // for multi values
+ public void setRecipient(List<?> recipients) {
+ if (recipients == null) {
+ return;
+ }
+ for (Object recipientOb : recipients) {
+ if (recipientOb instanceof String) {
+ String recipientName = (String)recipientOb;
+ String valueNoHash = recipientName.replaceAll("#", "");
+ if (getContext() != null && recipientName != null) {
+ RecipientInfo recipient = getContext().getRegistry().lookupByNameAndType(valueNoHash, RecipientInfo.class);
+ if (recipient != null) {
+ setRecipient(recipient);
+ }
+ }
+ }
+ }
+
+ }
+
+ public String getContentEncryptionAlgorithm() {
+ return contentEncryptionAlgorithm;
+ }
+
+ /**
+ * Encryption algorithm, for example "DESede/CBC/PKCS5Padding". Further
+ * possible values: DESede/CBC/PKCS5Padding, AES/CBC/PKCS5Padding,
+ * Camellia/CBC/PKCS5Padding, CAST5/CBC/PKCS5Padding.
+ */
+ public void setContentEncryptionAlgorithm(String contentEncryptionAlgorithm) {
+ this.contentEncryptionAlgorithm = contentEncryptionAlgorithm;
+ }
+
+ public int getSecretKeyLength() {
+ return secretKeyLength;
+ }
+
+ /**
+ * Key length for the secret symmetric key used for the content encryption.
+ * Only used if the specified content-encryption algorithm allows keys of
+ * different sizes. If contentEncryptionAlgorithm=AES/CBC/PKCS5Padding or
+ * Camellia/CBC/PKCS5Padding then 128; if
+ * contentEncryptionAlgorithm=DESede/CBC/PKCS5Padding then 192, 128; if
+ * strong encryption is enabled then for AES/CBC/PKCS5Padding and
+ * Camellia/CBC/PKCS5Padding also the key lengths 192 and 256 are possible.
+ */
+ public void setSecretKeyLength(int secretKeyLength) {
+ this.secretKeyLength = secretKeyLength;
+ }
+
+ public AttributesGeneratorProvider getUnprotectedAttributesGeneratorProvider() {
+ return unprotectedAttributesGeneratorProvider;
+ }
+
+ /**
+ * Provider of the generator for the unprotected attributes. The default
+ * value is <code>null</code> which means no unprotected attribute is added
+ * to the Enveloped Data object. See
+ * https://tools.ietf.org/html/rfc5652#section-6.1.
+ */
+ public void setUnprotectedAttributesGeneratorProvider(AttributesGeneratorProvider unprotectedAttributeTableGeneratorProvider) {
+ this.unprotectedAttributesGeneratorProvider = unprotectedAttributeTableGeneratorProvider;
+ }
+
+ public OriginatorInformationProvider getOriginatorInformationProvider() {
+ return originatorInformationProvider;
+ }
+
+ /**
+ * Provider for the originator info. See
+ * https://tools.ietf.org/html/rfc5652#section-6.1. The default value is
+ * <code>null</code>.
+ */
+ public void setOriginatorInformationProvider(OriginatorInformationProvider originatorInformationProvider) {
+ this.originatorInformationProvider = originatorInformationProvider;
+ }
+
+ public void init() throws CryptoCmsException {
+ if (recipient.size() == 0) {
+ logErrorAndThrow(LOG, "No recipient configured.");
+ }
+ checkEncryptionAlgorithmAndSecretKeyLength();
+
+ calcualteAlgorithmIdWithKeyLength();
+ }
+
+ private void checkEncryptionAlgorithmAndSecretKeyLength() throws CryptoCmsException {
+ if (contentEncryptionAlgorithm == null) {
+ logErrorAndThrow(LOG, "Content encryption algorithm is null");
+ } else if (!SUPPORTED_ENCRYPTION_ALGORITHMS.keySet().contains(contentEncryptionAlgorithm)) {
+ logErrorAndThrow(LOG, "Content encryption algorithm " + contentEncryptionAlgorithm + " not supported");
+ } else if (!SUPPORTED_ENCRYPTION_ALGORITHMS.get(contentEncryptionAlgorithm).contains(secretKeyLength)) {
+ logErrorAndThrow(LOG, "Content encryption algorithm " + contentEncryptionAlgorithm + " does not supported secretKeyLength of " + secretKeyLength);
+ }
+ }
+
+ private void calcualteAlgorithmIdWithKeyLength() {
+
+ if (DESEDE_CBC_PKCS5_PADDING.equals(getContentEncryptionAlgorithm())) {
+
+ algorithmId = CMSAlgorithm.DES_EDE3_CBC;
+
+ } else if (DES_CBC_PKCS5_PADDING.equals(getContentEncryptionAlgorithm())) {
+
+ algorithmId = CMSAlgorithm.DES_CBC;
+
+ } else if (AES_CBC_PKCS5_PADDING.equals(getContentEncryptionAlgorithm())) {
+
+ switch (getSecretKeyLength()) {
+ case 256:
+ algorithmId = CMSAlgorithm.AES256_CBC;
+ break;
+ case 192:
+ algorithmId = CMSAlgorithm.AES192_CBC;
+ break;
+ case 128:
+ algorithmId = CMSAlgorithm.AES128_CBC;
+ break;
+
+ default:
+ // should not happen, has already been checked
+ throw new IllegalStateException("Unsupported secret key length " + getSecretKeyLength() + " for algorithm AES");
+ }
+
+ } else if (CAMELLIA_CBC_PKCS5_PADDING.equals(getContentEncryptionAlgorithm())) {
+
+ switch (getSecretKeyLength()) {
+ case 256:
+ algorithmId = CMSAlgorithm.CAMELLIA256_CBC;
+ break;
+ case 192:
+ algorithmId = CMSAlgorithm.CAMELLIA192_CBC;
+ break;
+ case 128:
+ algorithmId = CMSAlgorithm.CAMELLIA128_CBC;
+ break;
+ default:
+ // should not happen, has already been checked
+ throw new IllegalStateException("Unsupported secret key length " + getSecretKeyLength() + " for algorithm Camellia");
+ }
+
+ } else if (RC2_CBC_PKCS5_PADDING.equals(getContentEncryptionAlgorithm())) {
+
+ algorithmId = CMSAlgorithm.RC2_CBC;
+
+ } else if (CAST5_CBC_PKCS5_PADDING.equals(getContentEncryptionAlgorithm())) {
+
+ algorithmId = CMSAlgorithm.CAST5_CBC;
+
+ } else {
+ // should not occur, has already been checked
+ throw new IllegalStateException("Content encryption algorithm " + getContentEncryptionAlgorithm() + " not supported");
+ }
+
+ }
+
+ /**
+ * Content encryption algorithm.
+ *
+ * @return algorithm Id
+ */
+ public ASN1ObjectIdentifier getAlgorithmID() {
+ return algorithmId;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/PrivateKeyWithCertificate.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/PrivateKeyWithCertificate.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/PrivateKeyWithCertificate.java
new file mode 100644
index 0000000..bc1e726
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/PrivateKeyWithCertificate.java
@@ -0,0 +1,44 @@
+/**
+ * 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.component.crypto.cms.crypt;
+
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import org.apache.camel.util.ObjectHelper;
+
+public class PrivateKeyWithCertificate {
+
+ private final PrivateKey privateKey;
+
+ private final X509Certificate certificate;
+
+ public PrivateKeyWithCertificate(PrivateKey privateKey, X509Certificate certificate) {
+
+ this.privateKey = ObjectHelper.notNull(privateKey, "privateKey");
+ this.certificate = ObjectHelper.notNull(certificate, "certificate");
+ }
+
+ public PrivateKey getPrivateKey() {
+ return privateKey;
+ }
+
+ public X509Certificate getCertificate() {
+ return certificate;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/RecipientInfo.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/RecipientInfo.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/RecipientInfo.java
new file mode 100644
index 0000000..06110a4
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/RecipientInfo.java
@@ -0,0 +1,54 @@
+/**
+ * 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.component.crypto.cms.crypt;
+
+/**
+ * Information about the receiver of an encrypted message used in
+ * {@link EnvelopedDataEncryptor}. The RecipientInfo type depends on the key
+ * management algorithm used for the recipient of an <code>EnvelopedData</code>
+ * or <code>AuthenticatedData</code>. CMS provides three alternatives (see rfc5652):
+ * <ul>
+ * <li>key transport: the content-encryption key is encrypted with the public
+ * key of the recipient. This technique id compatible to PKCS#7 when creating a
+ * RecipientInfo for the public key of the recipient's certificate, identified
+ * by issuer and serial number. CMS recommends to use RSA for encrypting the
+ * content encryption key.
+ * <li>key agreement: the recipient's public key and the sender's private key
+ * are used to generate a symmetric key, then the content encryption key is
+ * encrypted with the symmetric key. Each RecipientInfo of type may transfer the
+ * encrypted content encryption key to one or more recipient using the same key
+ * agreement algorithm and domain parameters for that algorithm. CMS recommends
+ * to use ESDH with an ephemeral sender key.
+ * <li>symmetric key-encryption keys: the content-encryption key is encrypted
+ * with a previously distributed symmetric key-encryption key. The RecipientInfo
+ * is using a CMS key wrap algorithm like Triple-DES key wrap or RC2 key wrap.
+ * <li>password based encryption: the content-encryption key is encrypted with
+ * key-encryption key derived from a password. The RecipientInfo is using a key
+ * derivation algorithm like PBKDF2 as specified by <a href =
+ * http://www.ietf.org/rfc/rfc2898.txt" target="_blank">RFC 2898</a> (PKCS#5)
+ * and a key encryption algorithm like PWRI-KEK as specified by <a href =
+ * http://www.ietf.org/rfc/rfc3211.txt" target="_blank">RFC 3211</a>.
+ * <li>any other technique: based on private, user defined key management
+ * techniques
+ * </ul>
+ * Currently we only support the "key transport" alternative. However in
+ * preparation to support in future further types, we have introduced this
+ * class.
+ */
+public interface RecipientInfo {
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/TransRecipientInfo.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/TransRecipientInfo.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/TransRecipientInfo.java
new file mode 100644
index 0000000..d4cfa42
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/crypt/TransRecipientInfo.java
@@ -0,0 +1,45 @@
+/**
+ * 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.component.crypto.cms.crypt;
+
+import java.security.cert.X509Certificate;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsException;
+
+/**
+ * Information about the receiver of an encrypted message used in
+ * CmsEnvelopedDataEncryptor.
+ * <p>
+ * Represents the "key transport" recipient info alternative: The
+ * content-encryption key is encrypted with the public key of the recipient.
+ * This technique is compatible to PKCS#7 when creating a RecipientInfo for the
+ * public key of the recipient's certificate, identified by issuer and serial
+ * number. CMS recommends to use RSA for encrypting the content encryption key.
+ */
+public interface TransRecipientInfo extends RecipientInfo {
+
+ /** Currently, the key encryption algorithm is fixed to "RSA". */
+ String getKeyEncryptionAlgorithm(Exchange exchange) throws CryptoCmsException;
+
+ /**
+ * Returns the certificate containign the public key which is used for the
+ * encryption and the issuer and serial number which is added to the
+ * recipient information.
+ */
+ X509Certificate getCertificate(Exchange exchange) throws CryptoCmsException;
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsException.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsException.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsException.java
new file mode 100644
index 0000000..073c166
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsException.java
@@ -0,0 +1,40 @@
+/**
+ * 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.component.crypto.cms.exception;
+
+public class CryptoCmsException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public CryptoCmsException() {
+ }
+
+ public CryptoCmsException(String message) {
+ super(message);
+
+ }
+
+ public CryptoCmsException(Throwable cause) {
+ super(cause);
+
+ }
+
+ public CryptoCmsException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsFormatException.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsFormatException.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsFormatException.java
new file mode 100644
index 0000000..02aea9a
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsFormatException.java
@@ -0,0 +1,37 @@
+/**
+ * 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.component.crypto.cms.exception;
+
+/**
+ * Exception thrown when the decoding of an input stream to a cms object, like
+ * singed data or enveloped data, fails.
+ *
+ *
+ */
+public class CryptoCmsFormatException extends CryptoCmsException {
+
+ private static final long serialVersionUID = 1L;
+
+ public CryptoCmsFormatException(String message) {
+ super(message);
+ }
+
+ public CryptoCmsFormatException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsInvalidKeyException.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsInvalidKeyException.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsInvalidKeyException.java
new file mode 100644
index 0000000..5609f50
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsInvalidKeyException.java
@@ -0,0 +1,33 @@
+/**
+ * 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.component.crypto.cms.exception;
+
+/**
+ * Exception thrown during singing if the key type does not fit to the signature
+ * algorithm.
+ *
+ *
+ */
+public class CryptoCmsInvalidKeyException extends CryptoCmsException {
+
+ private static final long serialVersionUID = 1L;
+
+ public CryptoCmsInvalidKeyException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoCertificateForRecipientsException.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoCertificateForRecipientsException.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoCertificateForRecipientsException.java
new file mode 100644
index 0000000..ebf78ac
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoCertificateForRecipientsException.java
@@ -0,0 +1,31 @@
+/**
+ * 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.component.crypto.cms.exception;
+
+/**
+ * Exception thrown when no certificate in the keystore fits to the recipients
+ * in the CMS enveloped data during the decryption process.
+ */
+public class CryptoCmsNoCertificateForRecipientsException extends CryptoCmsException {
+
+ private static final long serialVersionUID = 1L;
+
+ public CryptoCmsNoCertificateForRecipientsException(String message) {
+ super(message);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoCertificateForSignerInfoException.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoCertificateForSignerInfoException.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoCertificateForSignerInfoException.java
new file mode 100644
index 0000000..d1ed3a6
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoCertificateForSignerInfoException.java
@@ -0,0 +1,34 @@
+/**
+ * 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.component.crypto.cms.exception;
+
+/**
+ * Is thrown when the signature validation fails because no certificate is found
+ * the keystore which corresponds to the a specific signer information.
+ * <p>
+ * Can only be thrown when the configuration "verifySignatureOfAllSigners" is
+ * true.
+ */
+public class CryptoCmsNoCertificateForSignerInfoException extends CryptoCmsSignatureException {
+
+ private static final long serialVersionUID = 1L;
+
+ public CryptoCmsNoCertificateForSignerInfoException(String message) {
+ super(message);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoCertificateForSignerInfosException.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoCertificateForSignerInfosException.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoCertificateForSignerInfosException.java
new file mode 100644
index 0000000..ac05c77
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoCertificateForSignerInfosException.java
@@ -0,0 +1,35 @@
+/**
+ * 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.component.crypto.cms.exception;
+
+/**
+ * Is thrown when the signature validation fails because no certificate is found
+ * in the keystore which corresponds to the sent signer infos.
+ */
+public class CryptoCmsNoCertificateForSignerInfosException extends CryptoCmsSignatureException {
+
+ private static final long serialVersionUID = 1L;
+
+ public CryptoCmsNoCertificateForSignerInfosException(String message) {
+ super(message);
+ }
+
+ public CryptoCmsNoCertificateForSignerInfosException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoKeyOrCertificateForAliasException.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoKeyOrCertificateForAliasException.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoKeyOrCertificateForAliasException.java
new file mode 100644
index 0000000..00fa790
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsNoKeyOrCertificateForAliasException.java
@@ -0,0 +1,45 @@
+/**
+ * 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.component.crypto.cms.exception;
+
+/**
+ * Exception which is thrown when for a specified alias a key or certificate is
+ * not found in the keystore.
+ *
+ *
+ */
+public class CryptoCmsNoKeyOrCertificateForAliasException extends CryptoCmsException {
+
+ private static final long serialVersionUID = 1L;
+
+ public CryptoCmsNoKeyOrCertificateForAliasException() {
+
+ }
+
+ public CryptoCmsNoKeyOrCertificateForAliasException(String message) {
+ super(message);
+ }
+
+ public CryptoCmsNoKeyOrCertificateForAliasException(Throwable cause) {
+ super(cause);
+ }
+
+ public CryptoCmsNoKeyOrCertificateForAliasException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsSignatureException.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsSignatureException.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsSignatureException.java
new file mode 100644
index 0000000..66ba4b0
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsSignatureException.java
@@ -0,0 +1,33 @@
+/**
+ * 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.component.crypto.cms.exception;
+
+/**
+ * This exception is thrown if SignedData signature verification fails.
+ */
+public class CryptoCmsSignatureException extends CryptoCmsException {
+
+ private static final long serialVersionUID = 1L;
+
+ public CryptoCmsSignatureException(String message) {
+ super(message);
+ }
+
+ public CryptoCmsSignatureException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsSignatureInvalidContentHashException.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsSignatureInvalidContentHashException.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsSignatureInvalidContentHashException.java
new file mode 100644
index 0000000..cec8b9c
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsSignatureInvalidContentHashException.java
@@ -0,0 +1,33 @@
+/**
+ * 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.component.crypto.cms.exception;
+
+/**
+ * This exception is thrown if the verification of a SignedData signature fails
+ * because the hash calculated over the content does not match to the value of
+ * signed MessageDigest attribute value.
+ *
+ */
+public class CryptoCmsSignatureInvalidContentHashException extends CryptoCmsSignatureException {
+
+ private static final long serialVersionUID = 1L;
+
+ public CryptoCmsSignatureInvalidContentHashException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsVerifierCertificateNotValidException.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsVerifierCertificateNotValidException.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsVerifierCertificateNotValidException.java
new file mode 100644
index 0000000..836e790
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/CryptoCmsVerifierCertificateNotValidException.java
@@ -0,0 +1,31 @@
+/**
+ * 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.component.crypto.cms.exception;
+
+/**
+ * If the verifier tries to verify a signature with a certificate which is not
+ * valid at the time given as the SignerInfo's signing time.
+ */
+public class CryptoCmsVerifierCertificateNotValidException extends CryptoCmsSignatureException {
+
+ private static final long serialVersionUID = 1L;
+
+ public CryptoCmsVerifierCertificateNotValidException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/packageinfo
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/packageinfo b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/packageinfo
new file mode 100644
index 0000000..e252556
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/exception/packageinfo
@@ -0,0 +1 @@
+version 1.0.0
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/DefaultSignedDataVerifierConfiguration.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/DefaultSignedDataVerifierConfiguration.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/DefaultSignedDataVerifierConfiguration.java
new file mode 100644
index 0000000..59c5c2e
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/DefaultSignedDataVerifierConfiguration.java
@@ -0,0 +1,105 @@
+/**
+ * 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.component.crypto.cms.sig;
+
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.component.crypto.cms.common.DefaultCryptoCmsUnMarshallerConfiguration;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsException;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+
+/**
+ * Fetches the X.509 certificates which can be used for the verification from a
+ * Java keystore.
+ */
+@UriParams
+public class DefaultSignedDataVerifierConfiguration extends DefaultCryptoCmsUnMarshallerConfiguration implements SignedDataVerifierConfiguration, Cloneable {
+
+ @UriParam(label = "verify", defaultValue = "false")
+ private Boolean signedDataHeaderBase64 = Boolean.FALSE;
+
+ @UriParam(label = "verify", defaultValue = "true")
+ private Boolean verifySignaturesOfAllSigners = Boolean.TRUE;
+
+ /**
+ * Indicates whether the value in the header CamelCryptoCmsSignedData is
+ * base64 encoded. Default value is <code>false</code>.
+ * <p>
+ * Only relevant for detached signatures. In the detached signature case,
+ * the header contains the Signed Data object.
+ */
+ public void setSignedDataHeaderBase64(Boolean signedDataHeaderBase64) {
+ this.signedDataHeaderBase64 = signedDataHeaderBase64;
+ }
+
+ @Override
+ public Boolean isSignedDataHeaderBase64(Exchange exchange) throws CryptoCmsException {
+ return signedDataHeaderBase64;
+ }
+
+ /**
+ * If <code>true</code> then the signatures of all signers contained in the
+ * Signed Data object are verified. If <code>false</code> then only one
+ * signature whose signer info matches with one of the specified
+ * certificates is verified. Default value is <code>true</code>.
+ */
+ public void setVerifySignaturesOfAllSigners(Boolean verifySignaturesOfAllSigners) {
+ this.verifySignaturesOfAllSigners = verifySignaturesOfAllSigners;
+ }
+
+ @Override
+ public Boolean isVerifySignaturesOfAllSigners(Exchange exchange) throws CryptoCmsException {
+ return verifySignaturesOfAllSigners;
+ }
+
+ @Override
+ public Collection<X509Certificate> getCertificates(Exchange exchange) throws CryptoCmsException {
+ KeyStore keystore = getKeyStore();
+ try {
+ List<X509Certificate> certs = new ArrayList<>(keystore.size());
+ for (Enumeration<String> aliases = keystore.aliases(); aliases.hasMoreElements();) {
+ String alias = aliases.nextElement();
+ Certificate cert = keystore.getCertificate(alias);
+ if (cert instanceof X509Certificate) {
+ certs.add((X509Certificate)cert);
+ }
+ }
+ return certs;
+ } catch (KeyStoreException e) {
+ throw new CryptoCmsException("Problem during reading the certificates of the verifier keystore");
+ }
+ }
+
+ public DefaultSignedDataVerifierConfiguration copy() {
+ try {
+ return (DefaultSignedDataVerifierConfiguration)clone();
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeCamelException(e); // should never happen
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/DefaultSignerInfo.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/DefaultSignerInfo.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/DefaultSignerInfo.java
new file mode 100644
index 0000000..0a02a03
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/DefaultSignerInfo.java
@@ -0,0 +1,198 @@
+/**
+ * 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.component.crypto.cms.sig;
+
+import java.security.Key;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.component.crypto.cms.common.DefaultCryptoCmsConfiguration;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsException;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsNoKeyOrCertificateForAliasException;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.bouncycastle.cms.CMSAttributeTableGenerator;
+import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator;
+
+/**
+ * Reads the signer information from a Java keystore. You have to specify an
+ * alias for the private key entry, the signature algorithm, and the keystore.
+ */
+@UriParams
+public class DefaultSignerInfo extends DefaultCryptoCmsConfiguration implements SignerInfo {
+
+ @UriParam(label = "sign")
+ private String privateKeyAlias;
+
+ @UriParam(label = "sign")
+ private char[] password;
+ @UriParam(label = "sign", defaultValue = "SHA256withRSA")
+ private String signatureAlgorithm = "SHA256withRSA";
+ @UriParam(label = "sign", defaultValue = "true")
+ private boolean includeCertificates = true;
+
+ @UriParam(label = "sign,advanced")
+ private CMSAttributeTableGenerator signedAttributeGenerator = new DefaultSignedAttributeTableGenerator();
+
+ @UriParam(label = "sign,advanced", defaultValue = "null")
+ private CMSAttributeTableGenerator unsignedAttributeGenerator;
+
+ /**
+ * Password of the private key. If not set then the password set in the
+ * parameter 'keystoreParameters' is used.
+ */
+ public void setPassword(char[] password) {
+ this.password = password;
+ }
+
+ protected char[] getPassword(Exchange exchange) throws CryptoCmsException {
+ if (password != null) {
+ return password;
+ }
+
+ String pw = null;
+ if (getKeyStoreParameters() != null) {
+ pw = getKeyStoreParameters().getPassword();
+ }
+ if (pw == null) {
+ throw new CryptoCmsException("No password for accessing the private key from the keystore found for the singer infor " + this);
+ }
+ return pw.toCharArray();
+ }
+
+ protected String getPrivateKeyAlias(Exchange exchange) throws CryptoCmsException {
+ if (privateKeyAlias == null) {
+ throw new CryptoCmsException("No alias defined for signer info " + this);
+ }
+ return privateKeyAlias;
+ }
+
+ /**
+ * Alias of the private key entry in the keystore which is used for signing.
+ */
+ public void setPrivateKeyAlias(String privateKeyAlias) {
+ this.privateKeyAlias = privateKeyAlias;
+ }
+
+ /**
+ * Signature algorithm. The default algorithm is "SHA256withRSA".
+ * <p>
+ * Attention, the signature algorithm must fit to the signer private key.
+ */
+ public void setSignatureAlgorithm(String signatureAlgorithm) {
+ this.signatureAlgorithm = signatureAlgorithm;
+ }
+
+ /**
+ * If <tt>true</tt> then the certificate chain corresponding to the alias of
+ * the private key is added to the certificate list of the Signed Data
+ * instance.
+ */
+ public void setIncludeCertificates(boolean includeCertificates) {
+ this.includeCertificates = includeCertificates;
+ }
+
+ @Override
+ public String getSignatureAlgorithm(Exchange exchange) throws CryptoCmsException {
+ return signatureAlgorithm;
+ }
+
+ @Override
+ public PrivateKey getPrivateKey(Exchange exchange) throws CryptoCmsException {
+ String alias = getPrivateKeyAlias(exchange);
+ try {
+ Key key = getKeyStore().getKey(alias, getPassword(exchange));
+ if (key instanceof PrivateKey) {
+ return (PrivateKey)key;
+ }
+ } catch (UnrecoverableKeyException | KeyStoreException | NoSuchAlgorithmException e) {
+ throw new CryptoCmsException("Problem occured during accessing the private key for the alias '" + alias + "' in the keystore of signer " + this);
+ }
+ throw new CryptoCmsNoKeyOrCertificateForAliasException("No private key found for the alias '" + alias + "' in the keystore of signer " + this);
+ }
+
+ @Override
+ public X509Certificate getCertificate(Exchange exchange) throws CryptoCmsException {
+
+ String alias = getPrivateKeyAlias(exchange);
+ Certificate cert;
+ try {
+ cert = getKeyStore().getCertificate(alias);
+ } catch (KeyStoreException e) {
+ throw new CryptoCmsException("Problem during accessing the certificate for the alias '" + alias + "' in the signer " + this, e);
+ }
+ if (cert instanceof X509Certificate) {
+ return (X509Certificate)cert;
+ }
+ throw new CryptoCmsNoKeyOrCertificateForAliasException("No X.509 certificate found for alias '" + alias + "' in the keystore of signer " + this);
+ }
+
+ @Override
+ public Certificate[] getCertificateChain(Exchange exchange) throws CryptoCmsException {
+ if (includeCertificates) {
+ String alias = getPrivateKeyAlias(exchange);
+ Certificate[] certs;
+ try {
+ certs = getKeyStore().getCertificateChain(alias);
+ } catch (KeyStoreException e) {
+ throw new CryptoCmsException("Problem during accessing the certificate chain for the alias '" + alias + "' in the keystore of signer " + this, e);
+ }
+ if (certs == null) {
+ return new Certificate[0];
+ } else {
+ return certs;
+ }
+ } else {
+ return new Certificate[0];
+ }
+ }
+
+ /**
+ * Signed attributes of the Signed Data instance. By default contentType,
+ * signingTime, messageDigest, and id-aa-CMSAlgorithmProtection are set.
+ */
+ public void setSignedAttributeGenerator(CMSAttributeTableGenerator signedAttributeGenerator) {
+ this.signedAttributeGenerator = signedAttributeGenerator;
+ }
+
+ /**
+ * Unsigned attributes of the Signed Data instance. By default no unsigned
+ * attribute is set.
+ */
+ public void setUnsignedAttributeGenerator(CMSAttributeTableGenerator unsignedAttributeGenerator) {
+ this.unsignedAttributeGenerator = unsignedAttributeGenerator;
+ }
+
+ @Override
+ public CMSAttributeTableGenerator getSignedAttributeGenerator(Exchange exchange) throws CryptoCmsException {
+ return signedAttributeGenerator;
+ }
+
+ @Override
+ public CMSAttributeTableGenerator getUnsignedAttributeGenerator(Exchange exchange) throws CryptoCmsException {
+ return unsignedAttributeGenerator;
+ }
+
+ public String toString() {
+ return "private key alias=" + privateKeyAlias + ", signature algorithm=" + signatureAlgorithm + ", isIncludeCertificates=" + includeCertificates;
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/SignedDataCreator.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/SignedDataCreator.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/SignedDataCreator.java
new file mode 100644
index 0000000..1369551
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/SignedDataCreator.java
@@ -0,0 +1,129 @@
+/**
+ * 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.component.crypto.cms.sig;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.component.crypto.cms.common.CryptoCmsConstants;
+import org.apache.camel.component.crypto.cms.common.CryptoCmsMarshallerAbstract;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsException;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsInvalidKeyException;
+import org.apache.camel.util.IOHelper;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.CMSSignedDataStreamGenerator;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SignedDataCreator extends CryptoCmsMarshallerAbstract {
+ private static final Logger LOG = LoggerFactory.getLogger(SignedDataCreator.class);
+
+ private SignedDataCreatorConfiguration config;
+
+ public SignedDataCreator(SignedDataCreatorConfiguration conf) {
+ super(conf);
+ config = conf;
+ }
+
+ @Override
+ protected void setBodyAndHeader(Message out, Object encodedSignedData) {
+ if (config.getIncludeContent()) {
+ /*
+ * The encodedSignedData object contains the signer infos including
+ * the message content.
+ */
+ out.setBody(encodedSignedData);
+ } else {
+ /*
+ * The encodedSignedData object contains only the signer infos
+ * (without the message content). As the message body is not changed
+ * in this case and is passed through
+ */
+ out.setHeader(CryptoCmsConstants.CAMEL_CRYPTO_CMS_SIGNED_DATA, encodedSignedData);
+ }
+ }
+
+ @Override
+ protected void marshalInternal(InputStream is, OutputStream os, Exchange exchange) throws Exception {
+
+ CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
+
+ if (config.getSigner().isEmpty()) {
+ throw new CryptoCmsException("No signer information configured");
+ }
+ for (SignerInfo signer : config.getSigner()) {
+ // these certificates are sent within the signature
+ LOG.debug("Signer info: {}", signer);
+ X509Certificate signerCert = signer.getCertificate(exchange);
+ if (signerCert == null) {
+ throw new CryptoCmsException("Certificate missing in the singer information " + signer);
+ }
+
+ PrivateKey key = signer.getPrivateKey(exchange);
+ if (key == null) {
+ throw new CryptoCmsException("Private key missing in the singer information " + signer);
+ }
+
+ ContentSigner contentSigner;
+ try {
+ contentSigner = new JcaContentSignerBuilder(signer.getSignatureAlgorithm(exchange)).setProvider(BouncyCastleProvider.PROVIDER_NAME).build(key);
+ } catch (OperatorCreationException e) {
+ throw new CryptoCmsInvalidKeyException("The private key of the signer information '" + signer + "' does not fit to the specified signature algorithm '"
+ + signer.getSignatureAlgorithm(exchange) + "': " + e.getMessage(), e);
+ }
+
+ JcaSignerInfoGeneratorBuilder signerBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME)
+ .build());
+ signerBuilder.setSignedAttributeGenerator(signer.getSignedAttributeGenerator(exchange)).setUnsignedAttributeGenerator(signer.getUnsignedAttributeGenerator(exchange));
+
+ gen.addSignerInfoGenerator(signerBuilder.build(contentSigner, signerCert));
+
+ List<Certificate> certificateList = new ArrayList<Certificate>();
+ for (Certificate cert : signer.getCertificateChain(exchange)) {
+ if (!certificateList.contains(cert)) {
+ certificateList.add(cert);
+ gen.addCertificate(new X509CertificateHolder(cert.getEncoded()));
+ LOG.debug("Certificate added to Signed Data certificate list: {}", cert);
+ }
+ }
+ }
+
+ OutputStream sigOut = gen.open(os, config.getIncludeContent());
+ try {
+ IOHelper.copyAndCloseInput(is, sigOut);
+ } finally {
+ IOHelper.close(sigOut);
+ }
+
+ LOG.debug("CMS Signed Data generation successful");
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/SignedDataCreatorConfiguration.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/SignedDataCreatorConfiguration.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/SignedDataCreatorConfiguration.java
new file mode 100644
index 0000000..354aede
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/SignedDataCreatorConfiguration.java
@@ -0,0 +1,94 @@
+/**
+ * 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.component.crypto.cms.sig;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.crypto.cms.common.CryptoCmsMarshallerConfiguration;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsException;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@UriParams
+public class SignedDataCreatorConfiguration extends CryptoCmsMarshallerConfiguration {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SignedDataCreatorConfiguration.class);
+
+ @UriParam(label = "sign", defaultValue = "true")
+ private Boolean includeContent = Boolean.TRUE;
+
+ @UriParam(label = "sign", multiValue = true, description = "Signer information: reference to a bean which implements org.apache.camel.component.crypto.cms.api.SignerInfo")
+ private final List<SignerInfo> signer = new ArrayList<SignerInfo>(3);
+
+ public SignedDataCreatorConfiguration(CamelContext context) {
+ super(context);
+ }
+
+ public Boolean getIncludeContent() {
+ return includeContent;
+ }
+
+ /**
+ * Indicates whether the signed content should be included into the Signed
+ * Data instance. If false then a detached Signed Data instance is created
+ * in the header CamelCryptoCmsSignedData.
+ */
+ public void setIncludeContent(Boolean includeContent) {
+ this.includeContent = includeContent;
+ }
+
+ public List<SignerInfo> getSigner() {
+ return signer;
+ }
+
+ public void setSigner(SignerInfo signer) {
+ this.signer.add(signer);
+ }
+
+ // for multi values
+ public void setSigner(List<?> signers) {
+ if (signers == null) {
+ return;
+ }
+ for (Object signerOb : signers) {
+ if (signerOb instanceof String) {
+ String signerName = (String)signerOb;
+ String valueNoHash = signerName.replaceAll("#", "");
+ if (getContext() != null && signerName != null) {
+ SignerInfo signer = getContext().getRegistry().lookupByNameAndType(valueNoHash, SignerInfo.class);
+ if (signer != null) {
+ setSigner(signer);
+ }
+ }
+ }
+ }
+
+ }
+
+ public void init() throws CryptoCmsException {
+
+ if (signer.isEmpty()) {
+ logErrorAndThrow(LOG, "No signer set.");
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/b831203b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/SignedDataVerifier.java
----------------------------------------------------------------------
diff --git a/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/SignedDataVerifier.java b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/SignedDataVerifier.java
new file mode 100644
index 0000000..f3c1e11
--- /dev/null
+++ b/components/camel-crypto-cms/src/main/java/org/apache/camel/component/crypto/cms/sig/SignedDataVerifier.java
@@ -0,0 +1,286 @@
+/**
+ * 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.component.crypto.cms.sig;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.component.crypto.cms.common.CryptoCmsUnmarshaller;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsException;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsFormatException;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsNoCertificateForSignerInfoException;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsNoCertificateForSignerInfosException;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsSignatureException;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsSignatureInvalidContentHashException;
+import org.apache.camel.component.crypto.cms.exception.CryptoCmsVerifierCertificateNotValidException;
+import org.apache.camel.converter.stream.OutputStreamBuilder;
+import org.apache.camel.util.IOHelper;
+import org.bouncycastle.asn1.cms.Attribute;
+import org.bouncycastle.asn1.cms.CMSAttributes;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.cms.CMSAttributeTableGenerator;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSSignedDataParser;
+import org.bouncycastle.cms.CMSSignerDigestMismatchException;
+import org.bouncycastle.cms.CMSVerifierCertificateNotValidException;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SignedDataVerifier extends CryptoCmsUnmarshaller {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SignedDataVerifier.class);
+
+ private final SignedDataVerifierConfiguration conf;
+
+ public SignedDataVerifier(SignedDataVerifierConfiguration config) {
+ super(config);
+ this.conf = config;
+ }
+
+ @Override
+ protected Object unmarshalInternal(InputStream is, Exchange exchange) throws Exception {
+
+ CMSSignedDataParser sp;
+ try {
+ sp = new CMSSignedDataParser(new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build(), is);
+ } catch (CMSException e) {
+ throw new CryptoCmsFormatException(getFormatErrorMessage(), e);
+ }
+ OutputStreamBuilder output = getOutputStream(sp, exchange);
+
+ debugLog(sp);
+
+ verify(sp, exchange);
+
+ return output.build();
+ }
+
+ protected String getFormatErrorMessage() {
+ return "Message has invalid format. It was not possible to parse the message into a PKCS7/CMS content info object containing PKCS7/CMS Signed Data.";
+ }
+
+ protected OutputStreamBuilder getOutputStream(CMSSignedDataParser sp, Exchange exchange) throws Exception {
+ // get the InputStream with the plain data
+ InputStream data;
+ try {
+ data = sp.getSignedContent().getContentStream();
+ } catch (NullPointerException e) { // nullpointer exception is
+ // thrown when the signed content
+ // is missing
+ throw getContentMissingException(e);
+ }
+
+ // the input stream must be completely read, otherwise the signer
+ // info is not available!
+ OutputStreamBuilder osb = OutputStreamBuilder.withExchange(exchange);
+
+ try {
+ // data can be null in the case of explicit Signed Data
+ if (data != null) {
+ try {
+ IOHelper.copy(data, osb);
+ } finally {
+ IOHelper.close(data);
+ }
+ }
+ } catch (IOException e) {
+ throw new CryptoCmsException("Error during reading the signed content of the signed data object", e);
+ }
+ return osb;
+ }
+
+ protected CryptoCmsException getContentMissingException(NullPointerException e) {
+ return new CryptoCmsException("PKCS7/CMS signature validation not possible: The content for which the hash-value must be calculated is missing in the PKCS7/CMS signed data instance. "
+ + "Please check the configuration of the sender of the PKCS7/CMS signature.", e);
+ }
+
+ protected void debugLog(CMSSignedDataParser sp) throws CMSException {
+ if (!LOG.isDebugEnabled()) {
+ return;
+ }
+ SignerInformationStore signers = sp.getSignerInfos();
+ Set<AlgorithmIdentifier> messageDigestAlgorithms = sp.getDigestAlgorithmIDs();
+ for (AlgorithmIdentifier algorithm : messageDigestAlgorithms) {
+ LOG.debug("Message digest algorithm: {}", algorithm.getAlgorithm().getId());
+ }
+
+ LOG.debug("Included Signer Infos:");
+ int i = 0;
+ for (SignerInformation signer : signers.getSigners()) {
+ i++;
+ LOG.debug(" Signer {}: {} ", new Object[] {i, signerInformationToString(signer)});
+ if (signer.getSignedAttributes() != null) {
+ @SuppressWarnings("unchecked")
+ Hashtable<String, Attribute> authAttTable = signer.getSignedAttributes().toHashtable();
+ if (authAttTable != null) {
+ LOG.debug(" Signed attributes of signer {}: {}", i, attributesToString(authAttTable));
+ }
+ }
+ if (signer.getUnsignedAttributes() != null) {
+ @SuppressWarnings("unchecked")
+ Hashtable<String, Attribute> unAuthAtts = signer.getUnsignedAttributes().toHashtable();
+ if (unAuthAtts != null) {
+ LOG.debug(" Unsigned attributes of signer {}: {}", i, attributesToString(unAuthAtts));
+ }
+ }
+ }
+ }
+
+ protected void verify(CMSSignedDataParser signed, Exchange exchange) throws Exception {
+
+ SignerInformationStore signers = getNonEmptySenderInfos(signed);
+
+ Collection<X509Certificate> allowedVerifyCerts = conf.getCertificates(exchange);
+ if (allowedVerifyCerts.isEmpty()) {
+ throw new CryptoCmsNoCertificateForSignerInfosException("Cannot verify the signatures of the the PKCS7/CMS Signed Data object: No verifier certificate is configured.");
+ }
+
+ JcaCertStore certStore = new JcaCertStore(allowedVerifyCerts);
+
+ boolean atLeastOneSignatureVerified = false;
+ for (SignerInformation signer : signers.getSigners()) {
+ @SuppressWarnings("unchecked")
+ Collection<X509CertificateHolder> certCollection = certStore.getMatches(signer.getSID());
+
+ if (certCollection.isEmpty()) {
+ if (conf.isVerifySignaturesOfAllSigners(exchange)) {
+ throw new CryptoCmsNoCertificateForSignerInfoException("KCS7/CMS signature verification failed. The public key for the signer information with"
+ + signerInformationToString(signer) + " cannot be found in the configured certificates: "
+ + certsToString(allowedVerifyCerts));
+ } else {
+ continue;
+ }
+ }
+ Iterator<X509CertificateHolder> certIt = certCollection.iterator();
+ X509CertificateHolder cert = certIt.next();
+
+ try {
+ if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build(cert))) {
+ LOG.debug("Verification successful");
+ atLeastOneSignatureVerified = true;
+ if (!conf.isVerifySignaturesOfAllSigners(exchange)) {
+ return;
+ }
+ } else {
+ throw new CryptoCmsSignatureException("PKCS7/CMS signature verification failed for signer information with " + issuerSerialNumberSubject(cert));
+ }
+ } catch (CMSSignerDigestMismatchException e) {
+ throw new CryptoCmsSignatureInvalidContentHashException("PKCS7/CMS signature verification failed for signer information with " + issuerSerialNumberSubject(cert)
+ + ". Calculated hash differs from the signed hash value. Either the message content does not correspond "
+ + "to the signature or the message might be tampered.", e);
+ } catch (CMSVerifierCertificateNotValidException e) {
+ throw new CryptoCmsVerifierCertificateNotValidException("PKCS7/CMS signature verification failed for signer information with " + issuerSerialNumberSubject(cert)
+ + ". Certificate was not valid at the signing time.", e);
+ }
+ }
+
+ if (!atLeastOneSignatureVerified) {
+ throw new CryptoCmsNoCertificateForSignerInfosException("Cannot verify the signature of the PKCS7/CMS signed data object with the certificates "
+ + certsToString(allowedVerifyCerts)
+ + " specified in the configuration. The signers in the signed data object are: " + signersToString(signers));
+
+ }
+ }
+
+ SignerInformationStore getNonEmptySenderInfos(CMSSignedDataParser signed) throws CryptoCmsException, CMSException {
+ SignerInformationStore senders = signed.getSignerInfos();
+ if (senders.size() == 0) {
+ throw new CryptoCmsException("Sent CMS/PKCS7 signed data message is incorrect. No signer info found in signed data. Correct the sent message.");
+ }
+ return senders;
+ }
+
+ protected String signerInformationToString(SignerInformation sigInfo) {
+ if (sigInfo == null) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append("ContentTypeOID=");
+ sb.append(sigInfo.getContentType());
+ sb.append(", Issuer=");
+ sb.append(sigInfo.getSID().getIssuer());
+ sb.append(", SerialNumber=");
+ sb.append(sigInfo.getSID().getSerialNumber());
+ sb.append(", SignerInfoVersion=");
+ sb.append(sigInfo.getVersion());
+ sb.append(", SignatureAlgorithmOID=");
+ sb.append(sigInfo.getDigestAlgOID());
+ sb.append(", EncryptionAlgorithmOID=");
+ sb.append(sigInfo.getEncryptionAlgOID());
+ sb.append(", isCounterSignature=");
+ sb.append(sigInfo.isCounterSignature());
+
+ return sb.toString();
+
+ }
+
+ protected String signersToString(SignerInformationStore signers) {
+ if (signers == null) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ Collection<SignerInformation> sigInfos = signers.getSigners();
+ int size = sigInfos.size();
+ int counter = 0;
+ for (SignerInformation sigInfo : sigInfos) {
+ counter++;
+ sb.append('[');
+ sb.append("Issuer=");
+ sb.append(sigInfo.getSID().getIssuer());
+ sb.append(", SerialNumber=");
+ sb.append(sigInfo.getSID().getSerialNumber());
+ sb.append(']');
+ if (counter < size) {
+ sb.append("; ");
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String attributesToString(Hashtable<String, Attribute> attributes) {
+ if (attributes == null) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (Attribute attr : attributes.values()) {
+ sb.append(attr.getAttrType());
+ if (CMSAttributes.signingTime.equals(attr.getAttrType()) || CMSAttributes.messageDigest.equals(attr.getAttrType())
+ || CMSAttributes.cmsAlgorithmProtect.equals(attr.getAttrType()) || CMSAttributeTableGenerator.CONTENT_TYPE.equals(attr.getAttrType())) {
+ // for these attributes we can print the value because we know
+ // they do not contain confidential or personal data
+ sb.append("=");
+ sb.append(attr.getAttrValues());
+ }
+ sb.append(",");
+ }
+ return sb.toString();
+ }
+
+}