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.
+ * <p>
+ * A simple example of usage.
+ * </p>
+ * <pre>
+ * 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();
+ * </pre>
+ */
+ 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()
+ {
+ }
+
+ /// <summary>Constructor allowing specific source of randomness</summary>
+ /// <param name="rand">Instance of <c>SecureRandom</c> to use.</param>
+ 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);
+ }
+ }
+ }
+}
|