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(); + } + +}