Fix FindBySubjectName to find anything in the subject name (except the oid acronyms...
[mono-project.git] / mcs / class / System / System.Security.Cryptography.X509Certificates / X509Certificate2Collection.cs
blob5932efb758dea0a9eed2a5485a119d1b3003b292
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)
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.
32 #if SECURITY_DEP || MOONLIGHT
34 using System.Collections;
35 using System.Globalization;
37 namespace System.Security.Cryptography.X509Certificates {
39 public class X509Certificate2Collection : X509CertificateCollection {
41 // constructors
43 public X509Certificate2Collection ()
47 public X509Certificate2Collection (X509Certificate2Collection certificates)
49 AddRange (certificates);
52 public X509Certificate2Collection (X509Certificate2 certificate)
54 Add (certificate);
57 public X509Certificate2Collection (X509Certificate2[] certificates)
59 AddRange (certificates);
62 // properties
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];
72 set { InnerList [index] = value; }
75 // methods
77 public int Add (X509Certificate2 certificate)
79 if (certificate == null)
80 throw new ArgumentNullException ("certificate");
82 return InnerList.Add (certificate);
85 [MonoTODO ("Method isn't transactional (like documented)")]
86 public void AddRange (X509Certificate2[] certificates)
88 if (certificates == null)
89 throw new ArgumentNullException ("certificates");
91 for (int i=0; i < certificates.Length; i++)
92 InnerList.Add (certificates [i]);
95 [MonoTODO ("Method isn't transactional (like documented)")]
96 public void AddRange (X509Certificate2Collection certificates)
98 if (certificates == null)
99 throw new ArgumentNullException ("certificates");
101 InnerList.AddRange (certificates);
104 public bool Contains (X509Certificate2 certificate)
106 if (certificate == null)
107 throw new ArgumentNullException ("certificate");
109 foreach (X509Certificate2 c in InnerList) {
110 if (c.Equals (certificate))
111 return true;
113 return false;
116 [MonoTODO ("only support X509ContentType.Cert")]
117 public byte[] Export (X509ContentType contentType)
119 return Export (contentType, null);
122 [MonoTODO ("only support X509ContentType.Cert")]
123 public byte[] Export (X509ContentType contentType, string password)
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);
148 return null;
151 static string[] newline_split = new string[] { Environment.NewLine };
153 [MonoTODO ("Does not support X509FindType.FindByTemplateName, FindByApplicationPolicy and FindByCertificatePolicy")]
154 public X509Certificate2Collection Find (X509FindType findType, object findValue, bool validOnly)
156 if (findValue == null)
157 throw new ArgumentNullException ("findValue");
159 string str = String.Empty;
160 string oid = String.Empty;
161 X509KeyUsageFlags ku = X509KeyUsageFlags.None;
162 DateTime dt = DateTime.MinValue;
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;
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);
181 break;
182 case X509FindType.FindByApplicationPolicy:
183 case X509FindType.FindByCertificatePolicy:
184 case X509FindType.FindByExtension:
185 try {
186 oid = (string) findValue;
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);
193 // OID validation
194 try {
195 CryptoConfig.EncodeOID (oid);
197 catch (CryptographicUnexpectedOperationException) {
198 string msg = Locale.GetText ("Invalid OID value '{0}'.", oid);
199 throw new ArgumentException ("findValue", msg);
201 break;
202 case X509FindType.FindByKeyUsage:
203 try {
204 ku = (X509KeyUsageFlags) findValue;
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);
211 break;
212 case X509FindType.FindByTimeValid:
213 case X509FindType.FindByTimeNotYetValid:
214 case X509FindType.FindByTimeExpired:
215 try {
216 dt = (DateTime) findValue;
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 );
223 break;
224 default:
226 string msg = Locale.GetText ("Invalid find type '{0}'.", findType);
227 throw new CryptographicException (msg);
231 CultureInfo cinv = CultureInfo.InvariantCulture;
232 X509Certificate2Collection results = new X509Certificate2Collection ();
233 foreach (X509Certificate2 x in InnerList) {
234 bool value_match = false;
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;
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);
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);
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;
305 if (!value_match)
306 continue;
308 if (validOnly) {
309 try {
310 if (x.Verify ())
311 results.Add (x);
313 catch {
315 } else {
316 results.Add (x);
319 return results;
322 public new X509Certificate2Enumerator GetEnumerator ()
324 return new X509Certificate2Enumerator (this);
327 [MonoTODO ("same limitations as X509Certificate2.Import")]
328 public void Import (byte[] rawData)
330 // FIXME: can it import multiple certificates, e.g. a pkcs7 file ?
331 X509Certificate2 cert = new X509Certificate2 ();
332 cert.Import (rawData);
333 Add (cert);
336 [MonoTODO ("same limitations as X509Certificate2.Import")]
337 public void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags)
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);
345 [MonoTODO ("same limitations as X509Certificate2.Import")]
346 public void Import (string fileName)
348 // FIXME: can it import multiple certificates, e.g. a pkcs7 file ?
349 X509Certificate2 cert = new X509Certificate2 ();
350 cert.Import (fileName);
351 Add (cert);
354 [MonoTODO ("same limitations as X509Certificate2.Import")]
355 public void Import (string fileName, string password, X509KeyStorageFlags keyStorageFlags)
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);
363 public void Insert (int index, X509Certificate2 certificate)
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");
372 InnerList.Insert (index, certificate);
375 public void Remove (X509Certificate2 certificate)
377 if (certificate == null)
378 throw new ArgumentNullException ("certificate");
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;
390 [MonoTODO ("Method isn't transactional (like documented)")]
391 public void RemoveRange (X509Certificate2[] certificates)
393 if (certificates == null)
394 throw new ArgumentNullException ("certificate");
396 foreach (X509Certificate2 x in certificates)
397 Remove (x);
400 [MonoTODO ("Method isn't transactional (like documented)")]
401 public void RemoveRange (X509Certificate2Collection certificates)
403 if (certificates == null)
404 throw new ArgumentNullException ("certificate");
406 foreach (X509Certificate2 x in certificates)
407 Remove (x);
412 #endif