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