minor fix for bug 9520:
[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 using System.Security.Permissions;
33 using MX = Mono.Security.X509;
34
35 namespace System.Security.Cryptography.X509Certificates {
36
37         public sealed class X509Store {
38
39                 private string _name;
40                 private StoreLocation _location;
41                 private X509Certificate2Collection list;
42                 private OpenFlags _flags;
43                 private MX.X509Store store;
44
45                 // constructors
46
47                 // BUG: MY when using this constructor - My when using StoreName.My
48                 public X509Store () 
49                         : this ("MY", StoreLocation.CurrentUser) 
50                 {
51                 }
52
53                 public X509Store (string storeName) 
54                         : this (storeName, StoreLocation.CurrentUser) 
55                 {
56                 }
57
58                 public X509Store (StoreName storeName) 
59                         : this (storeName, StoreLocation.CurrentUser)
60                 {
61                 }
62
63                 public X509Store (StoreLocation storeLocation) 
64                         : this ("MY", storeLocation)
65                 {
66                 }
67
68                 public X509Store (StoreName storeName, StoreLocation storeLocation)
69                 {
70                         if ((storeName < StoreName.AddressBook) || (storeName > StoreName.TrustedPublisher))
71                                 throw new ArgumentException ("storeName");
72                         if ((storeLocation < StoreLocation.CurrentUser) || (storeLocation > StoreLocation.LocalMachine))
73                                 throw new ArgumentException ("storeLocation");
74
75                         switch (storeName) {
76                         case StoreName.CertificateAuthority:
77                                 _name = "CA";
78                                 break;
79                         default:
80                                 _name = storeName.ToString ();
81                                 break;
82                         }
83                         _location = storeLocation;
84                 }
85
86                 [MonoTODO ("Mono's stores are fully managed. All handles are invalid.")]
87                 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode=true)]
88                 public X509Store (IntPtr storeHandle)
89                 {
90                         if (storeHandle == IntPtr.Zero)
91                                 throw new ArgumentNullException ("storeHandle");
92                         throw new CryptographicException ("Invalid handle.");
93                 }
94
95                 public X509Store (string storeName, StoreLocation storeLocation)
96                 {
97                         if ((storeLocation < StoreLocation.CurrentUser) || (storeLocation > StoreLocation.LocalMachine))
98                                 throw new ArgumentException ("storeLocation");
99
100                         _name = storeName;
101                         _location = storeLocation;
102                 }
103
104                 // properties
105
106                 public X509Certificate2Collection Certificates {
107                         get {
108                                 if (list == null)
109                                         list = new X509Certificate2Collection ();
110                                 else if (store == null)
111                                         list.Clear ();
112
113                                 return list;
114                         }
115                 } 
116
117                 public StoreLocation Location {
118                         get { return _location; }
119                 }
120
121                 public string Name {
122                         get { return _name; }
123                 }
124
125                 private MX.X509Stores Factory {
126                         get {
127                                 if (_location == StoreLocation.CurrentUser)
128                                         return MX.X509StoreManager.CurrentUser;
129                                 else
130                                         return MX.X509StoreManager.LocalMachine;
131                         }
132                 }
133
134                 private bool IsOpen {
135                         get { return (store != null); }
136                 }
137
138                 private bool IsReadOnly {
139                         get { return ((_flags & OpenFlags.ReadWrite) == OpenFlags.ReadOnly); }
140                 }
141
142                 internal MX.X509Store Store {
143                         get { return store; }
144                 }
145
146                 [MonoTODO ("Mono's stores are fully managed. Always returns IntPtr.Zero.")]
147                 public IntPtr StoreHandle {
148                         get { return IntPtr.Zero; }
149                 }
150
151                 // methods
152
153                 public void Add (X509Certificate2 certificate)
154                 {
155                         if (certificate == null)
156                                 throw new ArgumentNullException ("certificate");
157                         if (!IsOpen)
158                                 throw new CryptographicException (Locale.GetText ("Store isn't opened."));
159                         if (IsReadOnly)
160                                 throw new CryptographicException (Locale.GetText ("Store is read-only."));
161
162                         if (!Exists (certificate)) {
163                                 try {
164                                         store.Import (new MX.X509Certificate (certificate.RawData));
165                                 }
166                                 finally {
167                                         Certificates.Add (certificate);
168                                 }
169                         }
170                 }
171
172                 [MonoTODO ("Method isn't transactional (like documented)")]
173                 public void AddRange (X509Certificate2Collection certificates)
174                 {
175                         if (certificates == null)
176                                 throw new ArgumentNullException ("certificates");
177
178                         if (certificates.Count == 0)
179                                 return;
180
181                         if (!IsOpen)
182                                 throw new CryptographicException (Locale.GetText ("Store isn't opened."));
183                         if (IsReadOnly)
184                                 throw new CryptographicException (Locale.GetText ("Store is read-only."));
185
186                         foreach (X509Certificate2 certificate in certificates) {
187                                 if (!Exists (certificate)) {
188                                         try {
189                                                 store.Import (new MX.X509Certificate (certificate.RawData));
190                                         }
191                                         finally {
192                                                 Certificates.Add (certificate);
193                                         }
194                                 }
195                         }
196                 }
197
198                 public void Close () 
199                 {
200                         store = null;
201                         if (list != null)
202                                 list.Clear ();
203                 }
204
205                 public void Open (OpenFlags flags)
206                 {
207                         if (String.IsNullOrEmpty (_name))
208                                 throw new CryptographicException (Locale.GetText ("Invalid store name (null or empty)."));
209
210                         /* keep existing Mono installations (pre 2.0) compatible with new stuff */
211                         string name;
212                         switch (_name) {
213                         case "Root":
214                                 name = "Trust";
215                                 break;
216                         default:
217                                 name = _name;
218                                 break;
219                         }
220
221                         bool create = ((flags & OpenFlags.OpenExistingOnly) != OpenFlags.OpenExistingOnly);
222                         store = Factory.Open (name, create);
223                         if (store == null)
224                                 throw new CryptographicException (Locale.GetText ("Store {0} doesn't exists.", _name));
225                         _flags = flags;
226
227                         foreach (MX.X509Certificate x in store.Certificates) {
228                                 var cert2 = new X509Certificate2 (x.RawData);
229                                 cert2.PrivateKey = x.RSA;
230                                 Certificates.Add (cert2);
231                         }
232                 }
233
234                 public void Remove (X509Certificate2 certificate) 
235                 {
236                         if (certificate == null)
237                                 throw new ArgumentNullException ("certificate");
238                         if (!IsOpen)
239                                 throw new CryptographicException (Locale.GetText ("Store isn't opened."));
240
241                         if (!Exists (certificate))
242                                 return;
243
244                         if (IsReadOnly)
245                                 throw new CryptographicException (Locale.GetText ("Store is read-only."));
246
247                         try {
248                                 store.Remove (new MX.X509Certificate (certificate.RawData));
249                         }
250                         finally {
251                                 Certificates.Remove (certificate);
252                         }
253                 }
254
255                 [MonoTODO ("Method isn't transactional (like documented)")]
256                 public void RemoveRange (X509Certificate2Collection certificates) 
257                 {
258                         if (certificates == null)
259                                 throw new ArgumentNullException ("certificates");
260
261                         if (certificates.Count == 0)
262                                 return;
263
264                         if (!IsOpen)
265                                 throw new CryptographicException (Locale.GetText ("Store isn't opened."));
266
267                         bool delete = false;
268                         foreach (X509Certificate2 certificate in certificates) {
269                                 if (Exists (certificate))
270                                         delete = true;
271                         }
272                         if (!delete)
273                                 return;
274
275                         if (IsReadOnly)
276                                 throw new CryptographicException (Locale.GetText ("Store is read-only."));
277
278                         try {
279                                 foreach (X509Certificate2 certificate in certificates)
280                                         store.Remove (new MX.X509Certificate (certificate.RawData));
281                         }
282                         finally {
283                                 Certificates.RemoveRange (certificates);
284                         }
285                 }
286
287                 private bool Exists (X509Certificate2 certificate)
288                 {
289                         if ((store == null) || (list == null) || (certificate == null))
290                                 return false;
291
292                         foreach (X509Certificate2 c in list) {
293                                 if (certificate.Equals (c)) {
294                                         return true;
295                                 }
296                         }
297                         return false;
298                 }
299         }
300 }
301
302 #endif