2005-04-26 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / Mono.Security / Mono.Security.X509 / PKCS12.cs
index 7e4d36e01883ee487757f3ca50ead8314a1ac66b..0df82b90c314fd6cb2b52bd9b18437f99fed17f7 100755 (executable)
@@ -2,14 +2,36 @@
 // PKCS12.cs: PKCS 12 - Personal Information Exchange Syntax
 //
 // Author:
-//     Sebastien Pouliot (spouliot@motus.com)
+//     Sebastien Pouliot <sebastien@ximian.com>
 //
 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
+// (C) 2004 Novell (http://www.novell.com)
 //
 // Key derivation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
 // See bouncycastle.txt for license.
 //
 
+//
+// 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.IO;
@@ -21,7 +43,12 @@ using Mono.Security.Cryptography;
 
 namespace Mono.Security.X509 {
 
-       public class PKCS5 {
+#if INSIDE_CORLIB
+       internal
+#else
+       public 
+#endif
+       class PKCS5 {
 
                public const string pbeWithMD2AndDESCBC = "1.2.840.113549.1.5.1";
                public const string pbeWithMD5AndDESCBC = "1.2.840.113549.1.5.3";
@@ -33,7 +60,45 @@ namespace Mono.Security.X509 {
                public PKCS5 () {}
        }
 
-       public class PKCS12 {
+#if INSIDE_CORLIB
+       internal
+#else
+       public 
+#endif
+       class PKCS9 {
+
+               public const string friendlyName = "1.2.840.113549.1.9.20";
+               public const string localKeyId = "1.2.840.113549.1.9.21";
+
+               public PKCS9 () {}
+       }
+
+
+       internal class SafeBag {
+               private string _bagOID;
+               private ASN1 _asn1;
+
+               public SafeBag(string bagOID, ASN1 asn1) {
+                       _bagOID = bagOID;
+                       _asn1 = asn1;
+               }
+
+               public string BagOID {
+                       get { return _bagOID; }
+               }
+
+               public ASN1 ASN1 {
+                       get { return _asn1; }
+               }
+       }
+
+
+#if INSIDE_CORLIB
+       internal
+#else
+       public 
+#endif
+       class PKCS12 : ICloneable {
 
                public const string pbeWithSHAAnd128BitRC4 = "1.2.840.113549.1.12.1.1";
                public const string pbeWithSHAAnd40BitRC4 = "1.2.840.113549.1.12.1.2";
@@ -202,20 +267,36 @@ namespace Mono.Security.X509 {
                        }
                }
 
-               private int _version;
+               static private int recommendedIterationCount = 2000;
+
+               //private int _version;
                private byte[] _password;
                private ArrayList _keyBags;
                private X509CertificateCollection _certs;
+               private bool _keyBagsChanged;
+               private bool _certsChanged;
+               private int _iterations;
+               private ArrayList _safeBags;
+               private RandomNumberGenerator _rng;
 
                // constructors
 
                public PKCS12 () 
                {
+                       _iterations = recommendedIterationCount;
                        _keyBags = new ArrayList ();
                        _certs = new X509CertificateCollection ();
+                       _keyBagsChanged = false;
+                       _certsChanged = false;
+                       _safeBags = new ArrayList ();
                }
 
-               public PKCS12 (byte[] data) : this (data, null) {}
+               public PKCS12 (byte[] data)
+                       : this ()
+               {
+                       Password = null;
+                       Decode (data);
+               }
 
                /*
                 * PFX ::= SEQUENCE {
@@ -240,10 +321,22 @@ namespace Mono.Security.X509 {
                 *      bagAttributes SET OF PKCS12Attribute OPTIONAL
                 * }
                 */
-               public PKCS12 (byte[] data, string password) : this ()
+               public PKCS12 (byte[] data, string password)
+                       : this ()
                {
                        Password = password;
-
+                       Decode (data);
+               }
+#if NET_2_0
+               public PKCS12 (byte[] data, byte[] password)
+                       : this ()
+               {
+                       _password = password;
+                       Decode (data);
+               }
+#endif
+               private void Decode (byte[] data)
+               {
                        ASN1 pfx = new ASN1 (data);
                        if (pfx.Tag != 0x30)
                                throw new ArgumentException ("invalid data");
@@ -251,10 +344,10 @@ namespace Mono.Security.X509 {
                        ASN1 version = pfx [0];
                        if (version.Tag != 0x02)
                                throw new ArgumentException ("invalid PFX version");
-                       _version = version.Value [0];
+                       //_version = version.Value [0];
 
                        PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (pfx [1]);
-                       if (authSafe.ContentType != PKCS7.data)
+                       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
@@ -268,7 +361,7 @@ namespace Mono.Security.X509 {
                                if (mac.Tag != 0x30)
                                        throw new ArgumentException ("invalid MAC");
                                ASN1 macAlgorithm = mac [0];
-                               string macOid = ASN1Convert.ToOID (macAlgorithm [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;
@@ -277,24 +370,16 @@ namespace Mono.Security.X509 {
                                if (macSalt.Tag != 0x04)
                                        throw new ArgumentException ("missing MAC salt");
 
-                               int iterations = 1; // default value
+                               _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);
+                                       _iterations = ASN1Convert.ToInt32 (iters);
                                }
 
-                               PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
-                               pd.HashName = "SHA1";
-                               pd.Password = _password;
-                               pd.Salt = macSalt.Value;
-                               pd.IterationCount = iterations;
-
-                               HMACSHA1 hmac = (HMACSHA1) HMACSHA1.Create ();
-                               hmac.Key = pd.DeriveMAC (20);
                                byte[] authSafeData = authSafe.Content [0].Value;
-                               byte[] calculatedMac = hmac.ComputeHash (authSafeData, 0, authSafeData.Length);
+                               byte[] calculatedMac = MAC (_password, macSalt.Value, _iterations, authSafeData);
                                if (!Compare (macValue, calculatedMac))
                                        throw new CryptographicException ("Invalid MAC - file may have been tampered!");
                        }
@@ -304,7 +389,7 @@ namespace Mono.Security.X509 {
                        for (int i=0; i < authenticatedSafe.Count; i++) {
                                PKCS7.ContentInfo ci = new PKCS7.ContentInfo (authenticatedSafe [i]);
                                switch (ci.ContentType) {
-                                       case PKCS7.data:
+                                       case PKCS7.Oid.data:
                                                // unencrypted (by PKCS#12)
                                                ASN1 safeContents = new ASN1 (ci.Content [0].Value);
                                                for (int j=0; j < safeContents.Count; j++) {
@@ -312,7 +397,7 @@ namespace Mono.Security.X509 {
                                                        ReadSafeBag (safeBag);
                                                }
                                                break;
-                                       case PKCS7.encryptedData:
+                                       case PKCS7.Oid.encryptedData:
                                                // password encrypted
                                                PKCS7.EncryptedData ed = new PKCS7.EncryptedData (ci.Content [0]);
                                                ASN1 decrypted = new ASN1 (Decrypt (ed));
@@ -321,7 +406,7 @@ namespace Mono.Security.X509 {
                                                        ReadSafeBag (safeBag);
                                                }
                                                break;
-                                       case PKCS7.envelopedData:
+                                       case PKCS7.Oid.envelopedData:
                                                // public key encrypted
                                                throw new NotImplementedException ("public key encrypted");
                                        default:
@@ -335,6 +420,7 @@ namespace Mono.Security.X509 {
                        if (_password != null) {
                                Array.Clear (_password, 0, _password.Length);
                        }
+                       _password = null;
                }
 
                // properties
@@ -352,12 +438,86 @@ namespace Mono.Security.X509 {
                        }
                }
 
+               public int IterationCount {
+                       get { return _iterations; }
+                       set { _iterations = value; }
+               }
+
                public ArrayList Keys {
-                       get { return _keyBags; }
+                       get {
+                               if (_keyBagsChanged) {
+                                       _keyBags.Clear ();
+                                       foreach (SafeBag sb in _safeBags) {
+                                               if (sb.BagOID.Equals (keyBag)) {
+                                                       ASN1 safeBag = sb.ASN1;
+                                                       ASN1 bagValue = safeBag [1];
+                                                       PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
+                                                       byte[] privateKey = pki.PrivateKey;
+                                                       switch (privateKey [0]) {
+                                                       case 0x02:
+                                                               DSAParameters p = new DSAParameters (); // FIXME
+                                                               _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
+                                                               break;
+                                                       case 0x30:
+                                                               _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
+                                                               break;
+                                                       default:
+                                                               break;
+                                                       }
+                                                       Array.Clear (privateKey, 0, privateKey.Length);
+
+                                               } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
+                                                       ASN1 safeBag = sb.ASN1;
+                                                       ASN1 bagValue = safeBag [1];
+                                                       PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
+                                                       byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
+                                                       PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
+                                                       byte[] privateKey = pki.PrivateKey;
+                                                       switch (privateKey [0]) {
+                                                       case 0x02:
+                                                               DSAParameters p = new DSAParameters (); // FIXME
+                                                               _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
+                                                               break;
+                                                       case 0x30:
+                                                               _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
+                                                               break;
+                                                       default:
+                                                               break;
+                                                       }
+                                                       Array.Clear (privateKey, 0, privateKey.Length);
+                                                       Array.Clear (decrypted, 0, decrypted.Length);
+                                               }
+                                       }
+                                       _keyBagsChanged = false;
+                               }
+                               return ArrayList.ReadOnly(_keyBags);
+                       }
                }
 
                public X509CertificateCollection Certificates {
-                       get { return _certs; }
+                       get {
+                               if (_certsChanged) {
+                                       _certs.Clear ();
+                                       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);
+                                                       _certs.Add (new X509Certificate (cert.Content [0].Value));
+                                               }
+                                       }
+                                       _certsChanged = false;
+                               }
+                               return _certs;
+                       }
+               }
+
+               internal RandomNumberGenerator RNG {
+                       get {
+                               if (_rng == null)
+                                       _rng = RandomNumberGenerator.Create ();
+                               return _rng;
+                       }
                }
 
                // private methods
@@ -375,7 +535,7 @@ namespace Mono.Security.X509 {
                        return compare;
                }
 
-               public byte[] Decrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] encryptedData) 
+               private SymmetricAlgorithm GetSymmetricAlgorithm (string algorithmOid, byte[] salt, int iterationCount)
                {
                        string algorithm = null;
                        int keyLength = 8;      // 64 bits (default)
@@ -463,8 +623,23 @@ namespace Mono.Security.X509 {
                                sa.IV = pd.DeriveIV (ivLength);
                                sa.Mode = CipherMode.CBC;
                        }
-                       ICryptoTransform ct = sa.CreateDecryptor ();
-                       return ct.TransformFinalBlock (encryptedData, 0, encryptedData.Length);
+                       return sa;
+               }
+
+               public byte[] Decrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] encryptedData) 
+               {
+                       SymmetricAlgorithm sa = null;
+                       byte[] result = null;
+                       try {
+                               sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount);
+                               ICryptoTransform ct = sa.CreateDecryptor ();
+                               result = ct.TransformFinalBlock (encryptedData, 0, encryptedData.Length);
+                       }
+                       finally {
+                               if (sa != null)
+                                       sa.Clear ();
+                       }
+                       return result;
                }
 
                public byte[] Decrypt (PKCS7.EncryptedData ed)
@@ -475,6 +650,16 @@ namespace Mono.Security.X509 {
                                ed.EncryptedContent);
                }
 
