Added tests for Task.WhenAll w/ empty list
[mono.git] / mcs / class / System / System.Security.Cryptography.X509Certificates / X509Store.cs
1 //
2 // System.Security.Cryptography.X509Certificates.X509Store class
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 #if SECURITY_DEP
31
32 extern alias MonoSecurity;
33
34 using System.Security.Permissions;
35 using MX = MonoSecurity::Mono.Security.X509;
36
37 namespace System.Security.Cryptography.X509Certificates {
38
39         public sealed class X509Store {
40
41                 private string _name;
42                 private StoreLocation _location;
43                 private X509Certificate2Collection list;
44                 private OpenFlags _flags;
45                 private MX.X509Store store;
46
47                 // constructors
48
49                 // BUG: MY when using this constructor - My when using StoreName.My
50                 public X509Store () 
51                         : this ("MY", StoreLocation.CurrentUser) 
52                 {
53                 }
54
55                 public X509Store (string storeName) 
56                         : this (storeName, StoreLocation.CurrentUser) 
57                 {
58                 }
59
60                 public X509Store (StoreName storeName) 
61                         : this (storeName, StoreLocation.CurrentUser)
62                 {
63                 }
64
65                 public X509Store (StoreLocation storeLocation) 
66                         : this ("MY", storeLocation)
67                 {
68                 }
69
70                 public X509Store (StoreName storeName, StoreLocation storeLocation)
71                 {
72                         if ((storeName < StoreName.AddressBook) || (storeName > StoreName.TrustedPublisher))
73                                 throw new ArgumentException ("storeName");
74                         if ((storeLocation < StoreLocation.CurrentUser) || (storeLocation > StoreLocation.LocalMachine))
75                                 throw new ArgumentException ("storeLocation");
76
77                         switch (storeName) {
78                         case StoreName.CertificateAuthority:
79                                 _name = "CA";
80                                 break;
81                         default:
82                                 _name = storeName.ToString ();
83                                 break;
84                         }
85                         _location = storeLocation;
86                 }
87
88                 [MonoTODO ("Mono's stores are fully managed. All handles are invalid.")]
89                 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode=true)]
90                 public X509Store (IntPtr storeHandle)
91                 {
92                         if (storeHandle == IntPtr.Zero)
93                                 throw new ArgumentNullException ("storeHandle");
94                         throw new CryptographicException ("Invalid handle.");
95                 }
96
97                 public X509Store (string storeName, StoreLocation storeLocation)
98                 {
99                         if ((storeLocation < StoreLocation.CurrentUser) || (storeLocation > StoreLocation.LocalMachine))
100                                 throw new ArgumentException ("storeLocation");
101
102                         _name = storeName;
103                         _location = storeLocation;
104                 }
105
106                 // properties
107
108                 public X509Certificate2Collection Certificates {
109                         get {
110                                 if (list == null)
111                                         list = new X509Certificate2Collection ();
112                                 else if (store == null)
113                                         list.Clear ();
114
115                                 return list;
116                         }
117                 } 
118
119                 public StoreLocation Location {
120                         get { return _location; }
121                 }
122
123                 public string Name {
124                         get { return _name; }
125                 }
126
127                 private MX.X509Stores Factory {
128                         get {
129                                 if (_location == StoreLocation.CurrentUser)
130                                         return MX.X509StoreManager.CurrentUser;
131                                 else
132                                         return MX.X509StoreManager.LocalMachine;
133                         }
134                 }
135
136                 private bool IsOpen {
137                         get { return (store != null); }
138                 }
139
140                 private bool IsReadOnly {
141                         get { return ((_flags & OpenFlags.ReadWrite) == OpenFlags.ReadOnly); }
142                 }
143
144                 internal MX.X509Store Store {
145                         get { return store; }
146                 }
147
148                 [MonoTODO ("Mono's stores are fully managed. Always returns IntPtr.Zero.")]
149                 public IntPtr StoreHandle {
150                         get { return IntPtr.Zero; }
151                 }
152
153                 // methods
154
155                 public void Add (X509Certificate2 certificate)
156                 {
157                         if (certificate == null)
158                                 throw new ArgumentNullException ("certificate");
159                         if (!IsOpen)
160                                 throw new CryptographicException (Locale.GetText ("Store isn't opened."));
161                         if (IsReadOnly)
162                                 throw new CryptographicException (Locale.GetText ("Store is read-only."));
163
164                         if (!Exists (certificate)) {
165                                 try {
166                                         store.Import (new MX.X509Certificate (certificate.RawData));
167                                 }
168                                 finally {
169                                         Certificates.Add (certificate);
170                                 }
171                         }
172                 }
173
174                 [MonoTODO ("Method isn't transactional (like documented)")]
175                 public void AddRange (X509Certificate2Collection certificates)
176                 {
177                         if (certificates == null)
178                                 throw new ArgumentNullException ("certificates");
179
180                         if (certificates.Count == 0)
181                                 return;
182
183                         if (!IsOpen)
184                                 throw new CryptographicException (Locale.GetText ("Store isn't opened."));
185                         if (IsReadOnly)
186                                 throw new CryptographicException (Locale.GetText ("Store is read-only."));
187
188                         foreach (X509Certificate2 certificate in certificates) {
189                                 if (!Exists (certificate)) {
190                                         try {
191                                                 store.Import (new MX.X509Certificate (certificate.RawData));
192                                         }
193                                         finally {
194                                                 Certificates.Add (certificate);
195                                         }
196                                 }
197                         }
198                 }
199
200                 public void Close () 
201                 {
202                         store = null;
203                         if (list != null)
204                                 list.Clear ();
205                 }
206
207                 public void Open (OpenFlags flags)
208                 {
209                         if (String.IsNullOrEmpty (_name))
210                                 throw new CryptographicException (Locale.GetText ("Invalid store name (null or empty)."));
211
212                         /* keep existing Mono installations (pre 2.0) compatible with new stuff */
213                         string name;
214                         switch (_name) {
215                         case "Root":
216                                 name = "Trust";
217                                 break;
218                         default:
219                                 name = _name;
220                                 break;
221                         }
222
223                         bool create = ((flags & OpenFlags.OpenExistingOnly) != OpenFlags.OpenExistingOnly);
224                         store = Factory.Open (name, create);
225                         if (store == null)
226                                 throw new CryptographicException (Locale.GetText ("Store {0} doesn't exists.", _name));
227                         _flags = flags;
228
229                         foreach (MX.X509Certificate x in store.Certificates) {
230                                 var cert2 = new X509Certificate2 (x.RawData);
231                                 cert2.PrivateKey = x.RSA;
232                                 Certificates.Add (cert2);
233                         }
234                 }
235
236                 public void Remove (X509Certificate2 certificate) 
237                 {
238                         if (certificate == null)
239                                 throw new ArgumentNullException ("certificate");
240                         if (!IsOpen)
241                                 throw new CryptographicException (Locale.GetText ("Store isn't opened."));
242
243                         if (!Exists (certificate))
244                                 return;
245
246                         if (IsReadOnly)
247                                 throw new CryptographicException (Locale.GetText ("Store is read-only."));
248
249                         try {
250                                 store.Remove (new MX.X509Certificate (certificate.RawData));
251                         }
252                         finally {
253                                 Certificates.Remove (certificate);
254                         }
255                 }
256
257                 [MonoTODO ("Method isn't transactional (like documented)")]
258                 public void RemoveRange (X509Certificate2Collection certificates) 
259                 {
260                         if (certificates == null)
261                                 throw new ArgumentNullException ("certificates");
262
263                         if (certificates.Count == 0)
264                                 return;
265
266                         if (!IsOpen)
267                                 throw new CryptographicException (Locale.GetText ("Store isn't opened."));
268
269                         bool delete = false;
270                         foreach (X509Certificate2 certificate in certificates) {
271                                 if (Exists (certificate))
272                                         delete = true;
273                         }
274                         if (!delete)
275                                 return;
276
277                         if (IsReadOnly)
278                                 throw new CryptographicException (Locale.GetText ("Store is read-only."));
279
280                         try {
281                                 foreach (X509Certificate2 certificate in certificates)
282                                         store.Remove (new MX.X509Certificate (certificate.RawData));
283                         }
284                         finally {
285                                 Certificates.RemoveRange (certificates);
286                         }
287                 }
288
289                 private bool Exists (X509Certificate2 certificate)
290                 {
291                         if ((store == null) || (list == null) || (certificate == null))
292                                 return false;
293
294                         foreach (X509Certificate2 c in list) {
295                                 if (certificate.Equals (c)) {
296                                         return true;
297                                 }
298                         }
299                         return false;
300                 }
301         }
302 }
303
304 #endif