Exemple #1
0
 public EnvelopedData()
 {
     this._version             = 0;
     this._content             = new PKCS7.ContentInfo();
     this._encryptionAlgorithm = new PKCS7.ContentInfo();
     this._recipientInfos      = new ArrayList();
 }
Exemple #2
0
 public SignedData()
 {
     this.version     = 1;
     this.contentInfo = new PKCS7.ContentInfo();
     this.certs       = new X509CertificateCollection();
     this.crls        = new ArrayList();
     this.signerInfo  = new PKCS7.SignerInfo();
     this.mda         = true;
     this.signed      = false;
 }
Exemple #3
0
            public EnvelopedData(ASN1 asn1) : this()
            {
                if (asn1[0].Tag != 48 || asn1[0].Count < 3)
                {
                    throw new ArgumentException("Invalid EnvelopedData");
                }
                if (asn1[0][0].Tag != 2)
                {
                    throw new ArgumentException("Invalid version");
                }
                this._version = asn1[0][0].Value[0];
                ASN1 asn2 = asn1[0][1];

                if (asn2.Tag != 49)
                {
                    throw new ArgumentException("missing RecipientInfos");
                }
                for (int i = 0; i < asn2.Count; i++)
                {
                    ASN1 data = asn2[i];
                    this._recipientInfos.Add(new PKCS7.RecipientInfo(data));
                }
                ASN1 asn3 = asn1[0][2];

                if (asn3.Tag != 48)
                {
                    throw new ArgumentException("missing EncryptedContentInfo");
                }
                ASN1 asn4 = asn3[0];

                if (asn4.Tag != 6)
                {
                    throw new ArgumentException("missing EncryptedContentInfo.ContentType");
                }
                this._content = new PKCS7.ContentInfo(ASN1Convert.ToOid(asn4));
                ASN1 asn5 = asn3[1];

                if (asn5.Tag != 48)
                {
                    throw new ArgumentException("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier");
                }
                this._encryptionAlgorithm         = new PKCS7.ContentInfo(ASN1Convert.ToOid(asn5[0]));
                this._encryptionAlgorithm.Content = asn5[1];
                ASN1 asn6 = asn3[2];

                if (asn6.Tag != 128)
                {
                    throw new ArgumentException("missing EncryptedContentInfo.EncryptedContent");
                }
                this._encrypted = asn6.Value;
            }
Exemple #4
0
            public SignedData(ASN1 asn1)
            {
                if (asn1[0].Tag != 48 || asn1[0].Count < 4)
                {
                    throw new ArgumentException("Invalid SignedData");
                }
                if (asn1[0][0].Tag != 2)
                {
                    throw new ArgumentException("Invalid version");
                }
                this.version     = asn1[0][0].Value[0];
                this.contentInfo = new PKCS7.ContentInfo(asn1[0][2]);
                int num = 3;

                this.certs = new X509CertificateCollection();
                if (asn1[0][num].Tag == 160)
                {
                    for (int i = 0; i < asn1[0][num].Count; i++)
                    {
                        this.certs.Add(new X509Certificate(asn1[0][num][i].GetBytes()));
                    }
                    num++;
                }
                this.crls = new ArrayList();
                if (asn1[0][num].Tag == 161)
                {
                    for (int j = 0; j < asn1[0][num].Count; j++)
                    {
                        this.crls.Add(asn1[0][num][j].GetBytes());
                    }
                    num++;
                }
                if (asn1[0][num].Count > 0)
                {
                    this.signerInfo = new PKCS7.SignerInfo(asn1[0][num]);
                }
                else
                {
                    this.signerInfo = new PKCS7.SignerInfo();
                }
                if (this.signerInfo.HashName != null)
                {
                    this.HashName = this.OidToName(this.signerInfo.HashName);
                }
                this.mda = (this.signerInfo.AuthenticatedAttributes.Count > 0);
            }
Exemple #5
0
            public EncryptedData(ASN1 asn1) : this()
            {
                if (asn1.Tag != 48 || asn1.Count < 2)
                {
                    throw new ArgumentException("Invalid EncryptedData");
                }
                if (asn1[0].Tag != 2)
                {
                    throw new ArgumentException("Invalid version");
                }
                this._version = asn1[0].Value[0];
                ASN1 asn2 = asn1[1];

                if (asn2.Tag != 48)
                {
                    throw new ArgumentException("missing EncryptedContentInfo");
                }
                ASN1 asn3 = asn2[0];

                if (asn3.Tag != 6)
                {
                    throw new ArgumentException("missing EncryptedContentInfo.ContentType");
                }
                this._content = new PKCS7.ContentInfo(ASN1Convert.ToOid(asn3));
                ASN1 asn4 = asn2[1];

                if (asn4.Tag != 48)
                {
                    throw new ArgumentException("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier");
                }
                this._encryptionAlgorithm         = new PKCS7.ContentInfo(ASN1Convert.ToOid(asn4[0]));
                this._encryptionAlgorithm.Content = asn4[1];
                ASN1 asn5 = asn2[2];

                if (asn5.Tag != 128)
                {
                    throw new ArgumentException("missing EncryptedContentInfo.EncryptedContent");
                }
                this._encrypted = asn5.Value;
            }
