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