+               public byte[] Encrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] data) 
+               {
+                       byte[] result = null;
+                       using (SymmetricAlgorithm sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount)) {
+                               ICryptoTransform ct = sa.CreateEncryptor ();
+                               result = ct.TransformFinalBlock (data, 0, data.Length);
+                       }
+                       return result;
+               }
+
                private void AddPrivateKey (PKCS8.PrivateKeyInfo pki) 
                {
                        byte[] privateKey = pki.PrivateKey;
@@ -503,7 +688,7 @@ namespace Mono.Security.X509 {
                                throw new ArgumentException ("invalid safeBag id");
 
                        ASN1 bagValue = safeBag [1];
-                       string oid = ASN1Convert.ToOID (bagId);
+                       string oid = ASN1Convert.ToOid (bagId);
                        switch (oid) {
                                case keyBag:
                                        // NEED UNIT TEST
@@ -534,11 +719,272 @@ namespace Mono.Security.X509 {
                                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));
                }
 
-               static private int recommendedIterationCount = 2000;
+               private ASN1 Pkcs8ShroudedKeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) 
+               {
+                       PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
+                       if (aa is RSA) {
+                               pki.Algorithm = "1.2.840.113549.1.1.1";
+                               pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
+                       }
+                       else if (aa is DSA) {
+                               pki.Algorithm = null;
+                               pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
+                       }
+                       else
+                               throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
+
+                       PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo ();
+                       epki.Algorithm = pbeWithSHAAnd3KeyTripleDESCBC;
+                       epki.IterationCount = _iterations;
+                       epki.EncryptedData = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, epki.Salt, _iterations, pki.GetBytes ());
+
+                       ASN1 safeBag = new ASN1 (0x30);
+                       safeBag.Add (ASN1Convert.FromOid (pkcs8ShroudedKeyBag));
+                       ASN1 bagValue = new ASN1 (0xA0);
+                       bagValue.Add (new ASN1 (epki.GetBytes ()));
+                       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;
+               }
+
+               private ASN1 KeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) 
+               {
+                       PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
+                       if (aa is RSA) {
+                               pki.Algorithm = "1.2.840.113549.1.1.1";
+                               pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
+                       }
+                       else if (aa is DSA) {
+                               pki.Algorithm = null;
+                               pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
+                       }
+                       else
+                               throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
+
+                       ASN1 safeBag = new ASN1 (0x30);
+                       safeBag.Add (ASN1Convert.FromOid (keyBag));
+                       ASN1 bagValue = new ASN1 (0xA0);
+                       bagValue.Add (new ASN1 (pki.GetBytes ()));
+                       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;
+               }
+
+               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;
+               }
+
+               private byte[] MAC (byte[] password, byte[] salt, int iterations, byte[] data) 
+               {
+                       PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
+                       pd.HashName = "SHA1";
+                       pd.Password = password;
+                       pd.Salt = salt;
+                       pd.IterationCount = iterations;
+
+                       HMACSHA1 hmac = (HMACSHA1) HMACSHA1.Create ();
+                       hmac.Key = pd.DeriveMAC (20);
+                       return hmac.ComputeHash (data, 0, data.Length);
+               }
+
+                /*
                 * SafeContents ::= SEQUENCE OF SafeBag
                 * 
                 * SafeBag ::= SEQUENCE {
@@ -549,25 +995,143 @@ namespace Mono.Security.X509 {
                 */
                public byte[] GetBytes () 
                {
-                       PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (PKCS7.data);
-                       
                        // 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 ();
 
-                       byte[] salt = new byte [20];
-                       RandomNumberGenerator rng = RandomNumberGenerator.Create ();
-                       rng.GetBytes (salt);
+                       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);
-                       byte[] macValue = null;
-                       if (macValue != null) {
+                       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 (ASN1Convert.FromOID ("1.3.14.3.2.26"));        // SHA1
+                               mac.Add (oidSeq);
                                mac.Add (new ASN1 (0x04, macValue));
 
                                macData.Add (mac);
                                macData.Add (new ASN1 (0x04, salt));
-                               macData.Add (ASN1Convert.FromInt32 (recommendedIterationCount));
+                               macData.Add (ASN1Convert.FromInt32 (_iterations));
                        }
                        
                        ASN1 version = new ASN1 (0x02, new byte [1] { 0x03 });