Exemple #6
0
		// Creates an encrypted PKCS#7 ContentInfo with safeBags as its SafeContents.  Used in GetBytes(), above.
		private PKCS7.ContentInfo EncryptedContentInfo(ASN1 safeBags, string algorithmOid)
		{
			byte[] salt = new byte [8];
			RNG.GetBytes (salt);

			ASN1 seqParams = new ASN1 (0x30);
			seqParams.Add (new ASN1 (0x04, salt));
			seqParams.Add (ASN1Convert.FromInt32 (_iterations));

			ASN1 seqPbe = new ASN1 (0x30);
			seqPbe.Add (ASN1Convert.FromOid (algorithmOid));
			seqPbe.Add (seqParams);

			byte[] encrypted = Encrypt (algorithmOid, salt, _iterations, safeBags.GetBytes ());
			ASN1 encryptedContent = new ASN1 (0x80, encrypted);

			ASN1 seq = new ASN1 (0x30);
			seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data));
			seq.Add (seqPbe);
			seq.Add (encryptedContent);

			ASN1 version = new ASN1 (0x02, new byte [1] { 0x00 });
			ASN1 encData = new ASN1 (0x30);
			encData.Add (version);
			encData.Add (seq);

			ASN1 finalContent = new ASN1 (0xA0);
			finalContent.Add (encData);

			PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData);
			bag.Content = finalContent;
			return bag;
		}
		public X509Certificate GetCertificate (IDictionary attrs)
		{
			foreach (SafeBag sb in _safeBags) {
				if (sb.BagOID.Equals (certBag)) {
					ASN1 safeBag = sb.ASN1;

					if (safeBag.Count == 3) {
						ASN1 bagAttributes = safeBag [2];

						int bagAttributesFound = 0;
						for (int i = 0; i < bagAttributes.Count; i++) {
							ASN1 pkcs12Attribute = bagAttributes [i];
							ASN1 attrId = pkcs12Attribute [0];
							string ao = ASN1Convert.ToOid (attrId);
							ArrayList dattrValues = (ArrayList)attrs [ao];

							if (dattrValues != null) {
								ASN1 attrValues = pkcs12Attribute [1];
								
								if (dattrValues.Count == attrValues.Count) {
									int attrValuesFound = 0;
									for (int j = 0; j < attrValues.Count; j++) {
										ASN1 attrValue = attrValues [j];
										byte[] value = (byte[])dattrValues [j];
									
										if (Compare (value, attrValue.Value)) {
											attrValuesFound += 1;
										}
									}
									if (attrValuesFound == attrValues.Count) {
										bagAttributesFound += 1;
									}
								}
							}
						}
						if (bagAttributesFound == bagAttributes.Count) {
							ASN1 bagValue = safeBag [1];
							PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
							return new X509Certificate (crt.Content [0].Value);
						}
					}
				}
			}

			return null;
		}
Exemple #8
0
		public static Oid GetContentType (byte[] encodedMessage)
		{
			if (encodedMessage == null)
				throw new ArgumentNullException ("algorithm");

			try {
				PKCS7.ContentInfo ci = new PKCS7.ContentInfo (encodedMessage);
				switch (ci.ContentType) {
				case PKCS7.Oid.data:
				case PKCS7.Oid.signedData:		// see SignedCms class
				case PKCS7.Oid.envelopedData:		// see EnvelopedCms class
				case PKCS7.Oid.digestedData:
				case PKCS7.Oid.encryptedData:
					return new Oid (ci.ContentType);
				default:
					// Note: the constructor will accept any "valid" OID (but that 
					// doesn't mean it's a valid ContentType structure - ASN.1 wise).
					string msg = Locale.GetText ("Bad ASN1 - invalid OID '{0}'");
					throw new CryptographicException (String.Format (msg, ci.ContentType));
				}
			}
			catch (Exception e) {
				throw new CryptographicException (Locale.GetText ("Bad ASN1 - invalid structure"), e);
			}
		}
		public ASN1 TimestampRequest (byte[] signature) 
		{
			PKCS7.ContentInfo ci = new PKCS7.ContentInfo (PKCS7.Oid.data);
			ci.Content.Add (new ASN1 (0x04, signature));
			return PKCS7.AlgorithmIdentifier (timestampCountersignature, ci.ASN1);
		}
