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