Facilitate the merge
[mono.git] / mcs / class / System.Web / System.Web.Configuration_2.0 / MachineKeyRegistryStorage.cs
1 //
2 // System.Web.Configuration.MachineKeyRegistryStorage
3 //
4 // Authors:
5 //      Marek Habersack <mhabersack@novell.com>
6 //
7 // (C) 2007 Novell, Inc
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.Security.Cryptography;
33 using Microsoft.Win32;
34
35 namespace System.Web.Configuration
36 {
37         internal class MachineKeyRegistryStorage
38         {
39                 public enum KeyType
40                 {
41                         Validation,
42                         Encryption
43                 };
44
45                 const int encryptionKeyLength = 64;
46                 const int validationKeyLength = 64;
47                 
48                 static string keyEncryption;
49                 static string keyValidation;
50                 
51                 static MachineKeyRegistryStorage ()
52                 {
53                         string appName = AppDomain.CurrentDomain.SetupInformation.ApplicationName;
54                         if (appName == null)
55                                 return;
56                         
57                         string hash = appName.GetHashCode ().ToString ("x");
58                         keyEncryption = "software\\mono\\asp.net\\" + Environment.Version.ToString () +
59                                 "\\autogenkeys\\" + hash + "-" + ((int)KeyType.Encryption).ToString ();
60                         keyValidation = "software\\mono\\asp.net\\" + Environment.Version.ToString () +
61                                 "\\autogenkeys\\" + hash + "-" + ((int)KeyType.Validation).ToString ();
62                 }
63                 
64                 public static byte[] Retrieve (KeyType kt)
65                 {
66                         byte[] ret = GetKey (kt);
67                         if (ret == null) {
68                                 ret = Generate (kt);
69                                 if (ret != null)
70                                         Store (ret, kt);
71                         }
72                         
73                         return ret;
74                 }
75
76                 static byte[] GetKey (KeyType kt)
77                 {
78                         string key = null;
79                         int len;
80                         
81                         switch (kt) {
82                                 case KeyType.Validation:
83                                         key = keyValidation;
84                                         len = validationKeyLength;
85                                         break;
86
87                                 case KeyType.Encryption:
88                                         key = keyEncryption;
89                                         len = validationKeyLength;
90                                         break;
91
92                                 default:
93                                         throw new ArgumentException ("Unknown key type.");
94                         }
95
96                         if (key == null)
97                                 return null;
98                         
99                         object o = null;
100
101                         try {
102                                 RegistryKey v = OpenRegistryKey (key, false);
103                                 o = v.GetValue ("AutoGenKey", null);
104                         } catch (Exception) {
105                                 return null;
106                         }
107
108                         if (o == null || o.GetType () != typeof (byte[]))
109                                 return null;
110                         byte[] ret = (byte[])o;
111                         if (ret.Length != len)
112                                 return null;
113
114                         return ret;
115                 }
116
117                 static RegistryKey OpenRegistryKey (string path, bool write)
118                 {
119                         RegistryKey ret, tmp;
120                         string[] keys = path.Split ('\\');
121                         int klen = keys.Length;
122
123                         ret = Registry.CurrentUser;
124                         for (int i = 0; i < klen; i++) {
125                                 tmp = ret.OpenSubKey (keys [i], true);
126                                 if (tmp == null) {
127                                         if (!write)
128                                                 return null;
129                                         tmp = ret.CreateSubKey (keys [i]);
130                                 }
131                                 ret = tmp;
132                         }
133
134                         return ret;
135                 }
136                 
137                 static void Store (byte[] buf, KeyType kt)
138                 {
139                         if (buf == null)
140                                 return;
141                         
142                         string key = null;
143                         int len;
144                         
145                         switch (kt) {
146                                 case KeyType.Validation:
147                                         key = keyValidation;
148                                         len = validationKeyLength;
149                                         break;
150
151                                 case KeyType.Encryption:
152                                         key = keyEncryption;
153                                         len = validationKeyLength;
154                                         break;
155
156                                 default:
157                                         throw new ArgumentException ("Unknown key type.");
158                         }
159
160                         if (key == null)
161                                 return;
162
163                         if (buf.Length != len)
164                                 throw new ArgumentException ("Key has invalid length");
165
166                         try {
167                                 using (RegistryKey rk = OpenRegistryKey (key, true)) {
168 #if NET_2_0
169                                         rk.SetValue ("AutoGenKey", buf, RegistryValueKind.Binary);
170                                         rk.SetValue ("AutoGenKeyCreationTime", DateTime.Now.Ticks, RegistryValueKind.QWord);
171                                         rk.SetValue ("AutoGenKeyFormat", 2, RegistryValueKind.DWord);
172 #else
173                                         rk.SetValue ("AutoGenKey", buf);
174                                         rk.SetValue ("AutoGenKeyCreationTime", DateTime.Now.Ticks);
175                                         rk.SetValue ("AutoGenKeyFormat", 2);
176 #endif
177                                         rk.Flush (); // we want it synchronous
178                                 }
179                         } catch (Exception ex) {
180                                 throw new ApplicationException ("Failed to store encryption key in the registry.", ex);
181                         }
182                 }
183
184                 static byte[] Generate (KeyType kt)
185                 {
186                         RandomNumberGenerator rng = RandomNumberGenerator.Create ();
187                         byte[] ret = null;
188                         
189                         switch (kt) {
190                                 case KeyType.Validation:
191                                         ret = new byte [validationKeyLength];
192                                         break;
193
194                                 case KeyType.Encryption:
195                                         ret = new byte [encryptionKeyLength];
196                                         break;
197
198                                 default:
199                                         throw new ArgumentException ("Unknown key type.");
200                         }
201                         
202                         rng.GetBytes (ret);
203                         return ret;
204                 }
205         }
206 }