Exemple #10
0
		public void Decode (byte[] encodedMessage)
		{
			if (encodedMessage == null)
				throw new ArgumentNullException ("encodedMessage");

			PKCS7.ContentInfo ci = new PKCS7.ContentInfo (encodedMessage);
			if (ci.ContentType != PKCS7.Oid.envelopedData)
				throw new Exception ("");

			PKCS7.EnvelopedData ed = new PKCS7.EnvelopedData (ci.Content);

			Oid oid = new Oid (ed.ContentInfo.ContentType);
			_content = new ContentInfo (oid, new byte [0]); //ed.ContentInfo.Content.Value);

			foreach (PKCS7.RecipientInfo ri in ed.RecipientInfos) {
				Oid o = new Oid (ri.Oid);
				AlgorithmIdentifier ai = new AlgorithmIdentifier (o);
				SubjectIdentifier si = null;
				if (ri.SubjectKeyIdentifier != null) {
					si = new SubjectIdentifier (SubjectIdentifierType.SubjectKeyIdentifier, ri.SubjectKeyIdentifier);
				}
				else if ((ri.Issuer != null) && (ri.Serial != null)) {
					X509IssuerSerial xis = GetIssuerSerial (ri.Issuer, ri.Serial);
					si = new SubjectIdentifier (SubjectIdentifierType.IssuerAndSerialNumber, (object)xis);
				}
				
				KeyTransRecipientInfo _keyTrans = new KeyTransRecipientInfo (ri.Key, ai, si, ri.Version);
				_recipients.Add (_keyTrans);
			}

			// TODO - Certificates
			// TODO - UnprotectedAttributes 

			_version = ed.Version;
		}
		public bool Sign (string fileName) 
		{
			try {
				Open (fileName);

				HashAlgorithm hash = HashAlgorithm.Create (Hash);
				// 0 to 215 (216) then skip 4 (checksum)

				byte[] digest = GetHash (hash);
				byte[] signature = Header (digest, Hash);
				if (timestamp != null) {
					byte[] ts = Timestamp (signature);
					// add timestamp information inside the current pkcs7 SignedData instance
					// (this is possible because the data isn't yet signed)
					ProcessTimestamp (ts);
				}

				PKCS7.ContentInfo sign = new PKCS7.ContentInfo (signedData);
				sign.Content.Add (pkcs7.ASN1);
				authenticode = sign.ASN1;
				Close ();

				return Save (fileName, authenticode.GetBytes ());
			}
			catch (Exception e) {
				Console.WriteLine (e);
			}
			return false;
		}
		private ASN1 CertificateSafeBag (X509Certificate x509, IDictionary attributes) 
		{
			ASN1 encapsulatedCertificate = new ASN1 (0x04, x509.RawData);

			PKCS7.ContentInfo ci = new PKCS7.ContentInfo ();
			ci.ContentType = x509Certificate;
			ci.Content.Add (encapsulatedCertificate);

			ASN1 bagValue = new ASN1 (0xA0);
			bagValue.Add (ci.ASN1);

			ASN1 safeBag = new ASN1 (0x30);
			safeBag.Add (ASN1Convert.FromOid (certBag));
			safeBag.Add (bagValue);

			if (attributes != null) {
				ASN1 bagAttributes = new ASN1 (0x31);
				IDictionaryEnumerator de = attributes.GetEnumerator ();

				while (de.MoveNext ()) {
					string oid = (string)de.Key;
					switch (oid) {
					case PKCS9.friendlyName:
						ArrayList names = (ArrayList)de.Value;
						if (names.Count > 0) {
							ASN1 pkcs12Attribute = new ASN1 (0x30);
							pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
							ASN1 attrValues = new ASN1 (0x31);
							foreach (byte[] name in names) {
								ASN1 attrValue = new ASN1 (0x1e);
								attrValue.Value = name;
								attrValues.Add (attrValue);
							}
							pkcs12Attribute.Add (attrValues);
							bagAttributes.Add (pkcs12Attribute);
						}
						break;
					case PKCS9.localKeyId:
						ArrayList keys = (ArrayList)de.Value;
						if (keys.Count > 0) {
							ASN1 pkcs12Attribute = new ASN1 (0x30);
							pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
							ASN1 attrValues = new ASN1 (0x31);
							foreach (byte[] key in keys) {
								ASN1 attrValue = new ASN1 (0x04);
								attrValue.Value = key;
								attrValues.Add (attrValue);
							}
							pkcs12Attribute.Add (attrValues);
							bagAttributes.Add (pkcs12Attribute);
						}
						break;
					default:
						break;
					}
				}

				if (bagAttributes.Count > 0) {
					safeBag.Add (bagAttributes);
				}
			}

			return safeBag;
		}
		public byte[] GetBytes () 
		{
			PKCS7.ContentInfo ci = new PKCS7.ContentInfo (PKCS7.Oid.signedData);
			ci.Content.Add (pkcs7.ASN1);
			return ci.GetBytes ();
		}
                /*
		 * SafeContents ::= SEQUENCE OF SafeBag
		 * 
		 * SafeBag ::= SEQUENCE {
		 *	bagId BAG-TYPE.&id ({PKCS12BagSet}),
		 *	bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
		 *	bagAttributes SET OF PKCS12Attribute OPTIONAL
		 * }
		 */
		public byte[] GetBytes () 
		{
			// TODO (incomplete)
			ASN1 safeBagSequence = new ASN1 (0x30);

			// Sync Safe Bag list since X509CertificateCollection may be updated
			ArrayList scs = new ArrayList ();
			foreach (SafeBag sb in _safeBags) {
				if (sb.BagOID.Equals (certBag)) {
					ASN1 safeBag = sb.ASN1;
					ASN1 bagValue = safeBag [1];
					PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
					scs.Add (new X509Certificate (cert.Content [0].Value));
				}
			}

			ArrayList addcerts = new ArrayList ();
			ArrayList removecerts = new ArrayList ();

			foreach (X509Certificate c in Certificates) {
				bool found = false;

				foreach (X509Certificate lc in scs) {
					if (Compare (c.RawData, lc.RawData)) {
						found = true;
					}
				}

				if (!found) {
					addcerts.Add (c);
				}
			}
			foreach (X509Certificate c in scs) {
				bool found = false;

				foreach (X509Certificate lc in Certificates) {
					if (Compare (c.RawData, lc.RawData)) {
						found = true;
					}
				}

				if (!found) {
					removecerts.Add (c);
				}
			}

			foreach (X509Certificate c in removecerts) {
				RemoveCertificate (c);
			}

			foreach (X509Certificate c in addcerts) {
				AddCertificate (c);
			}
			// Sync done

			if (_safeBags.Count > 0) {
				ASN1 certsSafeBag = new ASN1 (0x30);
				foreach (SafeBag sb in _safeBags) {
					if (sb.BagOID.Equals (certBag)) {
						certsSafeBag.Add (sb.ASN1);
					}
				}

				if (certsSafeBag.Count > 0) {
					byte[] certsSalt = new byte [8];
					RNG.GetBytes (certsSalt);

					ASN1 seqParams = new ASN1 (0x30);
					seqParams.Add (new ASN1 (0x04, certsSalt));
					seqParams.Add (ASN1Convert.FromInt32 (_iterations));

					ASN1 seqPbe = new ASN1 (0x30);
					seqPbe.Add (ASN1Convert.FromOid (pbeWithSHAAnd3KeyTripleDESCBC));
					seqPbe.Add (seqParams);

					byte[] encrypted = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, certsSalt, _iterations, certsSafeBag.GetBytes ());
					ASN1 encryptedCerts = new ASN1 (0x80, encrypted);

					ASN1 seq = new ASN1 (0x30);
					seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data));
					seq.Add (seqPbe);
					seq.Add (encryptedCerts);

					ASN1 certsVersion = new ASN1 (0x02, new byte [1] { 0x00 });
					ASN1 encData = new ASN1 (0x30);
					encData.Add (certsVersion);
					encData.Add (seq);

					ASN1 certsContent = new ASN1 (0xA0);
					certsContent.Add (encData);

					PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData);
					bag.Content = certsContent;
					safeBagSequence.Add (bag.ASN1);
				}
			}

			if (_safeBags.Count > 0) {
				ASN1 safeContents = new ASN1 (0x30);
				foreach (SafeBag sb in _safeBags) {
					if (sb.BagOID.Equals (keyBag) ||
					    sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
						safeContents.Add (sb.ASN1);
					}
				}
				if (safeContents.Count > 0) {
					ASN1 content = new ASN1 (0xA0);
					content.Add (new ASN1 (0x04, safeContents.GetBytes ()));
				
					PKCS7.ContentInfo keyBag = new PKCS7.ContentInfo (PKCS7.Oid.data);
					keyBag.Content = content;
					safeBagSequence.Add (keyBag.ASN1);
				}
			}


			ASN1 encapsulates = new ASN1 (0x04, safeBagSequence.GetBytes ());
			ASN1 ci = new ASN1 (0xA0);
			ci.Add (encapsulates);
			PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (PKCS7.Oid.data);
			authSafe.Content = ci;
			
			ASN1 macData = new ASN1 (0x30);
			if (_password != null) {
				// only for password based encryption
				byte[] salt = new byte [20];
				RNG.GetBytes (salt);
				byte[] macValue = MAC (_password, salt, _iterations, authSafe.Content [0].Value);
				ASN1 oidSeq = new ASN1 (0x30);
				oidSeq.Add (ASN1Convert.FromOid ("1.3.14.3.2.26"));	// SHA1
				oidSeq.Add (new ASN1 (0x05));

				ASN1 mac = new ASN1 (0x30);
				mac.Add (oidSeq);
				mac.Add (new ASN1 (0x04, macValue));

				macData.Add (mac);
				macData.Add (new ASN1 (0x04, salt));
				macData.Add (ASN1Convert.FromInt32 (_iterations));
			}
			
			ASN1 version = new ASN1 (0x02, new byte [1] { 0x03 });
			
			ASN1 pfx = new ASN1 (0x30);
			pfx.Add (version);
			pfx.Add (authSafe.ASN1);
			if (macData.Count > 0) {
				// only for password based encryption
				pfx.Add (macData);
			}

			return pfx.GetBytes ();
		}
		private void Decode (byte[] data)
		{
			ASN1 pfx = new ASN1 (data);
			if (pfx.Tag != 0x30)
				throw new ArgumentException ("invalid data");
			
			ASN1 version = pfx [0];
			if (version.Tag != 0x02)
				throw new ArgumentException ("invalid PFX version");
			//_version = version.Value [0];

			PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (pfx [1]);
			if (authSafe.ContentType != PKCS7.Oid.data)
				throw new ArgumentException ("invalid authenticated safe");

			// now that we know it's a PKCS#12 file, check the (optional) MAC
			// before decoding anything else in the file
			if (pfx.Count > 2) {
				ASN1 macData = pfx [2];
				if (macData.Tag != 0x30)
					throw new ArgumentException ("invalid MAC");
				
				ASN1 mac = macData [0];
				if (mac.Tag != 0x30)
					throw new ArgumentException ("invalid MAC");
				ASN1 macAlgorithm = mac [0];
				string macOid = ASN1Convert.ToOid (macAlgorithm [0]);
				if (macOid != "1.3.14.3.2.26")
					throw new ArgumentException ("unsupported HMAC");
				byte[] macValue = mac [1].Value;

				ASN1 macSalt = macData [1];
				if (macSalt.Tag != 0x04)
					throw new ArgumentException ("missing MAC salt");

				_iterations = 1; // default value
				if (macData.Count > 2) {
					ASN1 iters = macData [2];
					if (iters.Tag != 0x02)
						throw new ArgumentException ("invalid MAC iteration");
					_iterations = ASN1Convert.ToInt32 (iters);
				}

				byte[] authSafeData = authSafe.Content [0].Value;
				byte[] calculatedMac = MAC (_password, macSalt.Value, _iterations, authSafeData);
				if (!Compare (macValue, calculatedMac))
					throw new CryptographicException ("Invalid MAC - file may have been tampered!");
			}

			// we now returns to our original presentation - PFX
			ASN1 authenticatedSafe = new ASN1 (authSafe.Content [0].Value);
			for (int i=0; i < authenticatedSafe.Count; i++) {
				PKCS7.ContentInfo ci = new PKCS7.ContentInfo (authenticatedSafe [i]);
				switch (ci.ContentType) {
					case PKCS7.Oid.data:
						// unencrypted (by PKCS#12)
						ASN1 safeContents = new ASN1 (ci.Content [0].Value);
						for (int j=0; j < safeContents.Count; j++) {
							ASN1 safeBag = safeContents [j];
							ReadSafeBag (safeBag);
						}
						break;
					case PKCS7.Oid.encryptedData:
						// password encrypted
						PKCS7.EncryptedData ed = new PKCS7.EncryptedData (ci.Content [0]);
						ASN1 decrypted = new ASN1 (Decrypt (ed));
						for (int j=0; j < decrypted.Count; j++) {
							ASN1 safeBag = decrypted [j];
							ReadSafeBag (safeBag);
						}
						break;
					case PKCS7.Oid.envelopedData:
						// public key encrypted
						throw new NotImplementedException ("public key encrypted");
					default:
						throw new ArgumentException ("unknown authenticatedSafe");
				}
			}
		}
		private void ReadSafeBag (ASN1 safeBag) 
		{
			if (safeBag.Tag != 0x30)
				throw new ArgumentException ("invalid safeBag");

			ASN1 bagId = safeBag [0];
			if (bagId.Tag != 0x06)
				throw new ArgumentException ("invalid safeBag id");

			ASN1 bagValue = safeBag [1];
			string oid = ASN1Convert.ToOid (bagId);
			switch (oid) {
				case keyBag:
					// NEED UNIT TEST
					AddPrivateKey (new PKCS8.PrivateKeyInfo (bagValue.Value));
					break;
				case pkcs8ShroudedKeyBag:
					PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
					byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
					AddPrivateKey (new PKCS8.PrivateKeyInfo (decrypted));
					Array.Clear (decrypted, 0, decrypted.Length);
					break;
				case certBag:
					PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
					if (cert.ContentType != x509Certificate)
						throw new NotSupportedException ("unsupport certificate type");
					X509Certificate x509 = new X509Certificate (cert.Content [0].Value);
					_certs.Add (x509);
					break;
				case crlBag:
					// TODO
					break;
				case secretBag: 
					// TODO
					break;
				case safeContentsBag:
					// TODO - ? recurse ?
					break;
				default:
					throw new ArgumentException ("unknown safeBag oid");
			}

			if (safeBag.Count > 2) {
				ASN1 bagAttributes = safeBag [2];
				if (bagAttributes.Tag != 0x31)
					throw new ArgumentException ("invalid safeBag attributes id");

				for (int i = 0; i < bagAttributes.Count; i++) {
					ASN1 pkcs12Attribute = bagAttributes[i];
						
					if (pkcs12Attribute.Tag != 0x30)
						throw new ArgumentException ("invalid PKCS12 attributes id");

					ASN1 attrId = pkcs12Attribute [0];
					if (attrId.Tag != 0x06)
						throw new ArgumentException ("invalid attribute id");
						
					string attrOid = ASN1Convert.ToOid (attrId);

					ASN1 attrValues = pkcs12Attribute[1];
					for (int j = 0; j < attrValues.Count; j++) {
						ASN1 attrValue = attrValues[j];

						switch (attrOid) {
						case PKCS9.friendlyName:
							if (attrValue.Tag != 0x1e)
								throw new ArgumentException ("invalid attribute value id");
							break;
						case PKCS9.localKeyId:
							if (attrValue.Tag != 0x04)
								throw new ArgumentException ("invalid attribute value id");
							break;
						default:
							// Unknown OID -- don't check Tag
							break;
						}
					}
				}
			}

			_safeBags.Add (new SafeBag(oid, safeBag));
		}
		public IDictionary GetAttributes (X509Certificate cert)
		{
			IDictionary result = new Hashtable ();

			foreach (SafeBag sb in _safeBags) {
				if (sb.BagOID.Equals (certBag)) {
					ASN1 safeBag = sb.ASN1;
					ASN1 bagValue = safeBag [1];
					PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
					X509Certificate xc = new X509Certificate (crt.Content [0].Value);

					if (Compare (cert.RawData, xc.RawData)) {
						if (safeBag.Count == 3) {
							ASN1 bagAttributes = safeBag [2];

							for (int i = 0; i < bagAttributes.Count; i++) {
								ASN1 pkcs12Attribute = bagAttributes [i];
								ASN1 attrId = pkcs12Attribute [0];

								string aOid = ASN1Convert.ToOid (attrId);
								ArrayList aValues = new ArrayList ();

								ASN1 attrValues = pkcs12Attribute [1];
									
								for (int j = 0; j < attrValues.Count; j++) {
									ASN1 attrValue = attrValues [j];
									aValues.Add (attrValue.Value);
								}
								result.Add (aOid, aValues);
							}
						}
					}
				}
			}

			return result;
		}
		public bool Sign (string fileName) 
		{
			string hashAlgorithm = "MD5";
			byte[] file = null;

			using (FileStream fs = new FileStream (fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
				file = new byte [fs.Length];
				fs.Read (file, 0, file.Length);
				fs.Close ();
			}

			// MZ - DOS header
			if (BitConverterLE.ToUInt16 (file, 0) != 0x5A4D)
				return false;

			// find offset of PE header
			int peOffset = BitConverterLE.ToInt32 (file, 60);
			if (peOffset > file.Length)
				return false;

			// PE - NT header
			if (BitConverterLE.ToUInt16 (file, peOffset) != 0x4550)
				return false;

			// IMAGE_DIRECTORY_ENTRY_SECURITY
			int dirSecurityOffset = BitConverterLE.ToInt32 (file, peOffset + 152);
			int dirSecuritySize = BitConverterLE.ToInt32 (file, peOffset + 156);

			if (dirSecuritySize > 8) {
				entry = new byte [dirSecuritySize - 8];
				Buffer.BlockCopy (file, dirSecurityOffset + 8, entry, 0, entry.Length);
			}
			else
				entry = null;

			HashAlgorithm hash = HashAlgorithm.Create (hashAlgorithm);
			// 0 to 215 (216) then skip 4 (checksum)
			int pe = peOffset + 88;
			hash.TransformBlock (file, 0, pe, file, 0);
			pe += 4;
			// 220 to 279 (60) then skip 8 (IMAGE_DIRECTORY_ENTRY_SECURITY)
			hash.TransformBlock (file, pe, 60, file, pe);
			pe += 68;
			// 288 to end of file
			int n = file.Length - pe;
			// minus any authenticode signature (with 8 bytes header)
			if (dirSecurityOffset != 0)
				n -= (dirSecuritySize);
			hash.TransformFinalBlock (file, pe, n);

			//
			byte[] signature = Header (hash.Hash, hashAlgorithm);
			if (timestamp != null) {
				ASN1 tsreq = TimestampRequest (signature);
				WebClient wc = new WebClient ();
				wc.Headers.Add ("Content-Type", "application/octet-stream");
				wc.Headers.Add ("Accept", "application/octet-stream");
				byte[] tsdata = Encoding.ASCII.GetBytes (Convert.ToBase64String (tsreq.GetBytes ()));
				byte[] tsres = wc.UploadData (timestamp.ToString (), tsdata);
				ProcessTimestamp (tsres);
			}
			PKCS7.ContentInfo sign = new PKCS7.ContentInfo (signedData);
			sign.Content.Add (pkcs7.ASN1);
			authenticode = sign.ASN1;

			byte[] asn = authenticode.GetBytes ();
#if DEBUG
			using (FileStream fs = File.Open (fileName + ".sig", FileMode.Create, FileAccess.Write)) {
				fs.Write (asn, 0, asn.Length);
				fs.Close ();
			}
#endif
			// someday I may be sure enough to move this into DEBUG ;-)
			File.Copy (fileName, fileName + ".bak", true);

			using (FileStream fs = File.Open (fileName, FileMode.Create, FileAccess.Write)) {
				int filesize = (dirSecurityOffset == 0) ? file.Length : dirSecurityOffset;
				// IMAGE_DIRECTORY_ENTRY_SECURITY (offset, size)
				byte[] data = BitConverterLE.GetBytes (filesize);
				file [peOffset + 152] = data [0];
				file [peOffset + 153] = data [1];
				file [peOffset + 154] = data [2];
				file [peOffset + 155] = data [3];
				int size = asn.Length + 8;
				// must be a multiple of 8 bytes
				int addsize = (size % 8);
				if (addsize > 0)
					addsize = 8 - addsize;
				size += addsize;
				data = BitConverterLE.GetBytes (size);		// header
				file [peOffset + 156] = data [0];
				file [peOffset + 157] = data [1];
				file [peOffset + 158] = data [2];
				file [peOffset + 159] = data [3];
				fs.Write (file, 0, filesize);
				fs.Write (data, 0, data.Length);		// length (again)
				data = BitConverterLE.GetBytes (0x00020200);	// magic
				fs.Write (data, 0, data.Length);
				fs.Write (asn, 0, asn.Length);
				// fill up
				byte[] fillup = new byte [addsize];
				fs.Write (fillup, 0, fillup.Length);
				fs.Close ();
			}
			return true;
		}
		public void RemoveCertificate (X509Certificate cert, IDictionary attrs)
		{
			int certIndex = -1;

			for (int i = 0; certIndex == -1 && i < _safeBags.Count; i++) {
				SafeBag sb = (SafeBag)_safeBags [i];

				if (sb.BagOID.Equals (certBag)) {
					ASN1 safeBag = sb.ASN1;
					ASN1 bagValue = safeBag [1];
					PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
					X509Certificate c = new X509Certificate (crt.Content [0].Value);
					if (Compare (cert.RawData, c.RawData)) {
						if (attrs != null) {
							if (safeBag.Count == 3) {
								ASN1 bagAttributes = safeBag [2];
								int bagAttributesFound = 0;
								for (int j = 0; j < bagAttributes.Count; j++) {
									ASN1 pkcs12Attribute = bagAttributes [j];
									ASN1 attrId = pkcs12Attribute [0];
									string ao = ASN1Convert.ToOid (attrId);
									ArrayList dattrValues = (ArrayList)attrs [ao];

									if (dattrValues != null) {
										ASN1 attrValues = pkcs12Attribute [1];

										if (dattrValues.Count == attrValues.Count) {
											int attrValuesFound = 0;
											for (int k = 0; k < attrValues.Count; k++) {
												ASN1 attrValue = attrValues [k];
												byte[] value = (byte[])dattrValues [k];
									
												if (Compare (value, attrValue.Value)) {
													attrValuesFound += 1;
												}
											}
											if (attrValuesFound == attrValues.Count) {
												bagAttributesFound += 1;
											}
										}
									}
								}
								if (bagAttributesFound == bagAttributes.Count) {
									certIndex = i;
								}
							}
						} else {
							certIndex = i;
						}
					}
				}
			}

			if (certIndex != -1) {
				_safeBags.RemoveAt (certIndex);
				_certsChanged = true;
			}
		}
		public void AddCertificate (X509Certificate cert, IDictionary attributes)
		{
			bool found = false;

			for (int i = 0; !found && i < _safeBags.Count; i++) {
				SafeBag sb = (SafeBag)_safeBags [i];

				if (sb.BagOID.Equals (certBag)) {
					ASN1 safeBag = sb.ASN1;
					ASN1 bagValue = safeBag [1];
					PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
					X509Certificate c = new X509Certificate (crt.Content [0].Value);
					if (Compare (cert.RawData, c.RawData)) {
						found = true;
					}
				}
			}

			if (!found) {
				_safeBags.Add (new SafeBag (certBag, CertificateSafeBag (cert, attributes)));
				_certsChanged = true;
			}
		}
		public SoftwarePublisherCertificate (byte[] data) : this ()
		{
			if (data == null)
				throw new ArgumentNullException ("data");

			PKCS7.ContentInfo ci = new PKCS7.ContentInfo (data);
			if (ci.ContentType != PKCS7.Oid.signedData) {
				throw new ArgumentException (
					Locale.GetText ("Unsupported ContentType"));
			}
			pkcs7 = new PKCS7.SignedData (ci.Content);
		}
		// in case we just want to timestamp the file
		public bool Timestamp (string fileName) 
		{
			try {
				AuthenticodeDeformatter def = new AuthenticodeDeformatter (fileName);
				byte[] signature = def.Signature;
				if (signature != null) {
					Open (fileName);
					PKCS7.ContentInfo ci = new PKCS7.ContentInfo (signature);
					pkcs7 = new PKCS7.SignedData (ci.Content);

					byte[] response = Timestamp (pkcs7.SignerInfo.Signature);
					ASN1 ts = new ASN1 (Convert.FromBase64String (Encoding.ASCII.GetString (response)));
					// insert new certificates and countersignature into the original signature
					ASN1 asn = new ASN1 (signature);
					ASN1 content = asn.Element (1, 0xA0);
					if (content == null)
						return false;

					ASN1 signedData = content.Element (0, 0x30);
					if (signedData == null)
						return false;

					// add the supplied certificates inside our signature
					ASN1 certificates = signedData.Element (3, 0xA0);
					if (certificates == null) {
						certificates = new ASN1 (0xA0);
						signedData.Add (certificates);
					}
					for (int i = 0; i < ts[1][0][3].Count; i++) {
						certificates.Add (ts[1][0][3][i]);
					}

					// add an unauthentified attribute to our signature
					ASN1 signerInfoSet = signedData[signedData.Count - 1];
					ASN1 signerInfo = signerInfoSet[0];
					ASN1 unauthenticated = signerInfo[signerInfo.Count - 1];
					if (unauthenticated.Tag != 0xA1) {
						unauthenticated = new ASN1 (0xA1);
						signerInfo.Add (unauthenticated);
					}
					unauthenticated.Add (Attribute (countersignature, ts[1][0][4][0]));

					return Save (fileName, asn.GetBytes ());
				}
			}
			catch (Exception e) {
				Console.WriteLine (e);
			}
			return false;
		}
		public void Decode (byte[] encodedMessage) 
		{
			PKCS7.ContentInfo ci = new PKCS7.ContentInfo (encodedMessage);
			if (ci.ContentType != PKCS7.Oid.signedData) 
				throw new Exception ("");

			PKCS7.SignedData sd = new PKCS7.SignedData (ci.Content);
			SubjectIdentifierType type = SubjectIdentifierType.Unknown;
			object o = null;

			X509Certificate2 x509 = null;
			if (sd.SignerInfo.Certificate != null) {
				x509 = new X509Certificate2 (sd.SignerInfo.Certificate.RawData);
			}
			else if ((sd.SignerInfo.IssuerName != null) && (sd.SignerInfo.SerialNumber != null)) {
				byte[] serial = sd.SignerInfo.SerialNumber;
				Array.Reverse (serial); // ???
				type = SubjectIdentifierType.IssuerAndSerialNumber;
				X509IssuerSerial xis = new X509IssuerSerial ();
				xis.IssuerName = sd.SignerInfo.IssuerName;
				xis.SerialNumber = ToString (serial, true);
				o = xis;
				// TODO: move to a FindCertificate (issuer, serial, collection)
				foreach (Mono.Security.X509.X509Certificate x in sd.Certificates) {
					if (x.IssuerName == sd.SignerInfo.IssuerName) {
						if (ToString (x.SerialNumber, true) == xis.SerialNumber) {
							x509 = new X509Certificate2 (x.RawData);
							break;
						}
					}
				}
			}
			else if (sd.SignerInfo.SubjectKeyIdentifier != null) {
				string ski = ToString (sd.SignerInfo.SubjectKeyIdentifier, false);
				type = SubjectIdentifierType.SubjectKeyIdentifier;
				o = (object) ski;
				// TODO: move to a FindCertificate (ski, collection)
				foreach (Mono.Security.X509.X509Certificate x in sd.Certificates) {
					if (ToString (GetKeyIdentifier (x), false) == ski) {
						x509 = new X509Certificate2 (x.RawData);
						break;
					}
				}
			}

			SignerInfo si = new SignerInfo (sd.SignerInfo.HashName, x509, type, o, sd.SignerInfo.Version);
			// si.AuthenticatedAttributes
			// si.UnauthenticatedAttributes
			_info.Add (si);

			ASN1 content = sd.ContentInfo.Content;
			Oid oid = new Oid (sd.ContentInfo.ContentType);
			_content = new ContentInfo (oid, content[0].Value);

			foreach (Mono.Security.X509.X509Certificate x in sd.Certificates) {
				_certs.Add (new X509Certificate2 (x.RawData));
			}

			_version = sd.Version;
		}