context: //
  // PKCS7.cs: PKCS #7 - Cryptographic Message Syntax Standard 
  //	http://www.rsasecurity.com/rsalabs/pkcs/pkcs-7/index.html
  //
  // Authors:
  //	Sebastien Pouliot <sebastien@ximian.com>
  //	Daniel Granath <dgranath#gmail.com>
  //
  // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
  // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
  //
  // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  // 
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  
  using System;
  using System.Collections;
  using System.Security.Cryptography;
  
  using Nequeo.Security.X509;
  
  namespace Nequeo.Security.Common
  {
  
  #if INSIDE_CORLIB
  	internal
  #else
  	public
  #endif
  	sealed class PKCS7 {
  
  		public class Oid {
  			// pkcs 1
  			public const string rsaEncryption = "1.2.840.113549.1.1.1";
  			// pkcs 7
  			public const string data = "1.2.840.113549.1.7.1";
  			public const string signedData = "1.2.840.113549.1.7.2";
  			public const string envelopedData = "1.2.840.113549.1.7.3";
  			public const string signedAndEnvelopedData = "1.2.840.113549.1.7.4";
  			public const string digestedData = "1.2.840.113549.1.7.5";
  			public const string encryptedData = "1.2.840.113549.1.7.6";
  			// pkcs 9
  			public const string contentType = "1.2.840.113549.1.9.3";
  			public const string messageDigest  = "1.2.840.113549.1.9.4";
  			public const string signingTime = "1.2.840.113549.1.9.5";
  			public const string countersignature = "1.2.840.113549.1.9.6";
  
  			public Oid () 
  			{
  			}
  		}
  
  		private PKCS7 ()
  		{
  		}
  
  		static public ASN1 Attribute (string oid, ASN1 value) 
  		{
  			ASN1 attr = new ASN1 (0x30);
  			attr.Add (ASN1Convert.FromOid (oid));
  			ASN1 aset = attr.Add (new ASN1 (0x31));
  			aset.Add (value);
  			return attr;
  		}
  
  		static public ASN1 AlgorithmIdentifier (string oid)
  		{
  			ASN1 ai = new ASN1 (0x30);
  			ai.Add (ASN1Convert.FromOid (oid));
  			ai.Add (new ASN1 (0x05));	// NULL
  			return ai;
  		}
  
  		static public ASN1 AlgorithmIdentifier (string oid, ASN1 parameters) 
  		{
  			ASN1 ai = new ASN1 (0x30);
  			ai.Add (ASN1Convert.FromOid (oid));
  			ai.Add (parameters);
  			return ai;
  		}
  
  		/*
  		 * IssuerAndSerialNumber ::= SEQUENCE {
  		 *	issuer Name,
  		 *	serialNumber CertificateSerialNumber 
  		 * }
  		 */
  		static public ASN1 IssuerAndSerialNumber (X509Certificate x509) 
  		{
  			ASN1 issuer = null;
  			ASN1 serial = null;
  			ASN1 cert = new ASN1 (x509.RawData);
  			int tbs = 0;
  			bool flag = false;
  			while (tbs < cert[0].Count) {
  				ASN1 e = cert[0][tbs++];
  				if (e.Tag == 0x02)
  					serial = e;
  				else if (e.Tag == 0x30) {
  					if (flag) {
  						issuer = e;
  						break;
  					}
  					flag = true;
  				}
  			}
  			ASN1 iasn = new ASN1 (0x30);
  			iasn.Add (issuer);
  			iasn.Add (serial);
  			return iasn;
  		}
  
  		/*
  		 * ContentInfo ::= SEQUENCE {
  		 *	contentType ContentType,
  		 *	content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL 
  		 * }
  		 * ContentType ::= OBJECT IDENTIFIER
  		 */
  		public class ContentInfo {
  
  			private string contentType;
  			private ASN1 content;
  
  			public ContentInfo () 
  			{
  				content = new ASN1 (0xA0);
  			}
  
  			public ContentInfo (string oid) : this ()
  			{
  				contentType = oid;
  			}
  
  			public ContentInfo (byte[] data) 
  				: this (new ASN1 (data)) {}
  
  			public ContentInfo (ASN1 asn1) 
  			{
  				// SEQUENCE with 1 or 2 elements
  				if ((asn1.Tag != 0x30) || ((asn1.Count < 1) && (asn1.Count > 2)))
  					throw new ArgumentException ("Invalid ASN1");
  				if (asn1[0].Tag != 0x06)
  					throw new ArgumentException ("Invalid contentType");
  				contentType = ASN1Convert.ToOid (asn1[0]);
  				if (asn1.Count > 1) {
  					if (asn1[1].Tag != 0xA0)
  						throw new ArgumentException ("Invalid content");
  					content = asn1[1];
  				}
  			}
  
  			public ASN1 ASN1 {
  				get { return GetASN1(); }
  			}
  
  			public ASN1 Content {
  				get { return content; }
  				set { content = value; }
  			}
  
  			public string ContentType {
  				get { return contentType; }
  				set { contentType = value; }
  			}
  
  			internal ASN1 GetASN1 () 
  			{
  				// ContentInfo ::= SEQUENCE {
  				ASN1 contentInfo = new ASN1 (0x30);
  				// contentType ContentType, -> ContentType ::= OBJECT IDENTIFIER
  				contentInfo.Add (ASN1Convert.FromOid (contentType));
  				// content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL 
  				if ((content != null) && (content.Count > 0))
  					contentInfo.Add (content);
  				return contentInfo;
  			}
  
  			public byte[] GetBytes () 
  			{
  				return GetASN1 ().GetBytes ();
  			}
  		}
  
  		/*
  		 * EncryptedData ::= SEQUENCE {
  		 *	version		INTEGER {edVer0(0)} (edVer0),
  		 *	 encryptedContentInfo  EncryptedContentInfo
  		 * }
  		 */
  		public class EncryptedData {
  			private byte _version;
  			private ContentInfo _content;
  			private ContentInfo _encryptionAlgorithm;
  			private byte[] _encrypted;
  
  			public EncryptedData () 
  			{
  				_version = 0;
  			}
  
  			public EncryptedData (byte[] data) 
  				: this (new ASN1 (data))
  			{
  			}
  
  			public EncryptedData (ASN1 asn1) : this () 
  			{
  				if ((asn1.Tag != 0x30) || (asn1.Count < 2))
  					throw new ArgumentException ("Invalid EncryptedData");
  
  				if (asn1 [0].Tag != 0x02)
  					throw new ArgumentException ("Invalid version");
  				_version = asn1 [0].Value [0];
  
  				ASN1 encryptedContentInfo = asn1 [1];
  				if (encryptedContentInfo.Tag != 0x30)
  					throw new ArgumentException ("missing EncryptedContentInfo");
  
  				ASN1 contentType = encryptedContentInfo [0];
  				if (contentType.Tag != 0x06)
  					throw new ArgumentException ("missing EncryptedContentInfo.ContentType");
  				_content = new ContentInfo (ASN1Convert.ToOid (contentType));
  
  				ASN1 contentEncryptionAlgorithm = encryptedContentInfo [1];
  				if (contentEncryptionAlgorithm.Tag != 0x30)
  					throw new ArgumentException ("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier");
  				_encryptionAlgorithm = new ContentInfo (ASN1Convert.ToOid (contentEncryptionAlgorithm [0]));
  				_encryptionAlgorithm.Content = contentEncryptionAlgorithm [1];
  				
  				ASN1 encryptedContent = encryptedContentInfo [2];
  				if (encryptedContent.Tag != 0x80)
  					throw new ArgumentException ("missing EncryptedContentInfo.EncryptedContent");
  				_encrypted = encryptedContent.Value;
  			}
  
  			public ASN1 ASN1 {
  				get { return GetASN1(); }
  			}
  
  			public ContentInfo ContentInfo {
  				get { return _content; }
  			}
  
  			public ContentInfo EncryptionAlgorithm {
  				get { return _encryptionAlgorithm; }
  			}
  
  			public byte[] EncryptedContent {
  				get {
  					if (_encrypted == null)
  						return null;
  					return (byte[]) _encrypted.Clone ();
  				}
  			}
  
  			public byte Version {
  				get { return _version; }
  				set { _version = value; }
  			}
  
  			// methods
  
  			internal ASN1 GetASN1 () 
  			{
  				return null;
  			}
  
  			public byte[] GetBytes () 
  			{
  				return GetASN1 ().GetBytes ();
  			}
  		}
  
  		/*
  		 * EnvelopedData ::= SEQUENCE {
  		 *	version Version,
  		 *	recipientInfos RecipientInfos,
  		 *	encryptedContentInfo EncryptedContentInfo 
  		 * }
  		 * 
  		 * RecipientInfos ::= SET OF RecipientInfo
  		 * 
  		 * EncryptedContentInfo ::= SEQUENCE {
  		 *	contentType ContentType,
  		 *	contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
  		 *	encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL 
  		 * }
  		 * 
  		 * EncryptedContent ::= OCTET STRING
  		 * 
  		 */
  		public class EnvelopedData {
  			private byte _version;
  			private ContentInfo _content;
  			private ContentInfo _encryptionAlgorithm;
  			private ArrayList _recipientInfos;
  			private byte[] _encrypted;
  
  			public EnvelopedData () 
  			{
  				_version = 0;
  				_content = new ContentInfo ();
  				_encryptionAlgorithm = new ContentInfo ();
  				_recipientInfos = new ArrayList ();
  			}
  
  			public EnvelopedData (byte[] data) 
  				: this (new ASN1 (data))
  			{
  			}
  
  			public EnvelopedData (ASN1 asn1) : this ()
  			{
  				if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 3))
  					throw new ArgumentException ("Invalid EnvelopedData");
  
  				if (asn1[0][0].Tag != 0x02)
  					throw new ArgumentException ("Invalid version");
  				_version = asn1[0][0].Value[0];
  
  				// recipientInfos
  
  				ASN1 recipientInfos = asn1 [0][1];
  				if (recipientInfos.Tag != 0x31)
  					throw new ArgumentException ("missing RecipientInfos");
  				for (int i=0; i < recipientInfos.Count; i++) {
  					ASN1 recipientInfo = recipientInfos [i];
  					_recipientInfos.Add (new RecipientInfo (recipientInfo));
  				}
  
  				ASN1 encryptedContentInfo = asn1[0][2];
  				if (encryptedContentInfo.Tag != 0x30)
  					throw new ArgumentException ("missing EncryptedContentInfo");
  
  				ASN1 contentType = encryptedContentInfo [0];
  				if (contentType.Tag != 0x06)
  					throw new ArgumentException ("missing EncryptedContentInfo.ContentType");
  				_content = new ContentInfo (ASN1Convert.ToOid (contentType));
  
  				ASN1 contentEncryptionAlgorithm = encryptedContentInfo [1];
  
gt: 				if (contentEncryptionAlgorithm.Tag != 0x30)