New test.
[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.OpenWrite (filename)) {
112                                         byte[] data = certificate.RawData;
113                                         fs.Write (data, 0, data.Length);
114                                         fs.Close ();
115                                 }
116                         }
117                 }
118
119                 public void Remove (X509Certificate certificate) 
120                 {
121                         string filename = Path.Combine (_storePath, GetUniqueName (certificate));
122                         if (File.Exists (filename)) {
123                                 File.Delete (filename);
124                         }
125                 }
126
127                 // private stuff
128
129                 private string GetUniqueName (X509Certificate certificate) 
130                 {
131                         string method = null;
132                         byte[] name = null;
133
134                         // We prefer Subject Key Identifier as the unique name
135                         // as it will provide faster lookups
136                         X509Extension ext = certificate.Extensions ["2.5.29.14"];
137                         if (ext != null) {
138                                 SubjectKeyIdentifierExtension ski = new SubjectKeyIdentifierExtension (ext);
139                                 name = ski.Identifier;
140                                 method = "ski";
141                         }
142                         else {
143                                 method = "tbp"; // thumbprint
144                                 name = certificate.Hash;
145                         }
146
147                         StringBuilder sb = new StringBuilder (method);
148                         sb.Append ("-");
149                         foreach (byte b in name) {
150                                 sb.Append (b.ToString ("X2", CultureInfo.InvariantCulture));
151                         }
152                         sb.Append (".cer");
153
154                         return sb.ToString ();
155                 }
156
157                 private byte[] Load (string filename) 
158                 {
159                         byte[] data = null;
160                         using (FileStream fs = File.OpenRead (filename)) {
161                                 data = new byte [fs.Length];
162                                 fs.Read (data, 0, data.Length);
163                                 fs.Close ();
164                         }
165                         return data;
166                 }
167
168                 private X509Certificate LoadCertificate (string filename) 
169                 {
170                         byte[] data = Load (filename);
171                         X509Certificate cert = new X509Certificate (data);
172                         return cert;
173                 }
174
175                 private X509Crl LoadCrl (string filename) 
176                 {
177                         byte[] data = Load (filename);
178                         X509Crl crl = new X509Crl (data);
179                         return crl;
180                 }
181
182                 private bool CheckStore (string path, bool throwException)
183                 {
184                         try {
185                                 if (Directory.Exists (path))
186                                         return true;
187                                 Directory.CreateDirectory (path);
188                                 return Directory.Exists (path);
189                         }
190                         catch {
191                                 if (throwException)
192                                         throw;
193                                 return false;
194                         }
195                 }
196
197                 private X509CertificateCollection BuildCertificatesCollection (string storeName) 
198                 {
199                         X509CertificateCollection coll = new X509CertificateCollection ();
200                         string path = Path.Combine (_storePath, storeName);
201                         if (!CheckStore (path, false))
202                                 return coll;    // empty collection
203
204                         string[] files = Directory.GetFiles (path, "*.cer");
205                         if ((files != null) && (files.Length > 0)) {
206                                 foreach (string file in files) {
207                                         try {
208                                                 X509Certificate cert = LoadCertificate (file);
209                                                 coll.Add (cert);
210                                         }
211                                         catch {
212                                                 // in case someone is dumb enough
213                                                 // (like me) to include a base64
214                                                 // encoded certs (or other junk 
215                                                 // into the store).
216                                         }
217                                 }
218                         }
219                         return coll;
220                 }
221
222                 private ArrayList BuildCrlsCollection (string storeName) 
223                 {
224                         ArrayList list = new ArrayList ();
225                         string path = Path.Combine (_storePath, storeName);
226                         if (!CheckStore (path, false))
227                                 return list;    // empty list
228
229                         string[] files = Directory.GetFiles (path, "*.crl");
230                         if ((files != null) && (files.Length > 0)) {
231                                 foreach (string file in files) {
232                                         try {
233                                                 X509Crl crl = LoadCrl (file);
234                                                 list.Add (crl);
235                                         }
236                                         catch {
237                                                 // junk catcher
238                                         }
239                                 }
240                         }
241                         return list;
242                 }
243         }
244 }