2005-04-26 Sebastien Pouliot <sebastien@ximian.com>
[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)
295                         : this ()
296                 {
297                         Password = null;
298                         Decode (data);
299                 }
300
301                 /*
302                  * PFX ::= SEQUENCE {
303                  *      version INTEGER {v3(3)}(v3,...),
304                  *      authSafe ContentInfo,
305                  *      macData MacData OPTIONAL
306                  * }
307                  * 
308                  * MacData ::= SEQUENCE {
309                  *      mac DigestInfo,
310                  *      macSalt OCTET STRING,
311                  *      iterations INTEGER DEFAULT 1
312                  *      -- Note: The default is for historical reasons and its use is deprecated. A higher
313                  *      -- value, like 1024 is recommended.
314                  * }
315                  * 
316                  * SafeContents ::= SEQUENCE OF SafeBag
317                  * 
318                  * SafeBag ::= SEQUENCE {
319                  *      bagId BAG-TYPE.&id ({PKCS12BagSet}),
320                  *      bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
321                  *      bagAttributes SET OF PKCS12Attribute OPTIONAL
322                  * }
323                  */
324                 public PKCS12 (byte[] data, string password)
325                         : this ()
326                 {
327                         Password = password;
328                         Decode (data);
329                 }
330 #if NET_2_0
331                 public PKCS12 (byte[] data, byte[] password)
332                         : this ()
333                 {
334                         _password = password;
335                         Decode (data);
336                 }
337 #endif
338                 private void Decode (byte[] data)
339                 {
340                         ASN1 pfx = new ASN1 (data);
341                         if (pfx.Tag != 0x30)
342                                 throw new ArgumentException ("invalid data");
343                         
344                         ASN1 version = pfx [0];
345                         if (version.Tag != 0x02)
346                                 throw new ArgumentException ("invalid PFX version");
347                         //_version = version.Value [0];
348
349                         PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (pfx [1]);
350                         if (authSafe.ContentType != PKCS7.Oid.data)
351                                 throw new ArgumentException ("invalid authenticated safe");
352
353                         // now that we know it's a PKCS#12 file, check the (optional) MAC
354                         // before decoding anything else in the file
355                         if (pfx.Count > 2) {
356                                 ASN1 macData = pfx [2];
357                                 if (macData.Tag != 0x30)
358                                         throw new ArgumentException ("invalid MAC");
359                                 
360                                 ASN1 mac = macData [0];
361                                 if (mac.Tag != 0x30)
362                                         throw new ArgumentException ("invalid MAC");
363                                 ASN1 macAlgorithm = mac [0];
364                                 string macOid = ASN1Convert.ToOid (macAlgorithm [0]);
365                                 if (macOid != "1.3.14.3.2.26")
366                                         throw new ArgumentException ("unsupported HMAC");
367                                 byte[] macValue = mac [1].Value;
368
369                                 ASN1 macSalt = macData [1];
370                                 if (macSalt.Tag != 0x04)
371                                         throw new ArgumentException ("missing MAC salt");
372
373                                 _iterations = 1; // default value
374                                 if (macData.Count > 2) {
375                                         ASN1 iters = macData [2];
376                                         if (iters.Tag != 0x02)
377                                                 throw new ArgumentException ("invalid MAC iteration");
378                                         _iterations = ASN1Convert.ToInt32 (iters);
379                                 }
380
381                                 byte[] authSafeData = authSafe.Content [0].Value;
382                                 byte[] calculatedMac = MAC (_password, macSalt.Value, _iterations, authSafeData);
383                                 if (!Compare (macValue, calculatedMac))
384                                         throw new CryptographicException ("Invalid MAC - file may have been tampered!");
385                         }
386
387                         // we now returns to our original presentation - PFX
388                         ASN1 authenticatedSafe = new ASN1 (authSafe.Content [0].Value);
389                         for (int i=0; i < authenticatedSafe.Count; i++) {
390                                 PKCS7.ContentInfo ci = new PKCS7.ContentInfo (authenticatedSafe [i]);
391                                 switch (ci.ContentType) {
392                                         case PKCS7.Oid.data:
393                                                 // unencrypted (by PKCS#12)
394                                                 ASN1 safeContents = new ASN1 (ci.Content [0].Value);
395                                                 for (int j=0; j < safeContents.Count; j++) {
396                                                         ASN1 safeBag = safeContents [j];
397                                                         ReadSafeBag (safeBag);
398                                                 }
399                                                 break;
400                                         case PKCS7.Oid.encryptedData:
401                                                 // password encrypted
402                                                 PKCS7.EncryptedData ed = new PKCS7.EncryptedData (ci.Content [0]);
403                                                 ASN1 decrypted = new ASN1 (Decrypt (ed));
404                                                 for (int j=0; j < decrypted.Count; j++) {
405                                                         ASN1 safeBag = decrypted [j];
406                                                         ReadSafeBag (safeBag);
407                                                 }
408                                                 break;
409                                         case PKCS7.Oid.envelopedData:
410                                                 // public key encrypted
411                                                 throw new NotImplementedException ("public key encrypted");
412                                         default:
413                                                 throw new ArgumentException ("unknown authenticatedSafe");
414                                 }
415                         }
416                 }
417
418                 ~PKCS12 () 
419                 {
420                         if (_password != null) {
421                                 Array.Clear (_password, 0, _password.Length);
422                         }
423                         _password = null;
424                 }
425
426                 // properties
427
428                 public string Password {
429                         set {
430                                 if (value != null) {
431                                         if (value.EndsWith ("\0"))
432                                                 _password = Encoding.BigEndianUnicode.GetBytes (value); 
433                                         else
434                                                 _password = Encoding.BigEndianUnicode.GetBytes (value + "\0");                                  
435                                 }
436                                 else
437                                         _password = null;       // no password
438                         }
439                 }
440
441                 public int IterationCount {
442                         get { return _iterations; }
443                         set { _iterations = value; }
444                 }
445
446                 public ArrayList Keys {
447                         get {
448                                 if (_keyBagsChanged) {
449                                         _keyBags.Clear ();
450                                         foreach (SafeBag sb in _safeBags) {
451                                                 if (sb.BagOID.Equals (keyBag)) {
452                                                         ASN1 safeBag = sb.ASN1;
453                                                         ASN1 bagValue = safeBag [1];
454                                                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
455                                                         byte[] privateKey = pki.PrivateKey;
456                                                         switch (privateKey [0]) {
457                                                         case 0x02:
458                                                                 DSAParameters p = new DSAParameters (); // FIXME
459                                                                 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
460                                                                 break;
461                                                         case 0x30:
462                                                                 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
463                                                                 break;
464                                                         default:
465                                                                 break;
466                                                         }
467                                                         Array.Clear (privateKey, 0, privateKey.Length);
468
469                                                 } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
470                                                         ASN1 safeBag = sb.ASN1;
471                                                         ASN1 bagValue = safeBag [1];
472                                                         PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
473                                                         byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
474                                                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
475                                                         byte[] privateKey = pki.PrivateKey;
476                                                         switch (privateKey [0]) {
477                                                         case 0x02:
478                                                                 DSAParameters p = new DSAParameters (); // FIXME
479                                                                 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
480                                                                 break;
481                                                         case 0x30:
482                                                                 _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
483                                                                 break;
484                                                         default:
485                                                                 break;
486                                                         }
487                                                         Array.Clear (privateKey, 0, privateKey.Length);
488                                                         Array.Clear (decrypted, 0, decrypted.Length);
489                                                 }
490                                         }
491                                         _keyBagsChanged = false;
492                                 }
493                                 return ArrayList.ReadOnly(_keyBags);
494                         }
495                 }
496
497                 public X509CertificateCollection Certificates {
498                         get {
499                                 if (_certsChanged) {
500                                         _certs.Clear ();
501                                         foreach (SafeBag sb in _safeBags) {
502                                                 if (sb.BagOID.Equals (certBag)) {
503                                                         ASN1 safeBag = sb.ASN1;
504                                                         ASN1 bagValue = safeBag [1];
505                                                         PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
506                                                         _certs.Add (new X509Certificate (cert.Content [0].Value));
507                                                 }
508                                         }
509                                         _certsChanged = false;
510                                 }
511                                 return _certs;
512                         }
513                 }
514
515                 internal RandomNumberGenerator RNG {
516                         get {
517                                 if (_rng == null)
518                                         _rng = RandomNumberGenerator.Create ();
519                                 return _rng;
520                         }
521                 }
522
523                 // private methods
524
525                 private bool Compare (byte[] expected, byte[] actual) 
526                 {
527                         bool compare = false;
528                         if (expected.Length == actual.Length) {
529                                 for (int i=0; i < expected.Length; i++) {
530                                         if (expected [i] != actual [i])
531                                                 return false;
532                                 }
533                                 compare = true;
534                         }
535                         return compare;
536                 }
537
538                 private SymmetricAlgorithm GetSymmetricAlgorithm (string algorithmOid, byte[] salt, int iterationCount)
539                 {
540                         string algorithm = null;
541                         int keyLength = 8;      // 64 bits (default)
542                         int ivLength = 8;       // 64 bits (default)
543
544                         PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
545                         pd.Password = _password; 
546                         pd.Salt = salt;
547                         pd.IterationCount = iterationCount;
548
549                         switch (algorithmOid) {
550                                 case PKCS5.pbeWithMD2AndDESCBC:                 // no unit test available
551                                         pd.HashName = "MD2";
552                                         algorithm = "DES";
553                                         break;
554                                 case PKCS5.pbeWithMD5AndDESCBC:                 // no unit test available
555                                         pd.HashName = "MD5";
556                                         algorithm = "DES";
557                                         break;
558                                 case PKCS5.pbeWithMD2AndRC2CBC:                 // no unit test available
559                                         // TODO - RC2-CBC-Parameter (PKCS5)
560                                         // if missing default to 32 bits !!!
561                                         pd.HashName = "MD2";
562                                         algorithm = "RC2";
563                                         keyLength = 4;          // default
564                                         break;
565                                 case PKCS5.pbeWithMD5AndRC2CBC:                 // no unit test available
566                                         // TODO - RC2-CBC-Parameter (PKCS5)
567                                         // if missing default to 32 bits !!!
568                                         pd.HashName = "MD5";
569                                         algorithm = "RC2";
570                                         keyLength = 4;          // default
571                                         break;
572                                 case PKCS5.pbeWithSHA1AndDESCBC:                // no unit test available
573                                         pd.HashName = "SHA1";
574                                         algorithm = "DES";
575                                         break;
576                                 case PKCS5.pbeWithSHA1AndRC2CBC:                // no unit test available
577                                         // TODO - RC2-CBC-Parameter (PKCS5)
578                                         // if missing default to 32 bits !!!
579                                         pd.HashName = "SHA1";
580                                         algorithm = "RC2";
581                                         keyLength = 4;          // default
582                                         break;
583                                 case PKCS12.pbeWithSHAAnd128BitRC4:             // no unit test available
584                                         pd.HashName = "SHA1";
585                                         algorithm = "RC4";
586                                         keyLength = 16;
587                                         ivLength = 0;           // N/A
588                                         break;
589                                 case PKCS12.pbeWithSHAAnd40BitRC4:              // no unit test available
590                                         pd.HashName = "SHA1";
591                                         algorithm = "RC4";
592                                         keyLength = 5;
593                                         ivLength = 0;           // N/A
594                                         break;
595                                 case PKCS12.pbeWithSHAAnd3KeyTripleDESCBC: 
596                                         pd.HashName = "SHA1";
597                                         algorithm = "TripleDES";
598                                         keyLength = 24;
599                                         break;
600                                 case PKCS12.pbeWithSHAAnd2KeyTripleDESCBC:      // no unit test available
601                                         pd.HashName = "SHA1";
602                                         algorithm = "TripleDES";
603                                         keyLength = 16;
604                                         break;
605                                 case PKCS12.pbeWithSHAAnd128BitRC2CBC:          // no unit test available
606                                         pd.HashName = "SHA1";
607                                         algorithm = "RC2";
608                                         keyLength = 16;
609                                         break;
610                                 case PKCS12.pbeWithSHAAnd40BitRC2CBC: 
611                                         pd.HashName = "SHA1";
612                                         algorithm = "RC2";
613                                         keyLength = 5;
614                                         break;
615                                 default:
616                                         throw new NotSupportedException ("unknown oid " + algorithm);
617                         }
618
619                         SymmetricAlgorithm sa = SymmetricAlgorithm.Create (algorithm);
620                         sa.Key = pd.DeriveKey (keyLength);
621                         // IV required only for block ciphers (not stream ciphers)
622                         if (ivLength > 0) {
623                                 sa.IV = pd.DeriveIV (ivLength);
624                                 sa.Mode = CipherMode.CBC;
625                         }
626                         return sa;
627                 }
628
629                 public byte[] Decrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] encryptedData) 
630                 {
631                         SymmetricAlgorithm sa = null;
632                         byte[] result = null;
633                         try {
634                                 sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount);
635                                 ICryptoTransform ct = sa.CreateDecryptor ();
636                                 result = ct.TransformFinalBlock (encryptedData, 0, encryptedData.Length);
637                         }
638                         finally {
639                                 if (sa != null)
640                                         sa.Clear ();
641                         }
642                         return result;
643                 }
644
645                 public byte[] Decrypt (PKCS7.EncryptedData ed)
646                 {
647                         return Decrypt (ed.EncryptionAlgorithm.ContentType, 
648                                 ed.EncryptionAlgorithm.Content [0].Value, 
649                                 ASN1Convert.ToInt32 (ed.EncryptionAlgorithm.Content [1]),
650                                 ed.EncryptedContent);
651                 }
652
653                 public byte[] Encrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] data) 
654                 {
655                         byte[] result = null;
656                         using (SymmetricAlgorithm sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount)) {
657                                 ICryptoTransform ct = sa.CreateEncryptor ();
658                                 result = ct.TransformFinalBlock (data, 0, data.Length);
659                         }
660                         return result;
661                 }
662
663                 private void AddPrivateKey (PKCS8.PrivateKeyInfo pki) 
664                 {
665                         byte[] privateKey = pki.PrivateKey;
666                         switch (privateKey [0]) {
667                                 case 0x02:
668                                         DSAParameters p = new DSAParameters (); // FIXME
669                                         _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
670                                         break;
671                                 case 0x30:
672                                         _keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
673                                         break;
674                                 default:
675                                         Array.Clear (privateKey, 0, privateKey.Length);
676                                         throw new CryptographicException ("Unknown private key format");
677                         }
678                         Array.Clear (privateKey, 0, privateKey.Length);
679                 }
680
681                 private void ReadSafeBag (ASN1 safeBag) 
682                 {
683                         if (safeBag.Tag != 0x30)
684                                 throw new ArgumentException ("invalid safeBag");
685
686                         ASN1 bagId = safeBag [0];
687                         if (bagId.Tag != 0x06)
688                                 throw new ArgumentException ("invalid safeBag id");
689
690                         ASN1 bagValue = safeBag [1];
691                         string oid = ASN1Convert.ToOid (bagId);
692                         switch (oid) {
693                                 case keyBag:
694                                         // NEED UNIT TEST
695                                         AddPrivateKey (new PKCS8.PrivateKeyInfo (bagValue.Value));
696                                         break;
697                                 case pkcs8ShroudedKeyBag:
698                                         PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
699                                         byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
700                                         AddPrivateKey (new PKCS8.PrivateKeyInfo (decrypted));
701                                         Array.Clear (decrypted, 0, decrypted.Length);
702                                         break;
703                                 case certBag:
704                                         PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
705                                         if (cert.ContentType != x509Certificate)
706                                                 throw new NotSupportedException ("unsupport certificate type");
707                                         X509Certificate x509 = new X509Certificate (cert.Content [0].Value);
708                                         _certs.Add (x509);
709                                         break;
710                                 case crlBag:
711                                         // TODO
712                                         break;
713                                 case secretBag: 
714                                         // TODO
715                                         break;
716                                 case safeContentsBag:
717                                         // TODO - ? recurse ?
718                                         break;
719                                 default:
720                                         throw new ArgumentException ("unknown safeBag oid");
721                         }
722
723                         if (safeBag.Count > 2) {
724                                 ASN1 bagAttributes = safeBag [2];
725                                 if (bagAttributes.Tag != 0x31)
726                                         throw new ArgumentException ("invalid safeBag attributes id");
727
728                                 for (int i = 0; i < bagAttributes.Count; i++) {
729                                         ASN1 pkcs12Attribute = bagAttributes[i];
730                                                 
731                                         if (pkcs12Attribute.Tag != 0x30)
732                                                 throw new ArgumentException ("invalid PKCS12 attributes id");
733
734                                         ASN1 attrId = pkcs12Attribute [0];
735                                         if (attrId.Tag != 0x06)
736                                                 throw new ArgumentException ("invalid attribute id");
737                                                 
738                                         string attrOid = ASN1Convert.ToOid (attrId);
739
740                                         ASN1 attrValues = pkcs12Attribute[1];
741                                         for (int j = 0; j < attrValues.Count; j++) {
742                                                 ASN1 attrValue = attrValues[j];
743
744                                                 switch (attrOid) {
745                                                 case PKCS9.friendlyName:
746                                                         if (attrValue.Tag != 0x1e)
747                                                                 throw new ArgumentException ("invalid attribute value id");
748                                                         break;
749                                                 case PKCS9.localKeyId:
750                                                         if (attrValue.Tag != 0x04)
751                                                                 throw new ArgumentException ("invalid attribute value id");
752                                                         break;
753                                                 default:
754                                                         // Unknown OID -- don't check Tag
755                                                         break;
756                                                 }
757                                         }
758                                 }
759                         }
760
761                         _safeBags.Add (new SafeBag(oid, safeBag));
762                 }
763
764                 private ASN1 Pkcs8ShroudedKeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) 
765                 {
766                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
767                         if (aa is RSA) {
768                                 pki.Algorithm = "1.2.840.113549.1.1.1";
769                                 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
770                         }
771                         else if (aa is DSA) {
772                                 pki.Algorithm = null;
773                                 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
774                         }
775                         else
776                                 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
777
778                         PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo ();
779                         epki.Algorithm = pbeWithSHAAnd3KeyTripleDESCBC;
780                         epki.IterationCount = _iterations;
781                         epki.EncryptedData = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, epki.Salt, _iterations, pki.GetBytes ());
782
783                         ASN1 safeBag = new ASN1 (0x30);
784                         safeBag.Add (ASN1Convert.FromOid (pkcs8ShroudedKeyBag));
785                         ASN1 bagValue = new ASN1 (0xA0);
786                         bagValue.Add (new ASN1 (epki.GetBytes ()));
787                         safeBag.Add (bagValue);
788
789                         if (attributes != null) {
790                                 ASN1 bagAttributes = new ASN1 (0x31);
791                                 IDictionaryEnumerator de = attributes.GetEnumerator ();
792
793                                 while (de.MoveNext ()) {
794                                         string oid = (string)de.Key;
795                                         switch (oid) {
796                                         case PKCS9.friendlyName:
797                                                 ArrayList names = (ArrayList)de.Value;
798                                                 if (names.Count > 0) {
799                                                         ASN1 pkcs12Attribute = new ASN1 (0x30);
800                                                         pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
801                                                         ASN1 attrValues = new ASN1 (0x31);
802                                                         foreach (byte[] name in names) {
803                                                                 ASN1 attrValue = new ASN1 (0x1e);
804                                                                 attrValue.Value = name;
805                                                                 attrValues.Add (attrValue);
806                                                         }
807                                                         pkcs12Attribute.Add (attrValues);
808                                                         bagAttributes.Add (pkcs12Attribute);
809                                                 }
810                                                 break;
811                                         case PKCS9.localKeyId:
812                                                 ArrayList keys = (ArrayList)de.Value;
813                                                 if (keys.Count > 0) {
814                                                         ASN1 pkcs12Attribute = new ASN1 (0x30);
815                                                         pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
816                                                         ASN1 attrValues = new ASN1 (0x31);
817                                                         foreach (byte[] key in keys) {
818                                                                 ASN1 attrValue = new ASN1 (0x04);
819                                                                 attrValue.Value = key;
820                                                                 attrValues.Add (attrValue);
821                                                         }
822                                                         pkcs12Attribute.Add (attrValues);
823                                                         bagAttributes.Add (pkcs12Attribute);
824                                                 }
825                                                 break;
826                                         default:
827                                                 break;
828                                         }
829                                 }
830
831                                 if (bagAttributes.Count > 0) {
832                                         safeBag.Add (bagAttributes);
833                                 }
834                         }
835
836                         return safeBag;
837                 }
838
839                 private ASN1 KeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) 
840                 {
841                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
842                         if (aa is RSA) {
843                                 pki.Algorithm = "1.2.840.113549.1.1.1";
844                                 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
845                         }
846                         else if (aa is DSA) {
847                                 pki.Algorithm = null;
848                                 pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
849                         }
850                         else
851                                 throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
852
853                         ASN1 safeBag = new ASN1 (0x30);
854                         safeBag.Add (ASN1Convert.FromOid (keyBag));
855                         ASN1 bagValue = new ASN1 (0xA0);
856                         bagValue.Add (new ASN1 (pki.GetBytes ()));
857                         safeBag.Add (bagValue);
858
859                         if (attributes != null) {
860                                 ASN1 bagAttributes = new ASN1 (0x31);
861                                 IDictionaryEnumerator de = attributes.GetEnumerator ();
862
863                                 while (de.MoveNext ()) {
864                                         string oid = (string)de.Key;
865                                         switch (oid) {
866                                         case PKCS9.friendlyName:
867                                                 ArrayList names = (ArrayList)de.Value;
868                                                 if (names.Count > 0) {
869                                                         ASN1 pkcs12Attribute = new ASN1 (0x30);
870                                                         pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
871                                                         ASN1 attrValues = new ASN1 (0x31);
872                                                         foreach (byte[] name in names) {
873                                                                 ASN1 attrValue = new ASN1 (0x1e);
874                                                                 attrValue.Value = name;
875                                                                 attrValues.Add (attrValue);
876                                                         }
877                                                         pkcs12Attribute.Add (attrValues);
878                                                         bagAttributes.Add (pkcs12Attribute);
879                                                 }
880                                                 break;
881                                         case PKCS9.localKeyId:
882                                                 ArrayList keys = (ArrayList)de.Value;
883                                                 if (keys.Count > 0) {
884                                                         ASN1 pkcs12Attribute = new ASN1 (0x30);
885                                                         pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
886                                                         ASN1 attrValues = new ASN1 (0x31);
887                                                         foreach (byte[] key in keys) {
888                                                                 ASN1 attrValue = new ASN1 (0x04);
889                                                                 attrValue.Value = key;
890                                                                 attrValues.Add (attrValue);
891                                                         }
892                                                         pkcs12Attribute.Add (attrValues);
893                                                         bagAttributes.Add (pkcs12Attribute);
894                                                 }
895                                                 break;
896                                         default:
897                                                 break;
898                                         }
899                                 }
900
901                                 if (bagAttributes.Count > 0) {
902                                         safeBag.Add (bagAttributes);
903                                 }
904                         }
905
906                         return safeBag;
907                 }
908
909                 private ASN1 CertificateSafeBag (X509Certificate x509, IDictionary attributes) 
910                 {
911                         ASN1 encapsulatedCertificate = new ASN1 (0x04, x509.RawData);
912
913                         PKCS7.ContentInfo ci = new PKCS7.ContentInfo ();
914                         ci.ContentType = x509Certificate;
915                         ci.Content.Add (encapsulatedCertificate);
916
917                         ASN1 bagValue = new ASN1 (0xA0);
918                         bagValue.Add (ci.ASN1);
919
920                         ASN1 safeBag = new ASN1 (0x30);
921                         safeBag.Add (ASN1Convert.FromOid (certBag));
922                         safeBag.Add (bagValue);
923
924                         if (attributes != null) {
925                                 ASN1 bagAttributes = new ASN1 (0x31);
926                                 IDictionaryEnumerator de = attributes.GetEnumerator ();
927
928                                 while (de.MoveNext ()) {
929                                         string oid = (string)de.Key;
930                                         switch (oid) {
931                                         case PKCS9.friendlyName:
932                                                 ArrayList names = (ArrayList)de.Value;
933                                                 if (names.Count > 0) {
934                                                         ASN1 pkcs12Attribute = new ASN1 (0x30);
935                                                         pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
936                                                         ASN1 attrValues = new ASN1 (0x31);
937                                                         foreach (byte[] name in names) {
938                                                                 ASN1 attrValue = new ASN1 (0x1e);
939                                                                 attrValue.Value = name;
940                                                                 attrValues.Add (attrValue);
941                                                         }
942                                                         pkcs12Attribute.Add (attrValues);
943                                                         bagAttributes.Add (pkcs12Attribute);
944                                                 }
945                                                 break;
946                                         case PKCS9.localKeyId:
947                                                 ArrayList keys = (ArrayList)de.Value;
948                                                 if (keys.Count > 0) {
949                                                         ASN1 pkcs12Attribute = new ASN1 (0x30);
950                                                         pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
951                                                         ASN1 attrValues = new ASN1 (0x31);
952                                                         foreach (byte[] key in keys) {
953                                                                 ASN1 attrValue = new ASN1 (0x04);
954                                                                 attrValue.Value = key;
955                                                                 attrValues.Add (attrValue);
956                                                         }
957                                                         pkcs12Attribute.Add (attrValues);
958                                                         bagAttributes.Add (pkcs12Attribute);
959                                                 }
960                                                 break;
961                                         default:
962                                                 break;
963                                         }
964                                 }
965
966                                 if (bagAttributes.Count > 0) {
967                                         safeBag.Add (bagAttributes);
968                                 }
969                         }
970
971                         return safeBag;
972                 }
973
974                 private byte[] MAC (byte[] password, byte[] salt, int iterations, byte[] data) 
975                 {
976                         PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
977                         pd.HashName = "SHA1";
978                         pd.Password = password;
979                         pd.Salt = salt;
980                         pd.IterationCount = iterations;
981
982                         HMACSHA1 hmac = (HMACSHA1) HMACSHA1.Create ();
983                         hmac.Key = pd.DeriveMAC (20);
984                         return hmac.ComputeHash (data, 0, data.Length);
985                 }
986
987                 /*
988                  * SafeContents ::= SEQUENCE OF SafeBag
989                  * 
990                  * SafeBag ::= SEQUENCE {
991                  *      bagId BAG-TYPE.&id ({PKCS12BagSet}),
992                  *      bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
993                  *      bagAttributes SET OF PKCS12Attribute OPTIONAL
994                  * }
995                  */
996                 public byte[] GetBytes () 
997                 {
998                         // TODO (incomplete)
999                         ASN1 safeBagSequence = new ASN1 (0x30);
1000
1001                         // Sync Safe Bag list since X509CertificateCollection may be updated
1002                         ArrayList scs = new ArrayList ();
1003                         foreach (SafeBag sb in _safeBags) {
1004                                 if (sb.BagOID.Equals (certBag)) {
1005                                         ASN1 safeBag = sb.ASN1;
1006                                         ASN1 bagValue = safeBag [1];
1007                                         PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
1008                                         scs.Add (new X509Certificate (cert.Content [0].Value));
1009                                 }
1010                         }
1011
1012                         ArrayList addcerts = new ArrayList ();
1013                         ArrayList removecerts = new ArrayList ();
1014
1015                         foreach (X509Certificate c in Certificates) {
1016                                 bool found = false;
1017
1018                                 foreach (X509Certificate lc in scs) {
1019                                         if (Compare (c.RawData, lc.RawData)) {
1020                                                 found = true;
1021                                         }
1022                                 }
1023
1024                                 if (!found) {
1025                                         addcerts.Add (c);
1026                                 }
1027                         }
1028                         foreach (X509Certificate c in scs) {
1029                                 bool found = false;
1030
1031                                 foreach (X509Certificate lc in Certificates) {
1032                                         if (Compare (c.RawData, lc.RawData)) {
1033                                                 found = true;
1034                                         }
1035                                 }
1036
1037                                 if (!found) {
1038                                         removecerts.Add (c);
1039                                 }
1040                         }
1041
1042                         foreach (X509Certificate c in removecerts) {
1043                                 RemoveCertificate (c);
1044                         }
1045
1046                         foreach (X509Certificate c in addcerts) {
1047                                 AddCertificate (c);
1048                         }
1049                         // Sync done
1050
1051                         if (_safeBags.Count > 0) {
1052                                 ASN1 certsSafeBag = new ASN1 (0x30);
1053                                 foreach (SafeBag sb in _safeBags) {
1054                                         if (sb.BagOID.Equals (certBag)) {
1055                                                 certsSafeBag.Add (sb.ASN1);
1056                                         }
1057                                 }
1058
1059                                 if (certsSafeBag.Count > 0) {
1060                                         byte[] certsSalt = new byte [8];
1061                                         RNG.GetBytes (certsSalt);
1062
1063                                         ASN1 seqParams = new ASN1 (0x30);
1064                                         seqParams.Add (new ASN1 (0x04, certsSalt));
1065                                         seqParams.Add (ASN1Convert.FromInt32 (_iterations));
1066
1067                                         ASN1 seqPbe = new ASN1 (0x30);
1068                                         seqPbe.Add (ASN1Convert.FromOid (pbeWithSHAAnd3KeyTripleDESCBC));
1069                                         seqPbe.Add (seqParams);
1070
1071                                         byte[] encrypted = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, certsSalt, _iterations, certsSafeBag.GetBytes ());
1072                                         ASN1 encryptedCerts = new ASN1 (0x80, encrypted);
1073
1074                                         ASN1 seq = new ASN1 (0x30);
1075                                         seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data));
1076                                         seq.Add (seqPbe);
1077                                         seq.Add (encryptedCerts);
1078
1079                                         ASN1 certsVersion = new ASN1 (0x02, new byte [1] { 0x00 });
1080                                         ASN1 encData = new ASN1 (0x30);
1081                                         encData.Add (certsVersion);
1082                                         encData.Add (seq);
1083
1084                                         ASN1 certsContent = new ASN1 (0xA0);
1085                                         certsContent.Add (encData);
1086
1087                                         PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData);
1088                                         bag.Content = certsContent;
1089                                         safeBagSequence.Add (bag.ASN1);
1090                                 }
1091                         }
1092
1093                         if (_safeBags.Count > 0) {
1094                                 ASN1 safeContents = new ASN1 (0x30);
1095                                 foreach (SafeBag sb in _safeBags) {
1096                                         if (sb.BagOID.Equals (keyBag) ||
1097                                             sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1098                                                 safeContents.Add (sb.ASN1);
1099                                         }
1100                                 }
1101                                 if (safeContents.Count > 0) {
1102                                         ASN1 content = new ASN1 (0xA0);
1103                                         content.Add (new ASN1 (0x04, safeContents.GetBytes ()));
1104                                 
1105                                         PKCS7.ContentInfo keyBag = new PKCS7.ContentInfo (PKCS7.Oid.data);
1106                                         keyBag.Content = content;
1107                                         safeBagSequence.Add (keyBag.ASN1);
1108                                 }
1109                         }
1110
1111
1112                         ASN1 encapsulates = new ASN1 (0x04, safeBagSequence.GetBytes ());
1113                         ASN1 ci = new ASN1 (0xA0);
1114                         ci.Add (encapsulates);
1115                         PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (PKCS7.Oid.data);
1116                         authSafe.Content = ci;
1117                         
1118                         ASN1 macData = new ASN1 (0x30);
1119                         if (_password != null) {
1120                                 // only for password based encryption
1121                                 byte[] salt = new byte [20];
1122                                 RNG.GetBytes (salt);
1123                                 byte[] macValue = MAC (_password, salt, _iterations, authSafe.Content [0].Value);
1124                                 ASN1 oidSeq = new ASN1 (0x30);
1125                                 oidSeq.Add (ASN1Convert.FromOid ("1.3.14.3.2.26"));     // SHA1
1126                                 oidSeq.Add (new ASN1 (0x05));
1127
1128                                 ASN1 mac = new ASN1 (0x30);
1129                                 mac.Add (oidSeq);
1130                                 mac.Add (new ASN1 (0x04, macValue));
1131
1132                                 macData.Add (mac);
1133                                 macData.Add (new ASN1 (0x04, salt));
1134                                 macData.Add (ASN1Convert.FromInt32 (_iterations));
1135                         }
1136                         
1137                         ASN1 version = new ASN1 (0x02, new byte [1] { 0x03 });
1138                         
1139                         ASN1 pfx = new ASN1 (0x30);
1140                         pfx.Add (version);
1141                         pfx.Add (authSafe.ASN1);
1142                         if (macData.Count > 0) {
1143                                 // only for password based encryption
1144                                 pfx.Add (macData);
1145                         }
1146
1147                         return pfx.GetBytes ();
1148                 }
1149
1150                 public void AddCertificate (X509Certificate cert)
1151                 {
1152                         AddCertificate (cert, null);
1153                 }
1154
1155                 public void AddCertificate (X509Certificate cert, IDictionary attributes)
1156                 {
1157                         bool found = false;
1158
1159                         for (int i = 0; !found && i < _safeBags.Count; i++) {
1160                                 SafeBag sb = (SafeBag)_safeBags [i];
1161
1162                                 if (sb.BagOID.Equals (certBag)) {
1163                                         ASN1 safeBag = sb.ASN1;
1164                                         ASN1 bagValue = safeBag [1];
1165                                         PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1166                                         X509Certificate c = new X509Certificate (crt.Content [0].Value);
1167                                         if (Compare (cert.RawData, c.RawData)) {
1168                                                 found = true;
1169                                         }
1170                                 }
1171                         }
1172
1173                         if (!found) {
1174                                 _safeBags.Add (new SafeBag (certBag, CertificateSafeBag (cert, attributes)));
1175                                 _certsChanged = true;
1176                         }
1177                 }
1178
1179                 public void RemoveCertificate (X509Certificate cert)
1180                 {
1181                         RemoveCertificate (cert, null);
1182                 }
1183
1184                 public void RemoveCertificate (X509Certificate cert, IDictionary attrs)
1185                 {
1186                         int certIndex = -1;
1187
1188                         for (int i = 0; certIndex == -1 && i < _safeBags.Count; i++) {
1189                                 SafeBag sb = (SafeBag)_safeBags [i];
1190
1191                                 if (sb.BagOID.Equals (certBag)) {
1192                                         ASN1 safeBag = sb.ASN1;
1193                                         ASN1 bagValue = safeBag [1];
1194                                         PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1195                                         X509Certificate c = new X509Certificate (crt.Content [0].Value);
1196                                         if (Compare (cert.RawData, c.RawData)) {
1197                                                 if (attrs != null) {
1198                                                         if (safeBag.Count == 3) {
1199                                                                 ASN1 bagAttributes = safeBag [2];
1200                                                                 int bagAttributesFound = 0;
1201                                                                 for (int j = 0; j < bagAttributes.Count; j++) {
1202                                                                         ASN1 pkcs12Attribute = bagAttributes [j];
1203                                                                         ASN1 attrId = pkcs12Attribute [0];
1204                                                                         string ao = ASN1Convert.ToOid (attrId);
1205                                                                         ArrayList dattrValues = (ArrayList)attrs [ao];
1206
1207                                                                         if (dattrValues != null) {
1208                                                                                 ASN1 attrValues = pkcs12Attribute [1];
1209
1210                                                                                 if (dattrValues.Count == attrValues.Count) {
1211                                                                                         int attrValuesFound = 0;
1212                                                                                         for (int k = 0; k < attrValues.Count; k++) {
1213                                                                                                 ASN1 attrValue = attrValues [k];
1214                                                                                                 byte[] value = (byte[])dattrValues [k];
1215                                                                         
1216                                                                                                 if (Compare (value, attrValue.Value)) {
1217                                                                                                         attrValuesFound += 1;
1218                                                                                                 }
1219                                                                                         }
1220                                                                                         if (attrValuesFound == attrValues.Count) {
1221                                                                                                 bagAttributesFound += 1;
1222                                                                                         }
1223                                                                                 }
1224                                                                         }
1225                                                                 }
1226                                                                 if (bagAttributesFound == bagAttributes.Count) {
1227                                                                         certIndex = i;
1228                                                                 }
1229                                                         }
1230                                                 } else {
1231                                                         certIndex = i;
1232                                                 }
1233                                         }
1234                                 }
1235                         }
1236
1237                         if (certIndex != -1) {
1238                                 _safeBags.RemoveAt (certIndex);
1239                                 _certsChanged = true;
1240                         }
1241                 }
1242
1243                 private bool CompareAsymmetricAlgorithm (AsymmetricAlgorithm a1, AsymmetricAlgorithm a2)
1244                 {
1245                         bool result = a1.KeyExchangeAlgorithm.Equals (a2.KeyExchangeAlgorithm);
1246                         result = result && a1.KeySize == a2.KeySize;
1247
1248                         KeySizes[] keysizes = a2.LegalKeySizes;
1249                         if (a1.LegalKeySizes.Length != keysizes.Length)
1250                                 return false;
1251
1252                         for (int i = 0; i < a1.LegalKeySizes.Length; i++) {
1253                                 result = result && CompareKeySizes (a1.LegalKeySizes [i], keysizes [i]);
1254                         }
1255
1256                         result = result && a1.SignatureAlgorithm == a2.SignatureAlgorithm;
1257                         
1258                         return result;
1259                 }
1260
1261                 private bool CompareKeySizes (KeySizes k1, KeySizes k2)
1262                 { 
1263                         bool result = k1.MaxSize == k2.MaxSize;
1264                         result = result && k1.MinSize == k2.MinSize;
1265                         result = result && k1.SkipSize == k2.SkipSize;
1266                         return result;
1267                 }
1268
1269                 public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
1270                 {
1271                         AddPkcs8ShroudedKeyBag (aa, null);
1272                 }
1273
1274                 public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
1275                 {
1276                         bool found = false;
1277
1278                         for (int i = 0; !found && i < _safeBags.Count; i++) {
1279                                 SafeBag sb = (SafeBag)_safeBags [i];
1280
1281                                 if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1282                                         ASN1 bagValue = sb.ASN1 [1];
1283                                         PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1284                                         byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1285                                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1286                                         byte[] privateKey = pki.PrivateKey;
1287
1288                                         AsymmetricAlgorithm saa = null;
1289                                         switch (privateKey [0]) {
1290                                         case 0x02:
1291                                                 DSAParameters p = new DSAParameters (); // FIXME
1292                                                 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1293                                                 break;
1294                                         case 0x30:
1295                                                 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1296                                                 break;
1297                                         default:
1298                                                 Array.Clear (decrypted, 0, decrypted.Length);
1299                                                 Array.Clear (privateKey, 0, privateKey.Length);
1300                                                 throw new CryptographicException ("Unknown private key format");
1301                                         }
1302
1303                                         Array.Clear (decrypted, 0, decrypted.Length);
1304                                         Array.Clear (privateKey, 0, privateKey.Length);
1305
1306                                         if (CompareAsymmetricAlgorithm (aa , saa)) {
1307                                                 found = true;
1308                                         }
1309                                 }
1310                         }
1311
1312                         if (!found) {
1313                                 _safeBags.Add (new SafeBag (pkcs8ShroudedKeyBag, Pkcs8ShroudedKeyBagSafeBag (aa, attributes)));
1314                                 _keyBagsChanged = true;
1315                         }
1316                 }
1317
1318                 public void RemovePkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
1319                 {
1320                         int aaIndex = -1;
1321
1322                         for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
1323                                 SafeBag sb = (SafeBag)_safeBags [i];
1324
1325                                 if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1326                                         ASN1 bagValue = sb.ASN1 [1];
1327                                         PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1328                                         byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1329                                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1330                                         byte[] privateKey = pki.PrivateKey;
1331
1332                                         AsymmetricAlgorithm saa = null;
1333                                         switch (privateKey [0]) {
1334                                         case 0x02:
1335                                                 DSAParameters p = new DSAParameters (); // FIXME
1336                                                 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1337                                                 break;
1338                                         case 0x30:
1339                                                 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1340                                                 break;
1341                                         default:
1342                                                 Array.Clear (decrypted, 0, decrypted.Length);
1343                                                 Array.Clear (privateKey, 0, privateKey.Length);
1344                                                 throw new CryptographicException ("Unknown private key format");
1345                                         }
1346
1347                                         Array.Clear (decrypted, 0, decrypted.Length);
1348                                         Array.Clear (privateKey, 0, privateKey.Length);
1349
1350                                         if (CompareAsymmetricAlgorithm (aa, saa)) {
1351                                                 aaIndex = i;
1352                                         }
1353                                 }
1354                         }
1355
1356                         if (aaIndex != -1) {
1357                                 _safeBags.RemoveAt (aaIndex);
1358                                 _keyBagsChanged = true;
1359                         }
1360                 }
1361
1362                 public void AddKeyBag (AsymmetricAlgorithm aa)
1363                 {
1364                         AddKeyBag (aa, null);
1365                 }
1366
1367                 public void AddKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
1368                 {
1369                         bool found = false;
1370
1371                         for (int i = 0; !found && i < _safeBags.Count; i++) {
1372                                 SafeBag sb = (SafeBag)_safeBags [i];
1373
1374                                 if (sb.BagOID.Equals (keyBag)) {
1375                                         ASN1 bagValue = sb.ASN1 [1];
1376                                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1377                                         byte[] privateKey = pki.PrivateKey;
1378
1379                                         AsymmetricAlgorithm saa = null;
1380                                         switch (privateKey [0]) {
1381                                         case 0x02:
1382                                                 DSAParameters p = new DSAParameters (); // FIXME
1383                                                 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1384                                                 break;
1385                                         case 0x30:
1386                                                 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1387                                                 break;
1388                                         default:
1389                                                 Array.Clear (privateKey, 0, privateKey.Length);
1390                                                 throw new CryptographicException ("Unknown private key format");
1391                                         }
1392
1393                                         Array.Clear (privateKey, 0, privateKey.Length);
1394
1395                                         if (CompareAsymmetricAlgorithm (aa, saa)) {
1396                                                 found = true;
1397                                         }
1398                                 }
1399                         }
1400
1401                         if (!found) {
1402                                 _safeBags.Add (new SafeBag (keyBag, KeyBagSafeBag (aa, attributes)));
1403                                 _keyBagsChanged = true;
1404                         }
1405                 }
1406
1407                 public void RemoveKeyBag (AsymmetricAlgorithm aa)
1408                 {
1409                         int aaIndex = -1;
1410
1411                         for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
1412                                 SafeBag sb = (SafeBag)_safeBags [i];
1413
1414                                 if (sb.BagOID.Equals (keyBag)) {
1415                                         ASN1 bagValue = sb.ASN1 [1];
1416                                         PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
1417                                         byte[] privateKey = pki.PrivateKey;
1418
1419                                         AsymmetricAlgorithm saa = null;
1420                                         switch (privateKey [0]) {
1421                                         case 0x02:
1422                                                 DSAParameters p = new DSAParameters (); // FIXME
1423                                                 saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1424                                                 break;
1425                                         case 0x30:
1426                                                 saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1427                                                 break;
1428                                         default:
1429                                                 Array.Clear (privateKey, 0, privateKey.Length);
1430                                                 throw new CryptographicException ("Unknown private key format");
1431                                         }
1432
1433                                         Array.Clear (privateKey, 0, privateKey.Length);
1434
1435                                         if (CompareAsymmetricAlgorithm (aa, saa)) {
1436                                                 aaIndex = i;
1437                                         }
1438                                 }
1439                         }
1440
1441                         if (aaIndex != -1) {
1442                                 _safeBags.RemoveAt (aaIndex);
1443                                 _keyBagsChanged = true;
1444                         }
1445                 }
1446
1447
1448                 public AsymmetricAlgorithm GetAsymmetricAlgorithm (IDictionary attrs)
1449                 {
1450                         foreach (SafeBag sb in _safeBags) {
1451                                 if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1452                                         ASN1 safeBag = sb.ASN1;
1453
1454                                         if (safeBag.Count == 3) {
1455                                                 ASN1 bagAttributes = safeBag [2];
1456
1457                                                 int bagAttributesFound = 0;
1458                                                 for (int i = 0; i < bagAttributes.Count; i++) {
1459                                                         ASN1 pkcs12Attribute = bagAttributes [i];
1460                                                         ASN1 attrId = pkcs12Attribute [0];
1461                                                         string ao = ASN1Convert.ToOid (attrId);
1462                                                         ArrayList dattrValues = (ArrayList)attrs [ao];
1463
1464                                                         if (dattrValues != null) {
1465                                                                 ASN1 attrValues = pkcs12Attribute [1];
1466
1467                                                                 if (dattrValues.Count == attrValues.Count) {
1468                                                                         int attrValuesFound = 0;
1469                                                                         for (int j = 0; j < attrValues.Count; j++) {
1470                                                                                 ASN1 attrValue = attrValues [j];
1471                                                                                 byte[] value = (byte[])dattrValues [j];
1472                                                                         
1473                                                                                 if (Compare (value, attrValue.Value)) {
1474                                                                                         attrValuesFound += 1;
1475                                                                                 }
1476                                                                         }
1477                                                                         if (attrValuesFound == attrValues.Count) {
1478                                                                                 bagAttributesFound += 1;
1479                                                                         }
1480                                                                 }
1481                                                         }
1482                                                 }
1483                                                 if (bagAttributesFound == bagAttributes.Count) {
1484                                                         ASN1 bagValue = safeBag [1];
1485                                                         AsymmetricAlgorithm aa = null;
1486                                                         if (sb.BagOID.Equals (keyBag)) {
1487                                                                 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
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                                                         } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1502                                                                 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1503                                                                 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1504                                                                 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1505                                                                 byte[] privateKey = pki.PrivateKey;
1506                                                                 switch (privateKey [0]) {
1507                                                                 case 0x02:
1508                                                                         DSAParameters p = new DSAParameters (); // FIXME
1509                                                                         aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1510                                                                         break;
1511                                                                 case 0x30:
1512                                                                         aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1513                                                                         break;
1514                                                                 default:
1515                                                                         break;
1516                                                                 }
1517                                                                 Array.Clear (privateKey, 0, privateKey.Length);
1518                                                                 Array.Clear (decrypted, 0, decrypted.Length);
1519                                                         }
1520                                                         return aa;
1521                                                 }
1522                                         }
1523                                 }
1524                         }
1525
1526                         return null;
1527                 }
1528
1529                 public X509Certificate GetCertificate (IDictionary attrs)
1530                 {
1531                         foreach (SafeBag sb in _safeBags) {
1532                                 if (sb.BagOID.Equals (certBag)) {
1533                                         ASN1 safeBag = sb.ASN1;
1534
1535                                         if (safeBag.Count == 3) {
1536                                                 ASN1 bagAttributes = safeBag [2];
1537
1538                                                 int bagAttributesFound = 0;
1539                                                 for (int i = 0; i < bagAttributes.Count; i++) {
1540                                                         ASN1 pkcs12Attribute = bagAttributes [i];
1541                                                         ASN1 attrId = pkcs12Attribute [0];
1542                                                         string ao = ASN1Convert.ToOid (attrId);
1543                                                         ArrayList dattrValues = (ArrayList)attrs [ao];
1544
1545                                                         if (dattrValues != null) {
1546                                                                 ASN1 attrValues = pkcs12Attribute [1];
1547                                                                 
1548                                                                 if (dattrValues.Count == attrValues.Count) {
1549                                                                         int attrValuesFound = 0;
1550                                                                         for (int j = 0; j < attrValues.Count; j++) {
1551                                                                                 ASN1 attrValue = attrValues [j];
1552                                                                                 byte[] value = (byte[])dattrValues [j];
1553                                                                         
1554                                                                                 if (Compare (value, attrValue.Value)) {
1555                                                                                         attrValuesFound += 1;
1556                                                                                 }
1557                                                                         }
1558                                                                         if (attrValuesFound == attrValues.Count) {
1559                                                                                 bagAttributesFound += 1;
1560                                                                         }
1561                                                                 }
1562                                                         }
1563                                                 }
1564                                                 if (bagAttributesFound == bagAttributes.Count) {
1565                                                         ASN1 bagValue = safeBag [1];
1566                                                         PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1567                                                         return new X509Certificate (crt.Content [0].Value);
1568                                                 }
1569                                         }
1570                                 }
1571                         }
1572
1573                         return null;
1574                 }
1575
1576                 public IDictionary GetAttributes (AsymmetricAlgorithm aa)
1577                 {
1578                         IDictionary result = new Hashtable ();
1579
1580                         foreach (SafeBag sb in _safeBags) {
1581                                 if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1582                                         ASN1 safeBag = sb.ASN1;
1583
1584                                         ASN1 bagValue = safeBag [1];
1585                                         AsymmetricAlgorithm saa = null;
1586                                         if (sb.BagOID.Equals (keyBag)) {
1587                                                 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
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                                         } else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
1602                                                 PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
1603                                                 byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
1604                                                 PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
1605                                                 byte[] privateKey = pki.PrivateKey;
1606                                                 switch (privateKey [0]) {
1607                                                 case 0x02:
1608                                                         DSAParameters p = new DSAParameters (); // FIXME
1609                                                         saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
1610                                                         break;
1611                                                 case 0x30:
1612                                                         saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
1613                                                         break;
1614                                                 default:
1615                                                         break;
1616                                                 }
1617                                                 Array.Clear (privateKey, 0, privateKey.Length);
1618                                                 Array.Clear (decrypted, 0, decrypted.Length);
1619                                         }
1620
1621                                         if (saa != null && CompareAsymmetricAlgorithm (saa, aa)) {
1622                                                 if (safeBag.Count == 3) {
1623                                                         ASN1 bagAttributes = safeBag [2];
1624                                                         
1625                                                         for (int i = 0; i < bagAttributes.Count; i++) {
1626                                                                 ASN1 pkcs12Attribute = bagAttributes [i];
1627                                                                 ASN1 attrId = pkcs12Attribute [0];
1628                                                                 string aOid = ASN1Convert.ToOid (attrId);
1629                                                                 ArrayList aValues = new ArrayList ();
1630
1631                                                                 ASN1 attrValues = pkcs12Attribute [1];
1632                                                                         
1633                                                                 for (int j = 0; j < attrValues.Count; j++) {
1634                                                                         ASN1 attrValue = attrValues [j];
1635                                                                         aValues.Add (attrValue.Value);
1636                                                                 }
1637                                                                 result.Add (aOid, aValues);
1638                                                         }
1639                                                 }
1640                                         }
1641                                 }
1642                         }
1643
1644                         return result;
1645                 }
1646
1647                 public IDictionary GetAttributes (X509Certificate cert)
1648                 {
1649                         IDictionary result = new Hashtable ();
1650
1651                         foreach (SafeBag sb in _safeBags) {
1652                                 if (sb.BagOID.Equals (certBag)) {
1653                                         ASN1 safeBag = sb.ASN1;
1654                                         ASN1 bagValue = safeBag [1];
1655                                         PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
1656                                         X509Certificate xc = new X509Certificate (crt.Content [0].Value);
1657
1658                                         if (Compare (cert.RawData, xc.RawData)) {
1659                                                 if (safeBag.Count == 3) {
1660                                                         ASN1 bagAttributes = safeBag [2];
1661
1662                                                         for (int i = 0; i < bagAttributes.Count; i++) {
1663                                                                 ASN1 pkcs12Attribute = bagAttributes [i];
1664                                                                 ASN1 attrId = pkcs12Attribute [0];
1665
1666                                                                 string aOid = ASN1Convert.ToOid (attrId);
1667                                                                 ArrayList aValues = new ArrayList ();
1668
1669                                                                 ASN1 attrValues = pkcs12Attribute [1];
1670                                                                         
1671                                                                 for (int j = 0; j < attrValues.Count; j++) {
1672                                                                         ASN1 attrValue = attrValues [j];
1673                                                                         aValues.Add (attrValue.Value);
1674                                                                 }
1675                                                                 result.Add (aOid, aValues);
1676                                                         }
1677                                                 }
1678                                         }
1679                                 }
1680                         }
1681
1682                         return result;
1683                 }
1684
1685                 public void SaveToFile (string filename)
1686                 {
1687                         if (filename == null)
1688                                 throw new ArgumentNullException ("filename");
1689
1690                         using (FileStream fs = File.OpenWrite (filename)) {
1691                                 byte[] data = GetBytes ();
1692                                 fs.Write (data, 0, data.Length);
1693                                 fs.Flush ();
1694                                 fs.Close ();
1695                         }
1696                 }
1697
1698                 public object Clone ()
1699                 {
1700                         PKCS12 clone = null;
1701                         if (_password != null) {
1702                                 clone = new PKCS12 (GetBytes (), Encoding.BigEndianUnicode.GetString (_password));
1703                         } else {
1704                                 clone = new PKCS12 (GetBytes ());
1705                         }
1706                         clone.IterationCount = this.IterationCount;
1707
1708                         return clone;
1709                 }
1710
1711                 // static methods
1712
1713                 static private byte[] LoadFile (string filename) 
1714                 {
1715                         byte[] data = null;
1716                         using (FileStream fs = File.OpenRead (filename)) {
1717                                 data = new byte [fs.Length];
1718                                 fs.Read (data, 0, data.Length);
1719                                 fs.Close ();
1720                         }
1721                         return data;
1722                 }
1723
1724                 static public PKCS12 LoadFromFile (string filename) 
1725                 {
1726                         if (filename == null)
1727                                 throw new ArgumentNullException ("filename");
1728
1729                         return new PKCS12 (LoadFile (filename));
1730                 }
1731
1732                 static public PKCS12 LoadFromFile (string filename, string password) 
1733                 {
1734                         if (filename == null)
1735                                 throw new ArgumentNullException ("filename");
1736
1737                         return new PKCS12 (LoadFile (filename), password);
1738                 }
1739         }
1740 }