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