Merge pull request #285 from joncham/eglib-fixes
[mono.git] / mcs / class / System / System.Security.Cryptography.X509Certificates / X509Certificate2Collection.cs
1 //
2 // System.Security.Cryptography.X509Certificates.X509Certificate2Collection class
3 //
4 // Authors:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //      Tim Coleman (tim@timcoleman.com)
7 //
8 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
9 // Copyright (C) Tim Coleman, 2004
10 // Copyright (C) 2005, 2006 Novell Inc. (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 #if SECURITY_DEP || MOONLIGHT
33
34 using System.Collections;
35 using System.Globalization;
36
37 namespace System.Security.Cryptography.X509Certificates {
38
39         public class X509Certificate2Collection : X509CertificateCollection {
40
41                 // constructors
42
43                 public X509Certificate2Collection ()
44                 {
45                 }
46
47                 public X509Certificate2Collection (X509Certificate2Collection certificates)
48                 {
49                         AddRange (certificates);
50                 }
51
52                 public X509Certificate2Collection (X509Certificate2 certificate) 
53                 {
54                         Add (certificate);
55                 }
56
57                 public X509Certificate2Collection (X509Certificate2[] certificates) 
58                 {
59                         AddRange (certificates);
60                 }
61
62                 // properties
63
64                 public new X509Certificate2 this [int index] {
65                         get {
66                                 if (index < 0)
67                                         throw new ArgumentOutOfRangeException ("negative index");
68                                 if (index >= InnerList.Count)
69                                         throw new ArgumentOutOfRangeException ("index >= Count");
70                                 return (X509Certificate2) InnerList [index];
71                         }
72                         set { InnerList [index] = value; }
73                 }
74
75                 // methods
76
77                 public int Add (X509Certificate2 certificate)
78                 {
79                         if (certificate == null)
80                                 throw new ArgumentNullException ("certificate");
81
82                         return InnerList.Add (certificate);
83                 }
84
85                 [MonoTODO ("Method isn't transactional (like documented)")]
86                 public void AddRange (X509Certificate2[] certificates) 
87                 {
88                         if (certificates == null)
89                                 throw new ArgumentNullException ("certificates");
90
91                         for (int i=0; i < certificates.Length; i++)
92                                 InnerList.Add (certificates [i]);
93                 }
94
95                 [MonoTODO ("Method isn't transactional (like documented)")]
96                 public void AddRange (X509Certificate2Collection certificates) 
97                 {
98                         if (certificates == null)
99                                 throw new ArgumentNullException ("certificates");
100
101                         InnerList.AddRange (certificates);
102                 }
103
104                 public bool Contains (X509Certificate2 certificate) 
105                 {
106                         if (certificate == null)
107                                 throw new ArgumentNullException ("certificate");
108
109                         foreach (X509Certificate2 c in InnerList) {
110                                 if (c.Equals (certificate))
111                                         return true;
112                         }
113                         return false;
114                 }
115
116                 [MonoTODO ("only support X509ContentType.Cert")]
117                 public byte[] Export (X509ContentType contentType) 
118                 {
119                         return Export (contentType, null);
120                 }
121
122                 [MonoTODO ("only support X509ContentType.Cert")]
123                 public byte[] Export (X509ContentType contentType, string password) 
124                 {
125                         switch (contentType) {
126                         case X509ContentType.Cert:
127 #if !MOONLIGHT
128                         case X509ContentType.Pfx: // this includes Pkcs12
129                         case X509ContentType.SerializedCert:
130 #endif
131                                 // if multiple certificates are present we only export the last one
132                                 if (Count > 0)
133                                         return this [Count - 1].Export (contentType, password);
134                                 break;
135 #if !MOONLIGHT
136                         case X509ContentType.Pkcs7:
137                                 // TODO
138                                 break;
139                         case X509ContentType.SerializedStore:
140                                 // TODO
141                                 break;
142 #endif
143                         default:
144                                 // this includes Authenticode, Unknown and bad values
145                                 string msg = Locale.GetText ("Cannot export certificate(s) to the '{0}' format", contentType);
146                                 throw new CryptographicException (msg);
147                         }
148                         return null;
149                 }
150
151                 static string[] newline_split = new string[] { Environment.NewLine };
152                 
153                 [MonoTODO ("Does not support X509FindType.FindByTemplateName, FindByApplicationPolicy and FindByCertificatePolicy")]
154                 public X509Certificate2Collection Find (X509FindType findType, object findValue, bool validOnly) 
155                 {
156                         if (findValue == null)
157                                 throw new ArgumentNullException ("findValue");
158
159                         string str = String.Empty;
160                         string oid = String.Empty;
161                         X509KeyUsageFlags ku = X509KeyUsageFlags.None;
162                         DateTime dt = DateTime.MinValue;
163
164                         switch (findType) {
165                         case X509FindType.FindByThumbprint:
166                         case X509FindType.FindBySubjectName:
167                         case X509FindType.FindBySubjectDistinguishedName:
168                         case X509FindType.FindByIssuerName:
169                         case X509FindType.FindByIssuerDistinguishedName:
170                         case X509FindType.FindBySerialNumber:
171                         case X509FindType.FindByTemplateName:
172                         case X509FindType.FindBySubjectKeyIdentifier:
173                                 try {
174                                         str = (string) findValue;
175                                 }
176                                 catch (Exception e) {
177                                         string msg = Locale.GetText ("Invalid find value type '{0}', expected '{1}'.", 
178                                                 findValue.GetType (), "string");
179                                         throw new CryptographicException (msg, e);
180                                 }
181                                 break;
182                         case X509FindType.FindByApplicationPolicy:
183                         case X509FindType.FindByCertificatePolicy:
184                         case X509FindType.FindByExtension:
185                                 try {
186                                         oid = (string) findValue;
187                                 }
188                                 catch (Exception e) {
189                                         string msg = Locale.GetText ("Invalid find value type '{0}', expected '{1}'.", 
190                                                 findValue.GetType (), "X509KeyUsageFlags");
191                                         throw new CryptographicException (msg, e);
192                                 }
193                                 // OID validation
194                                 try {
195                                         CryptoConfig.EncodeOID (oid);
196                                 }
197                                 catch (CryptographicUnexpectedOperationException) {
198                                         string msg = Locale.GetText ("Invalid OID value '{0}'.", oid);
199                                         throw new ArgumentException ("findValue", msg);
200                                 }
201                                 break;
202                         case X509FindType.FindByKeyUsage:
203                                 try {
204                                         ku = (X509KeyUsageFlags) findValue;
205                                 }
206                                 catch (Exception e) {
207                                         string msg = Locale.GetText ("Invalid find value type '{0}', expected '{1}'.", 
208                                                 findValue.GetType (), "X509KeyUsageFlags");
209                                         throw new CryptographicException (msg, e);
210                                 }
211                                 break;
212                         case X509FindType.FindByTimeValid:
213                         case X509FindType.FindByTimeNotYetValid:
214                         case X509FindType.FindByTimeExpired:
215                                 try {
216                                         dt = (DateTime) findValue;
217                                 }
218                                 catch (Exception e) {
219                                         string msg = Locale.GetText ("Invalid find value type '{0}', expected '{1}'.", 
220                                                 findValue.GetType (), "X509DateTime");
221                                         throw new CryptographicException (msg,e );
222                                 }
223                                 break;
224                         default:
225                                 {
226                                         string msg = Locale.GetText ("Invalid find type '{0}'.", findType);
227                                         throw new CryptographicException (msg);
228                                 }
229                         }
230
231                         CultureInfo cinv = CultureInfo.InvariantCulture;
232                         X509Certificate2Collection results = new  X509Certificate2Collection ();
233                         foreach (X509Certificate2 x in InnerList) {
234                                 bool value_match = false;
235
236                                 switch (findType) {
237                                 case X509FindType.FindByThumbprint:
238                                         // works with Thumbprint, GetCertHashString in both normal (upper) and lower case
239                                         value_match = ((String.Compare (str, x.Thumbprint, true, cinv) == 0) ||
240                                                 (String.Compare (str, x.GetCertHashString (), true, cinv) == 0));
241                                         break;
242                                 case X509FindType.FindBySubjectName:
243                                         string [] names = x.SubjectName.Format (true).Split (newline_split, StringSplitOptions.RemoveEmptyEntries);
244                                         foreach (string name in names) {
245                                                 int pos = name.IndexOf ('=');
246                                                 value_match = (name.IndexOf (str, pos, StringComparison.InvariantCultureIgnoreCase) >= 0);
247                                                 if (value_match)
248                                                         break;
249                                         }
250                                         break;
251                                 case X509FindType.FindBySubjectDistinguishedName:
252                                         value_match = (String.Compare (str, x.Subject, true, cinv) == 0);
253                                         break;
254                                 case X509FindType.FindByIssuerName:
255                                         string iname = x.GetNameInfo (X509NameType.SimpleName, true);
256                                         value_match = (iname.IndexOf (str, StringComparison.InvariantCultureIgnoreCase) >= 0);
257                                         break;
258                                 case X509FindType.FindByIssuerDistinguishedName:
259                                         value_match = (String.Compare (str, x.Issuer, true, cinv) == 0);
260                                         break;
261                                 case X509FindType.FindBySerialNumber:
262                                         value_match = (String.Compare (str, x.SerialNumber, true, cinv) == 0);
263                                         break;
264                                 case X509FindType.FindByTemplateName:
265                                         // TODO - find a valid test case
266                                         break;
267                                 case X509FindType.FindBySubjectKeyIdentifier:
268                                         X509SubjectKeyIdentifierExtension ski = (x.Extensions ["2.5.29.14"] as X509SubjectKeyIdentifierExtension);
269                                         if (ski != null) {
270                                                 value_match = (String.Compare (str, ski.SubjectKeyIdentifier, true, cinv) == 0);
271                                         }
272                                         break;
273                                 case X509FindType.FindByApplicationPolicy:
274                                         // note: include when no extensions are present (even if v3)
275                                         value_match = (x.Extensions.Count == 0);
276                                         // TODO - find test case with extension
277                                         break;
278                                 case X509FindType.FindByCertificatePolicy:
279                                         // TODO - find test case with extension
280                                         break;
281                                 case X509FindType.FindByExtension:
282                                         value_match = (x.Extensions [oid] != null);
283                                         break;
284                                 case X509FindType.FindByKeyUsage:
285                                         X509KeyUsageExtension kue = (x.Extensions ["2.5.29.15"] as X509KeyUsageExtension);
286                                         if (kue == null) {
287                                                 // key doesn't have any hard coded limitations
288                                                 // note: MS doesn't check for ExtendedKeyUsage
289                                                 value_match = true; 
290                                         } else {
291                                                 value_match = ((kue.KeyUsages & ku) == ku);
292                                         }
293                                         break;
294                                 case X509FindType.FindByTimeValid:
295                                         value_match = ((dt >= x.NotBefore) && (dt <= x.NotAfter));
296                                         break;
297                                 case X509FindType.FindByTimeNotYetValid:
298                                         value_match = (dt < x.NotBefore);
299                                         break;
300                                 case X509FindType.FindByTimeExpired:
301                                         value_match = (dt > x.NotAfter);
302                                         break;
303                                 }
304
305                                 if (!value_match)
306                                         continue;
307
308                                 if (validOnly) {
309                                         try {
310                                                 if (x.Verify ())
311                                                         results.Add (x);
312                                         }
313                                         catch {
314                                         }
315                                 } else {
316                                         results.Add (x);
317                                 }
318                         }
319                         return results;
320                 }
321
322                 public new X509Certificate2Enumerator GetEnumerator () 
323                 {
324                         return new X509Certificate2Enumerator (this);
325                 }
326
327                 [MonoTODO ("same limitations as X509Certificate2.Import")]
328                 public void Import (byte[] rawData) 
329                 {
330                         // FIXME: can it import multiple certificates, e.g. a pkcs7 file ?
331                         X509Certificate2 cert = new X509Certificate2 ();
332                         cert.Import (rawData);
333                         Add (cert);
334                 }
335
336                 [MonoTODO ("same limitations as X509Certificate2.Import")]
337                 public void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
338                 {
339                         // FIXME: can it import multiple certificates, e.g. a pkcs7 file ?
340                         X509Certificate2 cert = new X509Certificate2 ();
341                         cert.Import (rawData, password, keyStorageFlags);
342                         Add (cert);
343                 }
344
345                 [MonoTODO ("same limitations as X509Certificate2.Import")]
346                 public void Import (string fileName) 
347                 {
348                         // FIXME: can it import multiple certificates, e.g. a pkcs7 file ?
349                         X509Certificate2 cert = new X509Certificate2 ();
350                         cert.Import (fileName);
351                         Add (cert);
352                 }
353
354                 [MonoTODO ("same limitations as X509Certificate2.Import")]
355                 public void Import (string fileName, string password, X509KeyStorageFlags keyStorageFlags) 
356                 {
357                         // FIXME: can it import multiple certificates, e.g. a pkcs7 file ?
358                         X509Certificate2 cert = new X509Certificate2 ();
359                         cert.Import (fileName, password, keyStorageFlags);
360                         Add (cert);
361                 }
362
363                 public void Insert (int index, X509Certificate2 certificate) 
364                 {
365                         if (certificate == null)
366                                 throw new ArgumentNullException ("certificate");
367                         if (index < 0)
368                                 throw new ArgumentOutOfRangeException ("negative index");
369                         if (index >= InnerList.Count)
370                                 throw new ArgumentOutOfRangeException ("index >= Count");
371
372                         InnerList.Insert (index, certificate);
373                 }
374
375                 public void Remove (X509Certificate2 certificate) 
376                 {
377                         if (certificate == null)
378                                 throw new ArgumentNullException ("certificate");
379
380                         for (int i=0; i < InnerList.Count; i++) {
381                                 X509Certificate c = (X509Certificate) InnerList [i];
382                                 if (c.Equals (certificate)) {
383                                         InnerList.RemoveAt (i);
384                                         // only first instance is removed
385                                         return;
386                                 }
387                         }
388                 }
389
390                 [MonoTODO ("Method isn't transactional (like documented)")]
391                 public void RemoveRange (X509Certificate2[] certificates)
392                 {
393                         if (certificates == null)
394                                 throw new ArgumentNullException ("certificate");
395
396                         foreach (X509Certificate2 x in certificates)
397                                 Remove (x);
398                 }
399
400                 [MonoTODO ("Method isn't transactional (like documented)")]
401                 public void RemoveRange (X509Certificate2Collection certificates) 
402                 {
403                         if (certificates == null)
404                                 throw new ArgumentNullException ("certificate");
405
406                         foreach (X509Certificate2 x in certificates)
407                                 Remove (x);
408                 }
409         }
410 }
411
412 #endif