From 44288db4414158ac9b98a507b15e81d0d3c66ca6 Mon Sep 17 00:00:00 2001
From: Peter Dettman
Date: Fri, 28 Jun 2013 15:26:06 +0700
Subject: Initial import of old CVS repository
---
crypto/src/cms/BaseDigestCalculator.cs | 23 +
.../cms/CMSAttributeTableGenerationException.cs | 28 +
crypto/src/cms/CMSAttributeTableGenerator.cs | 25 +
crypto/src/cms/CMSAuthEnvelopedData.cs | 112 +++
crypto/src/cms/CMSAuthEnvelopedGenerator.cs | 16 +
crypto/src/cms/CMSAuthenticatedData.cs | 137 ++++
crypto/src/cms/CMSAuthenticatedDataGenerator.cs | 156 ++++
crypto/src/cms/CMSAuthenticatedDataParser.cs | 214 +++++
.../src/cms/CMSAuthenticatedDataStreamGenerator.cs | 272 ++++++
crypto/src/cms/CMSAuthenticatedGenerator.cs | 35 +
crypto/src/cms/CMSCompressedData.cs | 107 +++
crypto/src/cms/CMSCompressedDataGenerator.cs | 66 ++
crypto/src/cms/CMSCompressedDataParser.cs | 57 ++
crypto/src/cms/CMSCompressedDataStreamGenerator.cs | 140 ++++
crypto/src/cms/CMSContentInfoParser.cs | 47 ++
crypto/src/cms/CMSEnvelopedData.cs | 115 +++
crypto/src/cms/CMSEnvelopedDataGenerator.cs | 178 ++++
crypto/src/cms/CMSEnvelopedDataParser.cs | 161 ++++
crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs | 281 +++++++
crypto/src/cms/CMSEnvelopedGenerator.cs | 331 ++++++++
crypto/src/cms/CMSEnvelopedHelper.cs | 311 +++++++
crypto/src/cms/CMSException.cs | 28 +
crypto/src/cms/CMSPBEKey.cs | 109 +++
crypto/src/cms/CMSProcessable.cs | 19 +
crypto/src/cms/CMSProcessableByteArray.cs | 36 +
crypto/src/cms/CMSProcessableFile.cs | 54 ++
crypto/src/cms/CMSProcessableInputStream.cs | 52 ++
crypto/src/cms/CMSReadable.cs | 10 +
crypto/src/cms/CMSSecureReadable.cs | 14 +
crypto/src/cms/CMSSignedData.cs | 425 ++++++++++
crypto/src/cms/CMSSignedDataGenerator.cs | 551 +++++++++++++
crypto/src/cms/CMSSignedDataParser.cs | 455 ++++++++++
crypto/src/cms/CMSSignedDataStreamGenerator.cs | 913 +++++++++++++++++++++
crypto/src/cms/CMSSignedGenerator.cs | 261 ++++++
crypto/src/cms/CMSSignedHelper.cs | 319 +++++++
crypto/src/cms/CMSStreamException.cs | 29 +
crypto/src/cms/CMSTypedStream.cs | 72 ++
crypto/src/cms/CMSUtils.cs | 186 +++++
crypto/src/cms/CounterSignatureDigestCalculator.cs | 28 +
.../DefaultAuthenticatedAttributeTableGenerator.cs | 90 ++
.../cms/DefaultSignedAttributeTableGenerator.cs | 124 +++
crypto/src/cms/DigOutputStream.cs | 28 +
crypto/src/cms/IDigestCalculator.cs | 9 +
crypto/src/cms/KEKRecipientInfoGenerator.cs | 137 ++++
crypto/src/cms/KEKRecipientInformation.cs | 62 ++
crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs | 171 ++++
crypto/src/cms/KeyAgreeRecipientInformation.cs | 226 +++++
crypto/src/cms/KeyTransRecipientInfoGenerator.cs | 87 ++
crypto/src/cms/KeyTransRecipientInformation.cs | 113 +++
crypto/src/cms/MacOutputStream.cs | 28 +
crypto/src/cms/NullOutputStream.cs | 20 +
crypto/src/cms/OriginatorId.cs | 51 ++
crypto/src/cms/OriginatorInfoGenerator.cs | 42 +
crypto/src/cms/OriginatorInformation.cs | 96 +++
crypto/src/cms/PKCS5Scheme2PBEKey.cs | 64 ++
crypto/src/cms/PKCS5Scheme2UTF8PBEKey.cs | 64 ++
crypto/src/cms/PasswordRecipientInfoGenerator.cs | 69 ++
crypto/src/cms/PasswordRecipientInformation.cs | 79 ++
crypto/src/cms/RecipientId.cs | 58 ++
crypto/src/cms/RecipientInfoGenerator.cs | 26 +
crypto/src/cms/RecipientInformation.cs | 126 +++
crypto/src/cms/RecipientInformationStore.cs | 86 ++
crypto/src/cms/SigOutputStream.cs | 43 +
crypto/src/cms/SignerId.cs | 51 ++
crypto/src/cms/SignerInfoGenerator.cs | 14 +
crypto/src/cms/SignerInformation.cs | 758 +++++++++++++++++
crypto/src/cms/SignerInformationStore.cs | 74 ++
crypto/src/cms/SimpleAttributeTableGenerator.cs | 28 +
68 files changed, 9197 insertions(+)
create mode 100644 crypto/src/cms/BaseDigestCalculator.cs
create mode 100644 crypto/src/cms/CMSAttributeTableGenerationException.cs
create mode 100644 crypto/src/cms/CMSAttributeTableGenerator.cs
create mode 100644 crypto/src/cms/CMSAuthEnvelopedData.cs
create mode 100644 crypto/src/cms/CMSAuthEnvelopedGenerator.cs
create mode 100644 crypto/src/cms/CMSAuthenticatedData.cs
create mode 100644 crypto/src/cms/CMSAuthenticatedDataGenerator.cs
create mode 100644 crypto/src/cms/CMSAuthenticatedDataParser.cs
create mode 100644 crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
create mode 100644 crypto/src/cms/CMSAuthenticatedGenerator.cs
create mode 100644 crypto/src/cms/CMSCompressedData.cs
create mode 100644 crypto/src/cms/CMSCompressedDataGenerator.cs
create mode 100644 crypto/src/cms/CMSCompressedDataParser.cs
create mode 100644 crypto/src/cms/CMSCompressedDataStreamGenerator.cs
create mode 100644 crypto/src/cms/CMSContentInfoParser.cs
create mode 100644 crypto/src/cms/CMSEnvelopedData.cs
create mode 100644 crypto/src/cms/CMSEnvelopedDataGenerator.cs
create mode 100644 crypto/src/cms/CMSEnvelopedDataParser.cs
create mode 100644 crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
create mode 100644 crypto/src/cms/CMSEnvelopedGenerator.cs
create mode 100644 crypto/src/cms/CMSEnvelopedHelper.cs
create mode 100644 crypto/src/cms/CMSException.cs
create mode 100644 crypto/src/cms/CMSPBEKey.cs
create mode 100644 crypto/src/cms/CMSProcessable.cs
create mode 100644 crypto/src/cms/CMSProcessableByteArray.cs
create mode 100644 crypto/src/cms/CMSProcessableFile.cs
create mode 100644 crypto/src/cms/CMSProcessableInputStream.cs
create mode 100644 crypto/src/cms/CMSReadable.cs
create mode 100644 crypto/src/cms/CMSSecureReadable.cs
create mode 100644 crypto/src/cms/CMSSignedData.cs
create mode 100644 crypto/src/cms/CMSSignedDataGenerator.cs
create mode 100644 crypto/src/cms/CMSSignedDataParser.cs
create mode 100644 crypto/src/cms/CMSSignedDataStreamGenerator.cs
create mode 100644 crypto/src/cms/CMSSignedGenerator.cs
create mode 100644 crypto/src/cms/CMSSignedHelper.cs
create mode 100644 crypto/src/cms/CMSStreamException.cs
create mode 100644 crypto/src/cms/CMSTypedStream.cs
create mode 100644 crypto/src/cms/CMSUtils.cs
create mode 100644 crypto/src/cms/CounterSignatureDigestCalculator.cs
create mode 100644 crypto/src/cms/DefaultAuthenticatedAttributeTableGenerator.cs
create mode 100644 crypto/src/cms/DefaultSignedAttributeTableGenerator.cs
create mode 100644 crypto/src/cms/DigOutputStream.cs
create mode 100644 crypto/src/cms/IDigestCalculator.cs
create mode 100644 crypto/src/cms/KEKRecipientInfoGenerator.cs
create mode 100644 crypto/src/cms/KEKRecipientInformation.cs
create mode 100644 crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs
create mode 100644 crypto/src/cms/KeyAgreeRecipientInformation.cs
create mode 100644 crypto/src/cms/KeyTransRecipientInfoGenerator.cs
create mode 100644 crypto/src/cms/KeyTransRecipientInformation.cs
create mode 100644 crypto/src/cms/MacOutputStream.cs
create mode 100644 crypto/src/cms/NullOutputStream.cs
create mode 100644 crypto/src/cms/OriginatorId.cs
create mode 100644 crypto/src/cms/OriginatorInfoGenerator.cs
create mode 100644 crypto/src/cms/OriginatorInformation.cs
create mode 100644 crypto/src/cms/PKCS5Scheme2PBEKey.cs
create mode 100644 crypto/src/cms/PKCS5Scheme2UTF8PBEKey.cs
create mode 100644 crypto/src/cms/PasswordRecipientInfoGenerator.cs
create mode 100644 crypto/src/cms/PasswordRecipientInformation.cs
create mode 100644 crypto/src/cms/RecipientId.cs
create mode 100644 crypto/src/cms/RecipientInfoGenerator.cs
create mode 100644 crypto/src/cms/RecipientInformation.cs
create mode 100644 crypto/src/cms/RecipientInformationStore.cs
create mode 100644 crypto/src/cms/SigOutputStream.cs
create mode 100644 crypto/src/cms/SignerId.cs
create mode 100644 crypto/src/cms/SignerInfoGenerator.cs
create mode 100644 crypto/src/cms/SignerInformation.cs
create mode 100644 crypto/src/cms/SignerInformationStore.cs
create mode 100644 crypto/src/cms/SimpleAttributeTableGenerator.cs
(limited to 'crypto/src/cms')
diff --git a/crypto/src/cms/BaseDigestCalculator.cs b/crypto/src/cms/BaseDigestCalculator.cs
new file mode 100644
index 000000000..3dcbca753
--- /dev/null
+++ b/crypto/src/cms/BaseDigestCalculator.cs
@@ -0,0 +1,23 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal class BaseDigestCalculator
+ : IDigestCalculator
+ {
+ private readonly byte[] digest;
+
+ internal BaseDigestCalculator(
+ byte[] digest)
+ {
+ this.digest = digest;
+ }
+
+ public byte[] GetDigest()
+ {
+ return Arrays.Clone(digest);
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSAttributeTableGenerationException.cs b/crypto/src/cms/CMSAttributeTableGenerationException.cs
new file mode 100644
index 000000000..31b06d6dd
--- /dev/null
+++ b/crypto/src/cms/CMSAttributeTableGenerationException.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Org.BouncyCastle.Cms
+{
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+ [Serializable]
+#endif
+ public class CmsAttributeTableGenerationException
+ : CmsException
+ {
+ public CmsAttributeTableGenerationException()
+ {
+ }
+
+ public CmsAttributeTableGenerationException(
+ string name)
+ : base(name)
+ {
+ }
+
+ public CmsAttributeTableGenerationException(
+ string name,
+ Exception e)
+ : base(name, e)
+ {
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSAttributeTableGenerator.cs b/crypto/src/cms/CMSAttributeTableGenerator.cs
new file mode 100644
index 000000000..92c9a29d9
--- /dev/null
+++ b/crypto/src/cms/CMSAttributeTableGenerator.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1.Cms;
+
+namespace Org.BouncyCastle.Cms
+{
+ ///
+ /// The 'Signature' parameter is only available when generating unsigned attributes.
+ ///
+ public enum CmsAttributeTableParameter
+ {
+// const string ContentType = "contentType";
+// const string Digest = "digest";
+// const string Signature = "encryptedDigest";
+// const string DigestAlgorithmIdentifier = "digestAlgID";
+
+ ContentType, Digest, Signature, DigestAlgorithmIdentifier
+ }
+
+ public interface CmsAttributeTableGenerator
+ {
+ AttributeTable GetAttributes(IDictionary parameters);
+ }
+}
diff --git a/crypto/src/cms/CMSAuthEnvelopedData.cs b/crypto/src/cms/CMSAuthEnvelopedData.cs
new file mode 100644
index 000000000..d35e946ae
--- /dev/null
+++ b/crypto/src/cms/CMSAuthEnvelopedData.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * containing class for an CMS AuthEnveloped Data object
+ */
+ internal class CmsAuthEnvelopedData
+ {
+ internal RecipientInformationStore recipientInfoStore;
+ internal ContentInfo contentInfo;
+
+ private OriginatorInfo originator;
+ private AlgorithmIdentifier authEncAlg;
+ private Asn1Set authAttrs;
+ private byte[] mac;
+ private Asn1Set unauthAttrs;
+
+ public CmsAuthEnvelopedData(
+ byte[] authEnvData)
+ : this(CmsUtilities.ReadContentInfo(authEnvData))
+ {
+ }
+
+ public CmsAuthEnvelopedData(
+ Stream authEnvData)
+ : this(CmsUtilities.ReadContentInfo(authEnvData))
+ {
+ }
+
+ public CmsAuthEnvelopedData(
+ ContentInfo contentInfo)
+ {
+ this.contentInfo = contentInfo;
+
+ AuthEnvelopedData authEnvData = AuthEnvelopedData.GetInstance(contentInfo.Content);
+
+ this.originator = authEnvData.OriginatorInfo;
+
+ //
+ // read the recipients
+ //
+ Asn1Set recipientInfos = authEnvData.RecipientInfos;
+
+ //
+ // read the auth-encrypted content info
+ //
+ EncryptedContentInfo authEncInfo = authEnvData.AuthEncryptedContentInfo;
+ this.authEncAlg = authEncInfo.ContentEncryptionAlgorithm;
+ CmsSecureReadable secureReadable = new AuthEnvelopedSecureReadable(this);
+
+ //
+ // build the RecipientInformationStore
+ //
+ this.recipientInfoStore = CmsEnvelopedHelper.BuildRecipientInformationStore(
+ recipientInfos, secureReadable);
+
+ // FIXME These need to be passed to the AEAD cipher as AAD (Additional Authenticated Data)
+ this.authAttrs = authEnvData.AuthAttrs;
+ this.mac = authEnvData.Mac.GetOctets();
+ this.unauthAttrs = authEnvData.UnauthAttrs;
+ }
+
+ private class AuthEnvelopedSecureReadable : CmsSecureReadable
+ {
+ private readonly CmsAuthEnvelopedData parent;
+
+ internal AuthEnvelopedSecureReadable(CmsAuthEnvelopedData parent)
+ {
+ this.parent = parent;
+ }
+
+ public AlgorithmIdentifier Algorithm
+ {
+ get { return parent.authEncAlg; }
+ }
+
+ public object CryptoObject
+ {
+ get { return null; }
+ }
+
+ public CmsReadable GetReadable(KeyParameter key)
+ {
+ // TODO Create AEAD cipher instance to decrypt and calculate tag ( MAC)
+ throw new CmsException("AuthEnveloped data decryption not yet implemented");
+
+// RFC 5084 ASN.1 Module
+// -- Parameters for AlgorithmIdentifier
+//
+// CCMParameters ::= SEQUENCE {
+// aes-nonce OCTET STRING (SIZE(7..13)),
+// aes-ICVlen AES-CCM-ICVlen DEFAULT 12 }
+//
+// AES-CCM-ICVlen ::= INTEGER (4 | 6 | 8 | 10 | 12 | 14 | 16)
+//
+// GCMParameters ::= SEQUENCE {
+// aes-nonce OCTET STRING, -- recommended size is 12 octets
+// aes-ICVlen AES-GCM-ICVlen DEFAULT 12 }
+//
+// AES-GCM-ICVlen ::= INTEGER (12 | 13 | 14 | 15 | 16)
+ }
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSAuthEnvelopedGenerator.cs b/crypto/src/cms/CMSAuthEnvelopedGenerator.cs
new file mode 100644
index 000000000..4273cff29
--- /dev/null
+++ b/crypto/src/cms/CMSAuthEnvelopedGenerator.cs
@@ -0,0 +1,16 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Nist;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal class CmsAuthEnvelopedGenerator
+ {
+ public static readonly string Aes128Ccm = NistObjectIdentifiers.IdAes128Ccm.Id;
+ public static readonly string Aes192Ccm = NistObjectIdentifiers.IdAes192Ccm.Id;
+ public static readonly string Aes256Ccm = NistObjectIdentifiers.IdAes256Ccm.Id;
+ public static readonly string Aes128Gcm = NistObjectIdentifiers.IdAes128Gcm.Id;
+ public static readonly string Aes192Gcm = NistObjectIdentifiers.IdAes192Gcm.Id;
+ public static readonly string Aes256Gcm = NistObjectIdentifiers.IdAes256Gcm.Id;
+ }
+}
diff --git a/crypto/src/cms/CMSAuthenticatedData.cs b/crypto/src/cms/CMSAuthenticatedData.cs
new file mode 100644
index 000000000..5e234da2b
--- /dev/null
+++ b/crypto/src/cms/CMSAuthenticatedData.cs
@@ -0,0 +1,137 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * containing class for an CMS Authenticated Data object
+ */
+ public class CmsAuthenticatedData
+ {
+ internal RecipientInformationStore recipientInfoStore;
+ internal ContentInfo contentInfo;
+
+ private AlgorithmIdentifier macAlg;
+ private Asn1Set authAttrs;
+ private Asn1Set unauthAttrs;
+ private byte[] mac;
+
+ public CmsAuthenticatedData(
+ byte[] authData)
+ : this(CmsUtilities.ReadContentInfo(authData))
+ {
+ }
+
+ public CmsAuthenticatedData(
+ Stream authData)
+ : this(CmsUtilities.ReadContentInfo(authData))
+ {
+ }
+
+ public CmsAuthenticatedData(
+ ContentInfo contentInfo)
+ {
+ this.contentInfo = contentInfo;
+
+ AuthenticatedData authData = AuthenticatedData.GetInstance(contentInfo.Content);
+
+ //
+ // read the recipients
+ //
+ Asn1Set recipientInfos = authData.RecipientInfos;
+
+ this.macAlg = authData.MacAlgorithm;
+
+ //
+ // read the authenticated content info
+ //
+ ContentInfo encInfo = authData.EncapsulatedContentInfo;
+ CmsReadable readable = new CmsProcessableByteArray(
+ Asn1OctetString.GetInstance(encInfo.Content).GetOctets());
+ CmsSecureReadable secureReadable = new CmsEnvelopedHelper.CmsAuthenticatedSecureReadable(
+ this.macAlg, readable);
+
+ //
+ // build the RecipientInformationStore
+ //
+ this.recipientInfoStore = CmsEnvelopedHelper.BuildRecipientInformationStore(
+ recipientInfos, secureReadable);
+
+ this.authAttrs = authData.AuthAttrs;
+ this.mac = authData.Mac.GetOctets();
+ this.unauthAttrs = authData.UnauthAttrs;
+ }
+
+ public byte[] GetMac()
+ {
+ return Arrays.Clone(mac);
+ }
+
+ public AlgorithmIdentifier MacAlgorithmID
+ {
+ get { return macAlg; }
+ }
+
+ /**
+ * return the object identifier for the content MAC algorithm.
+ */
+ public string MacAlgOid
+ {
+ get { return macAlg.ObjectID.Id; }
+ }
+
+ /**
+ * return a store of the intended recipients for this message
+ */
+ public RecipientInformationStore GetRecipientInfos()
+ {
+ return recipientInfoStore;
+ }
+
+ /**
+ * return the ContentInfo
+ */
+ public ContentInfo ContentInfo
+ {
+ get { return contentInfo; }
+ }
+
+ /**
+ * return a table of the digested attributes indexed by
+ * the OID of the attribute.
+ */
+ public Asn1.Cms.AttributeTable GetAuthAttrs()
+ {
+ if (authAttrs == null)
+ return null;
+
+ return new Asn1.Cms.AttributeTable(authAttrs);
+ }
+
+ /**
+ * return a table of the undigested attributes indexed by
+ * the OID of the attribute.
+ */
+ public Asn1.Cms.AttributeTable GetUnauthAttrs()
+ {
+ if (unauthAttrs == null)
+ return null;
+
+ return new Asn1.Cms.AttributeTable(unauthAttrs);
+ }
+
+ /**
+ * return the ASN.1 encoded representation of this object.
+ */
+ public byte[] GetEncoded()
+ {
+ return contentInfo.GetEncoded();
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSAuthenticatedDataGenerator.cs b/crypto/src/cms/CMSAuthenticatedDataGenerator.cs
new file mode 100644
index 000000000..846c19a24
--- /dev/null
+++ b/crypto/src/cms/CMSAuthenticatedDataGenerator.cs
@@ -0,0 +1,156 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * General class for generating a CMS authenticated-data message.
+ *
+ * A simple example of usage.
+ *
+ *
+ * CMSAuthenticatedDataGenerator fact = new CMSAuthenticatedDataGenerator();
+ *
+ * fact.addKeyTransRecipient(cert);
+ *
+ * CMSAuthenticatedData data = fact.generate(content, algorithm, "BC");
+ *
+ */
+ public class CmsAuthenticatedDataGenerator
+ : CmsAuthenticatedGenerator
+ {
+ /**
+ * base constructor
+ */
+ public CmsAuthenticatedDataGenerator()
+ {
+ }
+
+ /**
+ * constructor allowing specific source of randomness
+ * @param rand instance of SecureRandom to use
+ */
+ public CmsAuthenticatedDataGenerator(
+ SecureRandom rand)
+ : base(rand)
+ {
+ }
+
+ /**
+ * generate an enveloped object that contains an CMS Enveloped Data
+ * object using the given provider and the passed in key generator.
+ */
+ private CmsAuthenticatedData Generate(
+ CmsProcessable content,
+ string macOid,
+ CipherKeyGenerator keyGen)
+ {
+ AlgorithmIdentifier macAlgId;
+ KeyParameter encKey;
+ Asn1OctetString encContent;
+ Asn1OctetString macResult;
+
+ try
+ {
+ // FIXME Will this work for macs?
+ byte[] encKeyBytes = keyGen.GenerateKey();
+ encKey = ParameterUtilities.CreateKeyParameter(macOid, encKeyBytes);
+
+ Asn1Encodable asn1Params = GenerateAsn1Parameters(macOid, encKeyBytes);
+
+ ICipherParameters cipherParameters;
+ macAlgId = GetAlgorithmIdentifier(
+ macOid, encKey, asn1Params, out cipherParameters);
+
+ IMac mac = MacUtilities.GetMac(macOid);
+ // TODO Confirm no ParametersWithRandom needed
+ // FIXME Only passing key at the moment
+// mac.Init(cipherParameters);
+ mac.Init(encKey);
+
+ MemoryStream bOut = new MemoryStream();
+ Stream mOut = new TeeOutputStream(bOut, new MacOutputStream(mac));
+
+ content.Write(mOut);
+
+ mOut.Close();
+ bOut.Close();
+
+ encContent = new BerOctetString(bOut.ToArray());
+
+ byte[] macOctets = MacUtilities.DoFinal(mac);
+ macResult = new DerOctetString(macOctets);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("couldn't create cipher.", e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key invalid in message.", e);
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("exception decoding algorithm parameters.", e);
+ }
+
+ Asn1EncodableVector recipientInfos = new Asn1EncodableVector();
+
+ foreach (RecipientInfoGenerator rig in recipientInfoGenerators)
+ {
+ try
+ {
+ recipientInfos.Add(rig.Generate(encKey, rand));
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key inappropriate for algorithm.", e);
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new CmsException("error making encrypted content.", e);
+ }
+ }
+
+ ContentInfo eci = new ContentInfo(CmsObjectIdentifiers.Data, encContent);
+
+ ContentInfo contentInfo = new ContentInfo(
+ CmsObjectIdentifiers.AuthenticatedData,
+ new AuthenticatedData(null, new DerSet(recipientInfos), macAlgId, null, eci, null, macResult, null));
+
+ return new CmsAuthenticatedData(contentInfo);
+ }
+
+ /**
+ * generate an authenticated object that contains an CMS Authenticated Data object
+ */
+ public CmsAuthenticatedData Generate(
+ CmsProcessable content,
+ string encryptionOid)
+ {
+ try
+ {
+ // FIXME Will this work for macs?
+ CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid);
+
+ keyGen.Init(new KeyGenerationParameters(rand, keyGen.DefaultStrength));
+
+ return Generate(content, encryptionOid, keyGen);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("can't find key generation algorithm.", e);
+ }
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSAuthenticatedDataParser.cs b/crypto/src/cms/CMSAuthenticatedDataParser.cs
new file mode 100644
index 000000000..c99aac61c
--- /dev/null
+++ b/crypto/src/cms/CMSAuthenticatedDataParser.cs
@@ -0,0 +1,214 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * Parsing class for an CMS Authenticated Data object from an input stream.
+ *
+ * Note: that because we are in a streaming mode only one recipient can be tried and it is important
+ * that the methods on the parser are called in the appropriate order.
+ *
+ *
+ * Example of use - assuming the first recipient matches the private key we have.
+ *
+ * CMSAuthenticatedDataParser ad = new CMSAuthenticatedDataParser(inputStream);
+ *
+ * RecipientInformationStore recipients = ad.getRecipientInfos();
+ *
+ * Collection c = recipients.getRecipients();
+ * Iterator it = c.iterator();
+ *
+ * if (it.hasNext())
+ * {
+ * RecipientInformation recipient = (RecipientInformation)it.next();
+ *
+ * CMSTypedStream recData = recipient.getContentStream(privateKey, "BC");
+ *
+ * processDataStream(recData.getContentStream());
+ *
+ * if (!Arrays.equals(ad.getMac(), recipient.getMac())
+ * {
+ * System.err.println("Data corrupted!!!!");
+ * }
+ * }
+ *
+ * Note: this class does not introduce buffering - if you are processing large files you should create
+ * the parser with:
+ *
+ * CMSAuthenticatedDataParser ep = new CMSAuthenticatedDataParser(new BufferedInputStream(inputStream, bufSize));
+ *
+ * where bufSize is a suitably large buffer size.
+ *
+ */
+ public class CmsAuthenticatedDataParser
+ : CmsContentInfoParser
+ {
+ internal RecipientInformationStore _recipientInfoStore;
+ internal AuthenticatedDataParser authData;
+
+ private AlgorithmIdentifier macAlg;
+ private byte[] mac;
+ private Asn1.Cms.AttributeTable authAttrs;
+ private Asn1.Cms.AttributeTable unauthAttrs;
+
+ private bool authAttrNotRead;
+ private bool unauthAttrNotRead;
+
+ public CmsAuthenticatedDataParser(
+ byte[] envelopedData)
+ : this(new MemoryStream(envelopedData, false))
+ {
+ }
+
+ public CmsAuthenticatedDataParser(
+ Stream envelopedData)
+ : base(envelopedData)
+ {
+ this.authAttrNotRead = true;
+ this.authData = new AuthenticatedDataParser(
+ (Asn1SequenceParser)contentInfo.GetContent(Asn1Tags.Sequence));
+
+ // TODO Validate version?
+ //DerInteger version = this.authData.getVersion();
+
+ //
+ // read the recipients
+ //
+ Asn1Set recipientInfos = Asn1Set.GetInstance(authData.GetRecipientInfos().ToAsn1Object());
+
+ this.macAlg = authData.GetMacAlgorithm();
+
+ //
+ // read the authenticated content info
+ //
+ ContentInfoParser data = authData.GetEnapsulatedContentInfo();
+ CmsReadable readable = new CmsProcessableInputStream(
+ ((Asn1OctetStringParser)data.GetContent(Asn1Tags.OctetString)).GetOctetStream());
+ CmsSecureReadable secureReadable = new CmsEnvelopedHelper.CmsAuthenticatedSecureReadable(
+ this.macAlg, readable);
+
+ //
+ // build the RecipientInformationStore
+ //
+ this._recipientInfoStore = CmsEnvelopedHelper.BuildRecipientInformationStore(
+ recipientInfos, secureReadable);
+ }
+
+ public AlgorithmIdentifier MacAlgorithmID
+ {
+ get { return macAlg; }
+ }
+
+ /**
+ * return the object identifier for the mac algorithm.
+ */
+ public string MacAlgOid
+ {
+ get { return macAlg.ObjectID.Id; }
+ }
+
+
+ /**
+ * return the ASN.1 encoded encryption algorithm parameters, or null if
+ * there aren't any.
+ */
+ public Asn1Object MacAlgParams
+ {
+ get
+ {
+ Asn1Encodable ae = macAlg.Parameters;
+
+ return ae == null ? null : ae.ToAsn1Object();
+ }
+ }
+
+ /**
+ * return a store of the intended recipients for this message
+ */
+ public RecipientInformationStore GetRecipientInfos()
+ {
+ return _recipientInfoStore;
+ }
+
+ public byte[] GetMac()
+ {
+ if (mac == null)
+ {
+ GetAuthAttrs();
+ mac = authData.GetMac().GetOctets();
+ }
+ return Arrays.Clone(mac);
+ }
+
+ /**
+ * return a table of the unauthenticated attributes indexed by
+ * the OID of the attribute.
+ * @exception java.io.IOException
+ */
+ public Asn1.Cms.AttributeTable GetAuthAttrs()
+ {
+ if (authAttrs == null && authAttrNotRead)
+ {
+ Asn1SetParser s = authData.GetAuthAttrs();
+
+ authAttrNotRead = false;
+
+ if (s != null)
+ {
+ Asn1EncodableVector v = new Asn1EncodableVector();
+
+ IAsn1Convertible o;
+ while ((o = s.ReadObject()) != null)
+ {
+ Asn1SequenceParser seq = (Asn1SequenceParser)o;
+
+ v.Add(seq.ToAsn1Object());
+ }
+
+ authAttrs = new Asn1.Cms.AttributeTable(new DerSet(v));
+ }
+ }
+
+ return authAttrs;
+ }
+
+ /**
+ * return a table of the unauthenticated attributes indexed by
+ * the OID of the attribute.
+ * @exception java.io.IOException
+ */
+ public Asn1.Cms.AttributeTable GetUnauthAttrs()
+ {
+ if (unauthAttrs == null && unauthAttrNotRead)
+ {
+ Asn1SetParser s = authData.GetUnauthAttrs();
+
+ unauthAttrNotRead = false;
+
+ if (s != null)
+ {
+ Asn1EncodableVector v = new Asn1EncodableVector();
+
+ IAsn1Convertible o;
+ while ((o = s.ReadObject()) != null)
+ {
+ Asn1SequenceParser seq = (Asn1SequenceParser)o;
+
+ v.Add(seq.ToAsn1Object());
+ }
+
+ unauthAttrs = new Asn1.Cms.AttributeTable(new DerSet(v));
+ }
+ }
+
+ return unauthAttrs;
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs b/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
new file mode 100644
index 000000000..2603cb380
--- /dev/null
+++ b/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
@@ -0,0 +1,272 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * General class for generating a CMS authenticated-data message stream.
+ *
+ * A simple example of usage.
+ *
+ * CMSAuthenticatedDataStreamGenerator edGen = new CMSAuthenticatedDataStreamGenerator();
+ *
+ * edGen.addKeyTransRecipient(cert);
+ *
+ * ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ *
+ * OutputStream out = edGen.open(
+ * bOut, CMSAuthenticatedDataGenerator.AES128_CBC, "BC");*
+ * out.write(data);
+ *
+ * out.close();
+ *
+ *
+ */
+ public class CmsAuthenticatedDataStreamGenerator
+ : CmsAuthenticatedGenerator
+ {
+ // TODO Add support
+// private object _originatorInfo = null;
+// private object _unprotectedAttributes = null;
+ private int _bufferSize;
+ private bool _berEncodeRecipientSet;
+
+ /**
+ * base constructor
+ */
+ public CmsAuthenticatedDataStreamGenerator()
+ {
+ }
+
+ /**
+ * constructor allowing specific source of randomness
+ * @param rand instance of SecureRandom to use
+ */
+ public CmsAuthenticatedDataStreamGenerator(
+ SecureRandom rand)
+ : base(rand)
+ {
+ }
+
+ /**
+ * Set the underlying string size for encapsulated data
+ *
+ * @param bufferSize length of octet strings to buffer the data.
+ */
+ public void SetBufferSize(
+ int bufferSize)
+ {
+ _bufferSize = bufferSize;
+ }
+
+ /**
+ * Use a BER Set to store the recipient information
+ */
+ public void SetBerEncodeRecipients(
+ bool berEncodeRecipientSet)
+ {
+ _berEncodeRecipientSet = berEncodeRecipientSet;
+ }
+
+ /**
+ * generate an enveloped object that contains an CMS Enveloped Data
+ * object using the given provider and the passed in key generator.
+ * @throws java.io.IOException
+ */
+ private Stream Open(
+ Stream outStr,
+ string macOid,
+ CipherKeyGenerator keyGen)
+ {
+ // FIXME Will this work for macs?
+ byte[] encKeyBytes = keyGen.GenerateKey();
+ KeyParameter encKey = ParameterUtilities.CreateKeyParameter(macOid, encKeyBytes);
+
+ Asn1Encodable asn1Params = GenerateAsn1Parameters(macOid, encKeyBytes);
+
+ ICipherParameters cipherParameters;
+ AlgorithmIdentifier macAlgId = GetAlgorithmIdentifier(
+ macOid, encKey, asn1Params, out cipherParameters);
+
+ Asn1EncodableVector recipientInfos = new Asn1EncodableVector();
+
+ foreach (RecipientInfoGenerator rig in recipientInfoGenerators)
+ {
+ try
+ {
+ recipientInfos.Add(rig.Generate(encKey, rand));
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key inappropriate for algorithm.", e);
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new CmsException("error making encrypted content.", e);
+ }
+ }
+
+ // FIXME Only passing key at the moment
+// return Open(outStr, macAlgId, cipherParameters, recipientInfos);
+ return Open(outStr, macAlgId, encKey, recipientInfos);
+ }
+
+ protected Stream Open(
+ Stream outStr,
+ AlgorithmIdentifier macAlgId,
+ ICipherParameters cipherParameters,
+ Asn1EncodableVector recipientInfos)
+ {
+ try
+ {
+ //
+ // ContentInfo
+ //
+ BerSequenceGenerator cGen = new BerSequenceGenerator(outStr);
+
+ cGen.AddObject(CmsObjectIdentifiers.AuthenticatedData);
+
+ //
+ // Authenticated Data
+ //
+ BerSequenceGenerator authGen = new BerSequenceGenerator(
+ cGen.GetRawOutputStream(), 0, true);
+
+ authGen.AddObject(new DerInteger(AuthenticatedData.CalculateVersion(null)));
+
+ Stream authRaw = authGen.GetRawOutputStream();
+ Asn1Generator recipGen = _berEncodeRecipientSet
+ ? (Asn1Generator) new BerSetGenerator(authRaw)
+ : new DerSetGenerator(authRaw);
+
+ foreach (Asn1Encodable ae in recipientInfos)
+ {
+ recipGen.AddObject(ae);
+ }
+
+ recipGen.Close();
+
+ authGen.AddObject(macAlgId);
+
+ BerSequenceGenerator eiGen = new BerSequenceGenerator(authRaw);
+ eiGen.AddObject(CmsObjectIdentifiers.Data);
+
+ Stream octetOutputStream = CmsUtilities.CreateBerOctetOutputStream(
+ eiGen.GetRawOutputStream(), 0, false, _bufferSize);
+
+ IMac mac = MacUtilities.GetMac(macAlgId.ObjectID);
+ // TODO Confirm no ParametersWithRandom needed
+ mac.Init(cipherParameters);
+ Stream mOut = new TeeOutputStream(octetOutputStream, new MacOutputStream(mac));
+
+ return new CmsAuthenticatedDataOutputStream(mOut, mac, cGen, authGen, eiGen);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("couldn't create cipher.", e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key invalid in message.", e);
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("exception decoding algorithm parameters.", e);
+ }
+ }
+
+ /**
+ * generate an enveloped object that contains an CMS Enveloped Data object
+ */
+ public Stream Open(
+ Stream outStr,
+ string encryptionOid)
+ {
+ CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid);
+
+ keyGen.Init(new KeyGenerationParameters(rand, keyGen.DefaultStrength));
+
+ return Open(outStr, encryptionOid, keyGen);
+ }
+
+ /**
+ * generate an enveloped object that contains an CMS Enveloped Data object
+ */
+ public Stream Open(
+ Stream outStr,
+ string encryptionOid,
+ int keySize)
+ {
+ CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid);
+
+ keyGen.Init(new KeyGenerationParameters(rand, keySize));
+
+ return Open(outStr, encryptionOid, keyGen);
+ }
+
+ private class CmsAuthenticatedDataOutputStream
+ : BaseOutputStream
+ {
+ private readonly Stream macStream;
+ private readonly IMac mac;
+ private readonly BerSequenceGenerator cGen;
+ private readonly BerSequenceGenerator authGen;
+ private readonly BerSequenceGenerator eiGen;
+
+ public CmsAuthenticatedDataOutputStream(
+ Stream macStream,
+ IMac mac,
+ BerSequenceGenerator cGen,
+ BerSequenceGenerator authGen,
+ BerSequenceGenerator eiGen)
+ {
+ this.macStream = macStream;
+ this.mac = mac;
+ this.cGen = cGen;
+ this.authGen = authGen;
+ this.eiGen = eiGen;
+ }
+
+ public override void WriteByte(
+ byte b)
+ {
+ macStream.WriteByte(b);
+ }
+
+ public override void Write(
+ byte[] bytes,
+ int off,
+ int len)
+ {
+ macStream.Write(bytes, off, len);
+ }
+
+ public override void Close()
+ {
+ macStream.Close();
+
+ // TODO Parent context(s) should really be be closed explicitly
+
+ eiGen.Close();
+
+ // [TODO] auth attributes go here
+ byte[] macOctets = MacUtilities.DoFinal(mac);
+ authGen.AddObject(new DerOctetString(macOctets));
+ // [TODO] unauth attributes go here
+
+ authGen.Close();
+ cGen.Close();
+ }
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSAuthenticatedGenerator.cs b/crypto/src/cms/CMSAuthenticatedGenerator.cs
new file mode 100644
index 000000000..8824d1913
--- /dev/null
+++ b/crypto/src/cms/CMSAuthenticatedGenerator.cs
@@ -0,0 +1,35 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+ public class CmsAuthenticatedGenerator
+ : CmsEnvelopedGenerator
+ {
+ /**
+ * base constructor
+ */
+ public CmsAuthenticatedGenerator()
+ {
+ }
+
+ /**
+ * constructor allowing specific source of randomness
+ *
+ * @param rand instance of SecureRandom to use
+ */
+ public CmsAuthenticatedGenerator(
+ SecureRandom rand)
+ : base(rand)
+ {
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSCompressedData.cs b/crypto/src/cms/CMSCompressedData.cs
new file mode 100644
index 000000000..2d0107e88
--- /dev/null
+++ b/crypto/src/cms/CMSCompressedData.cs
@@ -0,0 +1,107 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Utilities.Zlib;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * containing class for an CMS Compressed Data object
+ */
+ public class CmsCompressedData
+ {
+ internal ContentInfo contentInfo;
+
+ public CmsCompressedData(
+ byte[] compressedData)
+ : this(CmsUtilities.ReadContentInfo(compressedData))
+ {
+ }
+
+ public CmsCompressedData(
+ Stream compressedDataStream)
+ : this(CmsUtilities.ReadContentInfo(compressedDataStream))
+ {
+ }
+
+ public CmsCompressedData(
+ ContentInfo contentInfo)
+ {
+ this.contentInfo = contentInfo;
+ }
+
+ /**
+ * Return the uncompressed content.
+ *
+ * @return the uncompressed content
+ * @throws CmsException if there is an exception uncompressing the data.
+ */
+ public byte[] GetContent()
+ {
+ CompressedData comData = CompressedData.GetInstance(contentInfo.Content);
+ ContentInfo content = comData.EncapContentInfo;
+
+ Asn1OctetString bytes = (Asn1OctetString) content.Content;
+ ZInputStream zIn = new ZInputStream(bytes.GetOctetStream());
+
+ try
+ {
+ return CmsUtilities.StreamToByteArray(zIn);
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("exception reading compressed stream.", e);
+ }
+ finally
+ {
+ zIn.Close();
+ }
+ }
+
+ /**
+ * Return the uncompressed content, throwing an exception if the data size
+ * is greater than the passed in limit. If the content is exceeded getCause()
+ * on the CMSException will contain a StreamOverflowException
+ *
+ * @param limit maximum number of bytes to read
+ * @return the content read
+ * @throws CMSException if there is an exception uncompressing the data.
+ */
+ public byte[] GetContent(int limit)
+ {
+ CompressedData comData = CompressedData.GetInstance(contentInfo.Content);
+ ContentInfo content = comData.EncapContentInfo;
+
+ Asn1OctetString bytes = (Asn1OctetString)content.Content;
+
+ ZInputStream zIn = new ZInputStream(new MemoryStream(bytes.GetOctets(), false));
+
+ try
+ {
+ return CmsUtilities.StreamToByteArray(zIn, limit);
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("exception reading compressed stream.", e);
+ }
+ }
+
+ /**
+ * return the ContentInfo
+ */
+ public ContentInfo ContentInfo
+ {
+ get { return contentInfo; }
+ }
+
+ /**
+ * return the ASN.1 encoded representation of this object.
+ */
+ public byte[] GetEncoded()
+ {
+ return contentInfo.GetEncoded();
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSCompressedDataGenerator.cs b/crypto/src/cms/CMSCompressedDataGenerator.cs
new file mode 100644
index 000000000..00e2a3df4
--- /dev/null
+++ b/crypto/src/cms/CMSCompressedDataGenerator.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities.Zlib;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * General class for generating a compressed CMS message.
+ *
+ * A simple example of usage.
+ *
+ *
+ * CMSCompressedDataGenerator fact = new CMSCompressedDataGenerator();
+ * CMSCompressedData data = fact.Generate(content, algorithm);
+ *
+ *
+ */
+ public class CmsCompressedDataGenerator
+ {
+ public const string ZLib = "1.2.840.113549.1.9.16.3.8";
+
+ public CmsCompressedDataGenerator()
+ {
+ }
+
+ /**
+ * Generate an object that contains an CMS Compressed Data
+ */
+ public CmsCompressedData Generate(
+ CmsProcessable content,
+ string compressionOid)
+ {
+ AlgorithmIdentifier comAlgId;
+ Asn1OctetString comOcts;
+
+ try
+ {
+ MemoryStream bOut = new MemoryStream();
+ ZOutputStream zOut = new ZOutputStream(bOut, JZlib.Z_DEFAULT_COMPRESSION);
+
+ content.Write(zOut);
+
+ zOut.Close();
+
+ comAlgId = new AlgorithmIdentifier(new DerObjectIdentifier(compressionOid));
+ comOcts = new BerOctetString(bOut.ToArray());
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("exception encoding data.", e);
+ }
+
+ ContentInfo comContent = new ContentInfo(CmsObjectIdentifiers.Data, comOcts);
+ ContentInfo contentInfo = new ContentInfo(
+ CmsObjectIdentifiers.CompressedData,
+ new CompressedData(comAlgId, comContent));
+
+ return new CmsCompressedData(contentInfo);
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSCompressedDataParser.cs b/crypto/src/cms/CMSCompressedDataParser.cs
new file mode 100644
index 000000000..93dfa1286
--- /dev/null
+++ b/crypto/src/cms/CMSCompressedDataParser.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Utilities.Zlib;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * Class for reading a CMS Compressed Data stream.
+ *
+ * CMSCompressedDataParser cp = new CMSCompressedDataParser(inputStream);
+ *
+ * process(cp.GetContent().GetContentStream());
+ *
+ * Note: this class does not introduce buffering - if you are processing large files you should create
+ * the parser with:
+ *
+ * CMSCompressedDataParser ep = new CMSCompressedDataParser(new BufferedInputStream(inputStream, bufSize));
+ *
+ * where bufSize is a suitably large buffer size.
+ */
+ public class CmsCompressedDataParser
+ : CmsContentInfoParser
+ {
+ public CmsCompressedDataParser(
+ byte[] compressedData)
+ : this(new MemoryStream(compressedData, false))
+ {
+ }
+
+ public CmsCompressedDataParser(
+ Stream compressedData)
+ : base(compressedData)
+ {
+ }
+
+ public CmsTypedStream GetContent()
+ {
+ try
+ {
+ CompressedDataParser comData = new CompressedDataParser((Asn1SequenceParser)this.contentInfo.GetContent(Asn1Tags.Sequence));
+ ContentInfoParser content = comData.GetEncapContentInfo();
+
+ Asn1OctetStringParser bytes = (Asn1OctetStringParser)content.GetContent(Asn1Tags.OctetString);
+
+ return new CmsTypedStream(content.ContentType.ToString(), new ZInputStream(bytes.GetOctetStream()));
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("IOException reading compressed content.", e);
+ }
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSCompressedDataStreamGenerator.cs b/crypto/src/cms/CMSCompressedDataStreamGenerator.cs
new file mode 100644
index 000000000..db0d19845
--- /dev/null
+++ b/crypto/src/cms/CMSCompressedDataStreamGenerator.cs
@@ -0,0 +1,140 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities.IO;
+using Org.BouncyCastle.Utilities.Zlib;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * General class for generating a compressed CMS message stream.
+ *
+ * A simple example of usage.
+ *
+ *
+ * CMSCompressedDataStreamGenerator gen = new CMSCompressedDataStreamGenerator();
+ *
+ * Stream cOut = gen.Open(outputStream, CMSCompressedDataStreamGenerator.ZLIB);
+ *
+ * cOut.Write(data);
+ *
+ * cOut.Close();
+ *
+ */
+ public class CmsCompressedDataStreamGenerator
+ {
+ public const string ZLib = "1.2.840.113549.1.9.16.3.8";
+
+ private int _bufferSize;
+
+ /**
+ * base constructor
+ */
+ public CmsCompressedDataStreamGenerator()
+ {
+ }
+
+ /**
+ * Set the underlying string size for encapsulated data
+ *
+ * @param bufferSize length of octet strings to buffer the data.
+ */
+ public void SetBufferSize(
+ int bufferSize)
+ {
+ _bufferSize = bufferSize;
+ }
+
+ public Stream Open(
+ Stream outStream,
+ string compressionOID)
+ {
+ return Open(outStream, CmsObjectIdentifiers.Data.Id, compressionOID);
+ }
+
+ public Stream Open(
+ Stream outStream,
+ string contentOID,
+ string compressionOID)
+ {
+ BerSequenceGenerator sGen = new BerSequenceGenerator(outStream);
+
+ sGen.AddObject(CmsObjectIdentifiers.CompressedData);
+
+ //
+ // Compressed Data
+ //
+ BerSequenceGenerator cGen = new BerSequenceGenerator(
+ sGen.GetRawOutputStream(), 0, true);
+
+ // CMSVersion
+ cGen.AddObject(new DerInteger(0));
+
+ // CompressionAlgorithmIdentifier
+ cGen.AddObject(new AlgorithmIdentifier(new DerObjectIdentifier(ZLib)));
+
+ //
+ // Encapsulated ContentInfo
+ //
+ BerSequenceGenerator eiGen = new BerSequenceGenerator(cGen.GetRawOutputStream());
+
+ eiGen.AddObject(new DerObjectIdentifier(contentOID));
+
+ Stream octetStream = CmsUtilities.CreateBerOctetOutputStream(
+ eiGen.GetRawOutputStream(), 0, true, _bufferSize);
+
+ return new CmsCompressedOutputStream(
+ new ZOutputStream(octetStream, JZlib.Z_DEFAULT_COMPRESSION), sGen, cGen, eiGen);
+ }
+
+ private class CmsCompressedOutputStream
+ : BaseOutputStream
+ {
+ private ZOutputStream _out;
+ private BerSequenceGenerator _sGen;
+ private BerSequenceGenerator _cGen;
+ private BerSequenceGenerator _eiGen;
+
+ internal CmsCompressedOutputStream(
+ ZOutputStream outStream,
+ BerSequenceGenerator sGen,
+ BerSequenceGenerator cGen,
+ BerSequenceGenerator eiGen)
+ {
+ _out = outStream;
+ _sGen = sGen;
+ _cGen = cGen;
+ _eiGen = eiGen;
+ }
+
+ public override void WriteByte(
+ byte b)
+ {
+ _out.WriteByte(b);
+ }
+
+ public override void Write(
+ byte[] bytes,
+ int off,
+ int len)
+ {
+ _out.Write(bytes, off, len);
+ }
+
+ public override void Close()
+ {
+ _out.Close();
+
+ // TODO Parent context(s) should really be be closed explicitly
+
+ _eiGen.Close();
+ _cGen.Close();
+ _sGen.Close();
+ base.Close();
+ }
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSContentInfoParser.cs b/crypto/src/cms/CMSContentInfoParser.cs
new file mode 100644
index 000000000..fde06cf4c
--- /dev/null
+++ b/crypto/src/cms/CMSContentInfoParser.cs
@@ -0,0 +1,47 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+
+namespace Org.BouncyCastle.Cms
+{
+ public class CmsContentInfoParser
+ {
+ protected ContentInfoParser contentInfo;
+ protected Stream data;
+
+ protected CmsContentInfoParser(
+ Stream data)
+ {
+ if (data == null)
+ throw new ArgumentNullException("data");
+
+ this.data = data;
+
+ try
+ {
+ Asn1StreamParser inStream = new Asn1StreamParser(data);
+
+ this.contentInfo = new ContentInfoParser((Asn1SequenceParser)inStream.ReadObject());
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("IOException reading content.", e);
+ }
+ catch (InvalidCastException e)
+ {
+ throw new CmsException("Unexpected object reading content.", e);
+ }
+ }
+
+ /**
+ * Close the underlying data stream.
+ * @throws IOException if the close fails.
+ */
+ public void Close()
+ {
+ this.data.Close();
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSEnvelopedData.cs b/crypto/src/cms/CMSEnvelopedData.cs
new file mode 100644
index 000000000..0731c307e
--- /dev/null
+++ b/crypto/src/cms/CMSEnvelopedData.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * containing class for an CMS Enveloped Data object
+ */
+ public class CmsEnvelopedData
+ {
+ internal RecipientInformationStore recipientInfoStore;
+ internal ContentInfo contentInfo;
+
+ private AlgorithmIdentifier encAlg;
+ private Asn1Set unprotectedAttributes;
+
+ public CmsEnvelopedData(
+ byte[] envelopedData)
+ : this(CmsUtilities.ReadContentInfo(envelopedData))
+ {
+ }
+
+ public CmsEnvelopedData(
+ Stream envelopedData)
+ : this(CmsUtilities.ReadContentInfo(envelopedData))
+ {
+ }
+
+ public CmsEnvelopedData(
+ ContentInfo contentInfo)
+ {
+ this.contentInfo = contentInfo;
+
+ EnvelopedData envData = EnvelopedData.GetInstance(contentInfo.Content);
+
+ //
+ // read the recipients
+ //
+ Asn1Set recipientInfos = envData.RecipientInfos;
+
+ //
+ // read the encrypted content info
+ //
+ EncryptedContentInfo encInfo = envData.EncryptedContentInfo;
+ this.encAlg = encInfo.ContentEncryptionAlgorithm;
+ CmsReadable readable = new CmsProcessableByteArray(encInfo.EncryptedContent.GetOctets());
+ CmsSecureReadable secureReadable = new CmsEnvelopedHelper.CmsEnvelopedSecureReadable(
+ this.encAlg, readable);
+
+ //
+ // build the RecipientInformationStore
+ //
+ this.recipientInfoStore = CmsEnvelopedHelper.BuildRecipientInformationStore(
+ recipientInfos, secureReadable);
+
+ this.unprotectedAttributes = envData.UnprotectedAttrs;
+ }
+
+ public AlgorithmIdentifier EncryptionAlgorithmID
+ {
+ get { return encAlg; }
+ }
+
+ /**
+ * return the object identifier for the content encryption algorithm.
+ */
+ public string EncryptionAlgOid
+ {
+ get { return encAlg.ObjectID.Id; }
+ }
+
+ /**
+ * return a store of the intended recipients for this message
+ */
+ public RecipientInformationStore GetRecipientInfos()
+ {
+ return recipientInfoStore;
+ }
+
+ /**
+ * return the ContentInfo
+ */
+ public ContentInfo ContentInfo
+ {
+ get { return contentInfo; }
+ }
+
+ /**
+ * return a table of the unprotected attributes indexed by
+ * the OID of the attribute.
+ */
+ public Asn1.Cms.AttributeTable GetUnprotectedAttributes()
+ {
+ if (unprotectedAttributes == null)
+ return null;
+
+ return new Asn1.Cms.AttributeTable(unprotectedAttributes);
+ }
+
+ /**
+ * return the ASN.1 encoded representation of this object.
+ */
+ public byte[] GetEncoded()
+ {
+ return contentInfo.GetEncoded();
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSEnvelopedDataGenerator.cs b/crypto/src/cms/CMSEnvelopedDataGenerator.cs
new file mode 100644
index 000000000..3b861cde5
--- /dev/null
+++ b/crypto/src/cms/CMSEnvelopedDataGenerator.cs
@@ -0,0 +1,178 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Oiw;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+ ///
+ /// General class for generating a CMS enveloped-data message.
+ ///
+ /// A simple example of usage.
+ ///
+ ///
+ /// CmsEnvelopedDataGenerator fact = new CmsEnvelopedDataGenerator();
+ ///
+ /// fact.AddKeyTransRecipient(cert);
+ ///
+ /// CmsEnvelopedData data = fact.Generate(content, algorithm);
+ ///
+ ///
+ public class CmsEnvelopedDataGenerator
+ : CmsEnvelopedGenerator
+ {
+ public CmsEnvelopedDataGenerator()
+ {
+ }
+
+ /// Constructor allowing specific source of randomness
+ /// Instance of SecureRandom to use.
+ public CmsEnvelopedDataGenerator(
+ SecureRandom rand)
+ : base(rand)
+ {
+ }
+
+ ///
+ /// Generate an enveloped object that contains a CMS Enveloped Data
+ /// object using the passed in key generator.
+ ///
+ private CmsEnvelopedData Generate(
+ CmsProcessable content,
+ string encryptionOid,
+ CipherKeyGenerator keyGen)
+ {
+ AlgorithmIdentifier encAlgId = null;
+ KeyParameter encKey;
+ Asn1OctetString encContent;
+
+ try
+ {
+ byte[] encKeyBytes = keyGen.GenerateKey();
+ encKey = ParameterUtilities.CreateKeyParameter(encryptionOid, encKeyBytes);
+
+ Asn1Encodable asn1Params = GenerateAsn1Parameters(encryptionOid, encKeyBytes);
+
+ ICipherParameters cipherParameters;
+ encAlgId = GetAlgorithmIdentifier(
+ encryptionOid, encKey, asn1Params, out cipherParameters);
+
+ IBufferedCipher cipher = CipherUtilities.GetCipher(encryptionOid);
+ cipher.Init(true, new ParametersWithRandom(cipherParameters, rand));
+
+ MemoryStream bOut = new MemoryStream();
+ CipherStream cOut = new CipherStream(bOut, null, cipher);
+
+ content.Write(cOut);
+
+ cOut.Close();
+
+ encContent = new BerOctetString(bOut.ToArray());
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("couldn't create cipher.", e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key invalid in message.", e);
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("exception decoding algorithm parameters.", e);
+ }
+
+
+ Asn1EncodableVector recipientInfos = new Asn1EncodableVector();
+
+ foreach (RecipientInfoGenerator rig in recipientInfoGenerators)
+ {
+ try
+ {
+ recipientInfos.Add(rig.Generate(encKey, rand));
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key inappropriate for algorithm.", e);
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new CmsException("error making encrypted content.", e);
+ }
+ }
+
+ EncryptedContentInfo eci = new EncryptedContentInfo(
+ CmsObjectIdentifiers.Data,
+ encAlgId,
+ encContent);
+
+ Asn1Set unprotectedAttrSet = null;
+ if (unprotectedAttributeGenerator != null)
+ {
+ Asn1.Cms.AttributeTable attrTable = unprotectedAttributeGenerator.GetAttributes(Platform.CreateHashtable());
+
+ unprotectedAttrSet = new BerSet(attrTable.ToAsn1EncodableVector());
+ }
+
+ ContentInfo contentInfo = new ContentInfo(
+ CmsObjectIdentifiers.EnvelopedData,
+ new EnvelopedData(null, new DerSet(recipientInfos), eci, unprotectedAttrSet));
+
+ return new CmsEnvelopedData(contentInfo);
+ }
+
+ /// Generate an enveloped object that contains an CMS Enveloped Data object.
+ public CmsEnvelopedData Generate(
+ CmsProcessable content,
+ string encryptionOid)
+ {
+ try
+ {
+ CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid);
+
+ keyGen.Init(new KeyGenerationParameters(rand, keyGen.DefaultStrength));
+
+ return Generate(content, encryptionOid, keyGen);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("can't find key generation algorithm.", e);
+ }
+ }
+
+ /// Generate an enveloped object that contains an CMS Enveloped Data object.
+ public CmsEnvelopedData Generate(
+ CmsProcessable content,
+ string encryptionOid,
+ int keySize)
+ {
+ try
+ {
+ CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid);
+
+ keyGen.Init(new KeyGenerationParameters(rand, keySize));
+
+ return Generate(content, encryptionOid, keyGen);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("can't find key generation algorithm.", e);
+ }
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSEnvelopedDataParser.cs b/crypto/src/cms/CMSEnvelopedDataParser.cs
new file mode 100644
index 000000000..01a949d47
--- /dev/null
+++ b/crypto/src/cms/CMSEnvelopedDataParser.cs
@@ -0,0 +1,161 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * Parsing class for an CMS Enveloped Data object from an input stream.
+ *
+ * Note: that because we are in a streaming mode only one recipient can be tried and it is important
+ * that the methods on the parser are called in the appropriate order.
+ *
+ *
+ * Example of use - assuming the first recipient matches the private key we have.
+ *
+ * CmsEnvelopedDataParser ep = new CmsEnvelopedDataParser(inputStream);
+ *
+ * RecipientInformationStore recipients = ep.GetRecipientInfos();
+ *
+ * Collection c = recipients.getRecipients();
+ * Iterator it = c.iterator();
+ *
+ * if (it.hasNext())
+ * {
+ * RecipientInformation recipient = (RecipientInformation)it.next();
+ *
+ * CMSTypedStream recData = recipient.getContentStream(privateKey);
+ *
+ * processDataStream(recData.getContentStream());
+ * }
+ *
+ * Note: this class does not introduce buffering - if you are processing large files you should create
+ * the parser with:
+ *
+ * CmsEnvelopedDataParser ep = new CmsEnvelopedDataParser(new BufferedInputStream(inputStream, bufSize));
+ *
+ * where bufSize is a suitably large buffer size.
+ *
+ */
+ public class CmsEnvelopedDataParser
+ : CmsContentInfoParser
+ {
+ internal RecipientInformationStore recipientInfoStore;
+ internal EnvelopedDataParser envelopedData;
+
+ private AlgorithmIdentifier _encAlg;
+ private Asn1.Cms.AttributeTable _unprotectedAttributes;
+ private bool _attrNotRead;
+
+ public CmsEnvelopedDataParser(
+ byte[] envelopedData)
+ : this(new MemoryStream(envelopedData, false))
+ {
+ }
+
+ public CmsEnvelopedDataParser(
+ Stream envelopedData)
+ : base(envelopedData)
+ {
+ this._attrNotRead = true;
+ this.envelopedData = new EnvelopedDataParser(
+ (Asn1SequenceParser)this.contentInfo.GetContent(Asn1Tags.Sequence));
+
+ // TODO Validate version?
+ //DerInteger version = this.envelopedData.Version;
+
+ //
+ // read the recipients
+ //
+ Asn1Set recipientInfos = Asn1Set.GetInstance(this.envelopedData.GetRecipientInfos().ToAsn1Object());
+
+ //
+ // read the encrypted content info
+ //
+ EncryptedContentInfoParser encInfo = this.envelopedData.GetEncryptedContentInfo();
+ this._encAlg = encInfo.ContentEncryptionAlgorithm;
+ CmsReadable readable = new CmsProcessableInputStream(
+ ((Asn1OctetStringParser)encInfo.GetEncryptedContent(Asn1Tags.OctetString)).GetOctetStream());
+ CmsSecureReadable secureReadable = new CmsEnvelopedHelper.CmsEnvelopedSecureReadable(
+ this._encAlg, readable);
+
+ //
+ // build the RecipientInformationStore
+ //
+ this.recipientInfoStore = CmsEnvelopedHelper.BuildRecipientInformationStore(
+ recipientInfos, secureReadable);
+ }
+
+ public AlgorithmIdentifier EncryptionAlgorithmID
+ {
+ get { return _encAlg; }
+ }
+
+ /**
+ * return the object identifier for the content encryption algorithm.
+ */
+ public string EncryptionAlgOid
+ {
+ get { return _encAlg.ObjectID.Id; }
+ }
+
+ /**
+ * return the ASN.1 encoded encryption algorithm parameters, or null if
+ * there aren't any.
+ */
+ public Asn1Object EncryptionAlgParams
+ {
+ get
+ {
+ Asn1Encodable ae = _encAlg.Parameters;
+
+ return ae == null ? null : ae.ToAsn1Object();
+ }
+ }
+
+ /**
+ * return a store of the intended recipients for this message
+ */
+ public RecipientInformationStore GetRecipientInfos()
+ {
+ return this.recipientInfoStore;
+ }
+
+ /**
+ * return a table of the unprotected attributes indexed by
+ * the OID of the attribute.
+ * @throws IOException
+ */
+ public Asn1.Cms.AttributeTable GetUnprotectedAttributes()
+ {
+ if (_unprotectedAttributes == null && _attrNotRead)
+ {
+ Asn1SetParser asn1Set = this.envelopedData.GetUnprotectedAttrs();
+
+ _attrNotRead = false;
+
+ if (asn1Set != null)
+ {
+ Asn1EncodableVector v = new Asn1EncodableVector();
+ IAsn1Convertible o;
+
+ while ((o = asn1Set.ReadObject()) != null)
+ {
+ Asn1SequenceParser seq = (Asn1SequenceParser)o;
+
+ v.Add(seq.ToAsn1Object());
+ }
+
+ _unprotectedAttributes = new Asn1.Cms.AttributeTable(new DerSet(v));
+ }
+ }
+
+ return _unprotectedAttributes;
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs b/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
new file mode 100644
index 000000000..a63ea7b7f
--- /dev/null
+++ b/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * General class for generating a CMS enveloped-data message stream.
+ *
+ * A simple example of usage.
+ *
+ * CmsEnvelopedDataStreamGenerator edGen = new CmsEnvelopedDataStreamGenerator();
+ *
+ * edGen.AddKeyTransRecipient(cert);
+ *
+ * MemoryStream bOut = new MemoryStream();
+ *
+ * Stream out = edGen.Open(
+ * bOut, CMSEnvelopedDataGenerator.AES128_CBC);*
+ * out.Write(data);
+ *
+ * out.Close();
+ *
+ *
+ */
+ public class CmsEnvelopedDataStreamGenerator
+ : CmsEnvelopedGenerator
+ {
+ private object _originatorInfo = null;
+ private object _unprotectedAttributes = null;
+ private int _bufferSize;
+ private bool _berEncodeRecipientSet;
+
+ public CmsEnvelopedDataStreamGenerator()
+ {
+ }
+
+ /// Constructor allowing specific source of randomness
+ /// Instance of SecureRandom to use.
+ public CmsEnvelopedDataStreamGenerator(
+ SecureRandom rand)
+ : base(rand)
+ {
+ }
+
+ /// Set the underlying string size for encapsulated data.
+ /// Length of octet strings to buffer the data.
+ public void SetBufferSize(
+ int bufferSize)
+ {
+ _bufferSize = bufferSize;
+ }
+
+ /// Use a BER Set to store the recipient information.
+ public void SetBerEncodeRecipients(
+ bool berEncodeRecipientSet)
+ {
+ _berEncodeRecipientSet = berEncodeRecipientSet;
+ }
+
+ private DerInteger Version
+ {
+ get
+ {
+ int version = (_originatorInfo != null || _unprotectedAttributes != null)
+ ? 2
+ : 0;
+
+ return new DerInteger(version);
+ }
+ }
+
+ ///
+ /// Generate an enveloped object that contains an CMS Enveloped Data
+ /// object using the passed in key generator.
+ ///
+ private Stream Open(
+ Stream outStream,
+ string encryptionOid,
+ CipherKeyGenerator keyGen)
+ {
+ byte[] encKeyBytes = keyGen.GenerateKey();
+ KeyParameter encKey = ParameterUtilities.CreateKeyParameter(encryptionOid, encKeyBytes);
+
+ Asn1Encodable asn1Params = GenerateAsn1Parameters(encryptionOid, encKeyBytes);
+
+ ICipherParameters cipherParameters;
+ AlgorithmIdentifier encAlgID = GetAlgorithmIdentifier(
+ encryptionOid, encKey, asn1Params, out cipherParameters);
+
+ Asn1EncodableVector recipientInfos = new Asn1EncodableVector();
+
+ foreach (RecipientInfoGenerator rig in recipientInfoGenerators)
+ {
+ try
+ {
+ recipientInfos.Add(rig.Generate(encKey, rand));
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key inappropriate for algorithm.", e);
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new CmsException("error making encrypted content.", e);
+ }
+ }
+
+ return Open(outStream, encAlgID, cipherParameters, recipientInfos);
+ }
+
+ private Stream Open(
+ Stream outStream,
+ AlgorithmIdentifier encAlgID,
+ ICipherParameters cipherParameters,
+ Asn1EncodableVector recipientInfos)
+ {
+ try
+ {
+ //
+ // ContentInfo
+ //
+ BerSequenceGenerator cGen = new BerSequenceGenerator(outStream);
+
+ cGen.AddObject(CmsObjectIdentifiers.EnvelopedData);
+
+ //
+ // Encrypted Data
+ //
+ BerSequenceGenerator envGen = new BerSequenceGenerator(
+ cGen.GetRawOutputStream(), 0, true);
+
+ envGen.AddObject(this.Version);
+
+ Stream envRaw = envGen.GetRawOutputStream();
+ Asn1Generator recipGen = _berEncodeRecipientSet
+ ? (Asn1Generator) new BerSetGenerator(envRaw)
+ : new DerSetGenerator(envRaw);
+
+ foreach (Asn1Encodable ae in recipientInfos)
+ {
+ recipGen.AddObject(ae);
+ }
+
+ recipGen.Close();
+
+ BerSequenceGenerator eiGen = new BerSequenceGenerator(envRaw);
+ eiGen.AddObject(CmsObjectIdentifiers.Data);
+ eiGen.AddObject(encAlgID);
+
+ Stream octetOutputStream = CmsUtilities.CreateBerOctetOutputStream(
+ eiGen.GetRawOutputStream(), 0, false, _bufferSize);
+
+ IBufferedCipher cipher = CipherUtilities.GetCipher(encAlgID.ObjectID);
+ cipher.Init(true, new ParametersWithRandom(cipherParameters, rand));
+ CipherStream cOut = new CipherStream(octetOutputStream, null, cipher);
+
+ return new CmsEnvelopedDataOutputStream(this, cOut, cGen, envGen, eiGen);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("couldn't create cipher.", e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key invalid in message.", e);
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("exception decoding algorithm parameters.", e);
+ }
+ }
+
+ /**
+ * generate an enveloped object that contains an CMS Enveloped Data object
+ * @throws IOException
+ */
+ public Stream Open(
+ Stream outStream,
+ string encryptionOid)
+ {
+ CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid);
+
+ keyGen.Init(new KeyGenerationParameters(rand, keyGen.DefaultStrength));
+
+ return Open(outStream, encryptionOid, keyGen);
+ }
+
+ /**
+ * generate an enveloped object that contains an CMS Enveloped Data object
+ * @throws IOException
+ */
+ public Stream Open(
+ Stream outStream,
+ string encryptionOid,
+ int keySize)
+ {
+ CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid);
+
+ keyGen.Init(new KeyGenerationParameters(rand, keySize));
+
+ return Open(outStream, encryptionOid, keyGen);
+ }
+
+ private class CmsEnvelopedDataOutputStream
+ : BaseOutputStream
+ {
+ private readonly CmsEnvelopedGenerator _outer;
+
+ private readonly CipherStream _out;
+ private readonly BerSequenceGenerator _cGen;
+ private readonly BerSequenceGenerator _envGen;
+ private readonly BerSequenceGenerator _eiGen;
+
+ public CmsEnvelopedDataOutputStream(
+ CmsEnvelopedGenerator outer,
+ CipherStream outStream,
+ BerSequenceGenerator cGen,
+ BerSequenceGenerator envGen,
+ BerSequenceGenerator eiGen)
+ {
+ _outer = outer;
+ _out = outStream;
+ _cGen = cGen;
+ _envGen = envGen;
+ _eiGen = eiGen;
+ }
+
+ public override void WriteByte(
+ byte b)
+ {
+ _out.WriteByte(b);
+ }
+
+ public override void Write(
+ byte[] bytes,
+ int off,
+ int len)
+ {
+ _out.Write(bytes, off, len);
+ }
+
+ public override void Close()
+ {
+ _out.Close();
+
+ // TODO Parent context(s) should really be be closed explicitly
+
+ _eiGen.Close();
+
+ if (_outer.unprotectedAttributeGenerator != null)
+ {
+ Asn1.Cms.AttributeTable attrTable = _outer.unprotectedAttributeGenerator.GetAttributes(Platform.CreateHashtable());
+
+ Asn1Set unprotectedAttrs = new BerSet(attrTable.ToAsn1EncodableVector());
+
+ _envGen.AddObject(new DerTaggedObject(false, 1, unprotectedAttrs));
+ }
+
+ _envGen.Close();
+ _cGen.Close();
+ base.Close();
+ }
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSEnvelopedGenerator.cs b/crypto/src/cms/CMSEnvelopedGenerator.cs
new file mode 100644
index 000000000..f92ae3824
--- /dev/null
+++ b/crypto/src/cms/CMSEnvelopedGenerator.cs
@@ -0,0 +1,331 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.Kisa;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Ntt;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * General class for generating a CMS enveloped-data message.
+ *
+ * A simple example of usage.
+ *
+ *
+ * CMSEnvelopedDataGenerator fact = new CMSEnvelopedDataGenerator();
+ *
+ * fact.addKeyTransRecipient(cert);
+ *
+ * CMSEnvelopedData data = fact.generate(content, algorithm, "BC");
+ *
+ */
+ public class CmsEnvelopedGenerator
+ {
+ // Note: These tables are complementary: If rc2Table[i]==j, then rc2Ekb[j]==i
+ internal static readonly short[] rc2Table =
+ {
+ 0xbd, 0x56, 0xea, 0xf2, 0xa2, 0xf1, 0xac, 0x2a, 0xb0, 0x93, 0xd1, 0x9c, 0x1b, 0x33, 0xfd, 0xd0,
+ 0x30, 0x04, 0xb6, 0xdc, 0x7d, 0xdf, 0x32, 0x4b, 0xf7, 0xcb, 0x45, 0x9b, 0x31, 0xbb, 0x21, 0x5a,
+ 0x41, 0x9f, 0xe1, 0xd9, 0x4a, 0x4d, 0x9e, 0xda, 0xa0, 0x68, 0x2c, 0xc3, 0x27, 0x5f, 0x80, 0x36,
+ 0x3e, 0xee, 0xfb, 0x95, 0x1a, 0xfe, 0xce, 0xa8, 0x34, 0xa9, 0x13, 0xf0, 0xa6, 0x3f, 0xd8, 0x0c,
+ 0x78, 0x24, 0xaf, 0x23, 0x52, 0xc1, 0x67, 0x17, 0xf5, 0x66, 0x90, 0xe7, 0xe8, 0x07, 0xb8, 0x60,
+ 0x48, 0xe6, 0x1e, 0x53, 0xf3, 0x92, 0xa4, 0x72, 0x8c, 0x08, 0x15, 0x6e, 0x86, 0x00, 0x84, 0xfa,
+ 0xf4, 0x7f, 0x8a, 0x42, 0x19, 0xf6, 0xdb, 0xcd, 0x14, 0x8d, 0x50, 0x12, 0xba, 0x3c, 0x06, 0x4e,
+ 0xec, 0xb3, 0x35, 0x11, 0xa1, 0x88, 0x8e, 0x2b, 0x94, 0x99, 0xb7, 0x71, 0x74, 0xd3, 0xe4, 0xbf,
+ 0x3a, 0xde, 0x96, 0x0e, 0xbc, 0x0a, 0xed, 0x77, 0xfc, 0x37, 0x6b, 0x03, 0x79, 0x89, 0x62, 0xc6,
+ 0xd7, 0xc0, 0xd2, 0x7c, 0x6a, 0x8b, 0x22, 0xa3, 0x5b, 0x05, 0x5d, 0x02, 0x75, 0xd5, 0x61, 0xe3,
+ 0x18, 0x8f, 0x55, 0x51, 0xad, 0x1f, 0x0b, 0x5e, 0x85, 0xe5, 0xc2, 0x57, 0x63, 0xca, 0x3d, 0x6c,
+ 0xb4, 0xc5, 0xcc, 0x70, 0xb2, 0x91, 0x59, 0x0d, 0x47, 0x20, 0xc8, 0x4f, 0x58, 0xe0, 0x01, 0xe2,
+ 0x16, 0x38, 0xc4, 0x6f, 0x3b, 0x0f, 0x65, 0x46, 0xbe, 0x7e, 0x2d, 0x7b, 0x82, 0xf9, 0x40, 0xb5,
+ 0x1d, 0x73, 0xf8, 0xeb, 0x26, 0xc7, 0x87, 0x97, 0x25, 0x54, 0xb1, 0x28, 0xaa, 0x98, 0x9d, 0xa5,
+ 0x64, 0x6d, 0x7a, 0xd4, 0x10, 0x81, 0x44, 0xef, 0x49, 0xd6, 0xae, 0x2e, 0xdd, 0x76, 0x5c, 0x2f,
+ 0xa7, 0x1c, 0xc9, 0x09, 0x69, 0x9a, 0x83, 0xcf, 0x29, 0x39, 0xb9, 0xe9, 0x4c, 0xff, 0x43, 0xab
+ };
+
+// internal static readonly short[] rc2Ekb =
+// {
+// 0x5d, 0xbe, 0x9b, 0x8b, 0x11, 0x99, 0x6e, 0x4d, 0x59, 0xf3, 0x85, 0xa6, 0x3f, 0xb7, 0x83, 0xc5,
+// 0xe4, 0x73, 0x6b, 0x3a, 0x68, 0x5a, 0xc0, 0x47, 0xa0, 0x64, 0x34, 0x0c, 0xf1, 0xd0, 0x52, 0xa5,
+// 0xb9, 0x1e, 0x96, 0x43, 0x41, 0xd8, 0xd4, 0x2c, 0xdb, 0xf8, 0x07, 0x77, 0x2a, 0xca, 0xeb, 0xef,
+// 0x10, 0x1c, 0x16, 0x0d, 0x38, 0x72, 0x2f, 0x89, 0xc1, 0xf9, 0x80, 0xc4, 0x6d, 0xae, 0x30, 0x3d,
+// 0xce, 0x20, 0x63, 0xfe, 0xe6, 0x1a, 0xc7, 0xb8, 0x50, 0xe8, 0x24, 0x17, 0xfc, 0x25, 0x6f, 0xbb,
+// 0x6a, 0xa3, 0x44, 0x53, 0xd9, 0xa2, 0x01, 0xab, 0xbc, 0xb6, 0x1f, 0x98, 0xee, 0x9a, 0xa7, 0x2d,
+// 0x4f, 0x9e, 0x8e, 0xac, 0xe0, 0xc6, 0x49, 0x46, 0x29, 0xf4, 0x94, 0x8a, 0xaf, 0xe1, 0x5b, 0xc3,
+// 0xb3, 0x7b, 0x57, 0xd1, 0x7c, 0x9c, 0xed, 0x87, 0x40, 0x8c, 0xe2, 0xcb, 0x93, 0x14, 0xc9, 0x61,
+// 0x2e, 0xe5, 0xcc, 0xf6, 0x5e, 0xa8, 0x5c, 0xd6, 0x75, 0x8d, 0x62, 0x95, 0x58, 0x69, 0x76, 0xa1,
+// 0x4a, 0xb5, 0x55, 0x09, 0x78, 0x33, 0x82, 0xd7, 0xdd, 0x79, 0xf5, 0x1b, 0x0b, 0xde, 0x26, 0x21,
+// 0x28, 0x74, 0x04, 0x97, 0x56, 0xdf, 0x3c, 0xf0, 0x37, 0x39, 0xdc, 0xff, 0x06, 0xa4, 0xea, 0x42,
+// 0x08, 0xda, 0xb4, 0x71, 0xb0, 0xcf, 0x12, 0x7a, 0x4e, 0xfa, 0x6c, 0x1d, 0x84, 0x00, 0xc8, 0x7f,
+// 0x91, 0x45, 0xaa, 0x2b, 0xc2, 0xb1, 0x8f, 0xd5, 0xba, 0xf2, 0xad, 0x19, 0xb2, 0x67, 0x36, 0xf7,
+// 0x0f, 0x0a, 0x92, 0x7d, 0xe3, 0x9d, 0xe9, 0x90, 0x3e, 0x23, 0x27, 0x66, 0x13, 0xec, 0x81, 0x15,
+// 0xbd, 0x22, 0xbf, 0x9f, 0x7e, 0xa9, 0x51, 0x4b, 0x4c, 0xfb, 0x02, 0xd3, 0x70, 0x86, 0x31, 0xe7,
+// 0x3b, 0x05, 0x03, 0x54, 0x60, 0x48, 0x65, 0x18, 0xd2, 0xcd, 0x5f, 0x32, 0x88, 0x0e, 0x35, 0xfd
+// };
+
+
+ // TODO Create named constants for all of these
+ public static readonly string DesEde3Cbc = PkcsObjectIdentifiers.DesEde3Cbc.Id;
+ public static readonly string RC2Cbc = PkcsObjectIdentifiers.RC2Cbc.Id;
+ public const string IdeaCbc = "1.3.6.1.4.1.188.7.1.1.2";
+ public const string Cast5Cbc = "1.2.840.113533.7.66.10";
+ public static readonly string Aes128Cbc = NistObjectIdentifiers.IdAes128Cbc.Id;
+ public static readonly string Aes192Cbc = NistObjectIdentifiers.IdAes192Cbc.Id;
+ public static readonly string Aes256Cbc = NistObjectIdentifiers.IdAes256Cbc.Id;
+ public static readonly string Camellia128Cbc = NttObjectIdentifiers.IdCamellia128Cbc.Id;
+ public static readonly string Camellia192Cbc = NttObjectIdentifiers.IdCamellia192Cbc.Id;
+ public static readonly string Camellia256Cbc = NttObjectIdentifiers.IdCamellia256Cbc.Id;
+ public static readonly string SeedCbc = KisaObjectIdentifiers.IdSeedCbc.Id;
+
+ public static readonly string DesEde3Wrap = PkcsObjectIdentifiers.IdAlgCms3DesWrap.Id;
+ public static readonly string Aes128Wrap = NistObjectIdentifiers.IdAes128Wrap.Id;
+ public static readonly string Aes192Wrap = NistObjectIdentifiers.IdAes192Wrap.Id;
+ public static readonly string Aes256Wrap = NistObjectIdentifiers.IdAes256Wrap.Id;
+ public static readonly string Camellia128Wrap = NttObjectIdentifiers.IdCamellia128Wrap.Id;
+ public static readonly string Camellia192Wrap = NttObjectIdentifiers.IdCamellia192Wrap.Id;
+ public static readonly string Camellia256Wrap = NttObjectIdentifiers.IdCamellia256Wrap.Id;
+ public static readonly string SeedWrap = KisaObjectIdentifiers.IdNpkiAppCmsSeedWrap.Id;
+
+ public static readonly string ECDHSha1Kdf = X9ObjectIdentifiers.DHSinglePassStdDHSha1KdfScheme.Id;
+ public static readonly string ECMqvSha1Kdf = X9ObjectIdentifiers.MqvSinglePassSha1KdfScheme.Id;
+
+ internal readonly IList recipientInfoGenerators = Platform.CreateArrayList();
+ internal readonly SecureRandom rand;
+
+ internal CmsAttributeTableGenerator unprotectedAttributeGenerator = null;
+
+ public CmsEnvelopedGenerator()
+ : this(new SecureRandom())
+ {
+ }
+
+ /// Constructor allowing specific source of randomness
+ /// Instance of SecureRandom to use.
+ public CmsEnvelopedGenerator(
+ SecureRandom rand)
+ {
+ this.rand = rand;
+ }
+
+ public CmsAttributeTableGenerator UnprotectedAttributeGenerator
+ {
+ get { return this.unprotectedAttributeGenerator; }
+ set { this.unprotectedAttributeGenerator = value; }
+ }
+
+ /**
+ * add a recipient.
+ *
+ * @param cert recipient's public key certificate
+ * @exception ArgumentException if there is a problem with the certificate
+ */
+ public void AddKeyTransRecipient(
+ X509Certificate cert)
+ {
+ KeyTransRecipientInfoGenerator ktrig = new KeyTransRecipientInfoGenerator();
+ ktrig.RecipientCert = cert;
+
+ recipientInfoGenerators.Add(ktrig);
+ }
+
+ /**
+ * add a recipient
+ *
+ * @param key the public key used by the recipient
+ * @param subKeyId the identifier for the recipient's public key
+ * @exception ArgumentException if there is a problem with the key
+ */
+ public void AddKeyTransRecipient(
+ AsymmetricKeyParameter pubKey,
+ byte[] subKeyId)
+ {
+ KeyTransRecipientInfoGenerator ktrig = new KeyTransRecipientInfoGenerator();
+ ktrig.RecipientPublicKey = pubKey;
+ ktrig.SubjectKeyIdentifier = new DerOctetString(subKeyId);
+
+ recipientInfoGenerators.Add(ktrig);
+ }
+
+ /**
+ * add a KEK recipient.
+ * @param key the secret key to use for wrapping
+ * @param keyIdentifier the byte string that identifies the key
+ */
+ public void AddKekRecipient(
+ string keyAlgorithm, // TODO Remove need for this parameter
+ KeyParameter key,
+ byte[] keyIdentifier)
+ {
+ AddKekRecipient(keyAlgorithm, key, new KekIdentifier(keyIdentifier, null, null));
+ }
+
+ /**
+ * add a KEK recipient.
+ * @param key the secret key to use for wrapping
+ * @param keyIdentifier the byte string that identifies the key
+ */
+ public void AddKekRecipient(
+ string keyAlgorithm, // TODO Remove need for this parameter
+ KeyParameter key,
+ KekIdentifier kekIdentifier)
+ {
+ KekRecipientInfoGenerator kekrig = new KekRecipientInfoGenerator();
+ kekrig.KekIdentifier = kekIdentifier;
+ kekrig.KeyEncryptionKeyOID = keyAlgorithm;
+ kekrig.KeyEncryptionKey = key;
+
+ recipientInfoGenerators.Add(kekrig);
+ }
+
+ public void AddPasswordRecipient(
+ CmsPbeKey pbeKey,
+ string kekAlgorithmOid)
+ {
+ Pbkdf2Params p = new Pbkdf2Params(pbeKey.Salt, pbeKey.IterationCount);
+
+ PasswordRecipientInfoGenerator prig = new PasswordRecipientInfoGenerator();
+ prig.KeyDerivationAlgorithm = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdPbkdf2, p);
+ prig.KeyEncryptionKeyOID = kekAlgorithmOid;
+ prig.KeyEncryptionKey = pbeKey.GetEncoded(kekAlgorithmOid);
+
+ recipientInfoGenerators.Add(prig);
+ }
+
+ /**
+ * Add a key agreement based recipient.
+ *
+ * @param agreementAlgorithm key agreement algorithm to use.
+ * @param senderPrivateKey private key to initialise sender side of agreement with.
+ * @param senderPublicKey sender public key to include with message.
+ * @param recipientCert recipient's public key certificate.
+ * @param cekWrapAlgorithm OID for key wrapping algorithm to use.
+ * @exception SecurityUtilityException if the algorithm requested cannot be found
+ * @exception InvalidKeyException if the keys are inappropriate for the algorithm specified
+ */
+ public void AddKeyAgreementRecipient(
+ string agreementAlgorithm,
+ AsymmetricKeyParameter senderPrivateKey,
+ AsymmetricKeyParameter senderPublicKey,
+ X509Certificate recipientCert,
+ string cekWrapAlgorithm)
+ {
+ IList recipientCerts = Platform.CreateArrayList(1);
+ recipientCerts.Add(recipientCert);
+
+ AddKeyAgreementRecipients(agreementAlgorithm, senderPrivateKey, senderPublicKey,
+ recipientCerts, cekWrapAlgorithm);
+ }
+
+ /**
+ * Add multiple key agreement based recipients (sharing a single KeyAgreeRecipientInfo structure).
+ *
+ * @param agreementAlgorithm key agreement algorithm to use.
+ * @param senderPrivateKey private key to initialise sender side of agreement with.
+ * @param senderPublicKey sender public key to include with message.
+ * @param recipientCerts recipients' public key certificates.
+ * @param cekWrapAlgorithm OID for key wrapping algorithm to use.
+ * @exception SecurityUtilityException if the algorithm requested cannot be found
+ * @exception InvalidKeyException if the keys are inappropriate for the algorithm specified
+ */
+ public void AddKeyAgreementRecipients(
+ string agreementAlgorithm,
+ AsymmetricKeyParameter senderPrivateKey,
+ AsymmetricKeyParameter senderPublicKey,
+ ICollection recipientCerts,
+ string cekWrapAlgorithm)
+ {
+ if (!senderPrivateKey.IsPrivate)
+ throw new ArgumentException("Expected private key", "senderPrivateKey");
+ if (senderPublicKey.IsPrivate)
+ throw new ArgumentException("Expected public key", "senderPublicKey");
+
+ /* TODO
+ * "a recipient X.509 version 3 certificate that contains a key usage extension MUST
+ * assert the keyAgreement bit."
+ */
+
+ KeyAgreeRecipientInfoGenerator karig = new KeyAgreeRecipientInfoGenerator();
+ karig.KeyAgreementOID = new DerObjectIdentifier(agreementAlgorithm);
+ karig.KeyEncryptionOID = new DerObjectIdentifier(cekWrapAlgorithm);
+ karig.RecipientCerts = recipientCerts;
+ karig.SenderKeyPair = new AsymmetricCipherKeyPair(senderPublicKey, senderPrivateKey);
+
+ recipientInfoGenerators.Add(karig);
+ }
+
+ protected internal virtual AlgorithmIdentifier GetAlgorithmIdentifier(
+ string encryptionOid,
+ KeyParameter encKey,
+ Asn1Encodable asn1Params,
+ out ICipherParameters cipherParameters)
+ {
+ Asn1Object asn1Object;
+ if (asn1Params != null)
+ {
+ asn1Object = asn1Params.ToAsn1Object();
+ cipherParameters = ParameterUtilities.GetCipherParameters(
+ encryptionOid, encKey, asn1Object);
+ }
+ else
+ {
+ asn1Object = DerNull.Instance;
+ cipherParameters = encKey;
+ }
+
+ return new AlgorithmIdentifier(
+ new DerObjectIdentifier(encryptionOid),
+ asn1Object);
+ }
+
+ protected internal virtual Asn1Encodable GenerateAsn1Parameters(
+ string encryptionOid,
+ byte[] encKeyBytes)
+ {
+ Asn1Encodable asn1Params = null;
+
+ try
+ {
+ if (encryptionOid.Equals(RC2Cbc))
+ {
+ byte[] iv = new byte[8];
+ rand.NextBytes(iv);
+
+ // TODO Is this detailed repeat of Java version really necessary?
+ int effKeyBits = encKeyBytes.Length * 8;
+ int parameterVersion;
+
+ if (effKeyBits < 256)
+ {
+ parameterVersion = rc2Table[effKeyBits];
+ }
+ else
+ {
+ parameterVersion = effKeyBits;
+ }
+
+ asn1Params = new RC2CbcParameter(parameterVersion, iv);
+ }
+ else
+ {
+ asn1Params = ParameterUtilities.GenerateParameters(encryptionOid, rand);
+ }
+ }
+ catch (SecurityUtilityException)
+ {
+ // No problem... no parameters generated
+ }
+
+ return asn1Params;
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSEnvelopedHelper.cs b/crypto/src/cms/CMSEnvelopedHelper.cs
new file mode 100644
index 000000000..fe2b14cd9
--- /dev/null
+++ b/crypto/src/cms/CMSEnvelopedHelper.cs
@@ -0,0 +1,311 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+ class CmsEnvelopedHelper
+ {
+ internal static readonly CmsEnvelopedHelper Instance = new CmsEnvelopedHelper();
+
+ private static readonly IDictionary KeySizes = Platform.CreateHashtable();
+ private static readonly IDictionary BaseCipherNames = Platform.CreateHashtable();
+
+ static CmsEnvelopedHelper()
+ {
+ KeySizes.Add(CmsEnvelopedGenerator.DesEde3Cbc, 192);
+ KeySizes.Add(CmsEnvelopedGenerator.Aes128Cbc, 128);
+ KeySizes.Add(CmsEnvelopedGenerator.Aes192Cbc, 192);
+ KeySizes.Add(CmsEnvelopedGenerator.Aes256Cbc, 256);
+
+ BaseCipherNames.Add(CmsEnvelopedGenerator.DesEde3Cbc, "DESEDE");
+ BaseCipherNames.Add(CmsEnvelopedGenerator.Aes128Cbc, "AES");
+ BaseCipherNames.Add(CmsEnvelopedGenerator.Aes192Cbc, "AES");
+ BaseCipherNames.Add(CmsEnvelopedGenerator.Aes256Cbc, "AES");
+ }
+
+ private string GetAsymmetricEncryptionAlgName(
+ string encryptionAlgOid)
+ {
+ if (Asn1.Pkcs.PkcsObjectIdentifiers.RsaEncryption.Id.Equals(encryptionAlgOid))
+ {
+ return "RSA/ECB/PKCS1Padding";
+ }
+
+ return encryptionAlgOid;
+ }
+
+ internal IBufferedCipher CreateAsymmetricCipher(
+ string encryptionOid)
+ {
+ string asymName = GetAsymmetricEncryptionAlgName(encryptionOid);
+ if (!asymName.Equals(encryptionOid))
+ {
+ try
+ {
+ return CipherUtilities.GetCipher(asymName);
+ }
+ catch (SecurityUtilityException)
+ {
+ // Ignore
+ }
+ }
+ return CipherUtilities.GetCipher(encryptionOid);
+ }
+
+ internal IWrapper CreateWrapper(
+ string encryptionOid)
+ {
+ try
+ {
+ return WrapperUtilities.GetWrapper(encryptionOid);
+ }
+ catch (SecurityUtilityException)
+ {
+ return WrapperUtilities.GetWrapper(GetAsymmetricEncryptionAlgName(encryptionOid));
+ }
+ }
+
+ internal string GetRfc3211WrapperName(
+ string oid)
+ {
+ if (oid == null)
+ throw new ArgumentNullException("oid");
+
+ string alg = (string) BaseCipherNames[oid];
+
+ if (alg == null)
+ throw new ArgumentException("no name for " + oid, "oid");
+
+ return alg + "RFC3211Wrap";
+ }
+
+ internal int GetKeySize(
+ string oid)
+ {
+ if (!KeySizes.Contains(oid))
+ {
+ throw new ArgumentException("no keysize for " + oid, "oid");
+ }
+
+ return (int) KeySizes[oid];
+ }
+
+ internal static RecipientInformationStore BuildRecipientInformationStore(
+ Asn1Set recipientInfos, CmsSecureReadable secureReadable)
+ {
+ IList infos = Platform.CreateArrayList();
+ for (int i = 0; i != recipientInfos.Count; i++)
+ {
+ RecipientInfo info = RecipientInfo.GetInstance(recipientInfos[i]);
+
+ ReadRecipientInfo(infos, info, secureReadable);
+ }
+ return new RecipientInformationStore(infos);
+ }
+
+ private static void ReadRecipientInfo(
+ IList infos, RecipientInfo info, CmsSecureReadable secureReadable)
+ {
+ Asn1Encodable recipInfo = info.Info;
+ if (recipInfo is KeyTransRecipientInfo)
+ {
+ infos.Add(new KeyTransRecipientInformation((KeyTransRecipientInfo)recipInfo, secureReadable));
+ }
+ else if (recipInfo is KekRecipientInfo)
+ {
+ infos.Add(new KekRecipientInformation((KekRecipientInfo)recipInfo, secureReadable));
+ }
+ else if (recipInfo is KeyAgreeRecipientInfo)
+ {
+ KeyAgreeRecipientInformation.ReadRecipientInfo(infos, (KeyAgreeRecipientInfo)recipInfo, secureReadable);
+ }
+ else if (recipInfo is PasswordRecipientInfo)
+ {
+ infos.Add(new PasswordRecipientInformation((PasswordRecipientInfo)recipInfo, secureReadable));
+ }
+ }
+
+ internal class CmsAuthenticatedSecureReadable : CmsSecureReadable
+ {
+ private AlgorithmIdentifier algorithm;
+ private IMac mac;
+ private CmsReadable readable;
+
+ internal CmsAuthenticatedSecureReadable(AlgorithmIdentifier algorithm, CmsReadable readable)
+ {
+ this.algorithm = algorithm;
+ this.readable = readable;
+ }
+
+ public AlgorithmIdentifier Algorithm
+ {
+ get { return this.algorithm; }
+ }
+
+ public object CryptoObject
+ {
+ get { return this.mac; }
+ }
+
+ public CmsReadable GetReadable(KeyParameter sKey)
+ {
+ string macAlg = this.algorithm.ObjectID.Id;
+// Asn1Object sParams = this.algorithm.Parameters.ToAsn1Object();
+
+ try
+ {
+ this.mac = MacUtilities.GetMac(macAlg);
+
+ // FIXME Support for MAC algorithm parameters similar to cipher parameters
+// ASN1Object sParams = (ASN1Object)macAlg.getParameters();
+//
+// if (sParams != null && !(sParams instanceof ASN1Null))
+// {
+// AlgorithmParameters params = CMSEnvelopedHelper.INSTANCE.createAlgorithmParameters(macAlg.getObjectId().getId(), provider);
+//
+// params.init(sParams.getEncoded(), "ASN.1");
+//
+// mac.init(sKey, params.getParameterSpec(IvParameterSpec.class));
+// }
+// else
+ {
+ mac.Init(sKey);
+ }
+
+// Asn1Object asn1Params = asn1Enc == null ? null : asn1Enc.ToAsn1Object();
+//
+// ICipherParameters cipherParameters = sKey;
+//
+// if (asn1Params != null && !(asn1Params is Asn1Null))
+// {
+// cipherParameters = ParameterUtilities.GetCipherParameters(
+// macAlg.ObjectID, cipherParameters, asn1Params);
+// }
+// else
+// {
+// string alg = macAlg.ObjectID.Id;
+// if (alg.Equals(CmsEnvelopedDataGenerator.DesEde3Cbc)
+// || alg.Equals(CmsEnvelopedDataGenerator.IdeaCbc)
+// || alg.Equals(CmsEnvelopedDataGenerator.Cast5Cbc))
+// {
+// cipherParameters = new ParametersWithIV(cipherParameters, new byte[8]);
+// }
+// }
+//
+// mac.Init(cipherParameters);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("couldn't create cipher.", e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key invalid in message.", e);
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("error decoding algorithm parameters.", e);
+ }
+
+ try
+ {
+ return new CmsProcessableInputStream(
+ new TeeInputStream(
+ readable.GetInputStream(),
+ new MacOutputStream(this.mac)));
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("error reading content.", e);
+ }
+ }
+ }
+
+ internal class CmsEnvelopedSecureReadable : CmsSecureReadable
+ {
+ private AlgorithmIdentifier algorithm;
+ private IBufferedCipher cipher;
+ private CmsReadable readable;
+
+ internal CmsEnvelopedSecureReadable(AlgorithmIdentifier algorithm, CmsReadable readable)
+ {
+ this.algorithm = algorithm;
+ this.readable = readable;
+ }
+
+ public AlgorithmIdentifier Algorithm
+ {
+ get { return this.algorithm; }
+ }
+
+ public object CryptoObject
+ {
+ get { return this.cipher; }
+ }
+
+ public CmsReadable GetReadable(KeyParameter sKey)
+ {
+ try
+ {
+ this.cipher = CipherUtilities.GetCipher(this.algorithm.ObjectID);
+
+ Asn1Encodable asn1Enc = this.algorithm.Parameters;
+ Asn1Object asn1Params = asn1Enc == null ? null : asn1Enc.ToAsn1Object();
+
+ ICipherParameters cipherParameters = sKey;
+
+ if (asn1Params != null && !(asn1Params is Asn1Null))
+ {
+ cipherParameters = ParameterUtilities.GetCipherParameters(
+ this.algorithm.ObjectID, cipherParameters, asn1Params);
+ }
+ else
+ {
+ string alg = this.algorithm.ObjectID.Id;
+ if (alg.Equals(CmsEnvelopedDataGenerator.DesEde3Cbc)
+ || alg.Equals(CmsEnvelopedDataGenerator.IdeaCbc)
+ || alg.Equals(CmsEnvelopedDataGenerator.Cast5Cbc))
+ {
+ cipherParameters = new ParametersWithIV(cipherParameters, new byte[8]);
+ }
+ }
+
+ cipher.Init(false, cipherParameters);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("couldn't create cipher.", e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key invalid in message.", e);
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("error decoding algorithm parameters.", e);
+ }
+
+ try
+ {
+ return new CmsProcessableInputStream(
+ new CipherStream(readable.GetInputStream(), cipher, null));
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("error reading content.", e);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/crypto/src/cms/CMSException.cs b/crypto/src/cms/CMSException.cs
new file mode 100644
index 000000000..5a6e33502
--- /dev/null
+++ b/crypto/src/cms/CMSException.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Org.BouncyCastle.Cms
+{
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+ [Serializable]
+#endif
+ public class CmsException
+ : Exception
+ {
+ public CmsException()
+ {
+ }
+
+ public CmsException(
+ string msg)
+ : base(msg)
+ {
+ }
+
+ public CmsException(
+ string msg,
+ Exception e)
+ : base(msg, e)
+ {
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSPBEKey.cs b/crypto/src/cms/CMSPBEKey.cs
new file mode 100644
index 000000000..cb1e54c36
--- /dev/null
+++ b/crypto/src/cms/CMSPBEKey.cs
@@ -0,0 +1,109 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+//import javax.crypto.interfaces.PBEKey;
+
+namespace Org.BouncyCastle.Cms
+{
+ public abstract class CmsPbeKey
+ // TODO Create an equivalent interface somewhere?
+ // : PBEKey
+ : ICipherParameters
+ {
+ internal readonly char[] password;
+ internal readonly byte[] salt;
+ internal readonly int iterationCount;
+
+ [Obsolete("Use version taking 'char[]' instead")]
+ public CmsPbeKey(
+ string password,
+ byte[] salt,
+ int iterationCount)
+ : this(password.ToCharArray(), salt, iterationCount)
+ {
+ }
+
+ [Obsolete("Use version taking 'char[]' instead")]
+ public CmsPbeKey(
+ string password,
+ AlgorithmIdentifier keyDerivationAlgorithm)
+ : this(password.ToCharArray(), keyDerivationAlgorithm)
+ {
+ }
+
+ public CmsPbeKey(
+ char[] password,
+ byte[] salt,
+ int iterationCount)
+ {
+ this.password = (char[])password.Clone();
+ this.salt = Arrays.Clone(salt);
+ this.iterationCount = iterationCount;
+ }
+
+ public CmsPbeKey(
+ char[] password,
+ AlgorithmIdentifier keyDerivationAlgorithm)
+ {
+ if (!keyDerivationAlgorithm.ObjectID.Equals(PkcsObjectIdentifiers.IdPbkdf2))
+ throw new ArgumentException("Unsupported key derivation algorithm: "
+ + keyDerivationAlgorithm.ObjectID);
+
+ Pbkdf2Params kdfParams = Pbkdf2Params.GetInstance(
+ keyDerivationAlgorithm.Parameters.ToAsn1Object());
+
+ this.password = (char[])password.Clone();
+ this.salt = kdfParams.GetSalt();
+ this.iterationCount = kdfParams.IterationCount.IntValue;
+ }
+
+ ~CmsPbeKey()
+ {
+ Array.Clear(this.password, 0, this.password.Length);
+ }
+
+ [Obsolete("Will be removed")]
+ public string Password
+ {
+ get { return new string(password); }
+ }
+
+ public byte[] Salt
+ {
+ get { return Arrays.Clone(salt); }
+ }
+
+ [Obsolete("Use 'Salt' property instead")]
+ public byte[] GetSalt()
+ {
+ return Salt;
+ }
+
+ public int IterationCount
+ {
+ get { return iterationCount; }
+ }
+
+ public string Algorithm
+ {
+ get { return "PKCS5S2"; }
+ }
+
+ public string Format
+ {
+ get { return "RAW"; }
+ }
+
+ public byte[] GetEncoded()
+ {
+ return null;
+ }
+
+ internal abstract KeyParameter GetEncoded(string algorithmOid);
+ }
+}
diff --git a/crypto/src/cms/CMSProcessable.cs b/crypto/src/cms/CMSProcessable.cs
new file mode 100644
index 000000000..41018d12b
--- /dev/null
+++ b/crypto/src/cms/CMSProcessable.cs
@@ -0,0 +1,19 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+ public interface CmsProcessable
+ {
+ ///
+ /// Generic routine to copy out the data we want processed.
+ ///
+ ///
+ /// This routine may be called multiple times.
+ ///
+ void Write(Stream outStream);
+
+ [Obsolete]
+ object GetContent();
+ }
+}
diff --git a/crypto/src/cms/CMSProcessableByteArray.cs b/crypto/src/cms/CMSProcessableByteArray.cs
new file mode 100644
index 000000000..4bee4f8bd
--- /dev/null
+++ b/crypto/src/cms/CMSProcessableByteArray.cs
@@ -0,0 +1,36 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * a holding class for a byte array of data to be processed.
+ */
+ public class CmsProcessableByteArray
+ : CmsProcessable, CmsReadable
+ {
+ private readonly byte[] bytes;
+
+ public CmsProcessableByteArray(
+ byte[] bytes)
+ {
+ this.bytes = bytes;
+ }
+
+ public Stream GetInputStream()
+ {
+ return new MemoryStream(bytes, false);
+ }
+
+ public virtual void Write(Stream zOut)
+ {
+ zOut.Write(bytes, 0, bytes.Length);
+ }
+
+ /// A clone of the byte array
+ public virtual object GetContent()
+ {
+ return bytes.Clone();
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSProcessableFile.cs b/crypto/src/cms/CMSProcessableFile.cs
new file mode 100644
index 000000000..cbc74f44b
--- /dev/null
+++ b/crypto/src/cms/CMSProcessableFile.cs
@@ -0,0 +1,54 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * a holding class for a file of data to be processed.
+ */
+ public class CmsProcessableFile
+ : CmsProcessable, CmsReadable
+ {
+ private const int DefaultBufSize = 32 * 1024;
+
+ private readonly FileInfo _file;
+ private readonly int _bufSize;
+
+ public CmsProcessableFile(
+ FileInfo file)
+ : this(file, DefaultBufSize)
+ {
+ }
+
+ public CmsProcessableFile(
+ FileInfo file,
+ int bufSize)
+ {
+ _file = file;
+ _bufSize = bufSize;
+ }
+
+ public virtual Stream GetInputStream()
+ {
+ return new FileStream(
+ _file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, _bufSize);
+ }
+
+ public virtual void Write(
+ Stream zOut)
+ {
+ Stream inStr = GetInputStream();
+ Streams.PipeAll(inStr, zOut);
+ inStr.Close();
+ }
+
+ /// The file handle
+ [Obsolete]
+ public virtual object GetContent()
+ {
+ return _file;
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSProcessableInputStream.cs b/crypto/src/cms/CMSProcessableInputStream.cs
new file mode 100644
index 000000000..7fdd1dfef
--- /dev/null
+++ b/crypto/src/cms/CMSProcessableInputStream.cs
@@ -0,0 +1,52 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+ public class CmsProcessableInputStream
+ : CmsProcessable, CmsReadable
+ {
+ private Stream input;
+ private bool used = false;
+
+ public CmsProcessableInputStream(
+ Stream input)
+ {
+ this.input = input;
+ }
+
+ public Stream GetInputStream()
+ {
+ CheckSingleUsage();
+
+ return input;
+ }
+
+ public void Write(Stream output)
+ {
+ CheckSingleUsage();
+
+ Streams.PipeAll(input, output);
+ input.Close();
+ }
+
+ [Obsolete]
+ public object GetContent()
+ {
+ return GetInputStream();
+ }
+
+ private void CheckSingleUsage()
+ {
+ lock (this)
+ {
+ if (used)
+ throw new InvalidOperationException("CmsProcessableInputStream can only be used once");
+
+ used = true;
+ }
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSReadable.cs b/crypto/src/cms/CMSReadable.cs
new file mode 100644
index 000000000..9507b920c
--- /dev/null
+++ b/crypto/src/cms/CMSReadable.cs
@@ -0,0 +1,10 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal interface CmsReadable
+ {
+ Stream GetInputStream();
+ }
+}
diff --git a/crypto/src/cms/CMSSecureReadable.cs b/crypto/src/cms/CMSSecureReadable.cs
new file mode 100644
index 000000000..5ceac24fd
--- /dev/null
+++ b/crypto/src/cms/CMSSecureReadable.cs
@@ -0,0 +1,14 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal interface CmsSecureReadable
+ {
+ AlgorithmIdentifier Algorithm { get; }
+ object CryptoObject { get; }
+ CmsReadable GetReadable(KeyParameter key);
+ }
+}
diff --git a/crypto/src/cms/CMSSignedData.cs b/crypto/src/cms/CMSSignedData.cs
new file mode 100644
index 000000000..81c87a426
--- /dev/null
+++ b/crypto/src/cms/CMSSignedData.cs
@@ -0,0 +1,425 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * general class for handling a pkcs7-signature message.
+ *
+ * A simple example of usage - note, in the example below the validity of
+ * the certificate isn't verified, just the fact that one of the certs
+ * matches the given signer...
+ *
+ *
+ * IX509Store certs = s.GetCertificates();
+ * SignerInformationStore signers = s.GetSignerInfos();
+ *
+ * foreach (SignerInformation signer in signers.GetSigners())
+ * {
+ * ArrayList certList = new ArrayList(certs.GetMatches(signer.SignerID));
+ * X509Certificate cert = (X509Certificate) certList[0];
+ *
+ * if (signer.Verify(cert.GetPublicKey()))
+ * {
+ * verified++;
+ * }
+ * }
+ *
+ */
+ public class CmsSignedData
+ {
+ private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
+
+ private readonly CmsProcessable signedContent;
+ private SignedData signedData;
+ private ContentInfo contentInfo;
+ private SignerInformationStore signerInfoStore;
+ private IX509Store attrCertStore;
+ private IX509Store certificateStore;
+ private IX509Store crlStore;
+ private IDictionary hashes;
+
+ private CmsSignedData(
+ CmsSignedData c)
+ {
+ this.signedData = c.signedData;
+ this.contentInfo = c.contentInfo;
+ this.signedContent = c.signedContent;
+ this.signerInfoStore = c.signerInfoStore;
+ }
+
+ public CmsSignedData(
+ byte[] sigBlock)
+ : this(CmsUtilities.ReadContentInfo(new MemoryStream(sigBlock, false)))
+ {
+ }
+
+ public CmsSignedData(
+ CmsProcessable signedContent,
+ byte[] sigBlock)
+ : this(signedContent, CmsUtilities.ReadContentInfo(new MemoryStream(sigBlock, false)))
+ {
+ }
+
+ /**
+ * Content with detached signature, digests precomputed
+ *
+ * @param hashes a map of precomputed digests for content indexed by name of hash.
+ * @param sigBlock the signature object.
+ */
+ public CmsSignedData(
+ IDictionary hashes,
+ byte[] sigBlock)
+ : this(hashes, CmsUtilities.ReadContentInfo(sigBlock))
+ {
+ }
+
+ /**
+ * base constructor - content with detached signature.
+ *
+ * @param signedContent the content that was signed.
+ * @param sigData the signature object.
+ */
+ public CmsSignedData(
+ CmsProcessable signedContent,
+ Stream sigData)
+ : this(signedContent, CmsUtilities.ReadContentInfo(sigData))
+ {
+ }
+
+ /**
+ * base constructor - with encapsulated content
+ */
+ public CmsSignedData(
+ Stream sigData)
+ : this(CmsUtilities.ReadContentInfo(sigData))
+ {
+ }
+
+ public CmsSignedData(
+ CmsProcessable signedContent,
+ ContentInfo sigData)
+ {
+ this.signedContent = signedContent;
+ this.contentInfo = sigData;
+ this.signedData = SignedData.GetInstance(contentInfo.Content);
+ }
+
+ public CmsSignedData(
+ IDictionary hashes,
+ ContentInfo sigData)
+ {
+ this.hashes = hashes;
+ this.contentInfo = sigData;
+ this.signedData = SignedData.GetInstance(contentInfo.Content);
+ }
+
+ public CmsSignedData(
+ ContentInfo sigData)
+ {
+ this.contentInfo = sigData;
+ this.signedData = SignedData.GetInstance(contentInfo.Content);
+
+ //
+ // this can happen if the signed message is sent simply to send a
+ // certificate chain.
+ //
+ if (signedData.EncapContentInfo.Content != null)
+ {
+ this.signedContent = new CmsProcessableByteArray(
+ ((Asn1OctetString)(signedData.EncapContentInfo.Content)).GetOctets());
+ }
+// else
+// {
+// this.signedContent = null;
+// }
+ }
+
+ /// Return the version number for this object.
+ public int Version
+ {
+ get { return signedData.Version.Value.IntValue; }
+ }
+
+ /**
+ * return the collection of signers that are associated with the
+ * signatures for the message.
+ */
+ public SignerInformationStore GetSignerInfos()
+ {
+ if (signerInfoStore == null)
+ {
+ IList signerInfos = Platform.CreateArrayList();
+ Asn1Set s = signedData.SignerInfos;
+
+ foreach (object obj in s)
+ {
+ SignerInfo info = SignerInfo.GetInstance(obj);
+ DerObjectIdentifier contentType = signedData.EncapContentInfo.ContentType;
+
+ if (hashes == null)
+ {
+ signerInfos.Add(new SignerInformation(info, contentType, signedContent, null));
+ }
+ else
+ {
+ byte[] hash = (byte[]) hashes[info.DigestAlgorithm.ObjectID.Id];
+
+ signerInfos.Add(new SignerInformation(info, contentType, null, new BaseDigestCalculator(hash)));
+ }
+ }
+
+ signerInfoStore = new SignerInformationStore(signerInfos);
+ }
+
+ return signerInfoStore;
+ }
+
+ /**
+ * return a X509Store containing the attribute certificates, if any, contained
+ * in this message.
+ *
+ * @param type type of store to create
+ * @return a store of attribute certificates
+ * @exception NoSuchStoreException if the store type isn't available.
+ * @exception CmsException if a general exception prevents creation of the X509Store
+ */
+ public IX509Store GetAttributeCertificates(
+ string type)
+ {
+ if (attrCertStore == null)
+ {
+ attrCertStore = Helper.CreateAttributeStore(type, signedData.Certificates);
+ }
+
+ return attrCertStore;
+ }
+
+ /**
+ * return a X509Store containing the public key certificates, if any, contained
+ * in this message.
+ *
+ * @param type type of store to create
+ * @return a store of public key certificates
+ * @exception NoSuchStoreException if the store type isn't available.
+ * @exception CmsException if a general exception prevents creation of the X509Store
+ */
+ public IX509Store GetCertificates(
+ string type)
+ {
+ if (certificateStore == null)
+ {
+ certificateStore = Helper.CreateCertificateStore(type, signedData.Certificates);
+ }
+
+ return certificateStore;
+ }
+
+ /**
+ * return a X509Store containing CRLs, if any, contained
+ * in this message.
+ *
+ * @param type type of store to create
+ * @return a store of CRLs
+ * @exception NoSuchStoreException if the store type isn't available.
+ * @exception CmsException if a general exception prevents creation of the X509Store
+ */
+ public IX509Store GetCrls(
+ string type)
+ {
+ if (crlStore == null)
+ {
+ crlStore = Helper.CreateCrlStore(type, signedData.CRLs);
+ }
+
+ return crlStore;
+ }
+
+ [Obsolete("Use 'SignedContentType' property instead.")]
+ public string SignedContentTypeOid
+ {
+ get { return signedData.EncapContentInfo.ContentType.Id; }
+ }
+
+ ///
+ /// Return the DerObjectIdentifier associated with the encapsulated
+ /// content info structure carried in the signed data.
+ ///
+ public DerObjectIdentifier SignedContentType
+ {
+ get { return signedData.EncapContentInfo.ContentType; }
+ }
+
+ public CmsProcessable SignedContent
+ {
+ get { return signedContent; }
+ }
+
+ /**
+ * return the ContentInfo
+ */
+ public ContentInfo ContentInfo
+ {
+ get { return contentInfo; }
+ }
+
+ /**
+ * return the ASN.1 encoded representation of this object.
+ */
+ public byte[] GetEncoded()
+ {
+ return contentInfo.GetEncoded();
+ }
+
+ /**
+ * Replace the signerinformation store associated with this
+ * CmsSignedData object with the new one passed in. You would
+ * probably only want to do this if you wanted to change the unsigned
+ * attributes associated with a signer, or perhaps delete one.
+ *
+ * @param signedData the signed data object to be used as a base.
+ * @param signerInformationStore the new signer information store to use.
+ * @return a new signed data object.
+ */
+ public static CmsSignedData ReplaceSigners(
+ CmsSignedData signedData,
+ SignerInformationStore signerInformationStore)
+ {
+ //
+ // copy
+ //
+ CmsSignedData cms = new CmsSignedData(signedData);
+
+ //
+ // replace the store
+ //
+ cms.signerInfoStore = signerInformationStore;
+
+ //
+ // replace the signers in the SignedData object
+ //
+ Asn1EncodableVector digestAlgs = new Asn1EncodableVector();
+ Asn1EncodableVector vec = new Asn1EncodableVector();
+
+ foreach (SignerInformation signer in signerInformationStore.GetSigners())
+ {
+ digestAlgs.Add(Helper.FixAlgID(signer.DigestAlgorithmID));
+ vec.Add(signer.ToSignerInfo());
+ }
+
+ Asn1Set digests = new DerSet(digestAlgs);
+ Asn1Set signers = new DerSet(vec);
+ Asn1Sequence sD = (Asn1Sequence)signedData.signedData.ToAsn1Object();
+
+ //
+ // signers are the last item in the sequence.
+ //
+ vec = new Asn1EncodableVector(
+ sD[0], // version
+ digests);
+
+ for (int i = 2; i != sD.Count - 1; i++)
+ {
+ vec.Add(sD[i]);
+ }
+
+ vec.Add(signers);
+
+ cms.signedData = SignedData.GetInstance(new BerSequence(vec));
+
+ //
+ // replace the contentInfo with the new one
+ //
+ cms.contentInfo = new ContentInfo(cms.contentInfo.ContentType, cms.signedData);
+
+ return cms;
+ }
+
+ /**
+ * Replace the certificate and CRL information associated with this
+ * CmsSignedData object with the new one passed in.
+ *
+ * @param signedData the signed data object to be used as a base.
+ * @param x509Certs the new certificates to be used.
+ * @param x509Crls the new CRLs to be used.
+ * @return a new signed data object.
+ * @exception CmsException if there is an error processing the stores
+ */
+ public static CmsSignedData ReplaceCertificatesAndCrls(
+ CmsSignedData signedData,
+ IX509Store x509Certs,
+ IX509Store x509Crls,
+ IX509Store x509AttrCerts)
+ {
+ if (x509AttrCerts != null)
+ throw Platform.CreateNotImplementedException("Currently can't replace attribute certificates");
+
+ //
+ // copy
+ //
+ CmsSignedData cms = new CmsSignedData(signedData);
+
+ //
+ // replace the certs and crls in the SignedData object
+ //
+ Asn1Set certs = null;
+ try
+ {
+ Asn1Set asn1Set = CmsUtilities.CreateBerSetFromList(
+ CmsUtilities.GetCertificatesFromStore(x509Certs));
+
+ if (asn1Set.Count != 0)
+ {
+ certs = asn1Set;
+ }
+ }
+ catch (X509StoreException e)
+ {
+ throw new CmsException("error getting certificates from store", e);
+ }
+
+ Asn1Set crls = null;
+ try
+ {
+ Asn1Set asn1Set = CmsUtilities.CreateBerSetFromList(
+ CmsUtilities.GetCrlsFromStore(x509Crls));
+
+ if (asn1Set.Count != 0)
+ {
+ crls = asn1Set;
+ }
+ }
+ catch (X509StoreException e)
+ {
+ throw new CmsException("error getting CRLs from store", e);
+ }
+
+ //
+ // replace the CMS structure.
+ //
+ SignedData old = signedData.signedData;
+ cms.signedData = new SignedData(
+ old.DigestAlgorithms,
+ old.EncapContentInfo,
+ certs,
+ crls,
+ old.SignerInfos);
+
+ //
+ // replace the contentInfo with the new one
+ //
+ cms.contentInfo = new ContentInfo(cms.contentInfo.ContentType, cms.signedData);
+
+ return cms;
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSSignedDataGenerator.cs b/crypto/src/cms/CMSSignedDataGenerator.cs
new file mode 100644
index 000000000..f31105c41
--- /dev/null
+++ b/crypto/src/cms/CMSSignedDataGenerator.cs
@@ -0,0 +1,551 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * general class for generating a pkcs7-signature message.
+ *
+ * A simple example of usage.
+ *
+ *
+ * IX509Store certs...
+ * IX509Store crls...
+ * CmsSignedDataGenerator gen = new CmsSignedDataGenerator();
+ *
+ * gen.AddSigner(privKey, cert, CmsSignedGenerator.DigestSha1);
+ * gen.AddCertificates(certs);
+ * gen.AddCrls(crls);
+ *
+ * CmsSignedData data = gen.Generate(content);
+ *
+ *
+ */
+ public class CmsSignedDataGenerator
+ : CmsSignedGenerator
+ {
+ private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
+
+ private readonly IList signerInfs = Platform.CreateArrayList();
+
+ private class SignerInf
+ {
+ private readonly CmsSignedGenerator outer;
+
+ private readonly AsymmetricKeyParameter key;
+ private readonly SignerIdentifier signerIdentifier;
+ private readonly string digestOID;
+ private readonly string encOID;
+ private readonly CmsAttributeTableGenerator sAttr;
+ private readonly CmsAttributeTableGenerator unsAttr;
+ private readonly Asn1.Cms.AttributeTable baseSignedTable;
+
+ internal SignerInf(
+ CmsSignedGenerator outer,
+ AsymmetricKeyParameter key,
+ SignerIdentifier signerIdentifier,
+ string digestOID,
+ string encOID,
+ CmsAttributeTableGenerator sAttr,
+ CmsAttributeTableGenerator unsAttr,
+ Asn1.Cms.AttributeTable baseSignedTable)
+ {
+ this.outer = outer;
+ this.key = key;
+ this.signerIdentifier = signerIdentifier;
+ this.digestOID = digestOID;
+ this.encOID = encOID;
+ this.sAttr = sAttr;
+ this.unsAttr = unsAttr;
+ this.baseSignedTable = baseSignedTable;
+ }
+
+ internal AlgorithmIdentifier DigestAlgorithmID
+ {
+ get { return new AlgorithmIdentifier(new DerObjectIdentifier(digestOID), DerNull.Instance); }
+ }
+
+ internal CmsAttributeTableGenerator SignedAttributes
+ {
+ get { return sAttr; }
+ }
+
+ internal CmsAttributeTableGenerator UnsignedAttributes
+ {
+ get { return unsAttr; }
+ }
+
+ internal SignerInfo ToSignerInfo(
+ DerObjectIdentifier contentType,
+ CmsProcessable content,
+ SecureRandom random)
+ {
+ AlgorithmIdentifier digAlgId = DigestAlgorithmID;
+ string digestName = Helper.GetDigestAlgName(digestOID);
+ IDigest dig = Helper.GetDigestInstance(digestName);
+
+ string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(encOID);
+ ISigner sig = Helper.GetSignatureInstance(signatureName);
+
+ // TODO Optimise the case where more than one signer with same digest
+ if (content != null)
+ {
+ content.Write(new DigOutputStream(dig));
+ }
+
+ byte[] hash = DigestUtilities.DoFinal(dig);
+ outer._digests.Add(digestOID, hash.Clone());
+
+ sig.Init(true, new ParametersWithRandom(key, random));
+#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT
+ Stream sigStr = new SigOutputStream(sig);
+#else
+ Stream sigStr = new BufferedStream(new SigOutputStream(sig));
+#endif
+
+ Asn1Set signedAttr = null;
+ if (sAttr != null)
+ {
+ IDictionary parameters = outer.GetBaseParameters(contentType, digAlgId, hash);
+
+// Asn1.Cms.AttributeTable signed = sAttr.GetAttributes(Collections.unmodifiableMap(parameters));
+ Asn1.Cms.AttributeTable signed = sAttr.GetAttributes(parameters);
+
+ if (contentType == null) //counter signature
+ {
+ if (signed != null && signed[CmsAttributes.ContentType] != null)
+ {
+ IDictionary tmpSigned = signed.ToDictionary();
+ tmpSigned.Remove(CmsAttributes.ContentType);
+ signed = new Asn1.Cms.AttributeTable(tmpSigned);
+ }
+ }
+
+ // TODO Validate proposed signed attributes
+
+ signedAttr = outer.GetAttributeSet(signed);
+
+ // sig must be composed from the DER encoding.
+ new DerOutputStream(sigStr).WriteObject(signedAttr);
+ }
+ else if (content != null)
+ {
+ // TODO Use raw signature of the hash value instead
+ content.Write(sigStr);
+ }
+
+ sigStr.Close();
+ byte[] sigBytes = sig.GenerateSignature();
+
+ Asn1Set unsignedAttr = null;
+ if (unsAttr != null)
+ {
+ IDictionary baseParameters = outer.GetBaseParameters(contentType, digAlgId, hash);
+ baseParameters[CmsAttributeTableParameter.Signature] = sigBytes.Clone();
+
+// Asn1.Cms.AttributeTable unsigned = unsAttr.GetAttributes(Collections.unmodifiableMap(baseParameters));
+ Asn1.Cms.AttributeTable unsigned = unsAttr.GetAttributes(baseParameters);
+
+ // TODO Validate proposed unsigned attributes
+
+ unsignedAttr = outer.GetAttributeSet(unsigned);
+ }
+
+ // TODO[RSAPSS] Need the ability to specify non-default parameters
+ Asn1Encodable sigX509Parameters = SignerUtilities.GetDefaultX509Parameters(signatureName);
+ AlgorithmIdentifier encAlgId = CmsSignedGenerator.GetEncAlgorithmIdentifier(
+ new DerObjectIdentifier(encOID), sigX509Parameters);
+
+ return new SignerInfo(signerIdentifier, digAlgId,
+ signedAttr, encAlgId, new DerOctetString(sigBytes), unsignedAttr);
+ }
+ }
+
+ public CmsSignedDataGenerator()
+ {
+ }
+
+ /// Constructor allowing specific source of randomness
+ /// Instance of SecureRandom to use.
+ public CmsSignedDataGenerator(
+ SecureRandom rand)
+ : base(rand)
+ {
+ }
+
+ /**
+ * add a signer - no attributes other than the default ones will be
+ * provided here.
+ *
+ * @param key signing key to use
+ * @param cert certificate containing corresponding public key
+ * @param digestOID digest algorithm OID
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ X509Certificate cert,
+ string digestOID)
+ {
+ AddSigner(privateKey, cert, GetEncOid(privateKey, digestOID), digestOID);
+ }
+
+ /**
+ * add a signer, specifying the digest encryption algorithm to use - no attributes other than the default ones will be
+ * provided here.
+ *
+ * @param key signing key to use
+ * @param cert certificate containing corresponding public key
+ * @param encryptionOID digest encryption algorithm OID
+ * @param digestOID digest algorithm OID
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ X509Certificate cert,
+ string encryptionOID,
+ string digestOID)
+ {
+ doAddSigner(privateKey, GetSignerIdentifier(cert), encryptionOID, digestOID,
+ new DefaultSignedAttributeTableGenerator(), null, null);
+ }
+
+ /**
+ * add a signer - no attributes other than the default ones will be
+ * provided here.
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ byte[] subjectKeyID,
+ string digestOID)
+ {
+ AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOID), digestOID);
+ }
+
+ /**
+ * add a signer, specifying the digest encryption algorithm to use - no attributes other than the default ones will be
+ * provided here.
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ byte[] subjectKeyID,
+ string encryptionOID,
+ string digestOID)
+ {
+ doAddSigner(privateKey, GetSignerIdentifier(subjectKeyID), encryptionOID, digestOID,
+ new DefaultSignedAttributeTableGenerator(), null, null);
+ }
+
+ /**
+ * add a signer with extra signed/unsigned attributes.
+ *
+ * @param key signing key to use
+ * @param cert certificate containing corresponding public key
+ * @param digestOID digest algorithm OID
+ * @param signedAttr table of attributes to be included in signature
+ * @param unsignedAttr table of attributes to be included as unsigned
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ X509Certificate cert,
+ string digestOID,
+ Asn1.Cms.AttributeTable signedAttr,
+ Asn1.Cms.AttributeTable unsignedAttr)
+ {
+ AddSigner(privateKey, cert, GetEncOid(privateKey, digestOID), digestOID,
+ signedAttr, unsignedAttr);
+ }
+
+ /**
+ * add a signer, specifying the digest encryption algorithm, with extra signed/unsigned attributes.
+ *
+ * @param key signing key to use
+ * @param cert certificate containing corresponding public key
+ * @param encryptionOID digest encryption algorithm OID
+ * @param digestOID digest algorithm OID
+ * @param signedAttr table of attributes to be included in signature
+ * @param unsignedAttr table of attributes to be included as unsigned
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ X509Certificate cert,
+ string encryptionOID,
+ string digestOID,
+ Asn1.Cms.AttributeTable signedAttr,
+ Asn1.Cms.AttributeTable unsignedAttr)
+ {
+ doAddSigner(privateKey, GetSignerIdentifier(cert), encryptionOID, digestOID,
+ new DefaultSignedAttributeTableGenerator(signedAttr),
+ new SimpleAttributeTableGenerator(unsignedAttr),
+ signedAttr);
+ }
+
+ /**
+ * add a signer with extra signed/unsigned attributes.
+ *
+ * @param key signing key to use
+ * @param subjectKeyID subjectKeyID of corresponding public key
+ * @param digestOID digest algorithm OID
+ * @param signedAttr table of attributes to be included in signature
+ * @param unsignedAttr table of attributes to be included as unsigned
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ byte[] subjectKeyID,
+ string digestOID,
+ Asn1.Cms.AttributeTable signedAttr,
+ Asn1.Cms.AttributeTable unsignedAttr)
+ {
+ AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOID), digestOID,
+ signedAttr, unsignedAttr);
+ }
+
+ /**
+ * add a signer, specifying the digest encryption algorithm, with extra signed/unsigned attributes.
+ *
+ * @param key signing key to use
+ * @param subjectKeyID subjectKeyID of corresponding public key
+ * @param encryptionOID digest encryption algorithm OID
+ * @param digestOID digest algorithm OID
+ * @param signedAttr table of attributes to be included in signature
+ * @param unsignedAttr table of attributes to be included as unsigned
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ byte[] subjectKeyID,
+ string encryptionOID,
+ string digestOID,
+ Asn1.Cms.AttributeTable signedAttr,
+ Asn1.Cms.AttributeTable unsignedAttr)
+ {
+ doAddSigner(privateKey, GetSignerIdentifier(subjectKeyID), encryptionOID, digestOID,
+ new DefaultSignedAttributeTableGenerator(signedAttr),
+ new SimpleAttributeTableGenerator(unsignedAttr),
+ signedAttr);
+ }
+
+ /**
+ * add a signer with extra signed/unsigned attributes based on generators.
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ X509Certificate cert,
+ string digestOID,
+ CmsAttributeTableGenerator signedAttrGen,
+ CmsAttributeTableGenerator unsignedAttrGen)
+ {
+ AddSigner(privateKey, cert, GetEncOid(privateKey, digestOID), digestOID,
+ signedAttrGen, unsignedAttrGen);
+ }
+
+ /**
+ * add a signer, specifying the digest encryption algorithm, with extra signed/unsigned attributes based on generators.
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ X509Certificate cert,
+ string encryptionOID,
+ string digestOID,
+ CmsAttributeTableGenerator signedAttrGen,
+ CmsAttributeTableGenerator unsignedAttrGen)
+ {
+ doAddSigner(privateKey, GetSignerIdentifier(cert), encryptionOID, digestOID, signedAttrGen,
+ unsignedAttrGen, null);
+ }
+
+ /**
+ * add a signer with extra signed/unsigned attributes based on generators.
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ byte[] subjectKeyID,
+ string digestOID,
+ CmsAttributeTableGenerator signedAttrGen,
+ CmsAttributeTableGenerator unsignedAttrGen)
+ {
+ AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOID), digestOID,
+ signedAttrGen, unsignedAttrGen);
+ }
+
+ /**
+ * add a signer, including digest encryption algorithm, with extra signed/unsigned attributes based on generators.
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ byte[] subjectKeyID,
+ string encryptionOID,
+ string digestOID,
+ CmsAttributeTableGenerator signedAttrGen,
+ CmsAttributeTableGenerator unsignedAttrGen)
+ {
+ doAddSigner(privateKey, GetSignerIdentifier(subjectKeyID), encryptionOID, digestOID,
+ signedAttrGen, unsignedAttrGen, null);
+ }
+
+ private void doAddSigner(
+ AsymmetricKeyParameter privateKey,
+ SignerIdentifier signerIdentifier,
+ string encryptionOID,
+ string digestOID,
+ CmsAttributeTableGenerator signedAttrGen,
+ CmsAttributeTableGenerator unsignedAttrGen,
+ Asn1.Cms.AttributeTable baseSignedTable)
+ {
+ signerInfs.Add(new SignerInf(this, privateKey, signerIdentifier, digestOID, encryptionOID,
+ signedAttrGen, unsignedAttrGen, baseSignedTable));
+ }
+
+ /**
+ * generate a signed object that for a CMS Signed Data object
+ */
+ public CmsSignedData Generate(
+ CmsProcessable content)
+ {
+ return Generate(content, false);
+ }
+
+ /**
+ * generate a signed object that for a CMS Signed Data
+ * object - if encapsulate is true a copy
+ * of the message will be included in the signature. The content type
+ * is set according to the OID represented by the string signedContentType.
+ */
+ public CmsSignedData Generate(
+ string signedContentType,
+ // FIXME Avoid accessing more than once to support CmsProcessableInputStream
+ CmsProcessable content,
+ bool encapsulate)
+ {
+ Asn1EncodableVector digestAlgs = new Asn1EncodableVector();
+ Asn1EncodableVector signerInfos = new Asn1EncodableVector();
+
+ _digests.Clear(); // clear the current preserved digest state
+
+ //
+ // add the precalculated SignerInfo objects.
+ //
+ foreach (SignerInformation signer in _signers)
+ {
+ digestAlgs.Add(Helper.FixAlgID(signer.DigestAlgorithmID));
+
+ // TODO Verify the content type and calculated digest match the precalculated SignerInfo
+ signerInfos.Add(signer.ToSignerInfo());
+ }
+
+ //
+ // add the SignerInfo objects
+ //
+ bool isCounterSignature = (signedContentType == null);
+
+ DerObjectIdentifier contentTypeOid = isCounterSignature
+ ? null
+ : new DerObjectIdentifier(signedContentType);
+
+ foreach (SignerInf signer in signerInfs)
+ {
+ try
+ {
+ digestAlgs.Add(signer.DigestAlgorithmID);
+ signerInfos.Add(signer.ToSignerInfo(contentTypeOid, content, rand));
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("encoding error.", e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key inappropriate for signature.", e);
+ }
+ catch (SignatureException e)
+ {
+ throw new CmsException("error creating signature.", e);
+ }
+ catch (CertificateEncodingException e)
+ {
+ throw new CmsException("error creating sid.", e);
+ }
+ }
+
+ Asn1Set certificates = null;
+
+ if (_certs.Count != 0)
+ {
+ certificates = CmsUtilities.CreateBerSetFromList(_certs);
+ }
+
+ Asn1Set certrevlist = null;
+
+ if (_crls.Count != 0)
+ {
+ certrevlist = CmsUtilities.CreateBerSetFromList(_crls);
+ }
+
+ Asn1OctetString octs = null;
+ if (encapsulate)
+ {
+ MemoryStream bOut = new MemoryStream();
+ if (content != null)
+ {
+ try
+ {
+ content.Write(bOut);
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("encapsulation error.", e);
+ }
+ }
+ octs = new BerOctetString(bOut.ToArray());
+ }
+
+ ContentInfo encInfo = new ContentInfo(contentTypeOid, octs);
+
+ SignedData sd = new SignedData(
+ new DerSet(digestAlgs),
+ encInfo,
+ certificates,
+ certrevlist,
+ new DerSet(signerInfos));
+
+ ContentInfo contentInfo = new ContentInfo(CmsObjectIdentifiers.SignedData, sd);
+
+ return new CmsSignedData(content, contentInfo);
+ }
+
+ /**
+ * generate a signed object that for a CMS Signed Data
+ * object - if encapsulate is true a copy
+ * of the message will be included in the signature with the
+ * default content type "data".
+ */
+ public CmsSignedData Generate(
+ CmsProcessable content,
+ bool encapsulate)
+ {
+ return this.Generate(Data, content, encapsulate);
+ }
+
+ /**
+ * generate a set of one or more SignerInformation objects representing counter signatures on
+ * the passed in SignerInformation object.
+ *
+ * @param signer the signer to be countersigned
+ * @param sigProvider the provider to be used for counter signing.
+ * @return a store containing the signers.
+ */
+ public SignerInformationStore GenerateCounterSigners(
+ SignerInformation signer)
+ {
+ return this.Generate(null, new CmsProcessableByteArray(signer.GetSignature()), false).GetSignerInfos();
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSSignedDataParser.cs b/crypto/src/cms/CMSSignedDataParser.cs
new file mode 100644
index 000000000..35a125196
--- /dev/null
+++ b/crypto/src/cms/CMSSignedDataParser.cs
@@ -0,0 +1,455 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Utilities.IO;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * Parsing class for an CMS Signed Data object from an input stream.
+ *
+ * Note: that because we are in a streaming mode only one signer can be tried and it is important
+ * that the methods on the parser are called in the appropriate order.
+ *
+ *
+ * A simple example of usage for an encapsulated signature.
+ *
+ *
+ * Two notes: first, in the example below the validity of
+ * the certificate isn't verified, just the fact that one of the certs
+ * matches the given signer, and, second, because we are in a streaming
+ * mode the order of the operations is important.
+ *
+ *
+ * CmsSignedDataParser sp = new CmsSignedDataParser(encapSigData);
+ *
+ * sp.GetSignedContent().Drain();
+ *
+ * IX509Store certs = sp.GetCertificates();
+ * SignerInformationStore signers = sp.GetSignerInfos();
+ *
+ * foreach (SignerInformation signer in signers.GetSigners())
+ * {
+ * ArrayList certList = new ArrayList(certs.GetMatches(signer.SignerID));
+ * X509Certificate cert = (X509Certificate) certList[0];
+ *
+ * Console.WriteLine("verify returns: " + signer.Verify(cert));
+ * }
+ *
+ * Note also: this class does not introduce buffering - if you are processing large files you should create
+ * the parser with:
+ *
+ * CmsSignedDataParser ep = new CmsSignedDataParser(new BufferedInputStream(encapSigData, bufSize));
+ *
+ * where bufSize is a suitably large buffer size.
+ */
+ public class CmsSignedDataParser
+ : CmsContentInfoParser
+ {
+ private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
+
+ private SignedDataParser _signedData;
+ private DerObjectIdentifier _signedContentType;
+ private CmsTypedStream _signedContent;
+ private IDictionary _digests;
+ private ISet _digestOids;
+
+ private SignerInformationStore _signerInfoStore;
+ private Asn1Set _certSet, _crlSet;
+ private bool _isCertCrlParsed;
+ private IX509Store _attributeStore;
+ private IX509Store _certificateStore;
+ private IX509Store _crlStore;
+
+ public CmsSignedDataParser(
+ byte[] sigBlock)
+ : this(new MemoryStream(sigBlock, false))
+ {
+ }
+
+ public CmsSignedDataParser(
+ CmsTypedStream signedContent,
+ byte[] sigBlock)
+ : this(signedContent, new MemoryStream(sigBlock, false))
+ {
+ }
+
+ /**
+ * base constructor - with encapsulated content
+ */
+ public CmsSignedDataParser(
+ Stream sigData)
+ : this(null, sigData)
+ {
+ }
+
+ /**
+ * base constructor
+ *
+ * @param signedContent the content that was signed.
+ * @param sigData the signature object.
+ */
+ public CmsSignedDataParser(
+ CmsTypedStream signedContent,
+ Stream sigData)
+ : base(sigData)
+ {
+ try
+ {
+ this._signedContent = signedContent;
+ this._signedData = SignedDataParser.GetInstance(this.contentInfo.GetContent(Asn1Tags.Sequence));
+ this._digests = Platform.CreateHashtable();
+ this._digestOids = new HashSet();
+
+ Asn1SetParser digAlgs = _signedData.GetDigestAlgorithms();
+ IAsn1Convertible o;
+
+ while ((o = digAlgs.ReadObject()) != null)
+ {
+ AlgorithmIdentifier id = AlgorithmIdentifier.GetInstance(o.ToAsn1Object());
+
+ try
+ {
+ string digestOid = id.ObjectID.Id;
+ string digestName = Helper.GetDigestAlgName(digestOid);
+
+ if (!this._digests.Contains(digestName))
+ {
+ this._digests[digestName] = Helper.GetDigestInstance(digestName);
+ this._digestOids.Add(digestOid);
+ }
+ }
+ catch (SecurityUtilityException)
+ {
+ // TODO Should do something other than ignore it
+ }
+ }
+
+ //
+ // If the message is simply a certificate chain message GetContent() may return null.
+ //
+ ContentInfoParser cont = _signedData.GetEncapContentInfo();
+ Asn1OctetStringParser octs = (Asn1OctetStringParser)
+ cont.GetContent(Asn1Tags.OctetString);
+
+ if (octs != null)
+ {
+ CmsTypedStream ctStr = new CmsTypedStream(
+ cont.ContentType.Id, octs.GetOctetStream());
+
+ if (_signedContent == null)
+ {
+ this._signedContent = ctStr;
+ }
+ else
+ {
+ //
+ // content passed in, need to read past empty encapsulated content info object if present
+ //
+ ctStr.Drain();
+ }
+ }
+
+ _signedContentType = _signedContent == null
+ ? cont.ContentType
+ : new DerObjectIdentifier(_signedContent.ContentType);
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("io exception: " + e.Message, e);
+ }
+
+ if (_digests.Count < 1)
+ {
+ throw new CmsException("no digests could be created for message.");
+ }
+ }
+
+ /**
+ * Return the version number for the SignedData object
+ *
+ * @return the version number
+ */
+ public int Version
+ {
+ get { return _signedData.Version.Value.IntValue; }
+ }
+
+ public ISet DigestOids
+ {
+ get { return new HashSet(_digestOids); }
+ }
+
+ /**
+ * return the collection of signers that are associated with the
+ * signatures for the message.
+ * @throws CmsException
+ */
+ public SignerInformationStore GetSignerInfos()
+ {
+ if (_signerInfoStore == null)
+ {
+ PopulateCertCrlSets();
+
+ IList signerInfos = Platform.CreateArrayList();
+ IDictionary hashes = Platform.CreateHashtable();
+
+ foreach (object digestKey in _digests.Keys)
+ {
+ hashes[digestKey] = DigestUtilities.DoFinal(
+ (IDigest)_digests[digestKey]);
+ }
+
+ try
+ {
+ Asn1SetParser s = _signedData.GetSignerInfos();
+ IAsn1Convertible o;
+
+ while ((o = s.ReadObject()) != null)
+ {
+ SignerInfo info = SignerInfo.GetInstance(o.ToAsn1Object());
+ string digestName = Helper.GetDigestAlgName(
+ info.DigestAlgorithm.ObjectID.Id);
+
+ byte[] hash = (byte[]) hashes[digestName];
+
+ signerInfos.Add(new SignerInformation(info, _signedContentType, null, new BaseDigestCalculator(hash)));
+ }
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("io exception: " + e.Message, e);
+ }
+
+ _signerInfoStore = new SignerInformationStore(signerInfos);
+ }
+
+ return _signerInfoStore;
+ }
+
+ /**
+ * return a X509Store containing the attribute certificates, if any, contained
+ * in this message.
+ *
+ * @param type type of store to create
+ * @return a store of attribute certificates
+ * @exception org.bouncycastle.x509.NoSuchStoreException if the store type isn't available.
+ * @exception CmsException if a general exception prevents creation of the X509Store
+ */
+ public IX509Store GetAttributeCertificates(
+ string type)
+ {
+ if (_attributeStore == null)
+ {
+ PopulateCertCrlSets();
+
+ _attributeStore = Helper.CreateAttributeStore(type, _certSet);
+ }
+
+ return _attributeStore;
+ }
+
+ /**
+ * return a X509Store containing the public key certificates, if any, contained
+ * in this message.
+ *
+ * @param type type of store to create
+ * @return a store of public key certificates
+ * @exception NoSuchStoreException if the store type isn't available.
+ * @exception CmsException if a general exception prevents creation of the X509Store
+ */
+ public IX509Store GetCertificates(
+ string type)
+ {
+ if (_certificateStore == null)
+ {
+ PopulateCertCrlSets();
+
+ _certificateStore = Helper.CreateCertificateStore(type, _certSet);
+ }
+
+ return _certificateStore;
+ }
+
+ /**
+ * return a X509Store containing CRLs, if any, contained
+ * in this message.
+ *
+ * @param type type of store to create
+ * @return a store of CRLs
+ * @exception NoSuchStoreException if the store type isn't available.
+ * @exception CmsException if a general exception prevents creation of the X509Store
+ */
+ public IX509Store GetCrls(
+ string type)
+ {
+ if (_crlStore == null)
+ {
+ PopulateCertCrlSets();
+
+ _crlStore = Helper.CreateCrlStore(type, _crlSet);
+ }
+
+ return _crlStore;
+ }
+
+ private void PopulateCertCrlSets()
+ {
+ if (_isCertCrlParsed)
+ return;
+
+ _isCertCrlParsed = true;
+
+ try
+ {
+ // care! Streaming - Must process the GetCertificates() result before calling GetCrls()
+ _certSet = GetAsn1Set(_signedData.GetCertificates());
+ _crlSet = GetAsn1Set(_signedData.GetCrls());
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("problem parsing cert/crl sets", e);
+ }
+ }
+
+ ///
+ /// Return the DerObjectIdentifier associated with the encapsulated
+ /// content info structure carried in the signed data.
+ ///
+ public DerObjectIdentifier SignedContentType
+ {
+ get { return _signedContentType; }
+ }
+
+ public CmsTypedStream GetSignedContent()
+ {
+ if (_signedContent == null)
+ {
+ return null;
+ }
+
+ Stream digStream = _signedContent.ContentStream;
+
+ foreach (IDigest digest in _digests.Values)
+ {
+ digStream = new DigestStream(digStream, digest, null);
+ }
+
+ return new CmsTypedStream(_signedContent.ContentType, digStream);
+ }
+
+ /**
+ * Replace the signerinformation store associated with the passed
+ * in message contained in the stream original with the new one passed in.
+ * You would probably only want to do this if you wanted to change the unsigned
+ * attributes associated with a signer, or perhaps delete one.
+ *
+ * The output stream is returned unclosed.
+ *
+ * @param original the signed data stream to be used as a base.
+ * @param signerInformationStore the new signer information store to use.
+ * @param out the stream to Write the new signed data object to.
+ * @return out.
+ */
+ public static Stream ReplaceSigners(
+ Stream original,
+ SignerInformationStore signerInformationStore,
+ Stream outStr)
+ {
+ // NB: SecureRandom would be ignored since using existing signatures only
+ CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+ CmsSignedDataParser parser = new CmsSignedDataParser(original);
+
+// gen.AddDigests(parser.DigestOids);
+ gen.AddSigners(signerInformationStore);
+
+ CmsTypedStream signedContent = parser.GetSignedContent();
+ bool encapsulate = (signedContent != null);
+ Stream contentOut = gen.Open(outStr, parser.SignedContentType.Id, encapsulate);
+ if (encapsulate)
+ {
+ Streams.PipeAll(signedContent.ContentStream, contentOut);
+ }
+
+ gen.AddAttributeCertificates(parser.GetAttributeCertificates("Collection"));
+ gen.AddCertificates(parser.GetCertificates("Collection"));
+ gen.AddCrls(parser.GetCrls("Collection"));
+
+// gen.AddSigners(parser.GetSignerInfos());
+
+ contentOut.Close();
+
+ return outStr;
+ }
+
+ /**
+ * Replace the certificate and CRL information associated with this
+ * CMSSignedData object with the new one passed in.
+ *
+ * The output stream is returned unclosed.
+ *
+ * @param original the signed data stream to be used as a base.
+ * @param certsAndCrls the new certificates and CRLs to be used.
+ * @param out the stream to Write the new signed data object to.
+ * @return out.
+ * @exception CmsException if there is an error processing the CertStore
+ */
+ public static Stream ReplaceCertificatesAndCrls(
+ Stream original,
+ IX509Store x509Certs,
+ IX509Store x509Crls,
+ IX509Store x509AttrCerts,
+ Stream outStr)
+ {
+ // NB: SecureRandom would be ignored since using existing signatures only
+ CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+ CmsSignedDataParser parser = new CmsSignedDataParser(original);
+
+ gen.AddDigests(parser.DigestOids);
+
+ CmsTypedStream signedContent = parser.GetSignedContent();
+ bool encapsulate = (signedContent != null);
+ Stream contentOut = gen.Open(outStr, parser.SignedContentType.Id, encapsulate);
+ if (encapsulate)
+ {
+ Streams.PipeAll(signedContent.ContentStream, contentOut);
+ }
+
+// gen.AddAttributeCertificates(parser.GetAttributeCertificates("Collection"));
+// gen.AddCertificates(parser.GetCertificates("Collection"));
+// gen.AddCrls(parser.GetCrls("Collection"));
+ if (x509AttrCerts != null)
+ gen.AddAttributeCertificates(x509AttrCerts);
+ if (x509Certs != null)
+ gen.AddCertificates(x509Certs);
+ if (x509Crls != null)
+ gen.AddCrls(x509Crls);
+
+ gen.AddSigners(parser.GetSignerInfos());
+
+ contentOut.Close();
+
+ return outStr;
+ }
+
+ private static Asn1Set GetAsn1Set(
+ Asn1SetParser asn1SetParser)
+ {
+ return asn1SetParser == null
+ ? null
+ : Asn1Set.GetInstance(asn1SetParser.ToAsn1Object());
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSSignedDataStreamGenerator.cs b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
new file mode 100644
index 000000000..743e9c6c1
--- /dev/null
+++ b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
@@ -0,0 +1,913 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Utilities.IO;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * General class for generating a pkcs7-signature message stream.
+ *
+ * A simple example of usage.
+ *
+ *
+ * IX509Store certs...
+ * CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator();
+ *
+ * gen.AddSigner(privateKey, cert, CmsSignedDataStreamGenerator.DIGEST_SHA1);
+ *
+ * gen.AddCertificates(certs);
+ *
+ * Stream sigOut = gen.Open(bOut);
+ *
+ * sigOut.Write(Encoding.UTF8.GetBytes("Hello World!"));
+ *
+ * sigOut.Close();
+ *
+ */
+ public class CmsSignedDataStreamGenerator
+ : CmsSignedGenerator
+ {
+ private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
+
+ private readonly IList _signerInfs = Platform.CreateArrayList();
+ private readonly ISet _messageDigestOids = new HashSet();
+ private readonly IDictionary _messageDigests = Platform.CreateHashtable();
+ private readonly IDictionary _messageHashes = Platform.CreateHashtable();
+ private bool _messageDigestsLocked;
+ private int _bufferSize;
+
+ private class DigestAndSignerInfoGeneratorHolder
+ {
+ internal readonly SignerInfoGenerator signerInf;
+ internal readonly string digestOID;
+
+ internal DigestAndSignerInfoGeneratorHolder(SignerInfoGenerator signerInf, String digestOID)
+ {
+ this.signerInf = signerInf;
+ this.digestOID = digestOID;
+ }
+
+ internal AlgorithmIdentifier DigestAlgorithm
+ {
+ get { return new AlgorithmIdentifier(new DerObjectIdentifier(this.digestOID), DerNull.Instance); }
+ }
+ }
+
+ private class SignerInfoGeneratorImpl : SignerInfoGenerator
+ {
+ private readonly CmsSignedDataStreamGenerator outer;
+
+ private readonly SignerIdentifier _signerIdentifier;
+ private readonly string _digestOID;
+ private readonly string _encOID;
+ private readonly CmsAttributeTableGenerator _sAttr;
+ private readonly CmsAttributeTableGenerator _unsAttr;
+ private readonly string _encName;
+ private readonly ISigner _sig;
+
+ internal SignerInfoGeneratorImpl(
+ CmsSignedDataStreamGenerator outer,
+ AsymmetricKeyParameter key,
+ SignerIdentifier signerIdentifier,
+ string digestOID,
+ string encOID,
+ CmsAttributeTableGenerator sAttr,
+ CmsAttributeTableGenerator unsAttr)
+ {
+ this.outer = outer;
+
+ _signerIdentifier = signerIdentifier;
+ _digestOID = digestOID;
+ _encOID = encOID;
+ _sAttr = sAttr;
+ _unsAttr = unsAttr;
+ _encName = Helper.GetEncryptionAlgName(_encOID);
+
+ string digestName = Helper.GetDigestAlgName(_digestOID);
+ string signatureName = digestName + "with" + _encName;
+
+ if (_sAttr != null)
+ {
+ _sig = Helper.GetSignatureInstance(signatureName);
+ }
+ else
+ {
+ // Note: Need to use raw signatures here since we have already calculated the digest
+ if (_encName.Equals("RSA"))
+ {
+ _sig = Helper.GetSignatureInstance("RSA");
+ }
+ else if (_encName.Equals("DSA"))
+ {
+ _sig = Helper.GetSignatureInstance("NONEwithDSA");
+ }
+ // TODO Add support for raw PSS
+// else if (_encName.equals("RSAandMGF1"))
+// {
+// _sig = CMSSignedHelper.INSTANCE.getSignatureInstance("NONEWITHRSAPSS", _sigProvider);
+// try
+// {
+// // Init the params this way to avoid having a 'raw' version of each PSS algorithm
+// Signature sig2 = CMSSignedHelper.INSTANCE.getSignatureInstance(signatureName, _sigProvider);
+// PSSParameterSpec spec = (PSSParameterSpec)sig2.getParameters().getParameterSpec(PSSParameterSpec.class);
+// _sig.setParameter(spec);
+// }
+// catch (Exception e)
+// {
+// throw new SignatureException("algorithm: " + _encName + " could not be configured.");
+// }
+// }
+ else
+ {
+ throw new SignatureException("algorithm: " + _encName + " not supported in base signatures.");
+ }
+ }
+
+ _sig.Init(true, new ParametersWithRandom(key, outer.rand));
+ }
+
+ public SignerInfo Generate(DerObjectIdentifier contentType, AlgorithmIdentifier digestAlgorithm,
+ byte[] calculatedDigest)
+ {
+ try
+ {
+ string digestName = Helper.GetDigestAlgName(_digestOID);
+ string signatureName = digestName + "with" + _encName;
+
+// AlgorithmIdentifier digAlgId = DigestAlgorithmID;
+//
+// byte[] hash = (byte[])outer._messageHashes[Helper.GetDigestAlgName(this._digestOID)];
+// outer._digests[_digestOID] = hash.Clone();
+
+ byte[] bytesToSign = calculatedDigest;
+
+ /* RFC 3852 5.4
+ * The result of the message digest calculation process depends on
+ * whether the signedAttrs field is present. When the field is absent,
+ * the result is just the message digest of the content as described
+ *
+ * above. When the field is present, however, the result is the message
+ * digest of the complete DER encoding of the SignedAttrs value
+ * contained in the signedAttrs field.
+ */
+ Asn1Set signedAttr = null;
+ if (_sAttr != null)
+ {
+ IDictionary parameters = outer.GetBaseParameters(contentType, digestAlgorithm, calculatedDigest);
+
+// Asn1.Cms.AttributeTable signed = _sAttr.GetAttributes(Collections.unmodifiableMap(parameters));
+ Asn1.Cms.AttributeTable signed = _sAttr.GetAttributes(parameters);
+
+ if (contentType == null) //counter signature
+ {
+ if (signed != null && signed[CmsAttributes.ContentType] != null)
+ {
+ IDictionary tmpSigned = signed.ToDictionary();
+ tmpSigned.Remove(CmsAttributes.ContentType);
+ signed = new Asn1.Cms.AttributeTable(tmpSigned);
+ }
+ }
+
+ signedAttr = outer.GetAttributeSet(signed);
+
+ // sig must be composed from the DER encoding.
+ bytesToSign = signedAttr.GetEncoded(Asn1Encodable.Der);
+ }
+ else
+ {
+ // Note: Need to use raw signatures here since we have already calculated the digest
+ if (_encName.Equals("RSA"))
+ {
+ DigestInfo dInfo = new DigestInfo(digestAlgorithm, calculatedDigest);
+ bytesToSign = dInfo.GetEncoded(Asn1Encodable.Der);
+ }
+ }
+
+ _sig.BlockUpdate(bytesToSign, 0, bytesToSign.Length);
+ byte[] sigBytes = _sig.GenerateSignature();
+
+ Asn1Set unsignedAttr = null;
+ if (_unsAttr != null)
+ {
+ IDictionary parameters = outer.GetBaseParameters(
+ contentType, digestAlgorithm, calculatedDigest);
+ parameters[CmsAttributeTableParameter.Signature] = sigBytes.Clone();
+
+// Asn1.Cms.AttributeTable unsigned = _unsAttr.getAttributes(Collections.unmodifiableMap(parameters));
+ Asn1.Cms.AttributeTable unsigned = _unsAttr.GetAttributes(parameters);
+
+ unsignedAttr = outer.GetAttributeSet(unsigned);
+ }
+
+ // TODO[RSAPSS] Need the ability to specify non-default parameters
+ Asn1Encodable sigX509Parameters = SignerUtilities.GetDefaultX509Parameters(signatureName);
+ AlgorithmIdentifier digestEncryptionAlgorithm = CmsSignedGenerator.GetEncAlgorithmIdentifier(
+ new DerObjectIdentifier(_encOID), sigX509Parameters);
+
+ return new SignerInfo(_signerIdentifier, digestAlgorithm,
+ signedAttr, digestEncryptionAlgorithm, new DerOctetString(sigBytes), unsignedAttr);
+ }
+ catch (IOException e)
+ {
+ throw new CmsStreamException("encoding error.", e);
+ }
+ catch (SignatureException e)
+ {
+ throw new CmsStreamException("error creating signature.", e);
+ }
+ }
+ }
+
+ public CmsSignedDataStreamGenerator()
+ {
+ }
+
+ /// Constructor allowing specific source of randomness
+ /// Instance of SecureRandom to use.
+ public CmsSignedDataStreamGenerator(
+ SecureRandom rand)
+ : base(rand)
+ {
+ }
+
+ /**
+ * Set the underlying string size for encapsulated data
+ *
+ * @param bufferSize length of octet strings to buffer the data.
+ */
+ public void SetBufferSize(
+ int bufferSize)
+ {
+ _bufferSize = bufferSize;
+ }
+
+ public void AddDigests(
+ params string[] digestOids)
+ {
+ AddDigests((IEnumerable) digestOids);
+ }
+
+ public void AddDigests(
+ IEnumerable digestOids)
+ {
+ foreach (string digestOid in digestOids)
+ {
+ ConfigureDigest(digestOid);
+ }
+ }
+
+ /**
+ * add a signer - no attributes other than the default ones will be
+ * provided here.
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ X509Certificate cert,
+ string digestOid)
+ {
+ AddSigner(privateKey, cert, digestOid,
+ new DefaultSignedAttributeTableGenerator(), null);
+ }
+
+ /**
+ * add a signer, specifying the digest encryption algorithm - no attributes other than the default ones will be
+ * provided here.
+ * @throws NoSuchProviderException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ X509Certificate cert,
+ string encryptionOid,
+ string digestOid)
+ {
+ AddSigner(privateKey, cert, encryptionOid, digestOid,
+ new DefaultSignedAttributeTableGenerator(),
+ (CmsAttributeTableGenerator)null);
+ }
+
+ /**
+ * add a signer with extra signed/unsigned attributes.
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ X509Certificate cert,
+ string digestOid,
+ Asn1.Cms.AttributeTable signedAttr,
+ Asn1.Cms.AttributeTable unsignedAttr)
+ {
+ AddSigner(privateKey, cert, digestOid,
+ new DefaultSignedAttributeTableGenerator(signedAttr),
+ new SimpleAttributeTableGenerator(unsignedAttr));
+ }
+
+ /**
+ * add a signer with extra signed/unsigned attributes - specifying digest
+ * encryption algorithm.
+ * @throws NoSuchProviderException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ X509Certificate cert,
+ string encryptionOid,
+ string digestOid,
+ Asn1.Cms.AttributeTable signedAttr,
+ Asn1.Cms.AttributeTable unsignedAttr)
+ {
+ AddSigner(privateKey, cert, encryptionOid, digestOid,
+ new DefaultSignedAttributeTableGenerator(signedAttr),
+ new SimpleAttributeTableGenerator(unsignedAttr));
+ }
+
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ X509Certificate cert,
+ string digestOid,
+ CmsAttributeTableGenerator signedAttrGenerator,
+ CmsAttributeTableGenerator unsignedAttrGenerator)
+ {
+ AddSigner(privateKey, cert, GetEncOid(privateKey, digestOid), digestOid,
+ signedAttrGenerator, unsignedAttrGenerator);
+ }
+
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ X509Certificate cert,
+ string encryptionOid,
+ string digestOid,
+ CmsAttributeTableGenerator signedAttrGenerator,
+ CmsAttributeTableGenerator unsignedAttrGenerator)
+ {
+ DoAddSigner(privateKey, GetSignerIdentifier(cert), encryptionOid, digestOid,
+ signedAttrGenerator, unsignedAttrGenerator);
+ }
+
+ /**
+ * add a signer - no attributes other than the default ones will be
+ * provided here.
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ byte[] subjectKeyID,
+ string digestOid)
+ {
+ AddSigner(privateKey, subjectKeyID, digestOid, new DefaultSignedAttributeTableGenerator(),
+ (CmsAttributeTableGenerator)null);
+ }
+
+ /**
+ * add a signer - no attributes other than the default ones will be
+ * provided here.
+ * @throws NoSuchProviderException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ byte[] subjectKeyID,
+ string encryptionOid,
+ string digestOid)
+ {
+ AddSigner(privateKey, subjectKeyID, encryptionOid, digestOid,
+ new DefaultSignedAttributeTableGenerator(),
+ (CmsAttributeTableGenerator)null);
+ }
+
+ /**
+ * add a signer with extra signed/unsigned attributes.
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ */
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ byte[] subjectKeyID,
+ string digestOid,
+ Asn1.Cms.AttributeTable signedAttr,
+ Asn1.Cms.AttributeTable unsignedAttr)
+ {
+ AddSigner(privateKey, subjectKeyID, digestOid,
+ new DefaultSignedAttributeTableGenerator(signedAttr),
+ new SimpleAttributeTableGenerator(unsignedAttr));
+ }
+
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ byte[] subjectKeyID,
+ string digestOid,
+ CmsAttributeTableGenerator signedAttrGenerator,
+ CmsAttributeTableGenerator unsignedAttrGenerator)
+ {
+ AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOid),
+ digestOid, signedAttrGenerator, unsignedAttrGenerator);
+ }
+
+ public void AddSigner(
+ AsymmetricKeyParameter privateKey,
+ byte[] subjectKeyID,
+ string encryptionOid,
+ string digestOid,
+ CmsAttributeTableGenerator signedAttrGenerator,
+ CmsAttributeTableGenerator unsignedAttrGenerator)
+ {
+ DoAddSigner(privateKey, GetSignerIdentifier(subjectKeyID), encryptionOid, digestOid,
+ signedAttrGenerator, unsignedAttrGenerator);
+ }
+
+ private void DoAddSigner(
+ AsymmetricKeyParameter privateKey,
+ SignerIdentifier signerIdentifier,
+ string encryptionOid,
+ string digestOid,
+ CmsAttributeTableGenerator signedAttrGenerator,
+ CmsAttributeTableGenerator unsignedAttrGenerator)
+ {
+ ConfigureDigest(digestOid);
+
+ SignerInfoGeneratorImpl signerInf = new SignerInfoGeneratorImpl(this, privateKey,
+ signerIdentifier, digestOid, encryptionOid, signedAttrGenerator, unsignedAttrGenerator);
+
+ _signerInfs.Add(new DigestAndSignerInfoGeneratorHolder(signerInf, digestOid));
+ }
+
+ internal override void AddSignerCallback(
+ SignerInformation si)
+ {
+ // FIXME If there were parameters in si.DigestAlgorithmID.Parameters, they are lost
+ // NB: Would need to call FixAlgID on the DigestAlgorithmID
+
+ // For precalculated signers, just need to register the algorithm, not configure a digest
+ RegisterDigestOid(si.DigestAlgorithmID.ObjectID.Id);
+ }
+
+ /**
+ * generate a signed object that for a CMS Signed Data object
+ */
+ public Stream Open(
+ Stream outStream)
+ {
+ return Open(outStream, false);
+ }
+
+ /**
+ * generate a signed object that for a CMS Signed Data
+ * object - if encapsulate is true a copy
+ * of the message will be included in the signature with the
+ * default content type "data".
+ */
+ public Stream Open(
+ Stream outStream,
+ bool encapsulate)
+ {
+ return Open(outStream, Data, encapsulate);
+ }
+
+ /**
+ * generate a signed object that for a CMS Signed Data
+ * object using the given provider - if encapsulate is true a copy
+ * of the message will be included in the signature with the
+ * default content type "data". If dataOutputStream is non null the data
+ * being signed will be written to the stream as it is processed.
+ * @param out stream the CMS object is to be written to.
+ * @param encapsulate true if data should be encapsulated.
+ * @param dataOutputStream output stream to copy the data being signed to.
+ */
+ public Stream Open(
+ Stream outStream,
+ bool encapsulate,
+ Stream dataOutputStream)
+ {
+ return Open(outStream, Data, encapsulate, dataOutputStream);
+ }
+
+ /**
+ * generate a signed object that for a CMS Signed Data
+ * object - if encapsulate is true a copy
+ * of the message will be included in the signature. The content type
+ * is set according to the OID represented by the string signedContentType.
+ */
+ public Stream Open(
+ Stream outStream,
+ string signedContentType,
+ bool encapsulate)
+ {
+ return Open(outStream, signedContentType, encapsulate, null);
+ }
+
+ /**
+ * generate a signed object that for a CMS Signed Data
+ * object using the given provider - if encapsulate is true a copy
+ * of the message will be included in the signature. The content type
+ * is set according to the OID represented by the string signedContentType.
+ * @param out stream the CMS object is to be written to.
+ * @param signedContentType OID for data to be signed.
+ * @param encapsulate true if data should be encapsulated.
+ * @param dataOutputStream output stream to copy the data being signed to.
+ */
+ public Stream Open(
+ Stream outStream,
+ string signedContentType,
+ bool encapsulate,
+ Stream dataOutputStream)
+ {
+ if (outStream == null)
+ throw new ArgumentNullException("outStream");
+ if (!outStream.CanWrite)
+ throw new ArgumentException("Expected writeable stream", "outStream");
+ if (dataOutputStream != null && !dataOutputStream.CanWrite)
+ throw new ArgumentException("Expected writeable stream", "dataOutputStream");
+
+ _messageDigestsLocked = true;
+
+ //
+ // ContentInfo
+ //
+ BerSequenceGenerator sGen = new BerSequenceGenerator(outStream);
+
+ sGen.AddObject(CmsObjectIdentifiers.SignedData);
+
+ //
+ // Signed Data
+ //
+ BerSequenceGenerator sigGen = new BerSequenceGenerator(
+ sGen.GetRawOutputStream(), 0, true);
+
+ bool isCounterSignature = (signedContentType == null);
+
+ DerObjectIdentifier contentTypeOid = isCounterSignature
+ ? null
+ : new DerObjectIdentifier(signedContentType);
+
+ sigGen.AddObject(CalculateVersion(contentTypeOid));
+
+ Asn1EncodableVector digestAlgs = new Asn1EncodableVector();
+
+ foreach (string digestOid in _messageDigestOids)
+ {
+ digestAlgs.Add(
+ new AlgorithmIdentifier(new DerObjectIdentifier(digestOid), DerNull.Instance));
+ }
+
+ {
+ byte[] tmp = new DerSet(digestAlgs).GetEncoded();
+ sigGen.GetRawOutputStream().Write(tmp, 0, tmp.Length);
+ }
+
+ BerSequenceGenerator eiGen = new BerSequenceGenerator(sigGen.GetRawOutputStream());
+ eiGen.AddObject(contentTypeOid);
+
+ // If encapsulating, add the data as an octet string in the sequence
+ Stream encapStream = encapsulate
+ ? CmsUtilities.CreateBerOctetOutputStream(eiGen.GetRawOutputStream(), 0, true, _bufferSize)
+ : null;
+
+ // Also send the data to 'dataOutputStream' if necessary
+ Stream teeStream = GetSafeTeeOutputStream(dataOutputStream, encapStream);
+
+ // Let all the digests see the data as it is written
+ Stream digStream = AttachDigestsToOutputStream(_messageDigests.Values, teeStream);
+
+ return new CmsSignedDataOutputStream(this, digStream, signedContentType, sGen, sigGen, eiGen);
+ }
+
+ private void RegisterDigestOid(
+ string digestOid)
+ {
+ if (_messageDigestsLocked)
+ {
+ if (!_messageDigestOids.Contains(digestOid))
+ throw new InvalidOperationException("Cannot register new digest OIDs after the data stream is opened");
+ }
+ else
+ {
+ _messageDigestOids.Add(digestOid);
+ }
+ }
+
+ private void ConfigureDigest(
+ string digestOid)
+ {
+ RegisterDigestOid(digestOid);
+
+ string digestName = Helper.GetDigestAlgName(digestOid);
+ IDigest dig = (IDigest)_messageDigests[digestName];
+ if (dig == null)
+ {
+ if (_messageDigestsLocked)
+ throw new InvalidOperationException("Cannot configure new digests after the data stream is opened");
+
+ dig = Helper.GetDigestInstance(digestName);
+ _messageDigests[digestName] = dig;
+ }
+ }
+
+ // TODO Make public?
+ internal void Generate(
+ Stream outStream,
+ string eContentType,
+ bool encapsulate,
+ Stream dataOutputStream,
+ CmsProcessable content)
+ {
+ Stream signedOut = Open(outStream, eContentType, encapsulate, dataOutputStream);
+ if (content != null)
+ {
+ content.Write(signedOut);
+ }
+ signedOut.Close();
+ }
+
+ // RFC3852, section 5.1:
+ // IF ((certificates is present) AND
+ // (any certificates with a type of other are present)) OR
+ // ((crls is present) AND
+ // (any crls with a type of other are present))
+ // THEN version MUST be 5
+ // ELSE
+ // IF (certificates is present) AND
+ // (any version 2 attribute certificates are present)
+ // THEN version MUST be 4
+ // ELSE
+ // IF ((certificates is present) AND
+ // (any version 1 attribute certificates are present)) OR
+ // (any SignerInfo structures are version 3) OR
+ // (encapContentInfo eContentType is other than id-data)
+ // THEN version MUST be 3
+ // ELSE version MUST be 1
+ //
+ private DerInteger CalculateVersion(
+ DerObjectIdentifier contentOid)
+ {
+ bool otherCert = false;
+ bool otherCrl = false;
+ bool attrCertV1Found = false;
+ bool attrCertV2Found = false;
+
+ if (_certs != null)
+ {
+ foreach (object obj in _certs)
+ {
+ if (obj is Asn1TaggedObject)
+ {
+ Asn1TaggedObject tagged = (Asn1TaggedObject) obj;
+
+ if (tagged.TagNo == 1)
+ {
+ attrCertV1Found = true;
+ }
+ else if (tagged.TagNo == 2)
+ {
+ attrCertV2Found = true;
+ }
+ else if (tagged.TagNo == 3)
+ {
+ otherCert = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (otherCert)
+ {
+ return new DerInteger(5);
+ }
+
+ if (_crls != null)
+ {
+ foreach (object obj in _crls)
+ {
+ if (obj is Asn1TaggedObject)
+ {
+ otherCrl = true;
+ break;
+ }
+ }
+ }
+
+ if (otherCrl)
+ {
+ return new DerInteger(5);
+ }
+
+ if (attrCertV2Found)
+ {
+ return new DerInteger(4);
+ }
+
+ if (attrCertV1Found || !CmsObjectIdentifiers.Data.Equals(contentOid) || CheckForVersion3(_signers))
+ {
+ return new DerInteger(3);
+ }
+
+ return new DerInteger(1);
+ }
+
+ private bool CheckForVersion3(
+ IList signerInfos)
+ {
+ foreach (SignerInformation si in signerInfos)
+ {
+ SignerInfo s = SignerInfo.GetInstance(si.ToSignerInfo());
+
+ if (s.Version.Value.IntValue == 3)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static Stream AttachDigestsToOutputStream(ICollection digests, Stream s)
+ {
+ Stream result = s;
+ foreach (IDigest digest in digests)
+ {
+ result = GetSafeTeeOutputStream(result, new DigOutputStream(digest));
+ }
+ return result;
+ }
+
+ private static Stream GetSafeOutputStream(Stream s)
+ {
+ if (s == null)
+ return new NullOutputStream();
+ return s;
+ }
+
+ private static Stream GetSafeTeeOutputStream(Stream s1, Stream s2)
+ {
+ if (s1 == null)
+ return GetSafeOutputStream(s2);
+ if (s2 == null)
+ return GetSafeOutputStream(s1);
+ return new TeeOutputStream(s1, s2);
+ }
+
+ private class CmsSignedDataOutputStream
+ : BaseOutputStream
+ {
+ private readonly CmsSignedDataStreamGenerator outer;
+
+ private Stream _out;
+ private DerObjectIdentifier _contentOID;
+ private BerSequenceGenerator _sGen;
+ private BerSequenceGenerator _sigGen;
+ private BerSequenceGenerator _eiGen;
+
+ public CmsSignedDataOutputStream(
+ CmsSignedDataStreamGenerator outer,
+ Stream outStream,
+ string contentOID,
+ BerSequenceGenerator sGen,
+ BerSequenceGenerator sigGen,
+ BerSequenceGenerator eiGen)
+ {
+ this.outer = outer;
+
+ _out = outStream;
+ _contentOID = new DerObjectIdentifier(contentOID);
+ _sGen = sGen;
+ _sigGen = sigGen;
+ _eiGen = eiGen;
+ }
+
+ public override void WriteByte(
+ byte b)
+ {
+ _out.WriteByte(b);
+ }
+
+ public override void Write(
+ byte[] bytes,
+ int off,
+ int len)
+ {
+ _out.Write(bytes, off, len);
+ }
+
+ public override void Close()
+ {
+ _out.Close();
+
+ // TODO Parent context(s) should really be be closed explicitly
+
+ _eiGen.Close();
+
+ outer._digests.Clear(); // clear the current preserved digest state
+
+ if (outer._certs.Count > 0)
+ {
+ Asn1Set certs = CmsUtilities.CreateBerSetFromList(outer._certs);
+
+ WriteToGenerator(_sigGen, new BerTaggedObject(false, 0, certs));
+ }
+
+ if (outer._crls.Count > 0)
+ {
+ Asn1Set crls = CmsUtilities.CreateBerSetFromList(outer._crls);
+
+ WriteToGenerator(_sigGen, new BerTaggedObject(false, 1, crls));
+ }
+
+ //
+ // Calculate the digest hashes
+ //
+ foreach (DictionaryEntry de in outer._messageDigests)
+ {
+ outer._messageHashes.Add(de.Key, DigestUtilities.DoFinal((IDigest)de.Value));
+ }
+
+ // TODO If the digest OIDs for precalculated signers weren't mixed in with
+ // the others, we could fill in outer._digests here, instead of SignerInfoGenerator.Generate
+
+ //
+ // collect all the SignerInfo objects
+ //
+ Asn1EncodableVector signerInfos = new Asn1EncodableVector();
+
+ //
+ // add the generated SignerInfo objects
+ //
+ {
+ foreach (DigestAndSignerInfoGeneratorHolder holder in outer._signerInfs)
+ {
+ AlgorithmIdentifier digestAlgorithm = holder.DigestAlgorithm;
+
+ byte[] calculatedDigest = (byte[])outer._messageHashes[
+ Helper.GetDigestAlgName(holder.digestOID)];
+ outer._digests[holder.digestOID] = calculatedDigest.Clone();
+
+ signerInfos.Add(holder.signerInf.Generate(_contentOID, digestAlgorithm, calculatedDigest));
+ }
+ }
+
+ //
+ // add the precalculated SignerInfo objects.
+ //
+ {
+ foreach (SignerInformation signer in outer._signers)
+ {
+ // TODO Verify the content type and calculated digest match the precalculated SignerInfo
+// if (!signer.ContentType.Equals(_contentOID))
+// {
+// // TODO The precalculated content type did not match - error?
+// }
+//
+// byte[] calculatedDigest = (byte[])outer._digests[signer.DigestAlgOid];
+// if (calculatedDigest == null)
+// {
+// // TODO We can't confirm this digest because we didn't calculate it - error?
+// }
+// else
+// {
+// if (!Arrays.AreEqual(signer.GetContentDigest(), calculatedDigest))
+// {
+// // TODO The precalculated digest did not match - error?
+// }
+// }
+
+ signerInfos.Add(signer.ToSignerInfo());
+ }
+ }
+
+ WriteToGenerator(_sigGen, new DerSet(signerInfos));
+
+ _sigGen.Close();
+ _sGen.Close();
+ base.Close();
+ }
+
+ private static void WriteToGenerator(
+ Asn1Generator ag,
+ Asn1Encodable ae)
+ {
+ byte[] encoded = ae.GetEncoded();
+ ag.GetRawOutputStream().Write(encoded, 0, encoded.Length);
+ }
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSSignedGenerator.cs b/crypto/src/cms/CMSSignedGenerator.cs
new file mode 100644
index 000000000..f272c830e
--- /dev/null
+++ b/crypto/src/cms/CMSSignedGenerator.cs
@@ -0,0 +1,261 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Oiw;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.TeleTrust;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Utilities.IO;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Cms
+{
+ public class CmsSignedGenerator
+ {
+ /**
+ * Default type for the signed data.
+ */
+ public static readonly string Data = CmsObjectIdentifiers.Data.Id;
+
+ public static readonly string DigestSha1 = OiwObjectIdentifiers.IdSha1.Id;
+ public static readonly string DigestSha224 = NistObjectIdentifiers.IdSha224.Id;
+ public static readonly string DigestSha256 = NistObjectIdentifiers.IdSha256.Id;
+ public static readonly string DigestSha384 = NistObjectIdentifiers.IdSha384.Id;
+ public static readonly string DigestSha512 = NistObjectIdentifiers.IdSha512.Id;
+ public static readonly string DigestMD5 = PkcsObjectIdentifiers.MD5.Id;
+ public static readonly string DigestGost3411 = CryptoProObjectIdentifiers.GostR3411.Id;
+ public static readonly string DigestRipeMD128 = TeleTrusTObjectIdentifiers.RipeMD128.Id;
+ public static readonly string DigestRipeMD160 = TeleTrusTObjectIdentifiers.RipeMD160.Id;
+ public static readonly string DigestRipeMD256 = TeleTrusTObjectIdentifiers.RipeMD256.Id;
+
+ public static readonly string EncryptionRsa = PkcsObjectIdentifiers.RsaEncryption.Id;
+ public static readonly string EncryptionDsa = X9ObjectIdentifiers.IdDsaWithSha1.Id;
+ public static readonly string EncryptionECDsa = X9ObjectIdentifiers.ECDsaWithSha1.Id;
+ public static readonly string EncryptionRsaPss = PkcsObjectIdentifiers.IdRsassaPss.Id;
+ public static readonly string EncryptionGost3410 = CryptoProObjectIdentifiers.GostR3410x94.Id;
+ public static readonly string EncryptionECGost3410 = CryptoProObjectIdentifiers.GostR3410x2001.Id;
+
+ private static readonly string EncryptionECDsaWithSha1 = X9ObjectIdentifiers.ECDsaWithSha1.Id;
+ private static readonly string EncryptionECDsaWithSha224 = X9ObjectIdentifiers.ECDsaWithSha224.Id;
+ private static readonly string EncryptionECDsaWithSha256 = X9ObjectIdentifiers.ECDsaWithSha256.Id;
+ private static readonly string EncryptionECDsaWithSha384 = X9ObjectIdentifiers.ECDsaWithSha384.Id;
+ private static readonly string EncryptionECDsaWithSha512 = X9ObjectIdentifiers.ECDsaWithSha512.Id;
+
+ private static readonly ISet noParams = new HashSet();
+ private static readonly IDictionary ecAlgorithms = Platform.CreateHashtable();
+
+ static CmsSignedGenerator()
+ {
+ noParams.Add(EncryptionDsa);
+// noParams.Add(EncryptionECDsa);
+ noParams.Add(EncryptionECDsaWithSha1);
+ noParams.Add(EncryptionECDsaWithSha224);
+ noParams.Add(EncryptionECDsaWithSha256);
+ noParams.Add(EncryptionECDsaWithSha384);
+ noParams.Add(EncryptionECDsaWithSha512);
+
+ ecAlgorithms.Add(DigestSha1, EncryptionECDsaWithSha1);
+ ecAlgorithms.Add(DigestSha224, EncryptionECDsaWithSha224);
+ ecAlgorithms.Add(DigestSha256, EncryptionECDsaWithSha256);
+ ecAlgorithms.Add(DigestSha384, EncryptionECDsaWithSha384);
+ ecAlgorithms.Add(DigestSha512, EncryptionECDsaWithSha512);
+ }
+
+ internal IList _certs = Platform.CreateArrayList();
+ internal IList _crls = Platform.CreateArrayList();
+ internal IList _signers = Platform.CreateArrayList();
+ internal IDictionary _digests = Platform.CreateHashtable();
+
+ protected readonly SecureRandom rand;
+
+ protected CmsSignedGenerator()
+ : this(new SecureRandom())
+ {
+ }
+
+ /// Constructor allowing specific source of randomness
+ /// Instance of SecureRandom to use.
+ protected CmsSignedGenerator(
+ SecureRandom rand)
+ {
+ this.rand = rand;
+ }
+
+ protected string GetEncOid(
+ AsymmetricKeyParameter key,
+ string digestOID)
+ {
+ string encOID = null;
+
+ if (key is RsaKeyParameters)
+ {
+ if (!((RsaKeyParameters) key).IsPrivate)
+ throw new ArgumentException("Expected RSA private key");
+
+ encOID = EncryptionRsa;
+ }
+ else if (key is DsaPrivateKeyParameters)
+ {
+ if (!digestOID.Equals(DigestSha1))
+ throw new ArgumentException("can't mix DSA with anything but SHA1");
+
+ encOID = EncryptionDsa;
+ }
+ else if (key is ECPrivateKeyParameters)
+ {
+ ECPrivateKeyParameters ecPrivKey = (ECPrivateKeyParameters) key;
+ string algName = ecPrivKey.AlgorithmName;
+
+ if (algName == "ECGOST3410")
+ {
+ encOID = EncryptionECGost3410;
+ }
+ else
+ {
+ // TODO Should we insist on algName being one of "EC" or "ECDSA", as Java does?
+ encOID = (string) ecAlgorithms[digestOID];
+
+ if (encOID == null)
+ throw new ArgumentException("can't mix ECDSA with anything but SHA family digests");
+ }
+ }
+ else if (key is Gost3410PrivateKeyParameters)
+ {
+ encOID = EncryptionGost3410;
+ }
+ else
+ {
+ throw new ArgumentException("Unknown algorithm in CmsSignedGenerator.GetEncOid");
+ }
+
+ return encOID;
+ }
+
+ internal static AlgorithmIdentifier GetEncAlgorithmIdentifier(
+ DerObjectIdentifier encOid,
+ Asn1Encodable sigX509Parameters)
+ {
+ if (noParams.Contains(encOid.Id))
+ {
+ return new AlgorithmIdentifier(encOid);
+ }
+
+ return new AlgorithmIdentifier(encOid, sigX509Parameters);
+ }
+
+ internal protected virtual IDictionary GetBaseParameters(
+ DerObjectIdentifier contentType,
+ AlgorithmIdentifier digAlgId,
+ byte[] hash)
+ {
+ IDictionary param = Platform.CreateHashtable();
+
+ if (contentType != null)
+ {
+ param[CmsAttributeTableParameter.ContentType] = contentType;
+ }
+
+ param[CmsAttributeTableParameter.DigestAlgorithmIdentifier] = digAlgId;
+ param[CmsAttributeTableParameter.Digest] = hash.Clone();
+
+ return param;
+ }
+
+ internal protected virtual Asn1Set GetAttributeSet(
+ Asn1.Cms.AttributeTable attr)
+ {
+ return attr == null
+ ? null
+ : new DerSet(attr.ToAsn1EncodableVector());
+ }
+
+ public void AddCertificates(
+ IX509Store certStore)
+ {
+ CollectionUtilities.AddRange(_certs, CmsUtilities.GetCertificatesFromStore(certStore));
+ }
+
+ public void AddCrls(
+ IX509Store crlStore)
+ {
+ CollectionUtilities.AddRange(_crls, CmsUtilities.GetCrlsFromStore(crlStore));
+ }
+
+ /**
+ * Add the attribute certificates contained in the passed in store to the
+ * generator.
+ *
+ * @param store a store of Version 2 attribute certificates
+ * @throws CmsException if an error occurse processing the store.
+ */
+ public void AddAttributeCertificates(
+ IX509Store store)
+ {
+ try
+ {
+ foreach (IX509AttributeCertificate attrCert in store.GetMatches(null))
+ {
+ _certs.Add(new DerTaggedObject(false, 2,
+ AttributeCertificate.GetInstance(Asn1Object.FromByteArray(attrCert.GetEncoded()))));
+ }
+ }
+ catch (Exception e)
+ {
+ throw new CmsException("error processing attribute certs", e);
+ }
+ }
+
+ /**
+ * Add a store of precalculated signers to the generator.
+ *
+ * @param signerStore store of signers
+ */
+ public void AddSigners(
+ SignerInformationStore signerStore)
+ {
+ foreach (SignerInformation o in signerStore.GetSigners())
+ {
+ _signers.Add(o);
+ AddSignerCallback(o);
+ }
+ }
+
+ /**
+ * Return a map of oids and byte arrays representing the digests calculated on the content during
+ * the last generate.
+ *
+ * @return a map of oids (as String objects) and byte[] representing digests.
+ */
+ public IDictionary GetGeneratedDigests()
+ {
+ return Platform.CreateHashtable(_digests);
+ }
+
+ internal virtual void AddSignerCallback(
+ SignerInformation si)
+ {
+ }
+
+ internal static SignerIdentifier GetSignerIdentifier(X509Certificate cert)
+ {
+ return new SignerIdentifier(CmsUtilities.GetIssuerAndSerialNumber(cert));
+ }
+
+ internal static SignerIdentifier GetSignerIdentifier(byte[] subjectKeyIdentifier)
+ {
+ return new SignerIdentifier(new DerOctetString(subjectKeyIdentifier));
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSSignedHelper.cs b/crypto/src/cms/CMSSignedHelper.cs
new file mode 100644
index 000000000..b3406fc06
--- /dev/null
+++ b/crypto/src/cms/CMSSignedHelper.cs
@@ -0,0 +1,319 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.Eac;
+using Org.BouncyCastle.Asn1.Iana;
+using Org.BouncyCastle.Asn1.Misc;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Oiw;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.TeleTrust;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal class CmsSignedHelper
+ {
+ internal static readonly CmsSignedHelper Instance = new CmsSignedHelper();
+
+ private static readonly IDictionary encryptionAlgs = Platform.CreateHashtable();
+ private static readonly IDictionary digestAlgs = Platform.CreateHashtable();
+ private static readonly IDictionary digestAliases = Platform.CreateHashtable();
+
+ private static void AddEntries(DerObjectIdentifier oid, string digest, string encryption)
+ {
+ string alias = oid.Id;
+ digestAlgs.Add(alias, digest);
+ encryptionAlgs.Add(alias, encryption);
+ }
+
+ static CmsSignedHelper()
+ {
+ AddEntries(NistObjectIdentifiers.DsaWithSha224, "SHA224", "DSA");
+ AddEntries(NistObjectIdentifiers.DsaWithSha256, "SHA256", "DSA");
+ AddEntries(NistObjectIdentifiers.DsaWithSha384, "SHA384", "DSA");
+ AddEntries(NistObjectIdentifiers.DsaWithSha512, "SHA512", "DSA");
+ AddEntries(OiwObjectIdentifiers.DsaWithSha1, "SHA1", "DSA");
+ AddEntries(OiwObjectIdentifiers.MD4WithRsa, "MD4", "RSA");
+ AddEntries(OiwObjectIdentifiers.MD4WithRsaEncryption, "MD4", "RSA");
+ AddEntries(OiwObjectIdentifiers.MD5WithRsa, "MD5", "RSA");
+ AddEntries(OiwObjectIdentifiers.Sha1WithRsa, "SHA1", "RSA");
+ AddEntries(PkcsObjectIdentifiers.MD2WithRsaEncryption, "MD2", "RSA");
+ AddEntries(PkcsObjectIdentifiers.MD4WithRsaEncryption, "MD4", "RSA");
+ AddEntries(PkcsObjectIdentifiers.MD5WithRsaEncryption, "MD5", "RSA");
+ AddEntries(PkcsObjectIdentifiers.Sha1WithRsaEncryption, "SHA1", "RSA");
+ AddEntries(PkcsObjectIdentifiers.Sha224WithRsaEncryption, "SHA224", "RSA");
+ AddEntries(PkcsObjectIdentifiers.Sha256WithRsaEncryption, "SHA256", "RSA");
+ AddEntries(PkcsObjectIdentifiers.Sha384WithRsaEncryption, "SHA384", "RSA");
+ AddEntries(PkcsObjectIdentifiers.Sha512WithRsaEncryption, "SHA512", "RSA");
+ AddEntries(X9ObjectIdentifiers.ECDsaWithSha1, "SHA1", "ECDSA");
+ AddEntries(X9ObjectIdentifiers.ECDsaWithSha224, "SHA224", "ECDSA");
+ AddEntries(X9ObjectIdentifiers.ECDsaWithSha256, "SHA256", "ECDSA");
+ AddEntries(X9ObjectIdentifiers.ECDsaWithSha384, "SHA384", "ECDSA");
+ AddEntries(X9ObjectIdentifiers.ECDsaWithSha512, "SHA512", "ECDSA");
+ AddEntries(X9ObjectIdentifiers.IdDsaWithSha1, "SHA1", "DSA");
+ AddEntries(EacObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1", "ECDSA");
+ AddEntries(EacObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224", "ECDSA");
+ AddEntries(EacObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256", "ECDSA");
+ AddEntries(EacObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384", "ECDSA");
+ AddEntries(EacObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512", "ECDSA");
+ AddEntries(EacObjectIdentifiers.id_TA_RSA_v1_5_SHA_1, "SHA1", "RSA");
+ AddEntries(EacObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "SHA256", "RSA");
+ AddEntries(EacObjectIdentifiers.id_TA_RSA_PSS_SHA_1, "SHA1", "RSAandMGF1");
+ AddEntries(EacObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "SHA256", "RSAandMGF1");
+
+ encryptionAlgs.Add(X9ObjectIdentifiers.IdDsa.Id, "DSA");
+ encryptionAlgs.Add(PkcsObjectIdentifiers.RsaEncryption.Id, "RSA");
+ encryptionAlgs.Add(TeleTrusTObjectIdentifiers.TeleTrusTRsaSignatureAlgorithm, "RSA");
+ encryptionAlgs.Add(X509ObjectIdentifiers.IdEARsa.Id, "RSA");
+ encryptionAlgs.Add(CmsSignedGenerator.EncryptionRsaPss, "RSAandMGF1");
+ encryptionAlgs.Add(CryptoProObjectIdentifiers.GostR3410x94.Id, "GOST3410");
+ encryptionAlgs.Add(CryptoProObjectIdentifiers.GostR3410x2001.Id, "ECGOST3410");
+ encryptionAlgs.Add("1.3.6.1.4.1.5849.1.6.2", "ECGOST3410");
+ encryptionAlgs.Add("1.3.6.1.4.1.5849.1.1.5", "GOST3410");
+
+ digestAlgs.Add(PkcsObjectIdentifiers.MD2.Id, "MD2");
+ digestAlgs.Add(PkcsObjectIdentifiers.MD4.Id, "MD4");
+ digestAlgs.Add(PkcsObjectIdentifiers.MD5.Id, "MD5");
+ digestAlgs.Add(OiwObjectIdentifiers.IdSha1.Id, "SHA1");
+ digestAlgs.Add(NistObjectIdentifiers.IdSha224.Id, "SHA224");
+ digestAlgs.Add(NistObjectIdentifiers.IdSha256.Id, "SHA256");
+ digestAlgs.Add(NistObjectIdentifiers.IdSha384.Id, "SHA384");
+ digestAlgs.Add(NistObjectIdentifiers.IdSha512.Id, "SHA512");
+ digestAlgs.Add(TeleTrusTObjectIdentifiers.RipeMD128.Id, "RIPEMD128");
+ digestAlgs.Add(TeleTrusTObjectIdentifiers.RipeMD160.Id, "RIPEMD160");
+ digestAlgs.Add(TeleTrusTObjectIdentifiers.RipeMD256.Id, "RIPEMD256");
+ digestAlgs.Add(CryptoProObjectIdentifiers.GostR3411.Id, "GOST3411");
+ digestAlgs.Add("1.3.6.1.4.1.5849.1.2.1", "GOST3411");
+
+ digestAliases.Add("SHA1", new string[] { "SHA-1" });
+ digestAliases.Add("SHA224", new string[] { "SHA-224" });
+ digestAliases.Add("SHA256", new string[] { "SHA-256" });
+ digestAliases.Add("SHA384", new string[] { "SHA-384" });
+ digestAliases.Add("SHA512", new string[] { "SHA-512" });
+ }
+
+ /**
+ * Return the digest algorithm using one of the standard JCA string
+ * representations rather than the algorithm identifier (if possible).
+ */
+ internal string GetDigestAlgName(
+ string digestAlgOid)
+ {
+ string algName = (string)digestAlgs[digestAlgOid];
+
+ if (algName != null)
+ {
+ return algName;
+ }
+
+ return digestAlgOid;
+ }
+
+ internal string[] GetDigestAliases(
+ string algName)
+ {
+ string[] aliases = (string[]) digestAliases[algName];
+
+ return aliases == null ? new String[0] : (string[]) aliases.Clone();
+ }
+
+ /**
+ * Return the digest encryption algorithm using one of the standard
+ * JCA string representations rather than the algorithm identifier (if
+ * possible).
+ */
+ internal string GetEncryptionAlgName(
+ string encryptionAlgOid)
+ {
+ string algName = (string) encryptionAlgs[encryptionAlgOid];
+
+ if (algName != null)
+ {
+ return algName;
+ }
+
+ return encryptionAlgOid;
+ }
+
+ internal IDigest GetDigestInstance(
+ string algorithm)
+ {
+ try
+ {
+ return DigestUtilities.GetDigest(algorithm);
+ }
+ catch (SecurityUtilityException e)
+ {
+ // This is probably superfluous on C#, since no provider infrastructure,
+ // assuming DigestUtilities already knows all the aliases
+ foreach (string alias in GetDigestAliases(algorithm))
+ {
+ try { return DigestUtilities.GetDigest(alias); }
+ catch (SecurityUtilityException) {}
+ }
+ throw e;
+ }
+ }
+
+ internal ISigner GetSignatureInstance(
+ string algorithm)
+ {
+ return SignerUtilities.GetSigner(algorithm);
+ }
+
+ internal IX509Store CreateAttributeStore(
+ string type,
+ Asn1Set certSet)
+ {
+ IList certs = Platform.CreateArrayList();
+
+ if (certSet != null)
+ {
+ foreach (Asn1Encodable ae in certSet)
+ {
+ try
+ {
+ Asn1Object obj = ae.ToAsn1Object();
+
+ if (obj is Asn1TaggedObject)
+ {
+ Asn1TaggedObject tagged = (Asn1TaggedObject)obj;
+
+ if (tagged.TagNo == 2)
+ {
+ certs.Add(
+ new X509V2AttributeCertificate(
+ Asn1Sequence.GetInstance(tagged, false).GetEncoded()));
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new CmsException("can't re-encode attribute certificate!", ex);
+ }
+ }
+ }
+
+ try
+ {
+ return X509StoreFactory.Create(
+ "AttributeCertificate/" + type,
+ new X509CollectionStoreParameters(certs));
+ }
+ catch (ArgumentException e)
+ {
+ throw new CmsException("can't setup the X509Store", e);
+ }
+ }
+
+ internal IX509Store CreateCertificateStore(
+ string type,
+ Asn1Set certSet)
+ {
+ IList certs = Platform.CreateArrayList();
+
+ if (certSet != null)
+ {
+ AddCertsFromSet(certs, certSet);
+ }
+
+ try
+ {
+ return X509StoreFactory.Create(
+ "Certificate/" + type,
+ new X509CollectionStoreParameters(certs));
+ }
+ catch (ArgumentException e)
+ {
+ throw new CmsException("can't setup the X509Store", e);
+ }
+ }
+
+ internal IX509Store CreateCrlStore(
+ string type,
+ Asn1Set crlSet)
+ {
+ IList crls = Platform.CreateArrayList();
+
+ if (crlSet != null)
+ {
+ AddCrlsFromSet(crls, crlSet);
+ }
+
+ try
+ {
+ return X509StoreFactory.Create(
+ "CRL/" + type,
+ new X509CollectionStoreParameters(crls));
+ }
+ catch (ArgumentException e)
+ {
+ throw new CmsException("can't setup the X509Store", e);
+ }
+ }
+
+ private void AddCertsFromSet(
+ IList certs,
+ Asn1Set certSet)
+ {
+ X509CertificateParser cf = new X509CertificateParser();
+
+ foreach (Asn1Encodable ae in certSet)
+ {
+ try
+ {
+ Asn1Object obj = ae.ToAsn1Object();
+
+ if (obj is Asn1Sequence)
+ {
+ // TODO Build certificate directly from sequence?
+ certs.Add(cf.ReadCertificate(obj.GetEncoded()));
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new CmsException("can't re-encode certificate!", ex);
+ }
+ }
+ }
+
+ private void AddCrlsFromSet(
+ IList crls,
+ Asn1Set crlSet)
+ {
+ X509CrlParser cf = new X509CrlParser();
+
+ foreach (Asn1Encodable ae in crlSet)
+ {
+ try
+ {
+ // TODO Build CRL directly from ae.ToAsn1Object()?
+ crls.Add(cf.ReadCrl(ae.GetEncoded()));
+ }
+ catch (Exception ex)
+ {
+ throw new CmsException("can't re-encode CRL!", ex);
+ }
+ }
+ }
+
+ internal AlgorithmIdentifier FixAlgID(
+ AlgorithmIdentifier algId)
+ {
+ if (algId.Parameters == null)
+ return new AlgorithmIdentifier(algId.ObjectID, DerNull.Instance);
+
+ return algId;
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSStreamException.cs b/crypto/src/cms/CMSStreamException.cs
new file mode 100644
index 000000000..bf0a6adf4
--- /dev/null
+++ b/crypto/src/cms/CMSStreamException.cs
@@ -0,0 +1,29 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+ [Serializable]
+#endif
+ public class CmsStreamException
+ : IOException
+ {
+ public CmsStreamException()
+ {
+ }
+
+ public CmsStreamException(
+ string name)
+ : base(name)
+ {
+ }
+
+ public CmsStreamException(
+ string name,
+ Exception e)
+ : base(name, e)
+ {
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSTypedStream.cs b/crypto/src/cms/CMSTypedStream.cs
new file mode 100644
index 000000000..9cb314211
--- /dev/null
+++ b/crypto/src/cms/CMSTypedStream.cs
@@ -0,0 +1,72 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+ public class CmsTypedStream
+ {
+ private const int BufferSize = 32 * 1024;
+
+ private readonly string _oid;
+ private readonly Stream _in;
+
+ public CmsTypedStream(
+ Stream inStream)
+ : this(PkcsObjectIdentifiers.Data.Id, inStream, BufferSize)
+ {
+ }
+
+ public CmsTypedStream(
+ string oid,
+ Stream inStream)
+ : this(oid, inStream, BufferSize)
+ {
+ }
+
+ public CmsTypedStream(
+ string oid,
+ Stream inStream,
+ int bufSize)
+ {
+ _oid = oid;
+#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT
+ _in = new FullReaderStream(inStream);
+#else
+ _in = new FullReaderStream(new BufferedStream(inStream, bufSize));
+#endif
+ }
+
+ public string ContentType
+ {
+ get { return _oid; }
+ }
+
+ public Stream ContentStream
+ {
+ get { return _in; }
+ }
+
+ public void Drain()
+ {
+ Streams.Drain(_in);
+ _in.Close();
+ }
+
+ private class FullReaderStream : FilterStream
+ {
+ internal FullReaderStream(Stream input)
+ : base(input)
+ {
+ }
+
+ public override int Read(byte[] buf, int off, int len)
+ {
+ return Streams.ReadFully(base.s, buf, off, len);
+ }
+ }
+ }
+}
diff --git a/crypto/src/cms/CMSUtils.cs b/crypto/src/cms/CMSUtils.cs
new file mode 100644
index 000000000..95d710607
--- /dev/null
+++ b/crypto/src/cms/CMSUtils.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal class CmsUtilities
+ {
+ // TODO Is there a .NET equivalent to this?
+// private static readonly Runtime RUNTIME = Runtime.getRuntime();
+
+ internal static int MaximumMemory
+ {
+ get
+ {
+ // TODO Is there a .NET equivalent to this?
+ long maxMem = int.MaxValue;//RUNTIME.maxMemory();
+
+ if (maxMem > int.MaxValue)
+ {
+ return int.MaxValue;
+ }
+
+ return (int)maxMem;
+ }
+ }
+
+ internal static ContentInfo ReadContentInfo(
+ byte[] input)
+ {
+ // enforce limit checking as from a byte array
+ return ReadContentInfo(new Asn1InputStream(input));
+ }
+
+ internal static ContentInfo ReadContentInfo(
+ Stream input)
+ {
+ // enforce some limit checking
+ return ReadContentInfo(new Asn1InputStream(input, MaximumMemory));
+ }
+
+ private static ContentInfo ReadContentInfo(
+ Asn1InputStream aIn)
+ {
+ try
+ {
+ return ContentInfo.GetInstance(aIn.ReadObject());
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("IOException reading content.", e);
+ }
+ catch (InvalidCastException e)
+ {
+ throw new CmsException("Malformed content.", e);
+ }
+ catch (ArgumentException e)
+ {
+ throw new CmsException("Malformed content.", e);
+ }
+ }
+
+ public static byte[] StreamToByteArray(
+ Stream inStream)
+ {
+ return Streams.ReadAll(inStream);
+ }
+
+ public static byte[] StreamToByteArray(
+ Stream inStream,
+ int limit)
+ {
+ return Streams.ReadAllLimited(inStream, limit);
+ }
+
+ public static IList GetCertificatesFromStore(
+ IX509Store certStore)
+ {
+ try
+ {
+ IList certs = Platform.CreateArrayList();
+
+ if (certStore != null)
+ {
+ foreach (X509Certificate c in certStore.GetMatches(null))
+ {
+ certs.Add(
+ X509CertificateStructure.GetInstance(
+ Asn1Object.FromByteArray(c.GetEncoded())));
+ }
+ }
+
+ return certs;
+ }
+ catch (CertificateEncodingException e)
+ {
+ throw new CmsException("error encoding certs", e);
+ }
+ catch (Exception e)
+ {
+ throw new CmsException("error processing certs", e);
+ }
+ }
+
+ public static IList GetCrlsFromStore(
+ IX509Store crlStore)
+ {
+ try
+ {
+ IList crls = Platform.CreateArrayList();
+
+ if (crlStore != null)
+ {
+ foreach (X509Crl c in crlStore.GetMatches(null))
+ {
+ crls.Add(
+ CertificateList.GetInstance(
+ Asn1Object.FromByteArray(c.GetEncoded())));
+ }
+ }
+
+ return crls;
+ }
+ catch (CrlException e)
+ {
+ throw new CmsException("error encoding crls", e);
+ }
+ catch (Exception e)
+ {
+ throw new CmsException("error processing crls", e);
+ }
+ }
+
+ public static Asn1Set CreateBerSetFromList(
+ IList berObjects)
+ {
+ Asn1EncodableVector v = new Asn1EncodableVector();
+
+ foreach (Asn1Encodable ae in berObjects)
+ {
+ v.Add(ae);
+ }
+
+ return new BerSet(v);
+ }
+
+ public static Asn1Set CreateDerSetFromList(
+ IList derObjects)
+ {
+ Asn1EncodableVector v = new Asn1EncodableVector();
+
+ foreach (Asn1Encodable ae in derObjects)
+ {
+ v.Add(ae);
+ }
+
+ return new DerSet(v);
+ }
+
+ internal static Stream CreateBerOctetOutputStream(Stream s, int tagNo, bool isExplicit, int bufferSize)
+ {
+ BerOctetStringGenerator octGen = new BerOctetStringGenerator(s, tagNo, isExplicit);
+ return octGen.GetOctetOutputStream(bufferSize);
+ }
+
+ internal static TbsCertificateStructure GetTbsCertificateStructure(X509Certificate cert)
+ {
+ return TbsCertificateStructure.GetInstance(Asn1Object.FromByteArray(cert.GetTbsCertificate()));
+ }
+
+ internal static IssuerAndSerialNumber GetIssuerAndSerialNumber(X509Certificate cert)
+ {
+ TbsCertificateStructure tbsCert = GetTbsCertificateStructure(cert);
+ return new IssuerAndSerialNumber(tbsCert.Issuer, tbsCert.SerialNumber.Value);
+ }
+ }
+}
diff --git a/crypto/src/cms/CounterSignatureDigestCalculator.cs b/crypto/src/cms/CounterSignatureDigestCalculator.cs
new file mode 100644
index 000000000..6f8bf65a2
--- /dev/null
+++ b/crypto/src/cms/CounterSignatureDigestCalculator.cs
@@ -0,0 +1,28 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal class CounterSignatureDigestCalculator
+ : IDigestCalculator
+ {
+ private readonly string alg;
+ private readonly byte[] data;
+
+ internal CounterSignatureDigestCalculator(
+ string alg,
+ byte[] data)
+ {
+ this.alg = alg;
+ this.data = data;
+ }
+
+ public byte[] GetDigest()
+ {
+ IDigest digest = CmsSignedHelper.Instance.GetDigestInstance(alg);
+ return DigestUtilities.DoFinal(digest, data);
+ }
+ }
+}
diff --git a/crypto/src/cms/DefaultAuthenticatedAttributeTableGenerator.cs b/crypto/src/cms/DefaultAuthenticatedAttributeTableGenerator.cs
new file mode 100644
index 000000000..d49b1d9d2
--- /dev/null
+++ b/crypto/src/cms/DefaultAuthenticatedAttributeTableGenerator.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * Default authenticated attributes generator.
+ */
+ public class DefaultAuthenticatedAttributeTableGenerator
+ : CmsAttributeTableGenerator
+ {
+ private readonly IDictionary table;
+
+ /**
+ * Initialise to use all defaults
+ */
+ public DefaultAuthenticatedAttributeTableGenerator()
+ {
+ table = Platform.CreateHashtable();
+ }
+
+ /**
+ * Initialise with some extra attributes or overrides.
+ *
+ * @param attributeTable initial attribute table to use.
+ */
+ public DefaultAuthenticatedAttributeTableGenerator(
+ AttributeTable attributeTable)
+ {
+ if (attributeTable != null)
+ {
+ table = attributeTable.ToDictionary();
+ }
+ else
+ {
+ table = Platform.CreateHashtable();
+ }
+ }
+
+ /**
+ * Create a standard attribute table from the passed in parameters - this will
+ * normally include contentType and messageDigest. If the constructor
+ * using an AttributeTable was used, entries in it for contentType and
+ * messageDigest will override the generated ones.
+ *
+ * @param parameters source parameters for table generation.
+ *
+ * @return a filled in IDictionary of attributes.
+ */
+ protected virtual IDictionary CreateStandardAttributeTable(
+ IDictionary parameters)
+ {
+ IDictionary std = Platform.CreateHashtable(table);
+
+ if (!std.Contains(CmsAttributes.ContentType))
+ {
+ DerObjectIdentifier contentType = (DerObjectIdentifier)
+ parameters[CmsAttributeTableParameter.ContentType];
+ Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(CmsAttributes.ContentType,
+ new DerSet(contentType));
+ std[attr.AttrType] = attr;
+ }
+
+ if (!std.Contains(CmsAttributes.MessageDigest))
+ {
+ byte[] messageDigest = (byte[])parameters[CmsAttributeTableParameter.Digest];
+ Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(CmsAttributes.MessageDigest,
+ new DerSet(new DerOctetString(messageDigest)));
+ std[attr.AttrType] = attr;
+ }
+
+ return std;
+ }
+
+ /**
+ * @param parameters source parameters
+ * @return the populated attribute table
+ */
+ public virtual AttributeTable GetAttributes(
+ IDictionary parameters)
+ {
+ IDictionary table = CreateStandardAttributeTable(parameters);
+ return new AttributeTable(table);
+ }
+ }
+}
diff --git a/crypto/src/cms/DefaultSignedAttributeTableGenerator.cs b/crypto/src/cms/DefaultSignedAttributeTableGenerator.cs
new file mode 100644
index 000000000..055de8957
--- /dev/null
+++ b/crypto/src/cms/DefaultSignedAttributeTableGenerator.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * Default signed attributes generator.
+ */
+ public class DefaultSignedAttributeTableGenerator
+ : CmsAttributeTableGenerator
+ {
+ private readonly IDictionary table;
+
+ /**
+ * Initialise to use all defaults
+ */
+ public DefaultSignedAttributeTableGenerator()
+ {
+ table = Platform.CreateHashtable();
+ }
+
+ /**
+ * Initialise with some extra attributes or overrides.
+ *
+ * @param attributeTable initial attribute table to use.
+ */
+ public DefaultSignedAttributeTableGenerator(
+ AttributeTable attributeTable)
+ {
+ if (attributeTable != null)
+ {
+ table = attributeTable.ToDictionary();
+ }
+ else
+ {
+ table = Platform.CreateHashtable();
+ }
+ }
+
+#if SILVERLIGHT
+ /**
+ * Create a standard attribute table from the passed in parameters - this will
+ * normally include contentType, signingTime, and messageDigest. If the constructor
+ * using an AttributeTable was used, entries in it for contentType, signingTime, and
+ * messageDigest will override the generated ones.
+ *
+ * @param parameters source parameters for table generation.
+ *
+ * @return a filled in Hashtable of attributes.
+ */
+ protected virtual IDictionary createStandardAttributeTable(
+ IDictionary parameters)
+ {
+ IDictionary std = Platform.CreateHashtable(table);
+ DoCreateStandardAttributeTable(parameters, std);
+ return std;
+ }
+#else
+ /**
+ * Create a standard attribute table from the passed in parameters - this will
+ * normally include contentType, signingTime, and messageDigest. If the constructor
+ * using an AttributeTable was used, entries in it for contentType, signingTime, and
+ * messageDigest will override the generated ones.
+ *
+ * @param parameters source parameters for table generation.
+ *
+ * @return a filled in Hashtable of attributes.
+ */
+ protected virtual Hashtable createStandardAttributeTable(
+ IDictionary parameters)
+ {
+ Hashtable std = new Hashtable(table);
+ DoCreateStandardAttributeTable(parameters, std);
+ return std;
+ }
+#endif
+
+ private void DoCreateStandardAttributeTable(IDictionary parameters, IDictionary std)
+ {
+ // contentType will be absent if we're trying to generate a counter signature.
+ if (parameters.Contains(CmsAttributeTableParameter.ContentType))
+ {
+ if (!std.Contains(CmsAttributes.ContentType))
+ {
+ DerObjectIdentifier contentType = (DerObjectIdentifier)
+ parameters[CmsAttributeTableParameter.ContentType];
+ Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(CmsAttributes.ContentType,
+ new DerSet(contentType));
+ std[attr.AttrType] = attr;
+ }
+ }
+
+ if (!std.Contains(CmsAttributes.SigningTime))
+ {
+ Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(CmsAttributes.SigningTime,
+ new DerSet(new Time(DateTime.UtcNow)));
+ std[attr.AttrType] = attr;
+ }
+
+ if (!std.Contains(CmsAttributes.MessageDigest))
+ {
+ byte[] messageDigest = (byte[])parameters[CmsAttributeTableParameter.Digest];
+ Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(CmsAttributes.MessageDigest,
+ new DerSet(new DerOctetString(messageDigest)));
+ std[attr.AttrType] = attr;
+ }
+ }
+
+ /**
+ * @param parameters source parameters
+ * @return the populated attribute table
+ */
+ public virtual AttributeTable GetAttributes(
+ IDictionary parameters)
+ {
+ IDictionary table = createStandardAttributeTable(parameters);
+ return new AttributeTable(table);
+ }
+ }
+}
diff --git a/crypto/src/cms/DigOutputStream.cs b/crypto/src/cms/DigOutputStream.cs
new file mode 100644
index 000000000..103b45cac
--- /dev/null
+++ b/crypto/src/cms/DigOutputStream.cs
@@ -0,0 +1,28 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal class DigOutputStream
+ : BaseOutputStream
+ {
+ private readonly IDigest dig;
+
+ internal DigOutputStream(IDigest dig)
+ {
+ this.dig = dig;
+ }
+
+ public override void WriteByte(byte b)
+ {
+ dig.Update(b);
+ }
+
+ public override void Write(byte[] b, int off, int len)
+ {
+ dig.BlockUpdate(b, off, len);
+ }
+ }
+}
diff --git a/crypto/src/cms/IDigestCalculator.cs b/crypto/src/cms/IDigestCalculator.cs
new file mode 100644
index 000000000..3661e4023
--- /dev/null
+++ b/crypto/src/cms/IDigestCalculator.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal interface IDigestCalculator
+ {
+ byte[] GetDigest();
+ }
+}
diff --git a/crypto/src/cms/KEKRecipientInfoGenerator.cs b/crypto/src/cms/KEKRecipientInfoGenerator.cs
new file mode 100644
index 000000000..a9bedade6
--- /dev/null
+++ b/crypto/src/cms/KEKRecipientInfoGenerator.cs
@@ -0,0 +1,137 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.Kisa;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Ntt;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal class KekRecipientInfoGenerator : RecipientInfoGenerator
+ {
+ private static readonly CmsEnvelopedHelper Helper = CmsEnvelopedHelper.Instance;
+
+ private KeyParameter keyEncryptionKey;
+ // TODO Can get this from keyEncryptionKey?
+ private string keyEncryptionKeyOID;
+ private KekIdentifier kekIdentifier;
+
+ // Derived
+ private AlgorithmIdentifier keyEncryptionAlgorithm;
+
+ internal KekRecipientInfoGenerator()
+ {
+ }
+
+ internal KekIdentifier KekIdentifier
+ {
+ set { this.kekIdentifier = value; }
+ }
+
+ internal KeyParameter KeyEncryptionKey
+ {
+ set
+ {
+ this.keyEncryptionKey = value;
+ this.keyEncryptionAlgorithm = DetermineKeyEncAlg(keyEncryptionKeyOID, keyEncryptionKey);
+ }
+ }
+
+ internal string KeyEncryptionKeyOID
+ {
+ set { this.keyEncryptionKeyOID = value; }
+ }
+
+ public RecipientInfo Generate(KeyParameter contentEncryptionKey, SecureRandom random)
+ {
+ byte[] keyBytes = contentEncryptionKey.GetKey();
+
+ IWrapper keyWrapper = Helper.CreateWrapper(keyEncryptionAlgorithm.ObjectID.Id);
+ keyWrapper.Init(true, new ParametersWithRandom(keyEncryptionKey, random));
+ Asn1OctetString encryptedKey = new DerOctetString(
+ keyWrapper.Wrap(keyBytes, 0, keyBytes.Length));
+
+ return new RecipientInfo(new KekRecipientInfo(kekIdentifier, keyEncryptionAlgorithm, encryptedKey));
+ }
+
+ private static AlgorithmIdentifier DetermineKeyEncAlg(
+ string algorithm, KeyParameter key)
+ {
+ if (algorithm.StartsWith("DES"))
+ {
+ return new AlgorithmIdentifier(
+ PkcsObjectIdentifiers.IdAlgCms3DesWrap,
+ DerNull.Instance);
+ }
+ else if (algorithm.StartsWith("RC2"))
+ {
+ return new AlgorithmIdentifier(
+ PkcsObjectIdentifiers.IdAlgCmsRC2Wrap,
+ new DerInteger(58));
+ }
+ else if (algorithm.StartsWith("AES"))
+ {
+ int length = key.GetKey().Length * 8;
+ DerObjectIdentifier wrapOid;
+
+ if (length == 128)
+ {
+ wrapOid = NistObjectIdentifiers.IdAes128Wrap;
+ }
+ else if (length == 192)
+ {
+ wrapOid = NistObjectIdentifiers.IdAes192Wrap;
+ }
+ else if (length == 256)
+ {
+ wrapOid = NistObjectIdentifiers.IdAes256Wrap;
+ }
+ else
+ {
+ throw new ArgumentException("illegal keysize in AES");
+ }
+
+ return new AlgorithmIdentifier(wrapOid); // parameters absent
+ }
+ else if (algorithm.StartsWith("SEED"))
+ {
+ // parameters absent
+ return new AlgorithmIdentifier(KisaObjectIdentifiers.IdNpkiAppCmsSeedWrap);
+ }
+ else if (algorithm.StartsWith("CAMELLIA"))
+ {
+ int length = key.GetKey().Length * 8;
+ DerObjectIdentifier wrapOid;
+
+ if (length == 128)
+ {
+ wrapOid = NttObjectIdentifiers.IdCamellia128Wrap;
+ }
+ else if (length == 192)
+ {
+ wrapOid = NttObjectIdentifiers.IdCamellia192Wrap;
+ }
+ else if (length == 256)
+ {
+ wrapOid = NttObjectIdentifiers.IdCamellia256Wrap;
+ }
+ else
+ {
+ throw new ArgumentException("illegal keysize in Camellia");
+ }
+
+ return new AlgorithmIdentifier(wrapOid); // parameters must be absent
+ }
+ else
+ {
+ throw new ArgumentException("unknown algorithm");
+ }
+ }
+ }
+}
diff --git a/crypto/src/cms/KEKRecipientInformation.cs b/crypto/src/cms/KEKRecipientInformation.cs
new file mode 100644
index 000000000..f960197d6
--- /dev/null
+++ b/crypto/src/cms/KEKRecipientInformation.cs
@@ -0,0 +1,62 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * the RecipientInfo class for a recipient who has been sent a message
+ * encrypted using a secret key known to the other side.
+ */
+ public class KekRecipientInformation
+ : RecipientInformation
+ {
+ private KekRecipientInfo info;
+
+ internal KekRecipientInformation(
+ KekRecipientInfo info,
+ CmsSecureReadable secureReadable)
+ : base(info.KeyEncryptionAlgorithm, secureReadable)
+ {
+ this.info = info;
+ this.rid = new RecipientID();
+
+ KekIdentifier kekId = info.KekID;
+
+ rid.KeyIdentifier = kekId.KeyIdentifier.GetOctets();
+ }
+
+ /**
+ * decrypt the content and return an input stream.
+ */
+ public override CmsTypedStream GetContentStream(
+ ICipherParameters key)
+ {
+ try
+ {
+ byte[] encryptedKey = info.EncryptedKey.GetOctets();
+ IWrapper keyWrapper = WrapperUtilities.GetWrapper(keyEncAlg.ObjectID.Id);
+
+ keyWrapper.Init(false, key);
+
+ KeyParameter sKey = ParameterUtilities.CreateKeyParameter(
+ GetContentAlgorithmName(), keyWrapper.Unwrap(encryptedKey, 0, encryptedKey.Length));
+
+ return GetContentFromSessionKey(sKey);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("couldn't create cipher.", e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key invalid in message.", e);
+ }
+ }
+ }
+}
diff --git a/crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs b/crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs
new file mode 100644
index 000000000..4fafb7c6e
--- /dev/null
+++ b/crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs
@@ -0,0 +1,171 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.Cms.Ecc;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal class KeyAgreeRecipientInfoGenerator : RecipientInfoGenerator
+ {
+ private static readonly CmsEnvelopedHelper Helper = CmsEnvelopedHelper.Instance;
+
+ private DerObjectIdentifier keyAgreementOID;
+ private DerObjectIdentifier keyEncryptionOID;
+ private IList recipientCerts;
+ private AsymmetricCipherKeyPair senderKeyPair;
+
+ internal KeyAgreeRecipientInfoGenerator()
+ {
+ }
+
+ internal DerObjectIdentifier KeyAgreementOID
+ {
+ set { this.keyAgreementOID = value; }
+ }
+
+ internal DerObjectIdentifier KeyEncryptionOID
+ {
+ set { this.keyEncryptionOID = value; }
+ }
+
+ internal ICollection RecipientCerts
+ {
+ set { this.recipientCerts = Platform.CreateArrayList(value); }
+ }
+
+ internal AsymmetricCipherKeyPair SenderKeyPair
+ {
+ set { this.senderKeyPair = value; }
+ }
+
+ public RecipientInfo Generate(KeyParameter contentEncryptionKey, SecureRandom random)
+ {
+ byte[] keyBytes = contentEncryptionKey.GetKey();
+
+ AsymmetricKeyParameter senderPublicKey = senderKeyPair.Public;
+ ICipherParameters senderPrivateParams = senderKeyPair.Private;
+
+
+ OriginatorIdentifierOrKey originator;
+ try
+ {
+ originator = new OriginatorIdentifierOrKey(
+ CreateOriginatorPublicKey(senderPublicKey));
+ }
+ catch (IOException e)
+ {
+ throw new InvalidKeyException("cannot extract originator public key: " + e);
+ }
+
+
+ Asn1OctetString ukm = null;
+ if (keyAgreementOID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf))
+ {
+ try
+ {
+ IAsymmetricCipherKeyPairGenerator ephemKPG =
+ GeneratorUtilities.GetKeyPairGenerator(keyAgreementOID);
+ ephemKPG.Init(
+ ((ECPublicKeyParameters)senderPublicKey).CreateKeyGenerationParameters(random));
+
+ AsymmetricCipherKeyPair ephemKP = ephemKPG.GenerateKeyPair();
+
+ ukm = new DerOctetString(
+ new MQVuserKeyingMaterial(
+ CreateOriginatorPublicKey(ephemKP.Public), null));
+
+ senderPrivateParams = new MqvPrivateParameters(
+ (ECPrivateKeyParameters)senderPrivateParams,
+ (ECPrivateKeyParameters)ephemKP.Private,
+ (ECPublicKeyParameters)ephemKP.Public);
+ }
+ catch (IOException e)
+ {
+ throw new InvalidKeyException("cannot extract MQV ephemeral public key: " + e);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new InvalidKeyException("cannot determine MQV ephemeral key pair parameters from public key: " + e);
+ }
+ }
+
+
+ DerSequence paramSeq = new DerSequence(
+ keyEncryptionOID,
+ DerNull.Instance);
+ AlgorithmIdentifier keyEncAlg = new AlgorithmIdentifier(keyAgreementOID, paramSeq);
+
+
+ Asn1EncodableVector recipientEncryptedKeys = new Asn1EncodableVector();
+ foreach (X509Certificate recipientCert in recipientCerts)
+ {
+ TbsCertificateStructure tbsCert;
+ try
+ {
+ tbsCert = TbsCertificateStructure.GetInstance(
+ Asn1Object.FromByteArray(recipientCert.GetTbsCertificate()));
+ }
+ catch (Exception)
+ {
+ throw new ArgumentException("can't extract TBS structure from certificate");
+ }
+
+ // TODO Should there be a SubjectKeyIdentifier-based alternative?
+ IssuerAndSerialNumber issuerSerial = new IssuerAndSerialNumber(
+ tbsCert.Issuer, tbsCert.SerialNumber.Value);
+ KeyAgreeRecipientIdentifier karid = new KeyAgreeRecipientIdentifier(issuerSerial);
+
+ ICipherParameters recipientPublicParams = recipientCert.GetPublicKey();
+ if (keyAgreementOID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf))
+ {
+ recipientPublicParams = new MqvPublicParameters(
+ (ECPublicKeyParameters)recipientPublicParams,
+ (ECPublicKeyParameters)recipientPublicParams);
+ }
+
+ // Use key agreement to choose a wrap key for this recipient
+ IBasicAgreement keyAgreement = AgreementUtilities.GetBasicAgreementWithKdf(
+ keyAgreementOID, keyEncryptionOID.Id);
+ keyAgreement.Init(new ParametersWithRandom(senderPrivateParams, random));
+ BigInteger agreedValue = keyAgreement.CalculateAgreement(recipientPublicParams);
+
+ int keyEncryptionKeySize = GeneratorUtilities.GetDefaultKeySize(keyEncryptionOID) / 8;
+ byte[] keyEncryptionKeyBytes = X9IntegerConverter.IntegerToBytes(agreedValue, keyEncryptionKeySize);
+ KeyParameter keyEncryptionKey = ParameterUtilities.CreateKeyParameter(
+ keyEncryptionOID, keyEncryptionKeyBytes);
+
+ // Wrap the content encryption key with the agreement key
+ IWrapper keyWrapper = Helper.CreateWrapper(keyEncryptionOID.Id);
+ keyWrapper.Init(true, new ParametersWithRandom(keyEncryptionKey, random));
+ byte[] encryptedKeyBytes = keyWrapper.Wrap(keyBytes, 0, keyBytes.Length);
+
+ Asn1OctetString encryptedKey = new DerOctetString(encryptedKeyBytes);
+
+ recipientEncryptedKeys.Add(new RecipientEncryptedKey(karid, encryptedKey));
+ }
+
+ return new RecipientInfo(new KeyAgreeRecipientInfo(originator, ukm, keyEncAlg,
+ new DerSequence(recipientEncryptedKeys)));
+ }
+
+ private static OriginatorPublicKey CreateOriginatorPublicKey(
+ AsymmetricKeyParameter publicKey)
+ {
+ SubjectPublicKeyInfo spki = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey);
+ return new OriginatorPublicKey(
+ new AlgorithmIdentifier(spki.AlgorithmID.ObjectID, DerNull.Instance),
+ spki.PublicKeyData.GetBytes());
+ }
+ }
+}
diff --git a/crypto/src/cms/KeyAgreeRecipientInformation.cs b/crypto/src/cms/KeyAgreeRecipientInformation.cs
new file mode 100644
index 000000000..38a94b0a4
--- /dev/null
+++ b/crypto/src/cms/KeyAgreeRecipientInformation.cs
@@ -0,0 +1,226 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.Cms.Ecc;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Utilities;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Pkcs;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * the RecipientInfo class for a recipient who has been sent a message
+ * encrypted using key agreement.
+ */
+ public class KeyAgreeRecipientInformation
+ : RecipientInformation
+ {
+ private KeyAgreeRecipientInfo info;
+ private Asn1OctetString encryptedKey;
+
+ internal static void ReadRecipientInfo(IList infos, KeyAgreeRecipientInfo info,
+ CmsSecureReadable secureReadable)
+ {
+ try
+ {
+ foreach (Asn1Encodable rek in info.RecipientEncryptedKeys)
+ {
+ RecipientEncryptedKey id = RecipientEncryptedKey.GetInstance(rek.ToAsn1Object());
+
+ RecipientID rid = new RecipientID();
+
+ Asn1.Cms.KeyAgreeRecipientIdentifier karid = id.Identifier;
+
+ Asn1.Cms.IssuerAndSerialNumber iAndSN = karid.IssuerAndSerialNumber;
+ if (iAndSN != null)
+ {
+ rid.Issuer = iAndSN.Name;
+ rid.SerialNumber = iAndSN.SerialNumber.Value;
+ }
+ else
+ {
+ Asn1.Cms.RecipientKeyIdentifier rKeyID = karid.RKeyID;
+
+ // Note: 'date' and 'other' fields of RecipientKeyIdentifier appear to be only informational
+
+ rid.SubjectKeyIdentifier = rKeyID.SubjectKeyIdentifier.GetOctets();
+ }
+
+ infos.Add(new KeyAgreeRecipientInformation(info, rid, id.EncryptedKey,
+ secureReadable));
+ }
+ }
+ catch (IOException e)
+ {
+ throw new ArgumentException("invalid rid in KeyAgreeRecipientInformation", e);
+ }
+ }
+
+ internal KeyAgreeRecipientInformation(
+ KeyAgreeRecipientInfo info,
+ RecipientID rid,
+ Asn1OctetString encryptedKey,
+ CmsSecureReadable secureReadable)
+ : base(info.KeyEncryptionAlgorithm, secureReadable)
+ {
+ this.info = info;
+ this.rid = rid;
+ this.encryptedKey = encryptedKey;
+ }
+
+ private AsymmetricKeyParameter GetSenderPublicKey(
+ AsymmetricKeyParameter receiverPrivateKey,
+ OriginatorIdentifierOrKey originator)
+ {
+ OriginatorPublicKey opk = originator.OriginatorPublicKey;
+ if (opk != null)
+ {
+ return GetPublicKeyFromOriginatorPublicKey(receiverPrivateKey, opk);
+ }
+
+ OriginatorID origID = new OriginatorID();
+
+ Asn1.Cms.IssuerAndSerialNumber iAndSN = originator.IssuerAndSerialNumber;
+ if (iAndSN != null)
+ {
+ origID.Issuer = iAndSN.Name;
+ origID.SerialNumber = iAndSN.SerialNumber.Value;
+ }
+ else
+ {
+ SubjectKeyIdentifier ski = originator.SubjectKeyIdentifier;
+
+ origID.SubjectKeyIdentifier = ski.GetKeyIdentifier();
+ }
+
+ return GetPublicKeyFromOriginatorID(origID);
+ }
+
+ private AsymmetricKeyParameter GetPublicKeyFromOriginatorPublicKey(
+ AsymmetricKeyParameter receiverPrivateKey,
+ OriginatorPublicKey originatorPublicKey)
+ {
+ PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(receiverPrivateKey);
+ SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(
+ privInfo.AlgorithmID,
+ originatorPublicKey.PublicKey.GetBytes());
+ return PublicKeyFactory.CreateKey(pubInfo);
+ }
+
+ private AsymmetricKeyParameter GetPublicKeyFromOriginatorID(
+ OriginatorID origID)
+ {
+ // TODO Support all alternatives for OriginatorIdentifierOrKey
+ // see RFC 3852 6.2.2
+ throw new CmsException("No support for 'originator' as IssuerAndSerialNumber or SubjectKeyIdentifier");
+ }
+
+ private KeyParameter CalculateAgreedWrapKey(
+ string wrapAlg,
+ AsymmetricKeyParameter senderPublicKey,
+ AsymmetricKeyParameter receiverPrivateKey)
+ {
+ DerObjectIdentifier agreeAlgID = keyEncAlg.ObjectID;
+
+ ICipherParameters senderPublicParams = senderPublicKey;
+ ICipherParameters receiverPrivateParams = receiverPrivateKey;
+
+ if (agreeAlgID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf))
+ {
+ byte[] ukmEncoding = info.UserKeyingMaterial.GetOctets();
+ MQVuserKeyingMaterial ukm = MQVuserKeyingMaterial.GetInstance(
+ Asn1Object.FromByteArray(ukmEncoding));
+
+ AsymmetricKeyParameter ephemeralKey = GetPublicKeyFromOriginatorPublicKey(
+ receiverPrivateKey, ukm.EphemeralPublicKey);
+
+ senderPublicParams = new MqvPublicParameters(
+ (ECPublicKeyParameters)senderPublicParams,
+ (ECPublicKeyParameters)ephemeralKey);
+ receiverPrivateParams = new MqvPrivateParameters(
+ (ECPrivateKeyParameters)receiverPrivateParams,
+ (ECPrivateKeyParameters)receiverPrivateParams);
+ }
+
+ IBasicAgreement agreement = AgreementUtilities.GetBasicAgreementWithKdf(
+ agreeAlgID, wrapAlg);
+ agreement.Init(receiverPrivateParams);
+ BigInteger agreedValue = agreement.CalculateAgreement(senderPublicParams);
+
+ int wrapKeySize = GeneratorUtilities.GetDefaultKeySize(wrapAlg) / 8;
+ byte[] wrapKeyBytes = X9IntegerConverter.IntegerToBytes(agreedValue, wrapKeySize);
+ return ParameterUtilities.CreateKeyParameter(wrapAlg, wrapKeyBytes);
+ }
+
+ private KeyParameter UnwrapSessionKey(
+ string wrapAlg,
+ KeyParameter agreedKey)
+ {
+ byte[] encKeyOctets = encryptedKey.GetOctets();
+
+ IWrapper keyCipher = WrapperUtilities.GetWrapper(wrapAlg);
+ keyCipher.Init(false, agreedKey);
+ byte[] sKeyBytes = keyCipher.Unwrap(encKeyOctets, 0, encKeyOctets.Length);
+ return ParameterUtilities.CreateKeyParameter(GetContentAlgorithmName(), sKeyBytes);
+ }
+
+ internal KeyParameter GetSessionKey(
+ AsymmetricKeyParameter receiverPrivateKey)
+ {
+ try
+ {
+ string wrapAlg = DerObjectIdentifier.GetInstance(
+ Asn1Sequence.GetInstance(keyEncAlg.Parameters)[0]).Id;
+
+ AsymmetricKeyParameter senderPublicKey = GetSenderPublicKey(
+ receiverPrivateKey, info.Originator);
+
+ KeyParameter agreedWrapKey = CalculateAgreedWrapKey(wrapAlg,
+ senderPublicKey, receiverPrivateKey);
+
+ return UnwrapSessionKey(wrapAlg, agreedWrapKey);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("couldn't create cipher.", e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key invalid in message.", e);
+ }
+ catch (Exception e)
+ {
+ throw new CmsException("originator key invalid.", e);
+ }
+ }
+
+ /**
+ * decrypt the content and return an input stream.
+ */
+ public override CmsTypedStream GetContentStream(
+ ICipherParameters key)
+ {
+ if (!(key is AsymmetricKeyParameter))
+ throw new ArgumentException("KeyAgreement requires asymmetric key", "key");
+
+ AsymmetricKeyParameter receiverPrivateKey = (AsymmetricKeyParameter) key;
+
+ if (!receiverPrivateKey.IsPrivate)
+ throw new ArgumentException("Expected private key", "key");
+
+ KeyParameter sKey = GetSessionKey(receiverPrivateKey);
+
+ return GetContentFromSessionKey(sKey);
+ }
+ }
+}
diff --git a/crypto/src/cms/KeyTransRecipientInfoGenerator.cs b/crypto/src/cms/KeyTransRecipientInfoGenerator.cs
new file mode 100644
index 000000000..0992e6da6
--- /dev/null
+++ b/crypto/src/cms/KeyTransRecipientInfoGenerator.cs
@@ -0,0 +1,87 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal class KeyTransRecipientInfoGenerator : RecipientInfoGenerator
+ {
+ private static readonly CmsEnvelopedHelper Helper = CmsEnvelopedHelper.Instance;
+
+ private TbsCertificateStructure recipientTbsCert;
+ private AsymmetricKeyParameter recipientPublicKey;
+ private Asn1OctetString subjectKeyIdentifier;
+
+ // Derived fields
+ private SubjectPublicKeyInfo info;
+
+ internal KeyTransRecipientInfoGenerator()
+ {
+ }
+
+ internal X509Certificate RecipientCert
+ {
+ set
+ {
+ this.recipientTbsCert = CmsUtilities.GetTbsCertificateStructure(value);
+ this.recipientPublicKey = value.GetPublicKey();
+ this.info = recipientTbsCert.SubjectPublicKeyInfo;
+ }
+ }
+
+ internal AsymmetricKeyParameter RecipientPublicKey
+ {
+ set
+ {
+ this.recipientPublicKey = value;
+
+ try
+ {
+ info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(
+ recipientPublicKey);
+ }
+ catch (IOException)
+ {
+ throw new ArgumentException("can't extract key algorithm from this key");
+ }
+ }
+ }
+
+ internal Asn1OctetString SubjectKeyIdentifier
+ {
+ set { this.subjectKeyIdentifier = value; }
+ }
+
+ public RecipientInfo Generate(KeyParameter contentEncryptionKey, SecureRandom random)
+ {
+ byte[] keyBytes = contentEncryptionKey.GetKey();
+ AlgorithmIdentifier keyEncryptionAlgorithm = info.AlgorithmID;
+
+ IWrapper keyWrapper = Helper.CreateWrapper(keyEncryptionAlgorithm.ObjectID.Id);
+ keyWrapper.Init(true, new ParametersWithRandom(recipientPublicKey, random));
+ byte[] encryptedKeyBytes = keyWrapper.Wrap(keyBytes, 0, keyBytes.Length);
+
+ RecipientIdentifier recipId;
+ if (recipientTbsCert != null)
+ {
+ IssuerAndSerialNumber issuerAndSerial = new IssuerAndSerialNumber(
+ recipientTbsCert.Issuer, recipientTbsCert.SerialNumber.Value);
+ recipId = new RecipientIdentifier(issuerAndSerial);
+ }
+ else
+ {
+ recipId = new RecipientIdentifier(subjectKeyIdentifier);
+ }
+
+ return new RecipientInfo(new KeyTransRecipientInfo(recipId, keyEncryptionAlgorithm,
+ new DerOctetString(encryptedKeyBytes)));
+ }
+ }
+}
diff --git a/crypto/src/cms/KeyTransRecipientInformation.cs b/crypto/src/cms/KeyTransRecipientInformation.cs
new file mode 100644
index 000000000..24121cb2c
--- /dev/null
+++ b/crypto/src/cms/KeyTransRecipientInformation.cs
@@ -0,0 +1,113 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Asn1Pkcs = Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * the KeyTransRecipientInformation class for a recipient who has been sent a secret
+ * key encrypted using their public key that needs to be used to
+ * extract the message.
+ */
+ public class KeyTransRecipientInformation
+ : RecipientInformation
+ {
+ private KeyTransRecipientInfo info;
+
+ internal KeyTransRecipientInformation(
+ KeyTransRecipientInfo info,
+ CmsSecureReadable secureReadable)
+ : base(info.KeyEncryptionAlgorithm, secureReadable)
+ {
+ this.info = info;
+ this.rid = new RecipientID();
+
+ RecipientIdentifier r = info.RecipientIdentifier;
+
+ try
+ {
+ if (r.IsTagged)
+ {
+ Asn1OctetString octs = Asn1OctetString.GetInstance(r.ID);
+
+ rid.SubjectKeyIdentifier = octs.GetOctets();
+ }
+ else
+ {
+ IssuerAndSerialNumber iAnds = IssuerAndSerialNumber.GetInstance(r.ID);
+
+ rid.Issuer = iAnds.Name;
+ rid.SerialNumber = iAnds.SerialNumber.Value;
+ }
+ }
+ catch (IOException)
+ {
+ throw new ArgumentException("invalid rid in KeyTransRecipientInformation");
+ }
+ }
+
+ private string GetExchangeEncryptionAlgorithmName(
+ DerObjectIdentifier oid)
+ {
+ if (Asn1Pkcs.PkcsObjectIdentifiers.RsaEncryption.Equals(oid))
+ {
+ return "RSA//PKCS1Padding";
+ }
+
+ return oid.Id;
+ }
+
+ internal KeyParameter UnwrapKey(ICipherParameters key)
+ {
+ byte[] encryptedKey = info.EncryptedKey.GetOctets();
+ string keyExchangeAlgorithm = GetExchangeEncryptionAlgorithmName(keyEncAlg.ObjectID);
+
+ try
+ {
+ IWrapper keyWrapper = WrapperUtilities.GetWrapper(keyExchangeAlgorithm);
+ keyWrapper.Init(false, key);
+
+ // FIXME Support for MAC algorithm parameters similar to cipher parameters
+ return ParameterUtilities.CreateKeyParameter(
+ GetContentAlgorithmName(), keyWrapper.Unwrap(encryptedKey, 0, encryptedKey.Length));
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("couldn't create cipher.", e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key invalid in message.", e);
+ }
+// catch (IllegalBlockSizeException e)
+ catch (DataLengthException e)
+ {
+ throw new CmsException("illegal blocksize in message.", e);
+ }
+// catch (BadPaddingException e)
+ catch (InvalidCipherTextException e)
+ {
+ throw new CmsException("bad padding in message.", e);
+ }
+ }
+
+ /**
+ * decrypt the content and return it as a byte array.
+ */
+ public override CmsTypedStream GetContentStream(
+ ICipherParameters key)
+ {
+ KeyParameter sKey = UnwrapKey(key);
+
+ return GetContentFromSessionKey(sKey);
+ }
+ }
+}
diff --git a/crypto/src/cms/MacOutputStream.cs b/crypto/src/cms/MacOutputStream.cs
new file mode 100644
index 000000000..8891dbc2c
--- /dev/null
+++ b/crypto/src/cms/MacOutputStream.cs
@@ -0,0 +1,28 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal class MacOutputStream
+ : BaseOutputStream
+ {
+ private readonly IMac mac;
+
+ internal MacOutputStream(IMac mac)
+ {
+ this.mac = mac;
+ }
+
+ public override void Write(byte[] b, int off, int len)
+ {
+ mac.BlockUpdate(b, off, len);
+ }
+
+ public override void WriteByte(byte b)
+ {
+ mac.Update(b);
+ }
+ }
+}
diff --git a/crypto/src/cms/NullOutputStream.cs b/crypto/src/cms/NullOutputStream.cs
new file mode 100644
index 000000000..ed937bdeb
--- /dev/null
+++ b/crypto/src/cms/NullOutputStream.cs
@@ -0,0 +1,20 @@
+using System;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal class NullOutputStream
+ : BaseOutputStream
+ {
+ public override void WriteByte(byte b)
+ {
+ // do nothing
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ // do nothing
+ }
+ }
+}
diff --git a/crypto/src/cms/OriginatorId.cs b/crypto/src/cms/OriginatorId.cs
new file mode 100644
index 000000000..5a3b7374d
--- /dev/null
+++ b/crypto/src/cms/OriginatorId.cs
@@ -0,0 +1,51 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * a basic index for an originator.
+ */
+ public class OriginatorID
+ : X509CertStoreSelector
+ {
+ public override int GetHashCode()
+ {
+ int code = Arrays.GetHashCode(this.SubjectKeyIdentifier);
+
+ BigInteger serialNumber = this.SerialNumber;
+ if (serialNumber != null)
+ {
+ code ^= serialNumber.GetHashCode();
+ }
+
+ X509Name issuer = this.Issuer;
+ if (issuer != null)
+ {
+ code ^= issuer.GetHashCode();
+ }
+
+ return code;
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return false;
+
+ OriginatorID id = obj as OriginatorID;
+
+ if (id == null)
+ return false;
+
+ return Arrays.AreEqual(SubjectKeyIdentifier, id.SubjectKeyIdentifier)
+ && Platform.Equals(SerialNumber, id.SerialNumber)
+ && IssuersMatch(Issuer, id.Issuer);
+ }
+ }
+}
diff --git a/crypto/src/cms/OriginatorInfoGenerator.cs b/crypto/src/cms/OriginatorInfoGenerator.cs
new file mode 100644
index 000000000..6bf108799
--- /dev/null
+++ b/crypto/src/cms/OriginatorInfoGenerator.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Cms
+{
+ public class OriginatorInfoGenerator
+ {
+ private readonly IList origCerts;
+ private readonly IList origCrls;
+
+ public OriginatorInfoGenerator(X509Certificate origCert)
+ {
+ this.origCerts = Platform.CreateArrayList(1);
+ this.origCrls = null;
+ origCerts.Add(origCert.CertificateStructure);
+ }
+
+ public OriginatorInfoGenerator(IX509Store origCerts)
+ : this(origCerts, null)
+ {
+ }
+
+ public OriginatorInfoGenerator(IX509Store origCerts, IX509Store origCrls)
+ {
+ this.origCerts = CmsUtilities.GetCertificatesFromStore(origCerts);
+ this.origCrls = origCrls == null ? null : CmsUtilities.GetCrlsFromStore(origCrls);
+ }
+
+ public virtual OriginatorInfo Generate()
+ {
+ Asn1Set certSet = CmsUtilities.CreateDerSetFromList(origCerts);
+ Asn1Set crlSet = origCrls == null ? null : CmsUtilities.CreateDerSetFromList(origCrls);
+ return new OriginatorInfo(certSet, crlSet);
+ }
+ }
+}
diff --git a/crypto/src/cms/OriginatorInformation.cs b/crypto/src/cms/OriginatorInformation.cs
new file mode 100644
index 000000000..618add6e0
--- /dev/null
+++ b/crypto/src/cms/OriginatorInformation.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Cms
+{
+ public class OriginatorInformation
+ {
+ private readonly OriginatorInfo originatorInfo;
+
+ internal OriginatorInformation(OriginatorInfo originatorInfo)
+ {
+ this.originatorInfo = originatorInfo;
+ }
+
+ /**
+ * Return the certificates stored in the underlying OriginatorInfo object.
+ *
+ * @return a Store of X509CertificateHolder objects.
+ */
+ public virtual IX509Store GetCertificates()
+ {
+ Asn1Set certSet = originatorInfo.Certificates;
+
+ if (certSet != null)
+ {
+ IList certList = Platform.CreateArrayList(certSet.Count);
+
+ foreach (Asn1Encodable enc in certSet)
+ {
+ Asn1Object obj = enc.ToAsn1Object();
+ if (obj is Asn1Sequence)
+ {
+ certList.Add(new X509Certificate(X509CertificateStructure.GetInstance(obj)));
+ }
+ }
+
+ return X509StoreFactory.Create(
+ "Certificate/Collection",
+ new X509CollectionStoreParameters(certList));
+ }
+
+ return X509StoreFactory.Create(
+ "Certificate/Collection",
+ new X509CollectionStoreParameters(Platform.CreateArrayList()));
+ }
+
+ /**
+ * Return the CRLs stored in the underlying OriginatorInfo object.
+ *
+ * @return a Store of X509CRLHolder objects.
+ */
+ public virtual IX509Store GetCrls()
+ {
+ Asn1Set crlSet = originatorInfo.Certificates;
+
+ if (crlSet != null)
+ {
+ IList crlList = Platform.CreateArrayList(crlSet.Count);
+
+ foreach (Asn1Encodable enc in crlSet)
+ {
+ Asn1Object obj = enc.ToAsn1Object();
+ if (obj is Asn1Sequence)
+ {
+ crlList.Add(new X509Crl(CertificateList.GetInstance(obj)));
+ }
+ }
+
+ return X509StoreFactory.Create(
+ "CRL/Collection",
+ new X509CollectionStoreParameters(crlList));
+ }
+
+ return X509StoreFactory.Create(
+ "CRL/Collection",
+ new X509CollectionStoreParameters(Platform.CreateArrayList()));
+ }
+
+ /**
+ * Return the underlying ASN.1 object defining this SignerInformation object.
+ *
+ * @return a OriginatorInfo.
+ */
+ public virtual OriginatorInfo ToAsn1Structure()
+ {
+ return originatorInfo;
+ }
+ }
+}
diff --git a/crypto/src/cms/PKCS5Scheme2PBEKey.cs b/crypto/src/cms/PKCS5Scheme2PBEKey.cs
new file mode 100644
index 000000000..08b8518a1
--- /dev/null
+++ b/crypto/src/cms/PKCS5Scheme2PBEKey.cs
@@ -0,0 +1,64 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Cms
+{
+ ///
+ /// PKCS5 scheme-2 - password converted to bytes assuming ASCII.
+ ///
+ public class Pkcs5Scheme2PbeKey
+ : CmsPbeKey
+ {
+ [Obsolete("Use version taking 'char[]' instead")]
+ public Pkcs5Scheme2PbeKey(
+ string password,
+ byte[] salt,
+ int iterationCount)
+ : this(password.ToCharArray(), salt, iterationCount)
+ {
+ }
+
+ [Obsolete("Use version taking 'char[]' instead")]
+ public Pkcs5Scheme2PbeKey(
+ string password,
+ AlgorithmIdentifier keyDerivationAlgorithm)
+ : this(password.ToCharArray(), keyDerivationAlgorithm)
+ {
+ }
+
+ public Pkcs5Scheme2PbeKey(
+ char[] password,
+ byte[] salt,
+ int iterationCount)
+ : base(password, salt, iterationCount)
+ {
+ }
+
+ public Pkcs5Scheme2PbeKey(
+ char[] password,
+ AlgorithmIdentifier keyDerivationAlgorithm)
+ : base(password, keyDerivationAlgorithm)
+ {
+ }
+
+ internal override KeyParameter GetEncoded(
+ string algorithmOid)
+ {
+ Pkcs5S2ParametersGenerator gen = new Pkcs5S2ParametersGenerator();
+
+ gen.Init(
+ PbeParametersGenerator.Pkcs5PasswordToBytes(password),
+ salt,
+ iterationCount);
+
+ return (KeyParameter) gen.GenerateDerivedParameters(
+ algorithmOid,
+ CmsEnvelopedHelper.Instance.GetKeySize(algorithmOid));
+ }
+ }
+}
diff --git a/crypto/src/cms/PKCS5Scheme2UTF8PBEKey.cs b/crypto/src/cms/PKCS5Scheme2UTF8PBEKey.cs
new file mode 100644
index 000000000..7aecc2978
--- /dev/null
+++ b/crypto/src/cms/PKCS5Scheme2UTF8PBEKey.cs
@@ -0,0 +1,64 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * PKCS5 scheme-2 - password converted to bytes using UTF-8.
+ */
+ public class Pkcs5Scheme2Utf8PbeKey
+ : CmsPbeKey
+ {
+ [Obsolete("Use version taking 'char[]' instead")]
+ public Pkcs5Scheme2Utf8PbeKey(
+ string password,
+ byte[] salt,
+ int iterationCount)
+ : this(password.ToCharArray(), salt, iterationCount)
+ {
+ }
+
+ [Obsolete("Use version taking 'char[]' instead")]
+ public Pkcs5Scheme2Utf8PbeKey(
+ string password,
+ AlgorithmIdentifier keyDerivationAlgorithm)
+ : this(password.ToCharArray(), keyDerivationAlgorithm)
+ {
+ }
+
+ public Pkcs5Scheme2Utf8PbeKey(
+ char[] password,
+ byte[] salt,
+ int iterationCount)
+ : base(password, salt, iterationCount)
+ {
+ }
+
+ public Pkcs5Scheme2Utf8PbeKey(
+ char[] password,
+ AlgorithmIdentifier keyDerivationAlgorithm)
+ : base(password, keyDerivationAlgorithm)
+ {
+ }
+
+ internal override KeyParameter GetEncoded(
+ string algorithmOid)
+ {
+ Pkcs5S2ParametersGenerator gen = new Pkcs5S2ParametersGenerator();
+
+ gen.Init(
+ PbeParametersGenerator.Pkcs5PasswordToUtf8Bytes(password),
+ salt,
+ iterationCount);
+
+ return (KeyParameter) gen.GenerateDerivedParameters(
+ algorithmOid,
+ CmsEnvelopedHelper.Instance.GetKeySize(algorithmOid));
+ }
+ }
+}
diff --git a/crypto/src/cms/PasswordRecipientInfoGenerator.cs b/crypto/src/cms/PasswordRecipientInfoGenerator.cs
new file mode 100644
index 000000000..0a0b27b53
--- /dev/null
+++ b/crypto/src/cms/PasswordRecipientInfoGenerator.cs
@@ -0,0 +1,69 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal class PasswordRecipientInfoGenerator : RecipientInfoGenerator
+ {
+ private static readonly CmsEnvelopedHelper Helper = CmsEnvelopedHelper.Instance;
+
+ private AlgorithmIdentifier keyDerivationAlgorithm;
+ private KeyParameter keyEncryptionKey;
+ // TODO Can get this from keyEncryptionKey?
+ private string keyEncryptionKeyOID;
+
+ internal PasswordRecipientInfoGenerator()
+ {
+ }
+
+ internal AlgorithmIdentifier KeyDerivationAlgorithm
+ {
+ set { this.keyDerivationAlgorithm = value; }
+ }
+
+ internal KeyParameter KeyEncryptionKey
+ {
+ set { this.keyEncryptionKey = value; }
+ }
+
+ internal string KeyEncryptionKeyOID
+ {
+ set { this.keyEncryptionKeyOID = value; }
+ }
+
+ public RecipientInfo Generate(KeyParameter contentEncryptionKey, SecureRandom random)
+ {
+ byte[] keyBytes = contentEncryptionKey.GetKey();
+
+ string rfc3211WrapperName = Helper.GetRfc3211WrapperName(keyEncryptionKeyOID);
+ IWrapper keyWrapper = Helper.CreateWrapper(rfc3211WrapperName);
+
+ // Note: In Java build, the IV is automatically generated in JCE layer
+ int ivLength = rfc3211WrapperName.StartsWith("DESEDE") ? 8 : 16;
+ byte[] iv = new byte[ivLength];
+ random.NextBytes(iv);
+
+ ICipherParameters parameters = new ParametersWithIV(keyEncryptionKey, iv);
+ keyWrapper.Init(true, new ParametersWithRandom(parameters, random));
+ Asn1OctetString encryptedKey = new DerOctetString(
+ keyWrapper.Wrap(keyBytes, 0, keyBytes.Length));
+
+ DerSequence seq = new DerSequence(
+ new DerObjectIdentifier(keyEncryptionKeyOID),
+ new DerOctetString(iv));
+
+ AlgorithmIdentifier keyEncryptionAlgorithm = new AlgorithmIdentifier(
+ PkcsObjectIdentifiers.IdAlgPwriKek, seq);
+
+ return new RecipientInfo(new PasswordRecipientInfo(
+ keyDerivationAlgorithm, keyEncryptionAlgorithm, encryptedKey));
+ }
+ }
+}
diff --git a/crypto/src/cms/PasswordRecipientInformation.cs b/crypto/src/cms/PasswordRecipientInformation.cs
new file mode 100644
index 000000000..f629caba6
--- /dev/null
+++ b/crypto/src/cms/PasswordRecipientInformation.cs
@@ -0,0 +1,79 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * the RecipientInfo class for a recipient who has been sent a message
+ * encrypted using a password.
+ */
+ public class PasswordRecipientInformation
+ : RecipientInformation
+ {
+ private readonly PasswordRecipientInfo info;
+
+ internal PasswordRecipientInformation(
+ PasswordRecipientInfo info,
+ CmsSecureReadable secureReadable)
+ : base(info.KeyEncryptionAlgorithm, secureReadable)
+ {
+ this.info = info;
+ this.rid = new RecipientID();
+ }
+
+ /**
+ * return the object identifier for the key derivation algorithm, or null
+ * if there is none present.
+ *
+ * @return OID for key derivation algorithm, if present.
+ */
+ public virtual AlgorithmIdentifier KeyDerivationAlgorithm
+ {
+ get { return info.KeyDerivationAlgorithm; }
+ }
+
+ /**
+ * decrypt the content and return an input stream.
+ */
+ public override CmsTypedStream GetContentStream(
+ ICipherParameters key)
+ {
+ try
+ {
+ AlgorithmIdentifier kekAlg = AlgorithmIdentifier.GetInstance(info.KeyEncryptionAlgorithm);
+ Asn1Sequence kekAlgParams = (Asn1Sequence)kekAlg.Parameters;
+ byte[] encryptedKey = info.EncryptedKey.GetOctets();
+ string kekAlgName = DerObjectIdentifier.GetInstance(kekAlgParams[0]).Id;
+ string cName = CmsEnvelopedHelper.Instance.GetRfc3211WrapperName(kekAlgName);
+ IWrapper keyWrapper = WrapperUtilities.GetWrapper(cName);
+
+ byte[] iv = Asn1OctetString.GetInstance(kekAlgParams[1]).GetOctets();
+
+ ICipherParameters parameters = ((CmsPbeKey)key).GetEncoded(kekAlgName);
+ parameters = new ParametersWithIV(parameters, iv);
+
+ keyWrapper.Init(false, parameters);
+
+ KeyParameter sKey = ParameterUtilities.CreateKeyParameter(
+ GetContentAlgorithmName(), keyWrapper.Unwrap(encryptedKey, 0, encryptedKey.Length));
+
+ return GetContentFromSessionKey(sKey);
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw new CmsException("couldn't create cipher.", e);
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key invalid in message.", e);
+ }
+ }
+ }
+}
diff --git a/crypto/src/cms/RecipientId.cs b/crypto/src/cms/RecipientId.cs
new file mode 100644
index 000000000..9b6eb093b
--- /dev/null
+++ b/crypto/src/cms/RecipientId.cs
@@ -0,0 +1,58 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Cms
+{
+ public class RecipientID
+ : X509CertStoreSelector
+ {
+ private byte[] keyIdentifier;
+
+ public byte[] KeyIdentifier
+ {
+ get { return Arrays.Clone(keyIdentifier); }
+ set { keyIdentifier = Arrays.Clone(value); }
+ }
+
+ public override int GetHashCode()
+ {
+ int code = Arrays.GetHashCode(keyIdentifier)
+ ^ Arrays.GetHashCode(this.SubjectKeyIdentifier);
+
+ BigInteger serialNumber = this.SerialNumber;
+ if (serialNumber != null)
+ {
+ code ^= serialNumber.GetHashCode();
+ }
+
+ X509Name issuer = this.Issuer;
+ if (issuer != null)
+ {
+ code ^= issuer.GetHashCode();
+ }
+
+ return code;
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ RecipientID id = obj as RecipientID;
+
+ if (id == null)
+ return false;
+
+ return Arrays.AreEqual(keyIdentifier, id.keyIdentifier)
+ && Arrays.AreEqual(SubjectKeyIdentifier, id.SubjectKeyIdentifier)
+ && Platform.Equals(SerialNumber, id.SerialNumber)
+ && IssuersMatch(Issuer, id.Issuer);
+ }
+ }
+}
diff --git a/crypto/src/cms/RecipientInfoGenerator.cs b/crypto/src/cms/RecipientInfoGenerator.cs
new file mode 100644
index 000000000..c41db6122
--- /dev/null
+++ b/crypto/src/cms/RecipientInfoGenerator.cs
@@ -0,0 +1,26 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Cms
+{
+ interface RecipientInfoGenerator
+ {
+ ///
+ /// Generate a RecipientInfo object for the given key.
+ ///
+ ///
+ /// A
+ ///
+ ///
+ /// A
+ ///
+ ///
+ /// A
+ ///
+ ///
+ RecipientInfo Generate(KeyParameter contentEncryptionKey, SecureRandom random);
+ }
+}
diff --git a/crypto/src/cms/RecipientInformation.cs b/crypto/src/cms/RecipientInformation.cs
new file mode 100644
index 000000000..8b0316be4
--- /dev/null
+++ b/crypto/src/cms/RecipientInformation.cs
@@ -0,0 +1,126 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.Cms
+{
+ public abstract class RecipientInformation
+ {
+ internal RecipientID rid = new RecipientID();
+ internal AlgorithmIdentifier keyEncAlg;
+ internal CmsSecureReadable secureReadable;
+
+ private byte[] resultMac;
+
+ internal RecipientInformation(
+ AlgorithmIdentifier keyEncAlg,
+ CmsSecureReadable secureReadable)
+ {
+ this.keyEncAlg = keyEncAlg;
+ this.secureReadable = secureReadable;
+ }
+
+ internal string GetContentAlgorithmName()
+ {
+ AlgorithmIdentifier algorithm = secureReadable.Algorithm;
+// return CmsEnvelopedHelper.Instance.GetSymmetricCipherName(algorithm.ObjectID.Id);
+ return algorithm.ObjectID.Id;
+ }
+
+ public RecipientID RecipientID
+ {
+ get { return rid; }
+ }
+
+ public AlgorithmIdentifier KeyEncryptionAlgorithmID
+ {
+ get { return keyEncAlg; }
+ }
+
+ /**
+ * return the object identifier for the key encryption algorithm.
+ *
+ * @return OID for key encryption algorithm.
+ */
+ public string KeyEncryptionAlgOid
+ {
+ get { return keyEncAlg.ObjectID.Id; }
+ }
+
+ /**
+ * return the ASN.1 encoded key encryption algorithm parameters, or null if
+ * there aren't any.
+ *
+ * @return ASN.1 encoding of key encryption algorithm parameters.
+ */
+ public Asn1Object KeyEncryptionAlgParams
+ {
+ get
+ {
+ Asn1Encodable ae = keyEncAlg.Parameters;
+
+ return ae == null ? null : ae.ToAsn1Object();
+ }
+ }
+
+ internal CmsTypedStream GetContentFromSessionKey(
+ KeyParameter sKey)
+ {
+ CmsReadable readable = secureReadable.GetReadable(sKey);
+
+ try
+ {
+ return new CmsTypedStream(readable.GetInputStream());
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("error getting .", e);
+ }
+ }
+
+ public byte[] GetContent(
+ ICipherParameters key)
+ {
+ try
+ {
+ return CmsUtilities.StreamToByteArray(GetContentStream(key).ContentStream);
+ }
+ catch (IOException e)
+ {
+ throw new Exception("unable to parse internal stream: " + e);
+ }
+ }
+
+ /**
+ * Return the MAC calculated for the content stream. Note: this call is only meaningful once all
+ * the content has been read.
+ *
+ * @return byte array containing the mac.
+ */
+ public byte[] GetMac()
+ {
+ if (resultMac == null)
+ {
+ object cryptoObject = secureReadable.CryptoObject;
+ if (cryptoObject is IMac)
+ {
+ resultMac = MacUtilities.DoFinal((IMac)cryptoObject);
+ }
+ }
+
+ return Arrays.Clone(resultMac);
+ }
+
+ public abstract CmsTypedStream GetContentStream(ICipherParameters key);
+ }
+}
diff --git a/crypto/src/cms/RecipientInformationStore.cs b/crypto/src/cms/RecipientInformationStore.cs
new file mode 100644
index 000000000..33b472f9d
--- /dev/null
+++ b/crypto/src/cms/RecipientInformationStore.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Cms
+{
+ public class RecipientInformationStore
+ {
+ private readonly IList all; //ArrayList[RecipientInformation]
+ private readonly IDictionary table = Platform.CreateHashtable(); // Hashtable[RecipientID, ArrayList[RecipientInformation]]
+
+ public RecipientInformationStore(
+ ICollection recipientInfos)
+ {
+ foreach (RecipientInformation recipientInformation in recipientInfos)
+ {
+ RecipientID rid = recipientInformation.RecipientID;
+ IList list = (IList)table[rid];
+
+ if (list == null)
+ {
+ table[rid] = list = Platform.CreateArrayList(1);
+ }
+
+ list.Add(recipientInformation);
+ }
+
+ this.all = Platform.CreateArrayList(recipientInfos);
+ }
+
+ public RecipientInformation this[RecipientID selector]
+ {
+ get { return GetFirstRecipient(selector); }
+ }
+
+ /**
+ * Return the first RecipientInformation object that matches the
+ * passed in selector. Null if there are no matches.
+ *
+ * @param selector to identify a recipient
+ * @return a single RecipientInformation object. Null if none matches.
+ */
+ public RecipientInformation GetFirstRecipient(
+ RecipientID selector)
+ {
+ IList list = (IList) table[selector];
+
+ return list == null ? null : (RecipientInformation) list[0];
+ }
+
+ /**
+ * Return the number of recipients in the collection.
+ *
+ * @return number of recipients identified.
+ */
+ public int Count
+ {
+ get { return all.Count; }
+ }
+
+ /**
+ * Return all recipients in the collection
+ *
+ * @return a collection of recipients.
+ */
+ public ICollection GetRecipients()
+ {
+ return Platform.CreateArrayList(all);
+ }
+
+ /**
+ * Return possible empty collection with recipients matching the passed in RecipientID
+ *
+ * @param selector a recipient id to select against.
+ * @return a collection of RecipientInformation objects.
+ */
+ public ICollection GetRecipients(
+ RecipientID selector)
+ {
+ IList list = (IList)table[selector];
+
+ return list == null ? Platform.CreateArrayList() : Platform.CreateArrayList(list);
+ }
+ }
+}
diff --git a/crypto/src/cms/SigOutputStream.cs b/crypto/src/cms/SigOutputStream.cs
new file mode 100644
index 000000000..a807fa7fc
--- /dev/null
+++ b/crypto/src/cms/SigOutputStream.cs
@@ -0,0 +1,43 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Utilities.IO;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal class SigOutputStream
+ : BaseOutputStream
+ {
+ private readonly ISigner sig;
+
+ internal SigOutputStream(ISigner sig)
+ {
+ this.sig = sig;
+ }
+
+ public override void WriteByte(byte b)
+ {
+ try
+ {
+ sig.Update(b);
+ }
+ catch (SignatureException e)
+ {
+ throw new CmsStreamException("signature problem: " + e);
+ }
+ }
+
+ public override void Write(byte[] b, int off, int len)
+ {
+ try
+ {
+ sig.BlockUpdate(b, off, len);
+ }
+ catch (SignatureException e)
+ {
+ throw new CmsStreamException("signature problem: " + e);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/crypto/src/cms/SignerId.cs b/crypto/src/cms/SignerId.cs
new file mode 100644
index 000000000..baac9369b
--- /dev/null
+++ b/crypto/src/cms/SignerId.cs
@@ -0,0 +1,51 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * a basic index for a signer.
+ */
+ public class SignerID
+ : X509CertStoreSelector
+ {
+ public override int GetHashCode()
+ {
+ int code = Arrays.GetHashCode(this.SubjectKeyIdentifier);
+
+ BigInteger serialNumber = this.SerialNumber;
+ if (serialNumber != null)
+ {
+ code ^= serialNumber.GetHashCode();
+ }
+
+ X509Name issuer = this.Issuer;
+ if (issuer != null)
+ {
+ code ^= issuer.GetHashCode();
+ }
+
+ return code;
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return false;
+
+ SignerID id = obj as SignerID;
+
+ if (id == null)
+ return false;
+
+ return Arrays.AreEqual(SubjectKeyIdentifier, id.SubjectKeyIdentifier)
+ && Platform.Equals(SerialNumber, id.SerialNumber)
+ && IssuersMatch(Issuer, id.Issuer);
+ }
+ }
+}
diff --git a/crypto/src/cms/SignerInfoGenerator.cs b/crypto/src/cms/SignerInfoGenerator.cs
new file mode 100644
index 000000000..f78cf2c01
--- /dev/null
+++ b/crypto/src/cms/SignerInfoGenerator.cs
@@ -0,0 +1,14 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+ internal interface SignerInfoGenerator
+ {
+ SignerInfo Generate(DerObjectIdentifier contentType, AlgorithmIdentifier digestAlgorithm,
+ byte[] calculatedDigest);
+ }
+}
diff --git a/crypto/src/cms/SignerInformation.cs b/crypto/src/cms/SignerInformation.cs
new file mode 100644
index 000000000..20af29a50
--- /dev/null
+++ b/crypto/src/cms/SignerInformation.cs
@@ -0,0 +1,758 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * an expanded SignerInfo block from a CMS Signed message
+ */
+ public class SignerInformation
+ {
+ private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance;
+
+ private SignerID sid;
+ private SignerInfo info;
+ private AlgorithmIdentifier digestAlgorithm;
+ private AlgorithmIdentifier encryptionAlgorithm;
+ private readonly Asn1Set signedAttributeSet;
+ private readonly Asn1Set unsignedAttributeSet;
+ private CmsProcessable content;
+ private byte[] signature;
+ private DerObjectIdentifier contentType;
+ private IDigestCalculator digestCalculator;
+ private byte[] resultDigest;
+
+ // Derived
+ private Asn1.Cms.AttributeTable signedAttributeTable;
+ private Asn1.Cms.AttributeTable unsignedAttributeTable;
+ private readonly bool isCounterSignature;
+
+ internal SignerInformation(
+ SignerInfo info,
+ DerObjectIdentifier contentType,
+ CmsProcessable content,
+ IDigestCalculator digestCalculator)
+ {
+ this.info = info;
+ this.sid = new SignerID();
+ this.contentType = contentType;
+ this.isCounterSignature = contentType == null;
+
+ try
+ {
+ SignerIdentifier s = info.SignerID;
+
+ if (s.IsTagged)
+ {
+ Asn1OctetString octs = Asn1OctetString.GetInstance(s.ID);
+
+ sid.SubjectKeyIdentifier = octs.GetEncoded();
+ }
+ else
+ {
+ Asn1.Cms.IssuerAndSerialNumber iAnds =
+ Asn1.Cms.IssuerAndSerialNumber.GetInstance(s.ID);
+
+ sid.Issuer = iAnds.Name;
+ sid.SerialNumber = iAnds.SerialNumber.Value;
+ }
+ }
+ catch (IOException)
+ {
+ throw new ArgumentException("invalid sid in SignerInfo");
+ }
+
+ this.digestAlgorithm = info.DigestAlgorithm;
+ this.signedAttributeSet = info.AuthenticatedAttributes;
+ this.unsignedAttributeSet = info.UnauthenticatedAttributes;
+ this.encryptionAlgorithm = info.DigestEncryptionAlgorithm;
+ this.signature = info.EncryptedDigest.GetOctets();
+
+ this.content = content;
+ this.digestCalculator = digestCalculator;
+ }
+
+ public bool IsCounterSignature
+ {
+ get { return isCounterSignature; }
+ }
+
+ public DerObjectIdentifier ContentType
+ {
+ get { return contentType; }
+ }
+
+ public SignerID SignerID
+ {
+ get { return sid; }
+ }
+
+ /**
+ * return the version number for this objects underlying SignerInfo structure.
+ */
+ public int Version
+ {
+ get { return info.Version.Value.IntValue; }
+ }
+
+ public AlgorithmIdentifier DigestAlgorithmID
+ {
+ get { return digestAlgorithm; }
+ }
+
+ /**
+ * return the object identifier for the signature.
+ */
+ public string DigestAlgOid
+ {
+ get { return digestAlgorithm.ObjectID.Id; }
+ }
+
+ /**
+ * return the signature parameters, or null if there aren't any.
+ */
+ public Asn1Object DigestAlgParams
+ {
+ get
+ {
+ Asn1Encodable ae = digestAlgorithm.Parameters;
+
+ return ae == null ? null : ae.ToAsn1Object();
+ }
+ }
+
+ /**
+ * return the content digest that was calculated during verification.
+ */
+ public byte[] GetContentDigest()
+ {
+ if (resultDigest == null)
+ {
+ throw new InvalidOperationException("method can only be called after verify.");
+ }
+
+ return (byte[])resultDigest.Clone();
+ }
+
+ public AlgorithmIdentifier EncryptionAlgorithmID
+ {
+ get { return encryptionAlgorithm; }
+ }
+
+ /**
+ * return the object identifier for the signature.
+ */
+ public string EncryptionAlgOid
+ {
+ get { return encryptionAlgorithm.ObjectID.Id; }
+ }
+
+ /**
+ * return the signature/encryption algorithm parameters, or null if
+ * there aren't any.
+ */
+ public Asn1Object EncryptionAlgParams
+ {
+ get
+ {
+ Asn1Encodable ae = encryptionAlgorithm.Parameters;
+
+ return ae == null ? null : ae.ToAsn1Object();
+ }
+ }
+
+ /**
+ * return a table of the signed attributes - indexed by
+ * the OID of the attribute.
+ */
+ public Asn1.Cms.AttributeTable SignedAttributes
+ {
+ get
+ {
+ if (signedAttributeSet != null && signedAttributeTable == null)
+ {
+ signedAttributeTable = new Asn1.Cms.AttributeTable(signedAttributeSet);
+ }
+ return signedAttributeTable;
+ }
+ }
+
+ /**
+ * return a table of the unsigned attributes indexed by
+ * the OID of the attribute.
+ */
+ public Asn1.Cms.AttributeTable UnsignedAttributes
+ {
+ get
+ {
+ if (unsignedAttributeSet != null && unsignedAttributeTable == null)
+ {
+ unsignedAttributeTable = new Asn1.Cms.AttributeTable(unsignedAttributeSet);
+ }
+ return unsignedAttributeTable;
+ }
+ }
+
+ /**
+ * return the encoded signature
+ */
+ public byte[] GetSignature()
+ {
+ return (byte[]) signature.Clone();
+ }
+
+ /**
+ * Return a SignerInformationStore containing the counter signatures attached to this
+ * signer. If no counter signatures are present an empty store is returned.
+ */
+ public SignerInformationStore GetCounterSignatures()
+ {
+ // TODO There are several checks implied by the RFC3852 comments that are missing
+
+ /*
+ The countersignature attribute MUST be an unsigned attribute; it MUST
+ NOT be a signed attribute, an authenticated attribute, an
+ unauthenticated attribute, or an unprotected attribute.
+ */
+ Asn1.Cms.AttributeTable unsignedAttributeTable = UnsignedAttributes;
+ if (unsignedAttributeTable == null)
+ {
+ return new SignerInformationStore(Platform.CreateArrayList(0));
+ }
+
+ IList counterSignatures = Platform.CreateArrayList();
+
+ /*
+ The UnsignedAttributes syntax is defined as a SET OF Attributes. The
+ UnsignedAttributes in a signerInfo may include multiple instances of
+ the countersignature attribute.
+ */
+ Asn1EncodableVector allCSAttrs = unsignedAttributeTable.GetAll(CmsAttributes.CounterSignature);
+
+ foreach (Asn1.Cms.Attribute counterSignatureAttribute in allCSAttrs)
+ {
+ /*
+ A countersignature attribute can have multiple attribute values. The
+ syntax is defined as a SET OF AttributeValue, and there MUST be one
+ or more instances of AttributeValue present.
+ */
+ Asn1Set values = counterSignatureAttribute.AttrValues;
+ if (values.Count < 1)
+ {
+ // TODO Throw an appropriate exception?
+ }
+
+ foreach (Asn1Encodable asn1Obj in values)
+ {
+ /*
+ Countersignature values have the same meaning as SignerInfo values
+ for ordinary signatures, except that:
+
+ 1. The signedAttributes field MUST NOT contain a content-type
+ attribute; there is no content type for countersignatures.
+
+ 2. The signedAttributes field MUST contain a message-digest
+ attribute if it contains any other attributes.
+
+ 3. The input to the message-digesting process is the contents
+ octets of the DER encoding of the signatureValue field of the
+ SignerInfo value with which the attribute is associated.
+ */
+ SignerInfo si = SignerInfo.GetInstance(asn1Obj.ToAsn1Object());
+
+ string digestName = CmsSignedHelper.Instance.GetDigestAlgName(si.DigestAlgorithm.ObjectID.Id);
+
+ counterSignatures.Add(new SignerInformation(si, null, null, new CounterSignatureDigestCalculator(digestName, GetSignature())));
+ }
+ }
+
+ return new SignerInformationStore(counterSignatures);
+ }
+
+ /**
+ * return the DER encoding of the signed attributes.
+ * @throws IOException if an encoding error occurs.
+ */
+ public byte[] GetEncodedSignedAttributes()
+ {
+ return signedAttributeSet == null
+ ? null
+ : signedAttributeSet.GetEncoded(Asn1Encodable.Der);
+ }
+
+ private bool DoVerify(
+ AsymmetricKeyParameter key)
+ {
+ string digestName = Helper.GetDigestAlgName(this.DigestAlgOid);
+ IDigest digest = Helper.GetDigestInstance(digestName);
+
+ DerObjectIdentifier sigAlgOid = this.encryptionAlgorithm.ObjectID;
+ Asn1Encodable sigParams = this.encryptionAlgorithm.Parameters;
+ ISigner sig;
+
+ if (sigAlgOid.Equals(Asn1.Pkcs.PkcsObjectIdentifiers.IdRsassaPss))
+ {
+ // RFC 4056 2.2
+ // When the id-RSASSA-PSS algorithm identifier is used for a signature,
+ // the AlgorithmIdentifier parameters field MUST contain RSASSA-PSS-params.
+ if (sigParams == null)
+ throw new CmsException("RSASSA-PSS signature must specify algorithm parameters");
+
+ try
+ {
+ // TODO Provide abstract configuration mechanism
+ // (via alternate SignerUtilities.GetSigner method taking ASN.1 params)
+
+ Asn1.Pkcs.RsassaPssParameters pss = Asn1.Pkcs.RsassaPssParameters.GetInstance(
+ sigParams.ToAsn1Object());
+
+ if (!pss.HashAlgorithm.ObjectID.Equals(this.digestAlgorithm.ObjectID))
+ throw new CmsException("RSASSA-PSS signature parameters specified incorrect hash algorithm");
+ if (!pss.MaskGenAlgorithm.ObjectID.Equals(Asn1.Pkcs.PkcsObjectIdentifiers.IdMgf1))
+ throw new CmsException("RSASSA-PSS signature parameters specified unknown MGF");
+
+ IDigest pssDigest = DigestUtilities.GetDigest(pss.HashAlgorithm.ObjectID);
+ int saltLength = pss.SaltLength.Value.IntValue;
+ byte trailerField = (byte) pss.TrailerField.Value.IntValue;
+
+ // RFC 4055 3.1
+ // The value MUST be 1, which represents the trailer field with hexadecimal value 0xBC
+ if (trailerField != 1)
+ throw new CmsException("RSASSA-PSS signature parameters must have trailerField of 1");
+
+ sig = new PssSigner(new RsaBlindedEngine(), pssDigest, saltLength);
+ }
+ catch (Exception e)
+ {
+ throw new CmsException("failed to set RSASSA-PSS signature parameters", e);
+ }
+ }
+ else
+ {
+ // TODO Probably too strong a check at the moment
+// if (sigParams != null)
+// throw new CmsException("unrecognised signature parameters provided");
+
+ string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(this.EncryptionAlgOid);
+
+ sig = Helper.GetSignatureInstance(signatureName);
+ }
+
+ try
+ {
+ if (digestCalculator != null)
+ {
+ resultDigest = digestCalculator.GetDigest();
+ }
+ else
+ {
+ if (content != null)
+ {
+ content.Write(new DigOutputStream(digest));
+ }
+ else if (signedAttributeSet == null)
+ {
+ // TODO Get rid of this exception and just treat content==null as empty not missing?
+ throw new CmsException("data not encapsulated in signature - use detached constructor.");
+ }
+
+ resultDigest = DigestUtilities.DoFinal(digest);
+ }
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("can't process mime object to create signature.", e);
+ }
+
+ // RFC 3852 11.1 Check the content-type attribute is correct
+ {
+ Asn1Object validContentType = GetSingleValuedSignedAttribute(
+ CmsAttributes.ContentType, "content-type");
+ if (validContentType == null)
+ {
+ if (!isCounterSignature && signedAttributeSet != null)
+ throw new CmsException("The content-type attribute type MUST be present whenever signed attributes are present in signed-data");
+ }
+ else
+ {
+ if (isCounterSignature)
+ throw new CmsException("[For counter signatures,] the signedAttributes field MUST NOT contain a content-type attribute");
+
+ if (!(validContentType is DerObjectIdentifier))
+ throw new CmsException("content-type attribute value not of ASN.1 type 'OBJECT IDENTIFIER'");
+
+ DerObjectIdentifier signedContentType = (DerObjectIdentifier)validContentType;
+
+ if (!signedContentType.Equals(contentType))
+ throw new CmsException("content-type attribute value does not match eContentType");
+ }
+ }
+
+ // RFC 3852 11.2 Check the message-digest attribute is correct
+ {
+ Asn1Object validMessageDigest = GetSingleValuedSignedAttribute(
+ CmsAttributes.MessageDigest, "message-digest");
+ if (validMessageDigest == null)
+ {
+ if (signedAttributeSet != null)
+ throw new CmsException("the message-digest signed attribute type MUST be present when there are any signed attributes present");
+ }
+ else
+ {
+ if (!(validMessageDigest is Asn1OctetString))
+ {
+ throw new CmsException("message-digest attribute value not of ASN.1 type 'OCTET STRING'");
+ }
+
+ Asn1OctetString signedMessageDigest = (Asn1OctetString)validMessageDigest;
+
+ if (!Arrays.AreEqual(resultDigest, signedMessageDigest.GetOctets()))
+ throw new CmsException("message-digest attribute value does not match calculated value");
+ }
+ }
+
+ // RFC 3852 11.4 Validate countersignature attribute(s)
+ {
+ Asn1.Cms.AttributeTable signedAttrTable = this.SignedAttributes;
+ if (signedAttrTable != null
+ && signedAttrTable.GetAll(CmsAttributes.CounterSignature).Count > 0)
+ {
+ throw new CmsException("A countersignature attribute MUST NOT be a signed attribute");
+ }
+
+ Asn1.Cms.AttributeTable unsignedAttrTable = this.UnsignedAttributes;
+ if (unsignedAttrTable != null)
+ {
+ foreach (Asn1.Cms.Attribute csAttr in unsignedAttrTable.GetAll(CmsAttributes.CounterSignature))
+ {
+ if (csAttr.AttrValues.Count < 1)
+ throw new CmsException("A countersignature attribute MUST contain at least one AttributeValue");
+
+ // Note: We don't recursively validate the countersignature value
+ }
+ }
+ }
+
+ try
+ {
+ sig.Init(false, key);
+
+ if (signedAttributeSet == null)
+ {
+ if (digestCalculator != null)
+ {
+ // need to decrypt signature and check message bytes
+ return VerifyDigest(resultDigest, key, this.GetSignature());
+ }
+ else if (content != null)
+ {
+ // TODO Use raw signature of the hash value instead
+ content.Write(new SigOutputStream(sig));
+ }
+ }
+ else
+ {
+ byte[] tmp = this.GetEncodedSignedAttributes();
+ sig.BlockUpdate(tmp, 0, tmp.Length);
+ }
+
+ return sig.VerifySignature(this.GetSignature());
+ }
+ catch (InvalidKeyException e)
+ {
+ throw new CmsException("key not appropriate to signature in message.", e);
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("can't process mime object to create signature.", e);
+ }
+ catch (SignatureException e)
+ {
+ throw new CmsException("invalid signature format in message: " + e.Message, e);
+ }
+ }
+
+ private bool IsNull(
+ Asn1Encodable o)
+ {
+ return (o is Asn1Null) || (o == null);
+ }
+
+ private DigestInfo DerDecode(
+ byte[] encoding)
+ {
+ if (encoding[0] != (int)(Asn1Tags.Constructed | Asn1Tags.Sequence))
+ {
+ throw new IOException("not a digest info object");
+ }
+
+ DigestInfo digInfo = DigestInfo.GetInstance(Asn1Object.FromByteArray(encoding));
+
+ // length check to avoid Bleichenbacher vulnerability
+
+ if (digInfo.GetEncoded().Length != encoding.Length)
+ {
+ throw new CmsException("malformed RSA signature");
+ }
+
+ return digInfo;
+ }
+
+ private bool VerifyDigest(
+ byte[] digest,
+ AsymmetricKeyParameter key,
+ byte[] signature)
+ {
+ string algorithm = Helper.GetEncryptionAlgName(this.EncryptionAlgOid);
+
+ try
+ {
+ if (algorithm.Equals("RSA"))
+ {
+ IBufferedCipher c = CmsEnvelopedHelper.Instance.CreateAsymmetricCipher("RSA/ECB/PKCS1Padding");
+
+ c.Init(false, key);
+
+ byte[] decrypt = c.DoFinal(signature);
+
+ DigestInfo digInfo = DerDecode(decrypt);
+
+ if (!digInfo.AlgorithmID.ObjectID.Equals(digestAlgorithm.ObjectID))
+ {
+ return false;
+ }
+
+ if (!IsNull(digInfo.AlgorithmID.Parameters))
+ {
+ return false;
+ }
+
+ byte[] sigHash = digInfo.GetDigest();
+
+ return Arrays.ConstantTimeAreEqual(digest, sigHash);
+ }
+ else if (algorithm.Equals("DSA"))
+ {
+ ISigner sig = SignerUtilities.GetSigner("NONEwithDSA");
+
+ sig.Init(false, key);
+
+ sig.BlockUpdate(digest, 0, digest.Length);
+
+ return sig.VerifySignature(signature);
+ }
+ else
+ {
+ throw new CmsException("algorithm: " + algorithm + " not supported in base signatures.");
+ }
+ }
+ catch (SecurityUtilityException e)
+ {
+ throw e;
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new CmsException("Exception processing signature: " + e, e);
+ }
+ catch (IOException e)
+ {
+ throw new CmsException("Exception decoding signature: " + e, e);
+ }
+ }
+
+ /**
+ * verify that the given public key successfully handles and confirms the
+ * signature associated with this signer.
+ */
+ public bool Verify(
+ AsymmetricKeyParameter pubKey)
+ {
+ if (pubKey.IsPrivate)
+ throw new ArgumentException("Expected public key", "pubKey");
+
+ // Optional, but still need to validate if present
+ GetSigningTime();
+
+ return DoVerify(pubKey);
+ }
+
+ /**
+ * verify that the given certificate successfully handles and confirms
+ * the signature associated with this signer and, if a signingTime
+ * attribute is available, that the certificate was valid at the time the
+ * signature was generated.
+ */
+ public bool Verify(
+ X509Certificate cert)
+ {
+ Asn1.Cms.Time signingTime = GetSigningTime();
+ if (signingTime != null)
+ {
+ cert.CheckValidity(signingTime.Date);
+ }
+
+ return DoVerify(cert.GetPublicKey());
+ }
+
+ /**
+ * Return the base ASN.1 CMS structure that this object contains.
+ *
+ * @return an object containing a CMS SignerInfo structure.
+ */
+ public SignerInfo ToSignerInfo()
+ {
+ return info;
+ }
+
+ private Asn1Object GetSingleValuedSignedAttribute(
+ DerObjectIdentifier attrOID, string printableName)
+ {
+
+ Asn1.Cms.AttributeTable unsignedAttrTable = this.UnsignedAttributes;
+ if (unsignedAttrTable != null
+ && unsignedAttrTable.GetAll(attrOID).Count > 0)
+ {
+ throw new CmsException("The " + printableName
+ + " attribute MUST NOT be an unsigned attribute");
+ }
+
+ Asn1.Cms.AttributeTable signedAttrTable = this.SignedAttributes;
+ if (signedAttrTable == null)
+ {
+ return null;
+ }
+
+ Asn1EncodableVector v = signedAttrTable.GetAll(attrOID);
+ switch (v.Count)
+ {
+ case 0:
+ return null;
+ case 1:
+ Asn1.Cms.Attribute t = (Asn1.Cms.Attribute) v[0];
+ Asn1Set attrValues = t.AttrValues;
+
+ if (attrValues.Count != 1)
+ throw new CmsException("A " + printableName
+ + " attribute MUST have a single attribute value");
+
+ return attrValues[0].ToAsn1Object();
+ default:
+ throw new CmsException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the "
+ + printableName + " attribute");
+ }
+ }
+
+ private Asn1.Cms.Time GetSigningTime()
+ {
+ Asn1Object validSigningTime = GetSingleValuedSignedAttribute(
+ CmsAttributes.SigningTime, "signing-time");
+
+ if (validSigningTime == null)
+ return null;
+
+ try
+ {
+ return Asn1.Cms.Time.GetInstance(validSigningTime);
+ }
+ catch (ArgumentException)
+ {
+ throw new CmsException("signing-time attribute value not a valid 'Time' structure");
+ }
+ }
+
+ /**
+ * Return a signer information object with the passed in unsigned
+ * attributes replacing the ones that are current associated with
+ * the object passed in.
+ *
+ * @param signerInformation the signerInfo to be used as the basis.
+ * @param unsignedAttributes the unsigned attributes to add.
+ * @return a copy of the original SignerInformationObject with the changed attributes.
+ */
+ public static SignerInformation ReplaceUnsignedAttributes(
+ SignerInformation signerInformation,
+ Asn1.Cms.AttributeTable unsignedAttributes)
+ {
+ SignerInfo sInfo = signerInformation.info;
+ Asn1Set unsignedAttr = null;
+
+ if (unsignedAttributes != null)
+ {
+ unsignedAttr = new DerSet(unsignedAttributes.ToAsn1EncodableVector());
+ }
+
+ return new SignerInformation(
+ new SignerInfo(
+ sInfo.SignerID,
+ sInfo.DigestAlgorithm,
+ sInfo.AuthenticatedAttributes,
+ sInfo.DigestEncryptionAlgorithm,
+ sInfo.EncryptedDigest,
+ unsignedAttr),
+ signerInformation.contentType,
+ signerInformation.content,
+ null);
+ }
+
+ /**
+ * Return a signer information object with passed in SignerInformationStore representing counter
+ * signatures attached as an unsigned attribute.
+ *
+ * @param signerInformation the signerInfo to be used as the basis.
+ * @param counterSigners signer info objects carrying counter signature.
+ * @return a copy of the original SignerInformationObject with the changed attributes.
+ */
+ public static SignerInformation AddCounterSigners(
+ SignerInformation signerInformation,
+ SignerInformationStore counterSigners)
+ {
+ // TODO Perform checks from RFC 3852 11.4
+
+ SignerInfo sInfo = signerInformation.info;
+ Asn1.Cms.AttributeTable unsignedAttr = signerInformation.UnsignedAttributes;
+ Asn1EncodableVector v;
+
+ if (unsignedAttr != null)
+ {
+ v = unsignedAttr.ToAsn1EncodableVector();
+ }
+ else
+ {
+ v = new Asn1EncodableVector();
+ }
+
+ Asn1EncodableVector sigs = new Asn1EncodableVector();
+
+ foreach (SignerInformation sigInf in counterSigners.GetSigners())
+ {
+ sigs.Add(sigInf.ToSignerInfo());
+ }
+
+ v.Add(new Asn1.Cms.Attribute(CmsAttributes.CounterSignature, new DerSet(sigs)));
+
+ return new SignerInformation(
+ new SignerInfo(
+ sInfo.SignerID,
+ sInfo.DigestAlgorithm,
+ sInfo.AuthenticatedAttributes,
+ sInfo.DigestEncryptionAlgorithm,
+ sInfo.EncryptedDigest,
+ new DerSet(v)),
+ signerInformation.contentType,
+ signerInformation.content,
+ null);
+ }
+ }
+}
diff --git a/crypto/src/cms/SignerInformationStore.cs b/crypto/src/cms/SignerInformationStore.cs
new file mode 100644
index 000000000..bd613843d
--- /dev/null
+++ b/crypto/src/cms/SignerInformationStore.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Cms
+{
+ public class SignerInformationStore
+ {
+ private readonly IList all; //ArrayList[SignerInformation]
+ private readonly IDictionary table = Platform.CreateHashtable(); // Hashtable[SignerID, ArrayList[SignerInformation]]
+
+ public SignerInformationStore(
+ ICollection signerInfos)
+ {
+ foreach (SignerInformation signer in signerInfos)
+ {
+ SignerID sid = signer.SignerID;
+ IList list = (IList)table[sid];
+
+ if (list == null)
+ {
+ table[sid] = list = Platform.CreateArrayList(1);
+ }
+
+ list.Add(signer);
+ }
+
+ this.all = Platform.CreateArrayList(signerInfos);
+ }
+
+ /**
+ * Return the first SignerInformation object that matches the
+ * passed in selector. Null if there are no matches.
+ *
+ * @param selector to identify a signer
+ * @return a single SignerInformation object. Null if none matches.
+ */
+ public SignerInformation GetFirstSigner(
+ SignerID selector)
+ {
+ IList list = (IList) table[selector];
+
+ return list == null ? null : (SignerInformation) list[0];
+ }
+
+ /// The number of signers in the collection.
+ public int Count
+ {
+ get { return all.Count; }
+ }
+
+ /// An ICollection of all signers in the collection
+ public ICollection GetSigners()
+ {
+ return Platform.CreateArrayList(all);
+ }
+
+ /**
+ * Return possible empty collection with signers matching the passed in SignerID
+ *
+ * @param selector a signer id to select against.
+ * @return a collection of SignerInformation objects.
+ */
+ public ICollection GetSigners(
+ SignerID selector)
+ {
+ IList list = (IList) table[selector];
+
+ return list == null ? Platform.CreateArrayList() : Platform.CreateArrayList(list);
+ }
+ }
+}
diff --git a/crypto/src/cms/SimpleAttributeTableGenerator.cs b/crypto/src/cms/SimpleAttributeTableGenerator.cs
new file mode 100644
index 000000000..b3df21c29
--- /dev/null
+++ b/crypto/src/cms/SimpleAttributeTableGenerator.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1.Cms;
+
+namespace Org.BouncyCastle.Cms
+{
+ /**
+ * Basic generator that just returns a preconstructed attribute table
+ */
+ public class SimpleAttributeTableGenerator
+ : CmsAttributeTableGenerator
+ {
+ private readonly AttributeTable attributes;
+
+ public SimpleAttributeTableGenerator(
+ AttributeTable attributes)
+ {
+ this.attributes = attributes;
+ }
+
+ public virtual AttributeTable GetAttributes(
+ IDictionary parameters)
+ {
+ return attributes;
+ }
+ }
+}
--
cgit 1.4.1