context: 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 ISignerInfoGenerator	signerInf;
  			internal readonly string				digestOID;
  
  			internal DigestAndSignerInfoGeneratorHolder(ISignerInfoGenerator 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 : ISignerInfoGenerator
          {
  			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;
  
gt: 					if (_unsAttr != null)