@@ -575,7 +1139,7 @@ namespace Mono.Security.X509 {
                        ASN1 pfx = new ASN1 (0x30);
                        pfx.Add (version);
                        pfx.Add (authSafe.ASN1);
-                       if (macValue != null) {
+                       if (macData.Count > 0) {
                                // only for password based encryption
                                pfx.Add (macData);
                        }
@@ -583,6 +1147,567 @@ namespace Mono.Security.X509 {
                        return pfx.GetBytes ();
                }
 
+               public void AddCertificate (X509Certificate cert)
+               {
+                       AddCertificate (cert, null);
+               }
+
+               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 void RemoveCertificate (X509Certificate cert)
+               {
+                       RemoveCertificate (cert, null);
+               }
+
+               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;
+                       }
+               }
+
+               private bool CompareAsymmetricAlgorithm (AsymmetricAlgorithm a1, AsymmetricAlgorithm a2)
+               {
+                       bool result = a1.KeyExchangeAlgorithm.Equals (a2.KeyExchangeAlgorithm);
+                       result = result && a1.KeySize == a2.KeySize;
+
+                       KeySizes[] keysizes = a2.LegalKeySizes;
+                       if (a1.LegalKeySizes.Length != keysizes.Length)
+                               return false;
+
+                       for (int i = 0; i < a1.LegalKeySizes.Length; i++) {
+                               result = result && CompareKeySizes (a1.LegalKeySizes [i], keysizes [i]);
+                       }
+
+                       result = result && a1.SignatureAlgorithm == a2.SignatureAlgorithm;
+                       
+                       return result;
+               }
+
+               private bool CompareKeySizes (KeySizes k1, KeySizes k2)
+               { 
+                       bool result = k1.MaxSize == k2.MaxSize;
+                       result = result && k1.MinSize == k2.MinSize;
+                       result = result && k1.SkipSize == k2.SkipSize;
+                       return result;
+               }
+
+               public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
+               {
+                       AddPkcs8ShroudedKeyBag (aa, null);
+               }
+
+               public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
+               {
+                       bool found = false;
+
+                       for (int i = 0; !found && i < _safeBags.Count; i++) {
+                               SafeBag sb = (SafeBag)_safeBags [i];
+
+                               if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
+                                       ASN1 bagValue = sb.ASN1 [1];
+                                       PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
+                                       byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
+                                       PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
+                                       byte[] privateKey = pki.PrivateKey;
+
+                                       AsymmetricAlgorithm saa = null;
+                                       switch (privateKey [0]) {
+                                       case 0x02:
+                                               DSAParameters p = new DSAParameters (); // FIXME
+                                               saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
+                                               break;
+                                       case 0x30:
+                                               saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
+                                               break;
+                                       default:
+                                               Array.Clear (decrypted, 0, decrypted.Length);
+                                               Array.Clear (privateKey, 0, privateKey.Length);
+                                               throw new CryptographicException ("Unknown private key format");
+                                       }
+
+                                       Array.Clear (decrypted, 0, decrypted.Length);
+                                       Array.Clear (privateKey, 0, privateKey.Length);
+
+                                       if (CompareAsymmetricAlgorithm (aa , saa)) {
+                                               found = true;
+                                       }
+                               }
+                       }
+
+                       if (!found) {
+                               _safeBags.Add (new SafeBag (pkcs8ShroudedKeyBag, Pkcs8ShroudedKeyBagSafeBag (aa, attributes)));
+                               _keyBagsChanged = true;
+                       }
+               }
+
+               public void RemovePkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
+               {
+                       int aaIndex = -1;
+
+                       for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
+                               SafeBag sb = (SafeBag)_safeBags [i];
+
+                               if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
+                                       ASN1 bagValue = sb.ASN1 [1];
+                                       PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
+                                       byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
+                                       PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
+                                       byte[] privateKey = pki.PrivateKey;
+
+                                       AsymmetricAlgorithm saa = null;
+                                       switch (privateKey [0]) {
+                                       case 0x02:
+                                               DSAParameters p = new DSAParameters (); // FIXME
+                                               saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
+                                               break;
+                                       case 0x30:
+                                               saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
+                                               break;
+                                       default:
+                                               Array.Clear (decrypted, 0, decrypted.Length);
+                                               Array.Clear (privateKey, 0, privateKey.Length);
+                                               throw new CryptographicException ("Unknown private key format");
+                                       }
+
+                                       Array.Clear (decrypted, 0, decrypted.Length);
+                                       Array.Clear (privateKey, 0, privateKey.Length);
+
+                                       if (CompareAsymmetricAlgorithm (aa, saa)) {
+                                               aaIndex = i;
+                                       }
+                               }
+                       }
+
+                       if (aaIndex != -1) {
+                               _safeBags.RemoveAt (aaIndex);
+                               _keyBagsChanged = true;
+                       }
+               }
+
+               public void AddKeyBag (AsymmetricAlgorithm aa)
+               {
+                       AddKeyBag (aa, null);
+               }
+
+               public void AddKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
+               {
+                       bool found = false;
+
+                       for (int i = 0; !found && i < _safeBags.Count; i++) {
+                               SafeBag sb = (SafeBag)_safeBags [i];
+
+                               if (sb.BagOID.Equals (keyBag)) {
+                                       ASN1 bagValue = sb.ASN1 [1];
+                                       PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
+                                       byte[] privateKey = pki.PrivateKey;
+
+                                       AsymmetricAlgorithm saa = null;
+                                       switch (privateKey [0]) {
+                                       case 0x02:
+                                               DSAParameters p = new DSAParameters (); // FIXME
+                                               saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
+                                               break;
+                                       case 0x30:
+                                               saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
+                                               break;
+                                       default:
+                                               Array.Clear (privateKey, 0, privateKey.Length);
+                                               throw new CryptographicException ("Unknown private key format");
+                                       }
+
+                                       Array.Clear (privateKey, 0, privateKey.Length);
+
+                                       if (CompareAsymmetricAlgorithm (aa, saa)) {
+                                               found = true;
+                                       }
+                               }
+                       }
+
+                       if (!found) {
+                               _safeBags.Add (new SafeBag (keyBag, KeyBagSafeBag (aa, attributes)));
+                               _keyBagsChanged = true;
+                       }
+               }
+
+               public void RemoveKeyBag (AsymmetricAlgorithm aa)
+               {
+                       int aaIndex = -1;
+
+                       for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
+                               SafeBag sb = (SafeBag)_safeBags [i];
+
+                               if (sb.BagOID.Equals (keyBag)) {
+                                       ASN1 bagValue = sb.ASN1 [1];
+                                       PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
+                                       byte[] privateKey = pki.PrivateKey;
+
+                                       AsymmetricAlgorithm saa = null;
+                                       switch (privateKey [0]) {
+                                       case 0x02:
+                                               DSAParameters p = new DSAParameters (); // FIXME
+                                               saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
+                                               break;
+                                       case 0x30:
+                                               saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
+                                               break;
+                                       default:
+                                               Array.Clear (privateKey, 0, privateKey.Length);
+                                               throw new CryptographicException ("Unknown private key format");
+                                       }
+
+                                       Array.Clear (privateKey, 0, privateKey.Length);
+
+                                       if (CompareAsymmetricAlgorithm (aa, saa)) {
+                                               aaIndex = i;
+                                       }
+                               }
+                       }
+
+                       if (aaIndex != -1) {
+                               _safeBags.RemoveAt (aaIndex);
+                               _keyBagsChanged = true;
+                       }
+               }
+
+
+               public AsymmetricAlgorithm GetAsymmetricAlgorithm (IDictionary attrs)
+               {
+                       foreach (SafeBag sb in _safeBags) {
+                               if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
+                                       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];
+                                                       AsymmetricAlgorithm aa = null;
+                                                       if (sb.BagOID.Equals (keyBag)) {
+                                                               PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
+                                                               byte[] privateKey = pki.PrivateKey;
+                                                               switch (privateKey [0]) {
+                                                               case 0x02:
+                                                                       DSAParameters p = new DSAParameters (); // FIXME
+                                                                       aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
+                                                                       break;
+                                                               case 0x30:
+                                                                       aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
+                                                                       break;
+                                                               default:
+                                                                       break;
+                                                               }
+                                                               Array.Clear (privateKey, 0, privateKey.Length);
+                                                       } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
+                                                               PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
+                                                               byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
+                                                               PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
+                                                               byte[] privateKey = pki.PrivateKey;
+                                                               switch (privateKey [0]) {
+                                                               case 0x02:
+                                                                       DSAParameters p = new DSAParameters (); // FIXME
+                                                                       aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
+                                                                       break;
+                                                               case 0x30:
+                                                                       aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
+                                                                       break;
+                                                               default:
+                                                                       break;
+                                                               }
+                                                               Array.Clear (privateKey, 0, privateKey.Length);
+                                                               Array.Clear (decrypted, 0, decrypted.Length);
+                                                       }
+                                                       return aa;
+                                               }
+                                       }
+                               }
+                       }
+
+                       return null;
+               }
+
+               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;
+               }
+
+               public IDictionary GetAttributes (AsymmetricAlgorithm aa)
+               {
+                       IDictionary result = new Hashtable ();
+
+                       foreach (SafeBag sb in _safeBags) {
+                               if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
+                                       ASN1 safeBag = sb.ASN1;
+
+                                       ASN1 bagValue = safeBag [1];
+                                       AsymmetricAlgorithm saa = null;
+                                       if (sb.BagOID.Equals (keyBag)) {
+                                               PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
+                                               byte[] privateKey = pki.PrivateKey;
+                                               switch (privateKey [0]) {
+                                               case 0x02:
+                                                       DSAParameters p = new DSAParameters (); // FIXME
+                                                       saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
+                                                       break;
+                                               case 0x30:
+                                                       saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
+                                                       break;
+                                               default:
+                                                       break;
+                                               }
+                                               Array.Clear (privateKey, 0, privateKey.Length);
+                                       } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
+                                               PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
+                                               byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
+                                               PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
+                                               byte[] privateKey = pki.PrivateKey;
+                                               switch (privateKey [0]) {
+                                               case 0x02:
+                                                       DSAParameters p = new DSAParameters (); // FIXME
+                                                       saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
+                                                       break;
+                                               case 0x30:
+                                                       saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
+                                                       break;
+                                               default:
+                                                       break;
+                                               }
+                                               Array.Clear (privateKey, 0, privateKey.Length);
+                                               Array.Clear (decrypted, 0, decrypted.Length);
+                                       }
+
+                                       if (saa != null && CompareAsymmetricAlgorithm (saa, aa)) {
+                                               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 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 void SaveToFile (string filename)
+               {
+                       if (filename == null)
+                               throw new ArgumentNullException ("filename");
+
+                       using (FileStream fs = File.OpenWrite (filename)) {
+                               byte[] data = GetBytes ();
+                               fs.Write (data, 0, data.Length);
+                               fs.Flush ();
+                               fs.Close ();
+                       }
+               }
+
+               public object Clone ()
+               {
+                       PKCS12 clone = null;
+                       if (_password != null) {
+                               clone = new PKCS12 (GetBytes (), Encoding.BigEndianUnicode.GetString (_password));
+                       } else {
+                               clone = new PKCS12 (GetBytes ());
+                       }
+                       clone.IterationCount = this.IterationCount;
+
+                       return clone;
+               }
+
                // static methods
 
                static private byte[] LoadFile (string filename) 
@@ -608,8 +1733,6 @@ namespace Mono.Security.X509 {
                {
                        if (filename == null)
                                throw new ArgumentNullException ("filename");
-                       if (password == null)
-                               throw new ArgumentNullException ("password");
 
                        return new PKCS12 (LoadFile (filename), password);
                }