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