ASP.NET machinekey set keys in code

I want to adjust the machine keys dynamically in code during runtime, for an IIS hosted ASP.NET MVC 4 website.

The machine keys, encryption and validation keys as well as algorithms to use, are stored in a database. Instead of reading the values from the web.config file, I want to inject those values during application startup and let the system use those instead.

Is there any way to accomplish that without having to change web.config at all (to only change the in memory configuration)?

I have tried accessing the configuration section but it is marked as readonly and is also sealed, so I cannot override IsReadOnly(). However, there is a setter that is an indicator, to me, that there may be a way to potentially remove the readonly flag.

var configSection = (MachineKeySection)ConfigurationManager.GetSection("system.web/machineKey");
if (!configSection.IsReadOnly())
{
       configSection.ValidationKey = _platformInfo.MachineKey.ValidationKey;
       configSection.DecryptionKey = _platformInfo.MachineKey.EncryptionKey;
       ...
}

Is there any way to accomplish this?
The only alternative I can see is to use a custom method like AppHarbor, however I would rather stick with the built in approach if it is possible at all.

In case someone asks why I want to do that, the reason is,
this is for a large number of identical websites running in a webfarm. Hence, having non-auto-generated keys is a must (must be the same on each server). Also each website should be isolated and should not share the same keys. As all websites are identical in their physical representation, they share the same physical location. That is the reason the web.config file cannot contain application specific settings.

Edit: It would be very helpful to confirm, at least, if it is simply not possible. As said, one can use custom authentication and encryption methods which would avoid using the machine key settings altogether. Thanks.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

It’s ugly, but I was able to use reflection to temporarily remove the read-only bit from the config section, set the keys, then restore it:

var getter = typeof(MachineKeySection).GetMethod("GetApplicationConfig", BindingFlags.Static | BindingFlags.NonPublic);
var config = (MachineKeySection)getter.Invoke(null, Array.Empty<object>());

var readOnlyField = typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
readOnlyField.SetValue(config, false);

config.DecryptionKey = myKeys.EncryptionKey;
config.ValidationKey = myKeys.ValidationKey;

readOnlyField.SetValue(config, true);

Method 2

There is no way to set this programmatically once the web application starts. However, it is still possible to accomplish your goal.

If each application is running in its own application pool, and if each application pool has its own identity, then check out the CLRConfigFile switch in applicationHost.config. You can use this per-application pool to inject a new level of configuration. See http://weblogs.asp.net/owscott/archive/2011/12/01/setting-an-aspnet-config-file-per-application-pool.aspx for an example of how to use this. You could set an explicit and unique <system.web/machineKey> element in each application pool’s custom CLR config file.

This is the same mechanism used by Azure Web Sites, GoDaddy, and other hosters that need to set default explicit machine keys on a per-application basis. Remember to ACL each target .config file appropriately for the application pool which will be accessing it.

Method 3

Inside running code, probably not.

But, in another application like a Console, I solved using this code:

private static void ChangeWebConfig(string validationKey, string decryptionKey, string webConfigPath)
{
    ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
    configFileMap.ExeConfigFilename = webConfigPath;
    System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
    MachineKeySection section = (MachineKeySection)config.GetSection("system.web/machineKey");
    section.ValidationKey = validationKey;
    section.DecryptionKey = decryptionKey;
    config.Save();
}


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x