Add licensing info
[mono.git] / mcs / class / Mono.Security / Mono.Security.X509 / PKCS12.cs
1 //
2 // PKCS12.cs: PKCS 12 - Personal Information Exchange Syntax
3 //
4 // Author:
5 //      Sebastien Pouliot <sebastien@ximian.com>
6 //
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // (C) 2004 Novell (http://www.novell.com)
9 //
10 // Key derivation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
11 // See bouncycastle.txt for license.
12 //
13
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System;
36 using System.Collections;
37 using System.IO;
38 using System.Security.Cryptography;
39 using System.Text;
40
41 using Mono.Security;
42 using Mono.Security.Cryptography;
43
44 namespace Mono.Security.X509 {
45
46 #if INSIDE_CORLIB
47         internal
48 #else
49         public 
50 #endif
51         class PKCS5 {
52
53                 public const string pbeWithMD2AndDESCBC = "1.2.840.113549.1.5.1";
54                 public const string pbeWithMD5AndDESCBC = "1.2.840.113549.1.5.3";
55                 public const string pbeWithMD2AndRC2CBC = "1.2.840.113549.1.5.4";
56                 public const string pbeWithMD5AndRC2CBC = "1.2.840.113549.1.5.6";
57                 public const string pbeWithSHA1AndDESCBC = "1.2.840.113549.1.5.10";
58                 public const string pbeWithSHA1AndRC2CBC = "1.2.840.113549.1.5.11";
59
60                 public PKCS5 () {}
61         }
62
63 #if INSIDE_CORLIB
64         internal
65 #else
66         public 
67 #endif
68         class PKCS9 {
69
70                 public const string friendlyName = "1.2.840.113549.1.9.20";
71                 public const string localKeyId = "1.2.840.113549.1.9.21";
72
73                 public PKCS9 () {}
74         }
75
76
77         internal class SafeBag {
78                 private string _bagOID;
79                 private ASN1 _asn1;
80
81                 public SafeBag(string bagOID, ASN1 asn1) {
82                         _bagOID = bagOID;
83                         _asn1 = asn1;
84                 }
85
86                 public string BagOID {
87                         get { return _bagOID; }
88                 }
89
90                 public ASN1 ASN1 {
91                         get { return _asn1; }
92                 }
93         }
94
95
96 #if INSIDE_CORLIB
97         internal
98 #else
99         public 
100 #endif
101         class PKCS12 : ICloneable {
102
103                 public const string pbeWithSHAAnd128BitRC4 = "1.2.840.113549.1.12.1.1";
104                 public const string pbeWithSHAAnd40BitRC4 = "1.2.840.113549.1.12.1.2";
105                 public const string pbeWithSHAAnd3KeyTripleDESCBC = "1.2.840.113549.1.12.1.3";
106                 public const string pbeWithSHAAnd2KeyTripleDESCBC = "1.2.840.113549.1.12.1.4";
107                 public const string pbeWithSHAAnd128BitRC2CBC = "1.2.840.113549.1.12.1.5";
108                 public const string pbeWithSHAAnd40BitRC2CBC = "1.2.840.113549.1.12.1.6";
109
110                 // bags
111                 public const string keyBag  = "1.2.840.113549.1.12.10.1.1";
112                 public const string pkcs8ShroudedKeyBag  = "1.2.840.113549.1.12.10.1.2";
113                 public const string certBag  = "1.2.840.113549.1.12.10.1.3";
114                 public const string crlBag  = "1.2.840.113549.1.12.10.1.4";
115                 public const string secretBag  = "1.2.840.113549.1.12.10.1.5";
116                 public const string safeContentsBag  = "1.2.840.113549.1.12.10.1.6";
117
118                 // types
119                 public const string x509Certificate = "1.2.840.113549.1.9.22.1";
120                 public const string sdsiCertificate = "1.2.840.113549.1.9.22.2";
121                 public const string x509Crl = "1.2.840.113549.1.9.23.1";
122
123                 // Adapted from BouncyCastle PKCS12ParametersGenerator.java
124                 public class DeriveBytes {
125
126                         public enum Purpose {
127                                 Key,
128                                 IV,
129                                 MAC
130                         }
131
132                         static private byte[] keyDiversifier = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 };
133                         static private byte[] ivDiversifier  = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 };
134                         static private byte[] macDiversifier = { 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 };
135
136                         private string _hashName;
137                         private int _iterations;
138                         private byte[] _password;
139                         private byte[] _salt;
140
141                         public DeriveBytes () {}
142
143                         public string HashName {
144                                 get { return _hashName; } 
145                                 set { _hashName = value; }
146                         }
147
148                         public int IterationCount {
149                                 get { return _iterations; }
150                                 set { _iterations = value; }
151                         }
152
153                         public byte[] Password {
154                                 get { return (byte[]) _password.Clone (); }
155                                 set { 
156                                         if (value == null)
157                                                 _password = new byte [0];
158                                         else
159                                                 _password = (byte[]) value.Clone ();
160                                 }
161                         }
162
163                         public byte[] Salt {
164                                 get { return (byte[]) _salt.Clone ();  }
165                                 set {
166                                         if (value != null)
167                                                 _salt = (byte[]) value.Clone ();
168                                         else
169                                                 _salt = null;
170                                 }
171                         }
172
173                         private void Adjust (byte[] a, int aOff, byte[] b) 
174                         {
175                                 int x = (b[b.Length - 1] & 0xff) + (a [aOff + b.Length - 1] & 0xff) + 1;
176
177                                 a [aOff + b.Length - 1] = (byte) x;
178                                 x >>= 8;
179
180                                 for (int i = b.Length - 2; i >= 0; i--) {
181                                         x += (b [i] & 0xff) + (a [aOff + i] & 0xff);
182                                         a [aOff + i] = (byte) x;
183                                         x >>= 8;
184                                 }
185                         }
186
187                         private byte[] Derive (byte[] diversifier, int n) 
188                         {
189                                 HashAlgorithm digest = HashAlgorithm.Create (_hashName);
190                                 int u = (digest.HashSize >> 3); // div 8
191                                 int v = 64;
192                                 byte[] dKey = new byte [n];
193
194                                 byte[] S;
195                                 if ((_salt != null) && (_salt.Length != 0)) {
196                                         S = new byte[v * ((_salt.Length + v - 1) / v)];
197
198                                         for (int i = 0; i != S.Length; i++) {
199                                                 S[i] = _salt[i % _salt.Length];
200                                         }
201                                 }
202                                 else {
203                                         S = new byte[0];
204                                 }
205
206                                 byte[] P;
207                                 if ((_password != null) && (_password.Length != 0)) {
208                                         P = new byte[v * ((_password.Length + v - 1) / v)];
209
210                                         for (int i = 0; i != P.Length; i++) {
211                                                 P[i] = _password[i % _password.Length];
212                                         }
213                                 }
214                                 else {
215                                         P = new byte[0];
216                                 }
217
218                                 byte[] I = new byte [S.Length + P.Length];
219
220                                 Buffer.BlockCopy (S, 0, I, 0, S.Length);
221                                 Buffer.BlockCopy (P, 0, I, S.Length, P.Length);
222
223                                 byte[]  B = new byte[v];
224                                 int     c = (n + u - 1) / u;
225
226                                 for (int i = 1; i <= c; i++) {
227                                         digest.TransformBlock (diversifier, 0, diversifier.Length, diversifier, 0);
228                                         digest.TransformFinalBlock (I, 0, I.Length);
229                                         byte[] A = digest.Hash;
230                                         digest.Initialize ();
231                                         for (int j = 1; j != _iterations; j++) {
232                                                 A = digest.ComputeHash (A, 0, A.Length);
233                                         }
234
235                                         for (int j = 0; j != B.Length; j++) {
236                                                 B [j] = A [j % A.Length];
237                                         }
238
239                                         for (int j = 0; j != I.Length / v; j++) {
240                                                 Adjust (I, j * v, B);
241                                         }
242
243                                         if (i == c) {
244                                                 Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, dKey.Length - ((i - 1) * u));
245                                         }
246                                         else {
247                                                 Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, A.Length);
248                                         }
249                                 }
250
251                                 return dKey;
252                         }
253
254                         public byte[] DeriveKey (int size) 
255                         {
256                                 return Derive (keyDiversifier, size);
257                         }
258
259                         public byte[] DeriveIV (int size) 
260                         {
261                                 return Derive (ivDiversifier, size);
262                         }
263
264                         public byte[] DeriveMAC (int size) 
265                         {
266                                 return Derive (macDiversifier, size);
267                         }
268                 }
269
270                 static private int recommendedIterationCount = 2000;
271
272                 private int _version;
273                 private byte[] _password;
274                 private ArrayList _keyBags;
275                 private X509CertificateCollection _certs;
276                 private bool _keyBagsChanged;
277                 private bool _certsChanged;
278                 private int _iterations;
279                 private ArrayList _safeBags;
280                 private RandomNumberGenerator _rng;
281
282                 // constructors
283
284                 public PKCS12 () 
285                 {
286                         _iterations = recommendedIterationCount;
287                         _keyBags = new ArrayList ();
288                         _certs = new X509CertificateCollection ();
289                         _keyBagsChanged = false;
290                         _certsChanged = false;
291                         _safeBags = new ArrayList ();
292                 }
293
294                 public PKCS12 (byte[] data) : this (data, null) {}
295
296                 /*
297                  * PFX ::= SEQUENCE {
298                  *      version INTEGER {v3(3)}(v3,...),
299                  *      authSafe ContentInfo,
300                  *      macData MacData OPTIONAL
301                  * }
302                  * 
303                  * MacData ::= SEQUENCE {
304                  *      mac DigestInfo,
305                  *      macSalt OCTET STRING,
306                  *      iterations INTEGER DEFAULT 1
307                  *      -- Note: The default is for historical reasons and its use is deprecated. A higher
308                  *      -- value, like 1024 is recommended.
309                  * }
310                  * 
311                  * SafeContents ::= SEQUENCE OF SafeBag
312                  * 
313                  * SafeBag ::= SEQUENCE {
314                  *      bagId BAG-TYPE.&id ({PKCS12BagSet}),
315                  *      bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
316                  *      bagAttributes SET OF PKCS12Attribute OPTIONAL
317                  * }
318                  */
319                 public PKCS12 (byte[] data, string password) : this ()
320                 {
321                         Password = password;
322
323                         ASN1 pfx = new ASN1 (data);
324                         if (pfx.Tag != 0x30)
325                                 throw new ArgumentException ("invalid data");
326                         
327                         ASN1 version = pfx [0];
328                         if (version.Tag != 0x02)
329                                 throw new ArgumentException ("invalid PFX version");
330                         _version = version.Value [0];
331
332                         PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (pfx [1]);
333                         if (authSafe.ContentType != PKCS7.Oid.data)
334                                 throw new ArgumentException ("invalid authenticated safe");
335
336                         // now that we know it's a PKCS#12 file, check the (optional) MAC
337                         // before decoding anything else in the file
338                         if (pfx.Count > 2) {
339                                 ASN1 macData = pfx [2];
340                                 if (macData.Tag != 0x30)
341                                         throw new ArgumentException ("invalid MAC");
342                                 
343                                 ASN1 mac = macData [0];
344                                 if (mac.Tag != 0x30)
345                                         throw new ArgumentException ("invalid MAC");
346                                 ASN1 macAlgorithm = mac [0];
347                                 string macOid = ASN1Convert.ToOid (macAlgorithm [0]);
348                                 if (macOid != "1.3.14.3.2.26")
349                                         throw new ArgumentException ("unsupported HMAC");
350                                 byte[] macValue = mac [1].Value;
351
352                                 ASN1 macSalt = macData [1];
353                                 if (macSalt.Tag != 0x04)
354                                         throw new ArgumentException ("missing MAC salt");
355
356                                 _iterations = 1; // default value
357                                 if (macData.Count > 2) {
358                                         ASN1 iters = macData [2];
359                                         if (iters.Tag != 0x02)
360                                                 throw new ArgumentException ("invalid MAC iteration");
361                                         _iterations = ASN1Convert.ToInt32 (iters);
362                                 }
363
364                                 byte[] authSafeData = authSafe.Content [0].Value;
365                                 byte[] calculatedMac = MAC (_password, macSalt.Value, _iterations, authSafeData);
366                                 if (!Compare (macValue, calculatedMac))
367                                         throw new CryptographicException ("Invalid MAC - file may have been tampered!");
368                         }
369
370                         // we now returns to our original presentation - PFX
371                         ASN1 authenticatedSafe = new ASN1 (authSafe.Content [0].Value);
372                         for (int i=0; i < authenticatedSafe.Count; i++) {
373                                 PKCS7.ContentInfo ci = new PKCS7.ContentInfo (authenticatedSafe [i]);
374                                 switch (ci.ContentType) {
375                                         case PKCS7.Oid.data:
376                                                 // unencrypted (by PKCS#12)
377                                                 ASN1 safeContents = new ASN1 (ci.Content [0].Value);
378                                                 for (int j=0; j < safeContents.Count; j++) {
379                                                         ASN1 safeBag = safeContents [j];
380                                                         ReadSafeBag (safeBag);
381                                                 }
382                                                 break;
383                                         case PKCS7.Oid.encryptedData:
384                                                 // password encrypted
385                                                 PKCS7.EncryptedData ed = new PKCS7.EncryptedData (ci.Content [0]);
386                                                 ASN1 decrypted = new ASN1 (Decrypt (ed));
387                                                 for (int j=0; j < decrypted.Count; j++) {
388                                                         ASN1 safeBag = decrypted [j];
389                                                         ReadSafeBag (safeBag);
390                                                 }
391                                                 break;
392                                         case PKCS7.Oid.envelopedData:
393                                                 // public key encrypted
394                                                 throw new NotImplementedException ("public key encrypted");
395                                         default:
396                                                 throw new ArgumentException ("unknown authenticatedSafe");
397                                 }
398                         }
399                 }
400
401                 ~PKCS12 () 
402                 {
403                         if (_password != null) {
404                                 Array.Clear (_password, 0, _password.Length);
405                         }
406                         _password = null;
407                 }
408
409                 // properties
410
411                 public string Password {
412                         set {
413                                 if (value != null) {
414                                         if (value.EndsWith ("\0"))
415                                                 _password = Encoding.BigEndianUnicode.GetBytes (value); 
416                                         else
417                                                 _password = Encoding.BigEndianUnicode.GetBytes (value + "\0");                                  
418                                 }
419                                 else
420                                         _password = null;       // no password
421                         }
422                 }
423
424                 public int IterationCount {
425                         get { return _iterations; }
426                         set { _iterations = value; }
427                 }
428
429                 public ArrayList Keys {
430                         get {
431                                 if (_keyBagsChanged) {
432                                         _keyBags.Clear ();
433                                         foreach (SafeBag sb in _safeBags) {
434                                                 if (sb.BagOID.Equals (keyBag)) {
435                                                         ASN1 safeBag = sb.ASN1;
436                                                         ASN1 bagValue = safeBag [1];
437                                                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
438                                                         byte[] privateKey = pki.PrivateKey;
439                                                         switch (privateKey [0]) {
440                                                         case 0x02:
441                                                                 DSAParameters p = new DSAParameters (); // FIXME
442                                                                 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
443                                                                 break;
444                                                         case 0x30:
445                                                                 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
446                                                                 break;
447                                                         default:
448                                                                 break;
449                                                         }
450                                                         Array.Clear (privateKey, 0, privateKey.Length);
451
452                                                 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
453                                                         ASN1 safeBag = sb.ASN1;
454                                                         ASN1 bagValue = safeBag [1];
455                                                         PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
456                                                         byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
457                                                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
458                                                         byte[] privateKey = pki.PrivateKey;
459                                                         switch (privateKey [0]) {
460                                                         case 0x02:
461                                                                 DSAParameters p = new DSAParameters (); // FIXME
462                                                                 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
463                                                                 break;
464                                                         case 0x30:
465                                                                 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
466                                                                 break;
467                                                         default:
468                                                                 break;
469                                                         }
470                                                         Array.Clear (privateKey, 0, privateKey.Length);
471                                                         Array.Clear (decrypted, 0, decrypted.Length);
472                                                 }
473                                         }
474                                         _keyBagsChanged = false;
475                                 }
476                                 return ArrayList.ReadOnly(_keyBags);
477                         }
478                 }
479
480                 public X509CertificateCollection Certificates {
481                         get {
482                                 if (_certsChanged) {
483                                         _certs.Clear ();
484                                         foreach (SafeBag sb in _safeBags) {
485                                                 if (sb.BagOID.Equals (certBag)) {
486                                                         ASN1 safeBag = sb.ASN1;
487                                                         ASN1 bagValue = safeBag [1];
488                                                         PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
489                                                         _certs.Add (new X509Certificate (cert.Content [0].Value));
490                                                 }
491                                         }
492                                         _certsChanged = false;
493                                 }
494                                 return _certs;
495                         }
496                 }
497
498                 internal RandomNumberGenerator RNG {
499                         get {
500                                 if (_rng == null)
501                                         _rng = RandomNumberGenerator.Create ();
502                                 return _rng;
503                         }
504                 }
505
506                 // private methods
507
508                 private bool Compare (byte[] expected, byte[] actual) 
509                 {
510                         bool compare = false;
511                         if (expected.Length == actual.Length) {
512                                 for (int i=0; i < expected.Length; i++) {
513                                         if (expected [i] != actual [i])
514                                                 return false;
515                                 }
516                                 compare = true;
517                         }
518                         return compare;
519                 }
520
521                 private SymmetricAlgorithm GetSymmetricAlgorithm (string algorithmOid, byte[] salt, int iterationCount)
522                 {
523                         string algorithm = null;
524                         int keyLength = 8;      // 64 bits (default)
525                         int ivLength = 8;       // 64 bits (default)
526
527                         PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
528                         pd.Password = _password; 
529                         pd.Salt = salt;
530                         pd.IterationCount = iterationCount;
531
532                         switch (algorithmOid) {
533                                 case PKCS5.pbeWithMD2AndDESCBC:                 // no unit test available
534                                         pd.HashName = "MD2";
535                                         algorithm = "DES";
536                                         break;
537                                 case PKCS5.pbeWithMD5AndDESCBC:                 // no unit test available
538                                         pd.HashName = "MD5";
539                                         algorithm = "DES";
540                                         break;
541                                 case PKCS5.pbeWithMD2AndRC2CBC:                 // no unit test available
542                                         // TODO - RC2-CBC-Parameter (PKCS5)
543                                         // if missing default to 32 bits !!!
544                                         pd.HashName = "MD2";
545                                         algorithm = "RC2";
546                                         keyLength = 4;          // default
547                                         break;
548                                 case PKCS5.pbeWithMD5AndRC2CBC:                 // no unit test available
549                                         // TODO - RC2-CBC-Parameter (PKCS5)
550                                         // if missing default to 32 bits !!!
551                                         pd.HashName = "MD5";
552                                         algorithm = "RC2";
553                                         keyLength = 4;          // default
554                                         break;
555                                 case PKCS5.pbeWithSHA1AndDESCBC:                // no unit test available
556                                         pd.HashName = "SHA1";
557                                         algorithm = "DES";
558                                         break;
559                                 case PKCS5.pbeWithSHA1AndRC2CBC:                // no unit test available
560                                         // TODO - RC2-CBC-Parameter (PKCS5)
561                                         // if missing default to 32 bits !!!
562                                         pd.HashName = "SHA1";
563                                         algorithm = "RC2";
564                                         keyLength = 4;          // default
565                                         break;
566                                 case PKCS12.pbeWithSHAAnd128BitRC4:             // no unit test available
567                                         pd.HashName = "SHA1";
568                                         algorithm = "RC4";
569                                         keyLength = 16;
570                                         ivLength = 0;           // N/A
571                                         break;
572                                 case PKCS12.pbeWithSHAAnd40BitRC4:              // no unit test available
573                                         pd.HashName = "SHA1";
574                                         algorithm = "RC4";
575                                         keyLength = 5;
576                                         ivLength = 0;           // N/A
577                                         break;
578                                 case PKCS12.pbeWithSHAAnd3KeyTripleDESCBC: 
579                                         pd.HashName = "SHA1";
580                                         algorithm = "TripleDES";
581                                         keyLength = 24;
582                                         break;
583                                 case PKCS12.pbeWithSHAAnd2KeyTripleDESCBC:      // no unit test available
584                                         pd.HashName = "SHA1";
585                                         algorithm = "TripleDES";
586                                         keyLength = 16;
587                                         break;
588                                 case PKCS12.pbeWithSHAAnd128BitRC2CBC:          // no unit test available
589                                         pd.HashName = "SHA1";
590                                         algorithm = "RC2";
591                                         keyLength = 16;
592                                         break;
593                                 case PKCS12.pbeWithSHAAnd40BitRC2CBC: 
594                                         pd.HashName = "SHA1";
595                                         algorithm = "RC2";
596                                         keyLength = 5;
597                                         break;
598                                 default:
599                                         throw new NotSupportedException ("unknown oid " + algorithm);
600                         }
601
602                         SymmetricAlgorithm sa = SymmetricAlgorithm.Create (algorithm);
603                         sa.Key = pd.DeriveKey (keyLength);
604                         // IV required only for block ciphers (not stream ciphers)
605                         if (ivLength > 0) {
606                                 sa.IV = pd.DeriveIV (ivLength);
607                                 sa.Mode = CipherMode.CBC;
608                         }
609                         return sa;
610                 }
611
612                 public byte[] Decrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] encryptedData) 
613                 {
614                         SymmetricAlgorithm sa = null;
615                         byte[] result = null;
616                         try {
617                                 sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount);
618                                 ICryptoTransform ct = sa.CreateDecryptor ();
619                                 result = ct.TransformFinalBlock (encryptedData, 0, encryptedData.Length);
620                         }
621                         finally {
622                                 if (sa != null)
623                                         sa.Clear ();
624                         }
625                         return result;
626                 }
627
628                 public byte[] Decrypt (PKCS7.EncryptedData ed)
629                 {
630                         return Decrypt (ed.EncryptionAlgorithm.ContentType, 
631                                 ed.EncryptionAlgorithm.Content [0].Value, 
632                                 ASN1Convert.ToInt32 (ed.EncryptionAlgorithm.Content [1]),
633                                 ed.EncryptedContent);
634                 }
635
636                 public byte[] Encrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] data) 
637                 {
638                         byte[] result = null;
639                         using (SymmetricAlgorithm sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount)) {
640                                 ICryptoTransform ct = sa.CreateEncryptor ();
641                                 result = ct.TransformFinalBlock (data, 0, data.Length);
642                         }
643                         return result;
644                 }
645
646                 private void AddPrivateKey (PKCS8.PrivateKeyInfo pki) 
647                 {
648                         byte[] privateKey = pki.PrivateKey;
649                         switch (privateKey [0]) {
650                                 case 0x02:
651                                         DSAParameters p = new DSAParameters (); // FIXME
652                                         _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
653                                         break;
654                                 case 0x30:
655                                         _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
656                                         break;
657                                 default:
658                                         Array.Clear (privateKey, 0, privateKey.Length);
659                                         throw new CryptographicException ("Unknown private key format");
660                         }
661                         Array.Clear (privateKey, 0, privateKey.Length);
662                 }
663
664                 private void ReadSafeBag (ASN1 safeBag) 
665                 {
666                         if (safeBag.Tag != 0x30)
667                                 throw new ArgumentException ("invalid safeBag");
668
669                         ASN1 bagId = safeBag [0];
670                         if (bagId.Tag != 0x06)
671                                 throw new ArgumentException ("invalid safeBag id");
672
673                         ASN1 bagValue = safeBag [1];
674                         string oid = ASN1Convert.ToOid (bagId);
675                         switch (oid) {
676                                 case keyBag:
677                                         // NEED UNIT TEST
678                                         AddPrivateKey (new PKCS8.PrivateKeyInfo (bagValue.Value));
679                                         break;
680                                 case pkcs8ShroudedKeyBag:
681                                         PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
682                                         byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
683                                         AddPrivateKey (new PKCS8.PrivateKeyInfo (decrypted));
684                                         Array.Clear (decrypted, 0, decrypted.Length);
685                                         break;
686                                 case certBag:
687                                         PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
688                                         if (cert.ContentType != x509Certificate)
689                                                 throw new NotSupportedException ("unsupport certificate type");
690                                         X509Certificate x509 = new X509Certificate (cert.Content [0].Value);
691                                         _certs.Add (x509);
692                                         break;
693                                 case crlBag:
694                                         // TODO
695                                         break;
696                                 case secretBag: 
697                                         // TODO
698                                         break;
699                                 case safeContentsBag:
700                                         // TODO - ? recurse ?
701                                         break;
702                                 default:
703                                         throw new ArgumentException ("unknown safeBag oid");
704                         }
705
706                         if (safeBag.Count > 2) {
707                                 ASN1 bagAttributes = safeBag [2];
708                                 if (bagAttributes.Tag != 0x31)
709                                         throw new ArgumentException ("invalid safeBag attributes id");
710
711                                 for (int i = 0; i < bagAttributes.Count; i++) {
712                                         ASN1 pkcs12Attribute = bagAttributes[i];
713                                                 
714                                         if (pkcs12Attribute.Tag != 0x30)
715                                                 throw new ArgumentException ("invalid PKCS12 attributes id");
716
717                                         ASN1 attrId = pkcs12Attribute [0];
718                                         if (attrId.Tag != 0x06)
719                                                 throw new ArgumentException ("invalid attribute id");
720                                                 
721                                         string attrOid = ASN1Convert.ToOid (attrId);
722
723                                         ASN1 attrValues = pkcs12Attribute[1];
724                                         for (int j = 0; j < attrValues.Count; j++) {
725                                                 ASN1 attrValue = attrValues[j];
726
727                                                 switch (attrOid) {
728                                                 case PKCS9.friendlyName:
729                                                         if (attrValue.Tag != 0x1e)
730                                                                 throw new ArgumentException ("invalid attribute value id");
731                                                         break;
732                                                 case PKCS9.localKeyId:
733                                                         if (attrValue.Tag != 0x04)
734                                                                 throw new ArgumentException ("invalid attribute value id");
735                                                         break;
736                                                 default:
737                                                         // Unknown OID -- don't check Tag
738                                                         break;
739                                                 }
740                                         }
741                                 }
742                         }
743
744                         _safeBags.Add (new SafeBag(oid, safeBag));
745                 }
746
747                 private ASN1 Pkcs8ShroudedKeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) 
748                 {
749                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
750                         if (aa is RSA) {
751                                 pki.Algorithm = "1.2.840.113549.1.1.1";
752                                 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
753                         }
754                         else if (aa is DSA) {
755                                 pki.Algorithm = null;
756                                 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
757                         }
758                         else
759                                 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
760
761                         PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo ();
762                         epki.Algorithm = pbeWithSHAAnd3KeyTripleDESCBC;
763                         epki.IterationCount = _iterations;
764                         epki.EncryptedData = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, epki.Salt, _iterations, pki.GetBytes ());
765
766                         ASN1 safeBag = new ASN1 (0x30);
767                         safeBag.Add (ASN1Convert.FromOid (pkcs8ShroudedKeyBag));
768                         ASN1 bagValue = new ASN1 (0xA0);
769                         bagValue.Add (new ASN1 (epki.GetBytes ()));
770                         safeBag.Add (bagValue);
771
772                         if (attributes != null) {
773                                 ASN1 bagAttributes = new ASN1 (0x31);
774                                 IDictionaryEnumerator de = attributes.GetEnumerator ();
775
776                                 while (de.MoveNext ()) {
777                                         string oid = (string)de.Key;
778                                         switch (oid) {
779                                         case PKCS9.friendlyName:
780                                                 ArrayList names = (ArrayList)de.Value;
781                                                 if (names.Count > 0) {
782                                                         ASN1 pkcs12Attribute = new ASN1 (0x30);
783                                                         pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
784                                                         ASN1 attrValues = new ASN1 (0x31);
785                                                         foreach (byte[] name in names) {
786                                                                 ASN1 attrValue = new ASN1 (0x1e);
787                                                                 attrValue.Value = name;
788                                                                 attrValues.Add (attrValue);
789                                                         }
790                                                         pkcs12Attribute.Add (attrValues);
791                                                         bagAttributes.Add (pkcs12Attribute);
792                                                 }
793                                                 break;
794                                         case PKCS9.localKeyId:
795                                                 ArrayList keys = (ArrayList)de.Value;
796                                                 if (keys.Count > 0) {
797                                                         ASN1 pkcs12Attribute = new ASN1 (0x30);
798                                                         pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
799                                                         ASN1 attrValues = new ASN1 (0x31);
800                                                         foreach (byte[] key in keys) {
801                                                                 ASN1 attrValue = new ASN1 (0x04);
802                                                                 attrValue.Value = key;
803                                                                 attrValues.Add (attrValue);
804                                                         }
805                                                         pkcs12Attribute.Add (attrValues);
806                                                         bagAttributes.Add (pkcs12Attribute);
807                                                 }
808                                                 break;
809                                         default:
810                                                 break;
811                                         }
812                                 }
813
814                                 if (bagAttributes.Count > 0) {
815                                         safeBag.Add (bagAttributes);
816                                 }
817                         }
818
819                         return safeBag;
820                 }
821
822                 private ASN1 KeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) 
823                 {
824                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
825                         if (aa is RSA) {
826                                 pki.Algorithm = "1.2.840.113549.1.1.1";
827                                 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
828                         }
829                         else if (aa is DSA) {
830                                 pki.Algorithm = null;
831                                 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
832                         }
833                         else
834                                 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
835
836                         ASN1 safeBag = new ASN1 (0x30);
837                         safeBag.Add (ASN1Convert.FromOid (keyBag));
838                         ASN1 bagValue = new ASN1 (0xA0);
839                         bagValue.Add (new ASN1 (pki.GetBytes ()));
840                         safeBag.Add (bagValue);
841
842                         if (attributes != null) {
843                                 ASN1 bagAttributes = new ASN1 (0x31);
844                                 IDictionaryEnumerator de = attributes.GetEnumerator ();
845
846                                 while (de.MoveNext ()) {
847                                         string oid = (string)de.Key;
848                                         switch (oid) {
849                                         case PKCS9.friendlyName:
850                                                 ArrayList names = (ArrayList)de.Value;
851                                                 if (names.Count > 0) {
852                                                         ASN1 pkcs12Attribute = new ASN1 (0x30);
853                                                         pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
854                                                         ASN1 attrValues = new ASN1 (0x31);
855                                                         foreach (byte[] name in names) {
856                                                                 ASN1 attrValue = new ASN1 (0x1e);
857                                                                 attrValue.Value = name;
858                                                                 attrValues.Add (attrValue);
859                                                         }
860                                                         pkcs12Attribute.Add (attrValues);
861                                                         bagAttributes.Add (pkcs12Attribute);
862                                                 }
863                                                 break;
864                                         case PKCS9.localKeyId:
865                                                 ArrayList keys = (ArrayList)de.Value;
866                                                 if (keys.Count > 0) {
867                                                         ASN1 pkcs12Attribute = new ASN1 (0x30);
868                                                         pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
869                                                         ASN1 attrValues = new ASN1 (0x31);
870                                                         foreach (byte[] key in keys) {
871                                                                 ASN1 attrValue = new ASN1 (0x04);
872                                                                 attrValue.Value = key;
873                                                                 attrValues.Add (attrValue);
874                                                         }
875                                                         pkcs12Attribute.Add (attrValues);
876                                                         bagAttributes.Add (pkcs12Attribute);
877                                                 }
878                                                 break;
879                                         default:
880                                                 break;
881                                         }
882                                 }
883
884                                 if (bagAttributes.Count > 0) {
885                                         safeBag.Add (bagAttributes);
886                                 }
887                         }
888
889                         return safeBag;
890                 }
891
892                 private ASN1 CertificateSafeBag (X509Certificate x509, IDictionary attributes) 
893                 {
894                         ASN1 encapsulatedCertificate = new ASN1 (0x04, x509.RawData);
895
896                         PKCS7.ContentInfo ci = new PKCS7.ContentInfo ();
897                         ci.ContentType = x509Certificate;
898                         ci.Content.Add (encapsulatedCertificate);
899
900                         ASN1 bagValue = new ASN1 (0xA0);
901                         bagValue.Add (ci.ASN1);
902
903                         ASN1 safeBag = new ASN1 (0x30);
904                         safeBag.Add (ASN1Convert.FromOid (certBag));
905                         safeBag.Add (bagValue);
906
907                         if (attributes != null) {
908                                 ASN1 bagAttributes = new ASN1 (0x31);
909                                 IDictionaryEnumerator de = attributes.GetEnumerator ();
910
911                                 while (de.MoveNext ()) {
912                                         string oid = (string)de.Key;
913                                         switch (oid) {
914                                         case PKCS9.friendlyName:
915                                                 ArrayList names = (ArrayList)de.Value;
916                                                 if (names.Count > 0) {
917                                                         ASN1 pkcs12Attribute = new ASN1 (0x30);
918                                                         pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
919                                                         ASN1 attrValues = new ASN1 (0x31);
920                                                         foreach (byte[] name in names) {
921                                                                 ASN1 attrValue = new ASN1 (0x1e);
922                                                                 attrValue.Value = name;
923                                                                 attrValues.Add (attrValue);
924                                                         }
925                                                         pkcs12Attribute.Add (attrValues);
926                                                         bagAttributes.Add (pkcs12Attribute);
927                                                 }
928                                                 break;
929                                         case PKCS9.localKeyId:
930                                                 ArrayList keys = (ArrayList)de.Value;
931                                                 if (keys.Count > 0) {
932                                                         ASN1 pkcs12Attribute = new ASN1 (0x30);
933                                                         pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
934                                                         ASN1 attrValues = new ASN1 (0x31);
935                                                         foreach (byte[] key in keys) {
936                                                                 ASN1 attrValue = new ASN1 (0x04);
937                                                                 attrValue.Value = key;
938                                                                 attrValues.Add (attrValue);
939                                                         }
940                                                         pkcs12Attribute.Add (attrValues);
941                                                         bagAttributes.Add (pkcs12Attribute);
942                                                 }
943                                                 break;
944                                         default:
945                                                 break;
946                                         }
947                                 }
948
949                                 if (bagAttributes.Count > 0) {
950                                         safeBag.Add (bagAttributes);
951                                 }
952                         }
953
954                         return safeBag;
955                 }
956
957                 private byte[] MAC (byte[] password, byte[] salt, int iterations, byte[] data) 
958                 {
959                         PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
960                         pd.HashName = "SHA1";
961                         pd.Password = password;
962                         pd.Salt = salt;
963                         pd.IterationCount = iterations;
964
965                         HMACSHA1 hmac = (HMACSHA1) HMACSHA1.Create ();
966                         hmac.Key = pd.DeriveMAC (20);
967                         return hmac.ComputeHash (data, 0, data.Length);
968                 }
969
970                 /*
971                  * SafeContents ::= SEQUENCE OF SafeBag
972                  * 
973                  * SafeBag ::= SEQUENCE {
974                  *      bagId BAG-TYPE.&id ({PKCS12BagSet}),
975                  *      bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
976                  *      bagAttributes SET OF PKCS12Attribute OPTIONAL
977                  * }
978                  */
979                 public byte[] GetBytes () 
980                 {
981                         // TODO (incomplete)
982                         ASN1 safeBagSequence = new ASN1 (0x30);
983
984                         // Sync Safe Bag list since X509CertificateCollection may be updated
985                         ArrayList scs = new ArrayList ();
986                         foreach (SafeBag sb in _safeBags) {
987                                 if (sb.BagOID.Equals (certBag)) {
988                                         ASN1 safeBag = sb.ASN1;
989                                         ASN1 bagValue = safeBag [1];
990                                         PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
991                                         scs.Add (new X509Certificate (cert.Content [0].Value));
992                                 }
993                         }
994
995                         ArrayList addcerts = new ArrayList ();
996                         ArrayList removecerts = new ArrayList ();
997
998                         foreach (X509Certificate c in Certificates) {
999                                 bool found = false;
1000
1001                                 foreach (X509Certificate lc in scs) {
1002                                         if (Compare (c.RawData, lc.RawData)) {
1003                                                 found = true;
1004                                         }
1005                                 }
1006
1007                                 if (!found) {
1008                                         addcerts.Add (c);
1009                                 }
1010                         }
1011                         foreach (X509Certificate c in scs) {
1012                                 bool found = false;
1013
1014                                 foreach (X509Certificate lc in Certificates) {
1015                                         if (Compare (c.RawData, lc.RawData)) {
1016                                                 found = true;
1017                                         }
1018                                 }
1019
1020                                 if (!found) {
1021                                         removecerts.Add (c);
1022                                 }
1023                         }
1024
1025                         foreach (X509Certificate c in removecerts) {
1026                                 RemoveCertificate (c);
1027                         }
1028
1029                         foreach (X509Certificate c in addcerts) {
1030                                 AddCertificate (c);
1031                         }
1032                         // Sync done
1033
1034                         if (_safeBags.Count > 0) {
1035                                 ASN1 certsSafeBag = new ASN1 (0x30);
1036                                 foreach (SafeBag sb in _safeBags) {
1037                                         if (sb.BagOID.Equals (certBag)) {
1038                                                 certsSafeBag.Add (sb.ASN1);
1039                                         }
1040                                 }
1041
1042                                 if (certsSafeBag.Count > 0) {
1043                                         byte[] certsSalt = new byte [8];
1044                                         RNG.GetBytes (certsSalt);
1045
1046                                         ASN1 seqParams = new ASN1 (0x30);
1047                                         seqParams.Add (new ASN1 (0x04, certsSalt));
1048                                         seqParams.Add (ASN1Convert.FromInt32 (_iterations));
1049
1050                                         ASN1 seqPbe = new ASN1 (0x30);
1051                                         seqPbe.Add (ASN1Convert.FromOid (pbeWithSHAAnd3KeyTripleDESCBC));
1052                                         seqPbe.Add (seqParams);
1053
1054                                         byte[] encrypted = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, certsSalt, _iterations, certsSafeBag.GetBytes ());
1055                                         ASN1 encryptedCerts = new ASN1 (0x80, encrypted);
1056
1057                                         ASN1 seq = new ASN1 (0x30);
1058                                         seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data));
1059                                         seq.Add (seqPbe);
1060                                         seq.Add (encryptedCerts);
1061
1062                                         ASN1 certsVersion = new ASN1 (0x02, new byte [1] { 0x00 });
1063                                         ASN1 encData = new ASN1 (0x30);
1064                                         encData.Add (certsVersion);
1065                                         encData.Add (seq);
1066
1067                                         ASN1 certsContent = new ASN1 (0xA0);
1068                                         certsContent.Add (encData);
1069
1070                                         PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData);
1071                                         bag.Content = certsContent;
1072                                         safeBagSequence.Add (bag.ASN1);
1073                                 }
1074                         }
1075
1076                         if (_safeBags.Count > 0) {
1077                                 ASN1 safeContents = new ASN1 (0x30);
1078                                 foreach (SafeBag sb in _safeBags) {
1079                                         if (sb.BagOID.Equals (keyBag) ||
1080                                             sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1081                                                 safeContents.Add (sb.ASN1);
1082                                         }
1083                                 }
1084                                 if (safeContents.Count > 0) {
1085                                         ASN1 content = new ASN1 (0xA0);
1086                                         content.Add (new ASN1 (0x04, safeContents.GetBytes ()));
1087                                 
1088                                         PKCS7.ContentInfo keyBag = new PKCS7.ContentInfo (PKCS7.Oid.data);
1089                                         keyBag.Content = content;
1090                                         safeBagSequence.Add (keyBag.ASN1);
1091                                 }
1092                         }
1093
1094
1095                         ASN1 encapsulates = new ASN1 (0x04, safeBagSequence.GetBytes ());
1096                         ASN1 ci = new ASN1 (0xA0);
1097                         ci.Add (encapsulates);
1098                         PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (PKCS7.Oid.data);
1099                         authSafe.Content = ci;
1100                         
1101                         ASN1 macData = new ASN1 (0x30);
1102                         if (_password != null) {
1103                                 // only for password based encryption
1104                                 byte[] salt = new byte [20];
1105                                 RNG.GetBytes (salt);
1106                                 byte[] macValue = MAC (_password, salt, _iterations, authSafe.Content [0].Value);
1107                                 ASN1 oidSeq = new ASN1 (0x30);
1108                                 oidSeq.Add (ASN1Convert.FromOid ("1.3.14.3.2.26"));     // SHA1
1109                                 oidSeq.Add (new ASN1 (0x05));
1110
1111                                 ASN1 mac = new ASN1 (0x30);
1112                                 mac.Add (oidSeq);
1113                                 mac.Add (new ASN1 (0x04, macValue));
1114
1115                                 macData.Add (mac);
1116                                 macData.Add (new ASN1 (0x04, salt));
1117                                 macData.Add (ASN1Convert.FromInt32 (_iterations));
1118                         }
1119                         
1120                         ASN1 version = new ASN1 (0x02, new byte [1] { 0x03 });
1121                         
1122                         ASN1 pfx = new ASN1 (0x30);
1123                         pfx.Add (version);
1124                         pfx.Add (authSafe.ASN1);
1125                         if (macData.Count > 0) {
1126                                 // only for password based encryption
1127                                 pfx.Add (macData);
1128                         }
1129
1130                         return pfx.GetBytes ();
1131                 }
1132
1133                 public void AddCertificate (X509Certificate cert)
1134                 {
1135                         AddCertificate (cert, null);
1136                 }
1137
1138                 public void AddCertificate (X509Certificate cert, IDictionary attributes)
1139                 {
1140                         bool found = false;
1141
1142                         for (int i = 0; !found && i < _safeBags.Count; i++) {
1143                                 SafeBag sb = (SafeBag)_safeBags [i];
1144
1145                                 if (sb.BagOID.Equals (certBag)) {
1146                                         ASN1 safeBag = sb.ASN1;
1147                                         ASN1 bagValue = safeBag [1];
1148                                         PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1149                                         X509Certificate c = new X509Certificate (crt.Content [0].Value);
1150                                         if (Compare (cert.RawData, c.RawData)) {
1151                                                 found = true;
1152                                         }
1153                                 }
1154                         }
1155
1156                         if (!found) {
1157                                 _safeBags.Add (new SafeBag (certBag, CertificateSafeBag (cert, attributes)));
1158                                 _certsChanged = true;
1159                         }
1160                 }
1161
1162                 public void RemoveCertificate (X509Certificate cert)
1163                 {
1164                         RemoveCertificate (cert, null);
1165                 }
1166
1167                 public void RemoveCertificate (X509Certificate cert, IDictionary attrs)
1168                 {
1169                         int certIndex = -1;
1170
1171                         for (int i = 0; certIndex == -1 && i < _safeBags.Count; i++) {
1172                                 SafeBag sb = (SafeBag)_safeBags [i];
1173
1174                                 if (sb.BagOID.Equals (certBag)) {
1175                                         ASN1 safeBag = sb.ASN1;
1176                                         ASN1 bagValue = safeBag [1];
1177                                         PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1178                                         X509Certificate c = new X509Certificate (crt.Content [0].Value);
1179                                         if (Compare (cert.RawData, c.RawData)) {
1180                                                 if (attrs != null) {
1181                                                         if (safeBag.Count == 3) {
1182                                                                 ASN1 bagAttributes = safeBag [2];
1183                                                                 int bagAttributesFound = 0;
1184                                                                 for (int j = 0; j < bagAttributes.Count; j++) {
1185                                                                         ASN1 pkcs12Attribute = bagAttributes [j];
1186                                                                         ASN1 attrId = pkcs12Attribute [0];
1187                                                                         string ao = ASN1Convert.ToOid (attrId);
1188                                                                         ArrayList dattrValues = (ArrayList)attrs [ao];
1189
1190                                                                         if (dattrValues != null) {
1191                                                                                 ASN1 attrValues = pkcs12Attribute [1];
1192
1193                                                                                 if (dattrValues.Count == attrValues.Count) {
1194                                                                                         int attrValuesFound = 0;
1195                                                                                         for (int k = 0; k < attrValues.Count; k++) {
1196                                                                                                 ASN1 attrValue = attrValues [k];
1197                                                                                                 byte[] value = (byte[])dattrValues [k];
1198                                                                         
1199                                                                                                 if (Compare (value, attrValue.Value)) {
1200                                                                                                         attrValuesFound += 1;
1201                                                                                                 }
1202                                                                                         }
1203                                                                                         if (attrValuesFound == attrValues.Count) {
1204                                                                                                 bagAttributesFound += 1;
1205                                                                                         }
1206                                                                                 }
1207                                                                         }
1208                                                                 }
1209                                                                 if (bagAttributesFound == bagAttributes.Count) {
1210                                                                         certIndex = i;
1211                                                                 }
1212                                                         }
1213                                                 } else {
1214                                                         certIndex = i;
1215                                                 }
1216                                         }
1217                                 }
1218                         }
1219
1220                         if (certIndex != -1) {
1221                                 _safeBags.RemoveAt (certIndex);
1222                                 _certsChanged = true;
1223                         }
1224                 }
1225
1226                 private bool CompareAsymmetricAlgorithm (AsymmetricAlgorithm a1, AsymmetricAlgorithm a2)
1227                 {
1228                         bool result = a1.KeyExchangeAlgorithm.Equals (a2.KeyExchangeAlgorithm);
1229                         result = result && a1.KeySize == a2.KeySize;
1230
1231                         KeySizes[] keysizes = a2.LegalKeySizes;
1232                         if (a1.LegalKeySizes.Length != keysizes.Length)
1233                                 return false;
1234
1235                         for (int i = 0; i < a1.LegalKeySizes.Length; i++) {
1236                                 result = result && CompareKeySizes (a1.LegalKeySizes [i], keysizes [i]);
1237                         }
1238
1239                         result = result && a1.SignatureAlgorithm == a2.SignatureAlgorithm;
1240                         
1241                         return result;
1242                 }
1243
1244                 private bool CompareKeySizes (KeySizes k1, KeySizes k2)
1245                 { 
1246                         bool result = k1.MaxSize == k2.MaxSize;
1247                         result = result && k1.MinSize == k2.MinSize;
1248                         result = result && k1.SkipSize == k2.SkipSize;
1249                         return result;
1250                 }
1251
1252                 public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
1253                 {
1254                         AddPkcs8ShroudedKeyBag (aa, null);
1255                 }
1256
1257                 public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
1258                 {
1259                         bool found = false;
1260
1261                         for (int i = 0; !found && i < _safeBags.Count; i++) {
1262                                 SafeBag sb = (SafeBag)_safeBags [i];
1263
1264                                 if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1265                                         ASN1 bagValue = sb.ASN1 [1];
1266                                         PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1267                                         byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1268                                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1269                                         byte[] privateKey = pki.PrivateKey;
1270
1271                                         AsymmetricAlgorithm saa = null;
1272                                         switch (privateKey [0]) {
1273                                         case 0x02:
1274                                                 DSAParameters p = new DSAParameters (); // FIXME
1275                                                 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1276                                                 break;
1277                                         case 0x30:
1278                                                 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1279                                                 break;
1280                                         default:
1281                                                 Array.Clear (decrypted, 0, decrypted.Length);
1282                                                 Array.Clear (privateKey, 0, privateKey.Length);
1283                                                 throw new CryptographicException ("Unknown private key format");
1284                                         }
1285
1286                                         Array.Clear (decrypted, 0, decrypted.Length);
1287                                         Array.Clear (privateKey, 0, privateKey.Length);
1288
1289                                         if (CompareAsymmetricAlgorithm (aa , saa)) {
1290                                                 found = true;
1291                                         }
1292                                 }
1293                         }
1294
1295                         if (!found) {
1296                                 _safeBags.Add (new SafeBag (pkcs8ShroudedKeyBag, Pkcs8ShroudedKeyBagSafeBag (aa, attributes)));
1297                                 _keyBagsChanged = true;
1298                         }
1299                 }
1300
1301                 public void RemovePkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
1302                 {
1303                         int aaIndex = -1;
1304
1305                         for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
1306                                 SafeBag sb = (SafeBag)_safeBags [i];
1307
1308                                 if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1309                                         ASN1 bagValue = sb.ASN1 [1];
1310                                         PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1311                                         byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1312                                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1313                                         byte[] privateKey = pki.PrivateKey;
1314
1315                                         AsymmetricAlgorithm saa = null;
1316                                         switch (privateKey [0]) {
1317                                         case 0x02:
1318                                                 DSAParameters p = new DSAParameters (); // FIXME
1319                                                 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1320                                                 break;
1321                                         case 0x30:
1322                                                 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1323                                                 break;
1324                                         default:
1325                                                 Array.Clear (decrypted, 0, decrypted.Length);
1326                                                 Array.Clear (privateKey, 0, privateKey.Length);
1327                                                 throw new CryptographicException ("Unknown private key format");
1328                                         }
1329
1330                                         Array.Clear (decrypted, 0, decrypted.Length);
1331                                         Array.Clear (privateKey, 0, privateKey.Length);
1332
1333                                         if (CompareAsymmetricAlgorithm (aa, saa)) {
1334                                                 aaIndex = i;
1335                                         }
1336                                 }
1337                         }
1338
1339                         if (aaIndex != -1) {
1340                                 _safeBags.RemoveAt (aaIndex);
1341                                 _keyBagsChanged = true;
1342                         }
1343                 }
1344
1345                 public void AddKeyBag (AsymmetricAlgorithm aa)
1346                 {
1347                         AddKeyBag (aa, null);
1348                 }
1349
1350                 public void AddKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
1351                 {
1352                         bool found = false;
1353
1354                         for (int i = 0; !found && i < _safeBags.Count; i++) {
1355                                 SafeBag sb = (SafeBag)_safeBags [i];
1356
1357                                 if (sb.BagOID.Equals (keyBag)) {
1358                                         ASN1 bagValue = sb.ASN1 [1];
1359                                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1360                                         byte[] privateKey = pki.PrivateKey;
1361
1362                                         AsymmetricAlgorithm saa = null;
1363                                         switch (privateKey [0]) {
1364                                         case 0x02:
1365                                                 DSAParameters p = new DSAParameters (); // FIXME
1366                                                 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1367                                                 break;
1368                                         case 0x30:
1369                                                 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1370                                                 break;
1371                                         default:
1372                                                 Array.Clear (privateKey, 0, privateKey.Length);
1373                                                 throw new CryptographicException ("Unknown private key format");
1374                                         }
1375
1376                                         Array.Clear (privateKey, 0, privateKey.Length);
1377
1378                                         if (CompareAsymmetricAlgorithm (aa, saa)) {
1379                                                 found = true;
1380                                         }
1381                                 }
1382                         }
1383
1384                         if (!found) {
1385                                 _safeBags.Add (new SafeBag (keyBag, KeyBagSafeBag (aa, attributes)));
1386                                 _keyBagsChanged = true;
1387                         }
1388                 }
1389
1390                 public void RemoveKeyBag (AsymmetricAlgorithm aa)
1391                 {
1392                         int aaIndex = -1;
1393
1394                         for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
1395                                 SafeBag sb = (SafeBag)_safeBags [i];
1396
1397                                 if (sb.BagOID.Equals (keyBag)) {
1398                                         ASN1 bagValue = sb.ASN1 [1];
1399                                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1400                                         byte[] privateKey = pki.PrivateKey;
1401
1402                                         AsymmetricAlgorithm saa = null;
1403                                         switch (privateKey [0]) {
1404                                         case 0x02:
1405                                                 DSAParameters p = new DSAParameters (); // FIXME
1406                                                 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1407                                                 break;
1408                                         case 0x30:
1409                                                 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1410                                                 break;
1411                                         default:
1412                                                 Array.Clear (privateKey, 0, privateKey.Length);
1413                                                 throw new CryptographicException ("Unknown private key format");
1414                                         }
1415
1416                                         Array.Clear (privateKey, 0, privateKey.Length);
1417
1418                                         if (CompareAsymmetricAlgorithm (aa, saa)) {
1419                                                 aaIndex = i;
1420                                         }
1421                                 }
1422                         }
1423
1424                         if (aaIndex != -1) {
1425                                 _safeBags.RemoveAt (aaIndex);
1426                                 _keyBagsChanged = true;
1427                         }
1428                 }
1429
1430
1431                 public AsymmetricAlgorithm GetAsymmetricAlgorithm (IDictionary attrs)
1432                 {
1433                         foreach (SafeBag sb in _safeBags) {
1434                                 if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1435                                         ASN1 safeBag = sb.ASN1;
1436
1437                                         if (safeBag.Count == 3) {
1438                                                 ASN1 bagAttributes = safeBag [2];
1439
1440                                                 int bagAttributesFound = 0;
1441                                                 for (int i = 0; i < bagAttributes.Count; i++) {
1442                                                         ASN1 pkcs12Attribute = bagAttributes [i];
1443                                                         ASN1 attrId = pkcs12Attribute [0];
1444                                                         string ao = ASN1Convert.ToOid (attrId);
1445                                                         ArrayList dattrValues = (ArrayList)attrs [ao];
1446
1447                                                         if (dattrValues != null) {
1448                                                                 ASN1 attrValues = pkcs12Attribute [1];
1449
1450                                                                 if (dattrValues.Count == attrValues.Count) {
1451                                                                         int attrValuesFound = 0;
1452                                                                         for (int j = 0; j < attrValues.Count; j++) {
1453                                                                                 ASN1 attrValue = attrValues [j];
1454                                                                                 byte[] value = (byte[])dattrValues [j];
1455                                                                         
1456                                                                                 if (Compare (value, attrValue.Value)) {
1457                                                                                         attrValuesFound += 1;
1458                                                                                 }
1459                                                                         }
1460                                                                         if (attrValuesFound == attrValues.Count) {
1461                                                                                 bagAttributesFound += 1;
1462                                                                         }
1463                                                                 }
1464                                                         }
1465                                                 }
1466                                                 if (bagAttributesFound == bagAttributes.Count) {
1467                                                         ASN1 bagValue = safeBag [1];
1468                                                         AsymmetricAlgorithm aa = null;
1469                                                         if (sb.BagOID.Equals (keyBag)) {
1470                                                                 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1471                                                                 byte[] privateKey = pki.PrivateKey;
1472                                                                 switch (privateKey [0]) {
1473                                                                 case 0x02:
1474                                                                         DSAParameters p = new DSAParameters (); // FIXME
1475                                                                         aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1476                                                                         break;
1477                                                                 case 0x30:
1478                                                                         aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1479                                                                         break;
1480                                                                 default:
1481                                                                         break;
1482                                                                 }
1483                                                                 Array.Clear (privateKey, 0, privateKey.Length);
1484                                                         } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1485                                                                 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1486                                                                 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1487                                                                 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1488                                                                 byte[] privateKey = pki.PrivateKey;
1489                                                                 switch (privateKey [0]) {
1490                                                                 case 0x02:
1491                                                                         DSAParameters p = new DSAParameters (); // FIXME
1492                                                                         aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1493                                                                         break;
1494                                                                 case 0x30:
1495                                                                         aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1496                                                                         break;
1497                                                                 default:
1498                                                                         break;
1499                                                                 }
1500                                                                 Array.Clear (privateKey, 0, privateKey.Length);
1501                                                                 Array.Clear (decrypted, 0, decrypted.Length);
1502                                                         }
1503                                                         return aa;
1504                                                 }
1505                                         }
1506                                 }
1507                         }
1508
1509                         return null;
1510                 }
1511
1512                 public X509Certificate GetCertificate (IDictionary attrs)
1513                 {
1514                         foreach (SafeBag sb in _safeBags) {
1515                                 if (sb.BagOID.Equals (certBag)) {
1516                                         ASN1 safeBag = sb.ASN1;
1517
1518                                         if (safeBag.Count == 3) {
1519                                                 ASN1 bagAttributes = safeBag [2];
1520
1521                                                 int bagAttributesFound = 0;
1522                                                 for (int i = 0; i < bagAttributes.Count; i++) {
1523                                                         ASN1 pkcs12Attribute = bagAttributes [i];
1524                                                         ASN1 attrId = pkcs12Attribute [0];
1525                                                         string ao = ASN1Convert.ToOid (attrId);
1526                                                         ArrayList dattrValues = (ArrayList)attrs [ao];
1527
1528                                                         if (dattrValues != null) {
1529                                                                 ASN1 attrValues = pkcs12Attribute [1];
1530                                                                 
1531                                                                 if (dattrValues.Count == attrValues.Count) {
1532                                                                         int attrValuesFound = 0;
1533                                                                         for (int j = 0; j < attrValues.Count; j++) {
1534                                                                                 ASN1 attrValue = attrValues [j];
1535                                                                                 byte[] value = (byte[])dattrValues [j];
1536                                                                         
1537                                                                                 if (Compare (value, attrValue.Value)) {
1538                                                                                         attrValuesFound += 1;
1539                                                                                 }
1540                                                                         }
1541                                                                         if (attrValuesFound == attrValues.Count) {
1542                                                                                 bagAttributesFound += 1;
1543                                                                         }
1544                                                                 }
1545                                                         }
1546                                                 }
1547                                                 if (bagAttributesFound == bagAttributes.Count) {
1548                                                         ASN1 bagValue = safeBag [1];
1549                                                         PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1550                                                         return new X509Certificate (crt.Content [0].Value);
1551                                                 }
1552                                         }
1553                                 }
1554                         }
1555
1556                         return null;
1557                 }
1558
1559                 public IDictionary GetAttributes (AsymmetricAlgorithm aa)
1560                 {
1561                         IDictionary result = new Hashtable ();
1562
1563                         foreach (SafeBag sb in _safeBags) {
1564                                 if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1565                                         ASN1 safeBag = sb.ASN1;
1566
1567                                         ASN1 bagValue = safeBag [1];
1568                                         AsymmetricAlgorithm saa = null;
1569                                         if (sb.BagOID.Equals (keyBag)) {
1570                                                 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1571                                                 byte[] privateKey = pki.PrivateKey;
1572                                                 switch (privateKey [0]) {
1573                                                 case 0x02:
1574                                                         DSAParameters p = new DSAParameters (); // FIXME
1575                                                         saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1576                                                         break;
1577                                                 case 0x30:
1578                                                         saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1579                                                         break;
1580                                                 default:
1581                                                         break;
1582                                                 }
1583                                                 Array.Clear (privateKey, 0, privateKey.Length);
1584                                         } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1585                                                 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1586                                                 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1587                                                 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1588                                                 byte[] privateKey = pki.PrivateKey;
1589                                                 switch (privateKey [0]) {
1590                                                 case 0x02:
1591                                                         DSAParameters p = new DSAParameters (); // FIXME
1592                                                         saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1593                                                         break;
1594                                                 case 0x30:
1595                                                         saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1596                                                         break;
1597                                                 default:
1598                                                         break;
1599                                                 }
1600                                                 Array.Clear (privateKey, 0, privateKey.Length);
1601                                                 Array.Clear (decrypted, 0, decrypted.Length);
1602                                         }
1603
1604                                         if (saa != null && CompareAsymmetricAlgorithm (saa, aa)) {
1605                                                 if (safeBag.Count == 3) {
1606                                                         ASN1 bagAttributes = safeBag [2];
1607                                                         
1608                                                         for (int i = 0; i < bagAttributes.Count; i++) {
1609                                                                 ASN1 pkcs12Attribute = bagAttributes [i];
1610                                                                 ASN1 attrId = pkcs12Attribute [0];
1611                                                                 string aOid = ASN1Convert.ToOid (attrId);
1612                                                                 ArrayList aValues = new ArrayList ();
1613
1614                                                                 ASN1 attrValues = pkcs12Attribute [1];
1615                                                                         
1616                                                                 for (int j = 0; j < attrValues.Count; j++) {
1617                                                                         ASN1 attrValue = attrValues [j];
1618                                                                         aValues.Add (attrValue.Value);
1619                                                                 }
1620                                                                 result.Add (aOid, aValues);
1621                                                         }
1622                                                 }
1623                                         }
1624                                 }
1625                         }
1626
1627                         return result;
1628                 }
1629
1630                 public IDictionary GetAttributes (X509Certificate cert)
1631                 {
1632                         IDictionary result = new Hashtable ();
1633
1634                         foreach (SafeBag sb in _safeBags) {
1635                                 if (sb.BagOID.Equals (certBag)) {
1636                                         ASN1 safeBag = sb.ASN1;
1637                                         ASN1 bagValue = safeBag [1];
1638                                         PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1639                                         X509Certificate xc = new X509Certificate (crt.Content [0].Value);
1640
1641                                         if (Compare (cert.RawData, xc.RawData)) {
1642                                                 if (safeBag.Count == 3) {
1643                                                         ASN1 bagAttributes = safeBag [2];
1644
1645                                                         for (int i = 0; i < bagAttributes.Count; i++) {
1646                                                                 ASN1 pkcs12Attribute = bagAttributes [i];
1647                                                                 ASN1 attrId = pkcs12Attribute [0];
1648
1649                                                                 string aOid = ASN1Convert.ToOid (attrId);
1650                                                                 ArrayList aValues = new ArrayList ();
1651
1652                                                                 ASN1 attrValues = pkcs12Attribute [1];
1653                                                                         
1654                                                                 for (int j = 0; j < attrValues.Count; j++) {
1655                                                                         ASN1 attrValue = attrValues [j];
1656                                                                         aValues.Add (attrValue.Value);
1657                                                                 }
1658                                                                 result.Add (aOid, aValues);
1659                                                         }
1660                                                 }
1661                                         }
1662                                 }
1663                         }
1664
1665                         return result;
1666                 }
1667
1668                 public void SaveToFile (string filename)
1669                 {
1670                         if (filename == null)
1671                                 throw new ArgumentNullException ("filename");
1672
1673                         using (FileStream fs = File.OpenWrite (filename)) {
1674                                 byte[] data = GetBytes ();
1675                                 fs.Write (data, 0, data.Length);
1676                                 fs.Flush ();
1677                                 fs.Close ();
1678                         }
1679                 }
1680
1681                 public object Clone ()
1682                 {
1683                         PKCS12 clone = null;
1684                         if (_password != null) {
1685                                 clone = new PKCS12 (GetBytes (), Encoding.BigEndianUnicode.GetString (_password));
1686                         } else {
1687                                 clone = new PKCS12 (GetBytes ());
1688                         }
1689                         clone.IterationCount = this.IterationCount;
1690
1691                         return clone;
1692                 }
1693
1694                 // static methods
1695
1696                 static private byte[] LoadFile (string filename) 
1697                 {
1698                         byte[] data = null;
1699                         using (FileStream fs = File.OpenRead (filename)) {
1700                                 data = new byte [fs.Length];
1701                                 fs.Read (data, 0, data.Length);
1702                                 fs.Close ();
1703                         }
1704                         return data;
1705                 }
1706
1707                 static public PKCS12 LoadFromFile (string filename) 
1708                 {
1709                         if (filename == null)
1710                                 throw new ArgumentNullException ("filename");
1711
1712                         return new PKCS12 (LoadFile (filename));
1713                 }
1714
1715                 static public PKCS12 LoadFromFile (string filename, string password) 
1716                 {
1717                         if (filename == null)
1718                                 throw new ArgumentNullException ("filename");
1719
1720                         return new PKCS12 (LoadFile (filename), password);
1721                 }
1722         }
1723 }