2 // System.Web.Configuration.MachineKeySection
5 // Chris Toshok (toshok@ximian.com)
6 // Sebastien Pouliot <sebastien@ximian.com>
8 // (c) Copyright 2005, 2010 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.ComponentModel;
35 using System.Configuration;
36 using System.Security.Cryptography;
37 using System.Web.Util;
39 namespace System.Web.Configuration {
41 public sealed class MachineKeySection : ConfigurationSection
43 static ConfigurationProperty decryptionProp;
44 static ConfigurationProperty decryptionKeyProp;
45 static ConfigurationProperty validationProp;
46 static ConfigurationProperty validationKeyProp;
47 static ConfigurationPropertyCollection properties;
48 static MachineKeyValidationConverter converter = new MachineKeyValidationConverter ();
49 MachineKeyValidation validation;
51 static MachineKeySection ()
53 decryptionProp = new ConfigurationProperty ("decryption", typeof (string), "Auto",
54 PropertyHelper.WhiteSpaceTrimStringConverter,
55 PropertyHelper.NonEmptyStringValidator,
56 ConfigurationPropertyOptions.None);
57 decryptionKeyProp = new ConfigurationProperty ("decryptionKey", typeof (string), "AutoGenerate,IsolateApps",
58 PropertyHelper.WhiteSpaceTrimStringConverter,
59 PropertyHelper.NonEmptyStringValidator,
60 ConfigurationPropertyOptions.None);
61 validationProp = new ConfigurationProperty ("validation", typeof (string), "HMACSHA256",
62 PropertyHelper.WhiteSpaceTrimStringConverter,
63 PropertyHelper.NonEmptyStringValidator,
64 ConfigurationPropertyOptions.None);
65 validationKeyProp = new ConfigurationProperty ("validationKey", typeof (string), "AutoGenerate,IsolateApps",
66 PropertyHelper.WhiteSpaceTrimStringConverter,
67 PropertyHelper.NonEmptyStringValidator,
68 ConfigurationPropertyOptions.None);
70 properties = new ConfigurationPropertyCollection ();
72 properties.Add (decryptionProp);
73 properties.Add (decryptionKeyProp);
74 properties.Add (validationProp);
75 properties.Add (validationKeyProp);
77 Config.AutoGenerate (MachineKeyRegistryStorage.KeyType.Encryption);
78 Config.AutoGenerate (MachineKeyRegistryStorage.KeyType.Validation);
81 public MachineKeySection ()
83 // get DefaultValue from ValidationAlgorithm
84 validation = (MachineKeyValidation) converter.ConvertFrom (null, null, ValidationAlgorithm);
88 public MachineKeyCompatibilityMode CompatibilityMode {
92 protected internal override void Reset (ConfigurationElement parentElement)
94 base.Reset (parentElement);
95 decryption_key = null;
96 validation_key = null;
97 decryption_template = null;
98 validation_template = null;
101 [TypeConverter (typeof (WhiteSpaceTrimStringConverter))]
102 [StringValidator (MinLength = 1)]
103 [ConfigurationProperty ("decryption", DefaultValue = "Auto")]
104 public string Decryption {
105 get { return (string) base [decryptionProp];}
107 decryption_template = MachineKeySectionUtils.GetDecryptionAlgorithm (value);
108 base[decryptionProp] = value;
112 [TypeConverter (typeof (WhiteSpaceTrimStringConverter))]
113 [StringValidator (MinLength = 1)]
114 [ConfigurationProperty ("decryptionKey", DefaultValue = "AutoGenerate,IsolateApps")]
115 public string DecryptionKey {
116 get { return (string) base [decryptionKeyProp];}
118 base[decryptionKeyProp] = value;
119 SetDecryptionKey (value);
123 // property exists for backward compatibility
124 public MachineKeyValidation Validation {
125 get { return validation; }
127 if (value == MachineKeyValidation.Custom)
128 throw new ArgumentException ();
130 string algo = value.ToString ();
131 // enum and accept values differs for TripleDES
132 ValidationAlgorithm = (algo == "TripleDES") ? "3DES" : algo;
136 [StringValidator (MinLength = 1)]
137 [TypeConverter (typeof (WhiteSpaceTrimStringConverter))]
138 [ConfigurationProperty ("validation", DefaultValue = "HMACSHA256")]
139 public string ValidationAlgorithm {
140 get { return (string) base [validationProp];}
145 if (value.StartsWith ("alg:"))
146 validation = MachineKeyValidation.Custom;
148 validation = (MachineKeyValidation) converter.ConvertFrom (null, null, value);
150 base[validationProp] = value;
154 [TypeConverter (typeof (WhiteSpaceTrimStringConverter))]
155 [StringValidator (MinLength = 1)]
156 [ConfigurationProperty ("validationKey", DefaultValue = "AutoGenerate,IsolateApps")]
157 public string ValidationKey {
158 get { return (string) base [validationKeyProp];}
160 base[validationKeyProp] = value;
161 SetValidationKey (value);
165 protected internal override ConfigurationPropertyCollection Properties {
166 get { return properties; }
170 internal static MachineKeySection Config {
171 get { return WebConfigurationManager.GetSection ("system.web/machineKey") as MachineKeySection; }
174 byte[] decryption_key;
175 byte[] validation_key;
176 SymmetricAlgorithm decryption_template;
177 KeyedHashAlgorithm validation_template;
179 internal SymmetricAlgorithm GetDecryptionAlgorithm ()
181 // code location to help with unit testing the code
182 return MachineKeySectionUtils.GetDecryptionAlgorithm (Decryption);
185 // not to be reused outside algorithm and key validation purpose
186 SymmetricAlgorithm DecryptionTemplate {
188 if (decryption_template == null)
189 decryption_template = GetDecryptionAlgorithm ();
190 return decryption_template;
194 internal byte [] GetDecryptionKey ()
196 if (decryption_key == null)
197 SetDecryptionKey (DecryptionKey);
198 return decryption_key;
201 void SetDecryptionKey (string key)
203 if ((key == null) || key.StartsWith ("AutoGenerate")) {
204 decryption_key = AutoGenerate (MachineKeyRegistryStorage.KeyType.Encryption);
207 decryption_key = MachineKeySectionUtils.GetBytes (key, key.Length);
208 DecryptionTemplate.Key = decryption_key;
211 decryption_key = null;
212 throw new ArgumentException ("Invalid key length");
217 internal KeyedHashAlgorithm GetValidationAlgorithm ()
219 // code location to help with unit testing the code
220 return MachineKeySectionUtils.GetValidationAlgorithm (this);
223 // not to be reused outside algorithm and key validation purpose
224 KeyedHashAlgorithm ValidationTemplate {
226 if (validation_template == null)
227 validation_template = GetValidationAlgorithm ();
228 return validation_template;
232 internal byte [] GetValidationKey ()
234 if (validation_key == null)
235 SetValidationKey (ValidationKey);
236 return validation_key;
239 // key can be expended for HMAC - i.e. a small key, e.g. 32 bytes, is still accepted as valid
240 // the HMAC class already deals with keys larger than what it can use (digested to right size)
241 void SetValidationKey (string key)
243 if ((key == null) || key.StartsWith ("AutoGenerate")) {
244 validation_key = AutoGenerate (MachineKeyRegistryStorage.KeyType.Validation);
247 validation_key = MachineKeySectionUtils.GetBytes (key, key.Length);
248 ValidationTemplate.Key = validation_key;
250 catch (CryptographicException) {
251 // second chance, use the key length that the HMAC really wants
253 byte[] expanded_key = new byte [ValidationTemplate.Key.Length];
254 Array.Copy (validation_key, 0, expanded_key, 0, validation_key.Length);
255 ValidationTemplate.Key = expanded_key;
256 validation_key = expanded_key;
259 validation_key = null;
260 throw new ArgumentException ("Invalid key length");
266 byte[] AutoGenerate (MachineKeyRegistryStorage.KeyType type)
270 key = MachineKeyRegistryStorage.Retrieve (type);
272 // ensure the stored key is usable with the selection algorithm
273 if (type == MachineKeyRegistryStorage.KeyType.Encryption)
274 DecryptionTemplate.Key = key;
275 else if (type == MachineKeyRegistryStorage.KeyType.Validation)
276 ValidationTemplate.Key = key;
277 } catch (Exception) {
280 // some algorithms have special needs for key (e.g. length, parity, weak keys...)
281 // so we better ask them to provide a default key (than to generate/use bad ones)
283 if (type == MachineKeyRegistryStorage.KeyType.Encryption)
284 key = DecryptionTemplate.Key;
285 else if (type == MachineKeyRegistryStorage.KeyType.Validation)
286 key = ValidationTemplate.Key;
287 MachineKeyRegistryStorage.Store (key, type);