copied mono-api-diff.cs from mono-2-2 branch so new patch can be applied and history...
[mono.git] / mcs / class / Mono.Security / Mono.Security.X509 / X509Store.cs
1 //
2 // X509Store.cs: Handles a X.509 certificates/CRLs store
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections;
31 using System.Globalization;
32 using System.IO;
33 using System.Text;
34
35 using Mono.Security.X509.Extensions;
36
37 namespace Mono.Security.X509 {
38
39 #if INSIDE_CORLIB
40         internal
41 #else
42         public 
43 #endif
44         class X509Store {
45
46                 private string _storePath;
47                 private X509CertificateCollection _certificates;
48                 private ArrayList _crls;
49                 private bool _crl;
50                 private string _name;
51
52                 internal X509Store (string path, bool crl) 
53                 {
54                         _storePath = path;
55                         _crl = crl;
56                 }
57
58                 // properties
59
60                 public X509CertificateCollection Certificates {
61                         get { 
62                                 if (_certificates == null) {
63                                         _certificates = BuildCertificatesCollection (_storePath);
64                                 }
65                                 return _certificates; 
66                         }
67                 }
68
69                 public ArrayList Crls {
70                         get {
71                                 // CRL aren't applicable to all stores
72                                 // but returning null is a little rude
73                                 if (!_crl) {
74                                         _crls = new ArrayList ();
75                                 }
76                                 if (_crls == null) {
77                                         _crls = BuildCrlsCollection (_storePath);
78                                 }
79                                 return _crls; 
80                         }
81                 }
82
83                 public string Name {
84                         get {
85                                 if (_name == null) {
86                                         int n = _storePath.LastIndexOf (Path.DirectorySeparatorChar);
87                                         _name = _storePath.Substring (n+1);
88                                 }
89                                 return _name;
90                         }
91                 }
92
93                 // methods
94
95                 public void Clear () 
96                 {
97                         if (_certificates != null)
98                                 _certificates.Clear ();
99                         _certificates = null;
100                         if (_crls != null)
101                                 _crls.Clear ();
102                         _crls = null;
103                 }
104
105                 public void Import (X509Certificate certificate) 
106                 {
107                         CheckStore (_storePath, true);
108
109                         string filename = Path.Combine (_storePath, GetUniqueName (certificate));
110                         if (!File.Exists (filename)) {
111                                 using (FileStream fs = File.Create (filename)) {
112                                         byte[] data = certificate.RawData;
113                                         fs.Write (data, 0, data.Length);
114                                         fs.Close ();
115                                 }
116                         }
117                 }
118
119                 public void Import (X509Crl crl) 
120                 {
121                         CheckStore (_storePath, true);
122
123                         string filename = Path.Combine (_storePath, GetUniqueName (crl));
124                         if (!File.Exists (filename)) {
125                                 using (FileStream fs = File.Create (filename)) {
126                                         byte[] data = crl.RawData;
127                                         fs.Write (data, 0, data.Length);
128                                 }
129                         }
130                 }
131
132                 public void Remove (X509Certificate certificate) 
133                 {
134                         string filename = Path.Combine (_storePath, GetUniqueName (certificate));
135                         if (File.Exists (filename)) {
136                                 File.Delete (filename);
137                         }
138                 }
139
140                 public void Remove (X509Crl crl) 
141                 {
142                         string filename = Path.Combine (_storePath, GetUniqueName (crl));
143                         if (File.Exists (filename)) {
144                                 File.Delete (filename);
145                         }
146                 }
147
148                 // private stuff
149
150                 private string GetUniqueName (X509Certificate certificate) 
151                 {
152                         string method;
153                         byte[] name = GetUniqueName (certificate.Extensions);
154                         if (name == null) {
155                                 method = "tbp"; // thumbprint
156                                 name = certificate.Hash;
157                         } else {
158                                 method = "ski";
159                         }
160                         return GetUniqueName (method, name, ".cer");
161                 }
162
163                 private string GetUniqueName (X509Crl crl) 
164                 {
165                         string method;
166                         byte[] name = GetUniqueName (crl.Extensions);
167                         if (name == null) {
168                                 method = "tbp"; // thumbprint
169                                 name = crl.Hash;
170                         } else {
171                                 method = "ski";
172                         }
173                         return GetUniqueName (method, name, ".crl");
174                 }
175
176                 private byte[] GetUniqueName (X509ExtensionCollection extensions) 
177                 {
178                         // We prefer Subject Key Identifier as the unique name
179                         // as it will provide faster lookups
180                         X509Extension ext = extensions ["2.5.29.14"];
181                         if (ext == null)
182                                 return null;
183
184                         SubjectKeyIdentifierExtension ski = new SubjectKeyIdentifierExtension (ext);
185                         return ski.Identifier;
186                 }
187
188                 private string GetUniqueName (string method, byte[] name, string fileExtension) 
189                 {
190                         StringBuilder sb = new StringBuilder (method);
191                         sb.Append ("-");
192                         foreach (byte b in name) {
193                                 sb.Append (b.ToString ("X2", CultureInfo.InvariantCulture));
194                         }
195                         sb.Append (fileExtension);
196
197                         return sb.ToString ();
198                 }
199
200                 private byte[] Load (string filename) 
201                 {
202                         byte[] data = null;
203                         using (FileStream fs = File.OpenRead (filename)) {
204                                 data = new byte [fs.Length];
205                                 fs.Read (data, 0, data.Length);
206                                 fs.Close ();
207                         }
208                         return data;
209                 }
210
211                 private X509Certificate LoadCertificate (string filename) 
212                 {
213                         byte[] data = Load (filename);
214                         X509Certificate cert = new X509Certificate (data);
215                         return cert;
216                 }
217
218                 private X509Crl LoadCrl (string filename) 
219                 {
220                         byte[] data = Load (filename);
221                         X509Crl crl = new X509Crl (data);
222                         return crl;
223                 }
224
225                 private bool CheckStore (string path, bool throwException)
226                 {
227                         try {
228                                 if (Directory.Exists (path))
229                                         return true;
230                                 Directory.CreateDirectory (path);
231                                 return Directory.Exists (path);
232                         }
233                         catch {
234                                 if (throwException)
235                                         throw;
236                                 return false;
237                         }
238                 }
239
240                 private X509CertificateCollection BuildCertificatesCollection (string storeName) 
241                 {
242                         X509CertificateCollection coll = new X509CertificateCollection ();
243                         string path = Path.Combine (_storePath, storeName);
244                         if (!CheckStore (path, false))
245                                 return coll;    // empty collection
246
247                         string[] files = Directory.GetFiles (path, "*.cer");
248                         if ((files != null) && (files.Length > 0)) {
249                                 foreach (string file in files) {
250                                         try {
251                                                 X509Certificate cert = LoadCertificate (file);
252                                                 coll.Add (cert);
253                                         }
254                                         catch {
255                                                 // in case someone is dumb enough
256                                                 // (like me) to include a base64
257                                                 // encoded certs (or other junk 
258                                                 // into the store).
259                                         }
260                                 }
261                         }
262                         return coll;
263                 }
264
265                 private ArrayList BuildCrlsCollection (string storeName) 
266                 {
267                         ArrayList list = new ArrayList ();
268                         string path = Path.Combine (_storePath, storeName);
269                         if (!CheckStore (path, false))
270                                 return list;    // empty list
271
272                         string[] files = Directory.GetFiles (path, "*.crl");
273                         if ((files != null) && (files.Length > 0)) {
274                                 foreach (string file in files) {
275                                         try {
276                                                 X509Crl crl = LoadCrl (file);
277                                                 list.Add (crl);
278                                         }
279                                         catch {
280                                                 // junk catcher
281                                         }
282                                 }
283                         }
284                         return list;
285                 }
286         }
287 }