// // System.Security.Cryptography.X509Certificates.X509Certificate2Collection class // // Authors: // Sebastien Pouliot // Tim Coleman (tim@timcoleman.com) // // (C) 2003 Motus Technologies Inc. (http://www.motus.com) // Copyright (C) Tim Coleman, 2004 // Copyright (C) 2005, 2006 Novell Inc. (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // #if SECURITY_DEP using System.Collections; using System.Globalization; namespace System.Security.Cryptography.X509Certificates { public class X509Certificate2Collection : X509CertificateCollection { // constructors public X509Certificate2Collection () { } public X509Certificate2Collection (X509Certificate2Collection certificates) { AddRange (certificates); } public X509Certificate2Collection (X509Certificate2 certificate) { Add (certificate); } public X509Certificate2Collection (X509Certificate2[] certificates) { AddRange (certificates); } // properties public new X509Certificate2 this [int index] { get { if (index < 0) throw new ArgumentOutOfRangeException ("negative index"); if (index >= InnerList.Count) throw new ArgumentOutOfRangeException ("index >= Count"); return (X509Certificate2) InnerList [index]; } set { InnerList [index] = value; } } // methods public int Add (X509Certificate2 certificate) { if (certificate == null) throw new ArgumentNullException ("certificate"); return InnerList.Add (certificate); } [MonoTODO ("Method isn't transactional (like documented)")] public void AddRange (X509Certificate2[] certificates) { if (certificates == null) throw new ArgumentNullException ("certificates"); for (int i=0; i < certificates.Length; i++) InnerList.Add (certificates [i]); } [MonoTODO ("Method isn't transactional (like documented)")] public void AddRange (X509Certificate2Collection certificates) { if (certificates == null) throw new ArgumentNullException ("certificates"); InnerList.AddRange (certificates); } public bool Contains (X509Certificate2 certificate) { if (certificate == null) throw new ArgumentNullException ("certificate"); foreach (X509Certificate2 c in InnerList) { if (c.Equals (certificate)) return true; } return false; } [MonoTODO ("only support X509ContentType.Cert")] public byte[] Export (X509ContentType contentType) { return Export (contentType, null); } [MonoTODO ("only support X509ContentType.Cert")] public byte[] Export (X509ContentType contentType, string password) { switch (contentType) { case X509ContentType.Cert: case X509ContentType.Pfx: // this includes Pkcs12 case X509ContentType.SerializedCert: // if multiple certificates are present we only export the last one if (Count > 0) return this [Count - 1].Export (contentType, password); break; case X509ContentType.Pkcs7: // TODO break; case X509ContentType.SerializedStore: // TODO break; default: // this includes Authenticode, Unknown and bad values string msg = Locale.GetText ("Cannot export certificate(s) to the '{0}' format", contentType); throw new CryptographicException (msg); } return null; } static string[] newline_split = new string[] { Environment.NewLine }; [MonoTODO ("Does not support X509FindType.FindByTemplateName, FindByApplicationPolicy and FindByCertificatePolicy")] public X509Certificate2Collection Find (X509FindType findType, object findValue, bool validOnly) { if (findValue == null) throw new ArgumentNullException ("findValue"); string str = String.Empty; string oid = String.Empty; X509KeyUsageFlags ku = X509KeyUsageFlags.None; DateTime dt = DateTime.MinValue; switch (findType) { case X509FindType.FindByThumbprint: case X509FindType.FindBySubjectName: case X509FindType.FindBySubjectDistinguishedName: case X509FindType.FindByIssuerName: case X509FindType.FindByIssuerDistinguishedName: case X509FindType.FindBySerialNumber: case X509FindType.FindByTemplateName: case X509FindType.FindBySubjectKeyIdentifier: try { str = (string) findValue; } catch (Exception e) { string msg = Locale.GetText ("Invalid find value type '{0}', expected '{1}'.", findValue.GetType (), "string"); throw new CryptographicException (msg, e); } break; case X509FindType.FindByApplicationPolicy: case X509FindType.FindByCertificatePolicy: case X509FindType.FindByExtension: try { oid = (string) findValue; } catch (Exception e) { string msg = Locale.GetText ("Invalid find value type '{0}', expected '{1}'.", findValue.GetType (), "X509KeyUsageFlags"); throw new CryptographicException (msg, e); } // OID validation try { CryptoConfig.EncodeOID (oid); } catch (CryptographicUnexpectedOperationException) { string msg = Locale.GetText ("Invalid OID value '{0}'.", oid); throw new ArgumentException ("findValue", msg); } break; case X509FindType.FindByKeyUsage: try { ku = (X509KeyUsageFlags) findValue; } catch (Exception e) { string msg = Locale.GetText ("Invalid find value type '{0}', expected '{1}'.", findValue.GetType (), "X509KeyUsageFlags"); throw new CryptographicException (msg, e); } break; case X509FindType.FindByTimeValid: case X509FindType.FindByTimeNotYetValid: case X509FindType.FindByTimeExpired: try { dt = (DateTime) findValue; } catch (Exception e) { string msg = Locale.GetText ("Invalid find value type '{0}', expected '{1}'.", findValue.GetType (), "X509DateTime"); throw new CryptographicException (msg,e ); } break; default: { string msg = Locale.GetText ("Invalid find type '{0}'.", findType); throw new CryptographicException (msg); } } CultureInfo cinv = CultureInfo.InvariantCulture; X509Certificate2Collection results = new X509Certificate2Collection (); foreach (X509Certificate2 x in InnerList) { bool value_match = false; switch (findType) { case X509FindType.FindByThumbprint: // works with Thumbprint, GetCertHashString in both normal (upper) and lower case value_match = ((String.Compare (str, x.Thumbprint, true, cinv) == 0) || (String.Compare (str, x.GetCertHashString (), true, cinv) == 0)); break; case X509FindType.FindBySubjectName: string [] names = x.SubjectName.Format (true).Split (newline_split, StringSplitOptions.RemoveEmptyEntries); foreach (string name in names) { int pos = name.IndexOf ('='); value_match = (name.IndexOf (str, pos, StringComparison.InvariantCultureIgnoreCase) >= 0); if (value_match) break; } break; case X509FindType.FindBySubjectDistinguishedName: value_match = (String.Compare (str, x.Subject, true, cinv) == 0); break; case X509FindType.FindByIssuerName: string iname = x.GetNameInfo (X509NameType.SimpleName, true); value_match = (iname.IndexOf (str, StringComparison.InvariantCultureIgnoreCase) >= 0); break; case X509FindType.FindByIssuerDistinguishedName: value_match = (String.Compare (str, x.Issuer, true, cinv) == 0); break; case X509FindType.FindBySerialNumber: value_match = (String.Compare (str, x.SerialNumber, true, cinv) == 0); break; case X509FindType.FindByTemplateName: // TODO - find a valid test case break; case X509FindType.FindBySubjectKeyIdentifier: X509SubjectKeyIdentifierExtension ski = (x.Extensions ["2.5.29.14"] as X509SubjectKeyIdentifierExtension); if (ski != null) { value_match = (String.Compare (str, ski.SubjectKeyIdentifier, true, cinv) == 0); } break; case X509FindType.FindByApplicationPolicy: // note: include when no extensions are present (even if v3) value_match = (x.Extensions.Count == 0); // TODO - find test case with extension break; case X509FindType.FindByCertificatePolicy: // TODO - find test case with extension break; case X509FindType.FindByExtension: value_match = (x.Extensions [oid] != null); break; case X509FindType.FindByKeyUsage: X509KeyUsageExtension kue = (x.Extensions ["2.5.29.15"] as X509KeyUsageExtension); if (kue == null) { // key doesn't have any hard coded limitations // note: MS doesn't check for ExtendedKeyUsage value_match = true; } else { value_match = ((kue.KeyUsages & ku) == ku); } break; case X509FindType.FindByTimeValid: value_match = ((dt >= x.NotBefore) && (dt <= x.NotAfter)); break; case X509FindType.FindByTimeNotYetValid: value_match = (dt < x.NotBefore); break; case X509FindType.FindByTimeExpired: value_match = (dt > x.NotAfter); break; } if (!value_match) continue; if (validOnly) { try { if (x.Verify ()) results.Add (x); } catch { } } else { results.Add (x); } } return results; } public new X509Certificate2Enumerator GetEnumerator () { return new X509Certificate2Enumerator (this); } [MonoTODO ("same limitations as X509Certificate2.Import")] public void Import (byte[] rawData) { // FIXME: can it import multiple certificates, e.g. a pkcs7 file ? X509Certificate2 cert = new X509Certificate2 (); cert.Import (rawData); Add (cert); } [MonoTODO ("same limitations as X509Certificate2.Import")] public void Import (byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags) { // FIXME: can it import multiple certificates, e.g. a pkcs7 file ? X509Certificate2 cert = new X509Certificate2 (); cert.Import (rawData, password, keyStorageFlags); Add (cert); } [MonoTODO ("same limitations as X509Certificate2.Import")] public void Import (string fileName) { // FIXME: can it import multiple certificates, e.g. a pkcs7 file ? X509Certificate2 cert = new X509Certificate2 (); cert.Import (fileName); Add (cert); } [MonoTODO ("same limitations as X509Certificate2.Import")] public void Import (string fileName, string password, X509KeyStorageFlags keyStorageFlags) { // FIXME: can it import multiple certificates, e.g. a pkcs7 file ? X509Certificate2 cert = new X509Certificate2 (); cert.Import (fileName, password, keyStorageFlags); Add (cert); } public void Insert (int index, X509Certificate2 certificate) { if (certificate == null) throw new ArgumentNullException ("certificate"); if (index < 0) throw new ArgumentOutOfRangeException ("negative index"); if (index >= InnerList.Count) throw new ArgumentOutOfRangeException ("index >= Count"); InnerList.Insert (index, certificate); } public void Remove (X509Certificate2 certificate) { if (certificate == null) throw new ArgumentNullException ("certificate"); for (int i=0; i < InnerList.Count; i++) { X509Certificate c = (X509Certificate) InnerList [i]; if (c.Equals (certificate)) { InnerList.RemoveAt (i); // only first instance is removed return; } } } [MonoTODO ("Method isn't transactional (like documented)")] public void RemoveRange (X509Certificate2[] certificates) { if (certificates == null) throw new ArgumentNullException ("certificate"); foreach (X509Certificate2 x in certificates) Remove (x); } [MonoTODO ("Method isn't transactional (like documented)")] public void RemoveRange (X509Certificate2Collection certificates) { if (certificates == null) throw new ArgumentNullException ("certificate"); foreach (X509Certificate2 x in certificates) Remove (x); } } } #endif