context: using System;
  using System.Collections;
  
  namespace Org.BouncyCastle.Asn1.Cms
  {
      /**
       * a signed data object.
       */
      public class SignedData
          : Asn1Encodable
      {
          private readonly DerInteger		version;
          private readonly Asn1Set		digestAlgorithms;
          private readonly ContentInfo	contentInfo;
          private readonly Asn1Set		certificates;
          private readonly Asn1Set		crls;
          private readonly Asn1Set		signerInfos;
          private readonly bool			certsBer;
          private readonly bool		    crlsBer;
  
  		public static SignedData GetInstance(
              object obj)
          {
              if (obj is SignedData)
                  return (SignedData) obj;
  
  			if (obj is Asn1Sequence)
                  return new SignedData((Asn1Sequence) obj);
  
  			throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj");
  		}
  
  		public SignedData(
  			Asn1Set     digestAlgorithms,
  			ContentInfo contentInfo,
  			Asn1Set     certificates,
  			Asn1Set     crls,
  			Asn1Set     signerInfos)
  		{
  			this.version = CalculateVersion(contentInfo.ContentType, certificates, crls, signerInfos);
  			this.digestAlgorithms = digestAlgorithms;
  			this.contentInfo = contentInfo;
  			this.certificates = certificates;
  			this.crls = crls;
  			this.signerInfos = signerInfos;
  			this.crlsBer = crls is BerSet;
  			this.certsBer = certificates is BerSet;
  		}
  
  		// 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,
  			Asn1Set				certs,
  			Asn1Set				crls,
  			Asn1Set				signerInfs)
  		{
  			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(signerInfs))
  			{
  				return new DerInteger(3);
  			}
  
              return new DerInteger(1);
  		}
  
  		private bool CheckForVersion3(
  			Asn1Set signerInfs)
  		{
  			foreach (object obj in signerInfs)
  			{
  				SignerInfo s = SignerInfo.GetInstance(obj);
  
  				if (s.Version.Value.IntValue == 3)
  				{
  					return true;
  				}
  			}
  
  			return false;
  		}
  
  		private SignedData(
              Asn1Sequence seq)
          {
              IEnumerator e = seq.GetEnumerator();
  
  			e.MoveNext();
              version = (DerInteger)e.Current;
  
  			e.MoveNext();
              digestAlgorithms = ((Asn1Set)e.Current);
  
  			e.MoveNext();
              contentInfo = ContentInfo.GetInstance(e.Current);
  
  			while (e.MoveNext())
              {
                  Asn1Object o = (Asn1Object)e.Current;
  
  				//
                  // an interesting feature of SignedData is that there appear
                  // to be varying implementations...
                  // for the moment we ignore anything which doesn't fit.
                  //
                  if (o is Asn1TaggedObject)
                  {
                      Asn1TaggedObject tagged = (Asn1TaggedObject)o;
  
                      switch (tagged.TagNo)
                      {
  						case 0:
  							certsBer = tagged is BerTaggedObject;
  							certificates = Asn1Set.GetInstance(tagged, false);
  							break;
  						case 1:
  							crlsBer = tagged is BerTaggedObject;
  							crls = Asn1Set.GetInstance(tagged, false);
  							break;
  						default:
  							throw new ArgumentException("unknown tag value " + tagged.TagNo);
                      }
                  }
                  else
                  {
                      signerInfos = (Asn1Set) o;
                  }
              }
          }
  
  		public DerInteger Version
  		{
  			get { return version; }
  		}
  
  		public Asn1Set DigestAlgorithms
  		{
  			get { return digestAlgorithms; }
  		}
  
  		public ContentInfo EncapContentInfo
  		{
  			get { return contentInfo; }
  		}
  
  		public Asn1Set Certificates
  		{
  			get { return certificates; }
  		}
  
  		public Asn1Set CRLs
  		{
  			get { return crls; }
  		}
  
  		public Asn1Set SignerInfos
  		{
  			get { return signerInfos; }
  		}
  
  		/**
           * Produce an object suitable for an Asn1OutputStream.
           * <pre>
           * SignedData ::= Sequence {
           *     version CMSVersion,
           *     digestAlgorithms DigestAlgorithmIdentifiers,
           *     encapContentInfo EncapsulatedContentInfo,
           *     certificates [0] IMPLICIT CertificateSet OPTIONAL,
           *     crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
           *     signerInfos SignerInfos
           *   }
           * </pre>
           */
          public override Asn1Object ToAsn1Object()
          {
              Asn1EncodableVector v = new Asn1EncodableVector(
  				version, digestAlgorithms, contentInfo);
  
  			if (certificates != null)
              {
                  if (certsBer)
                  {
  
gt:                     v.Add(new BerTaggedObject(false, 0, certificates));