//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ namespace System.Web.Security.Cryptography { using System; using System.Collections.Generic; using System.Diagnostics; using System.Web.Configuration; // Gets this application's master keys from the element, // optionally going against the auto-gen keys if AutoGenerate has been specified. internal sealed class MachineKeyMasterKeyProvider : IMasterKeyProvider { private const int AUTOGEN_ENCRYPTION_OFFSET = 0; private const int AUTOGEN_ENCRYPTION_KEYLENGTH = 256; // AES-256 private const int AUTOGEN_VALIDATION_OFFSET = AUTOGEN_ENCRYPTION_KEYLENGTH; private const int AUTOGEN_VALIDATION_KEYLENGTH = 256; // HMACSHA256 private const string AUTOGEN_KEYDERIVATION_PRIMARYPURPOSE = "MachineKeyDerivation"; private const string AUTOGEN_KEYDERIVATION_ISOLATEAPPS_SPECIFICPURPOSE = "IsolateApps"; private const string AUTOGEN_KEYDERIVATION_ISOLATEBYAPPID_SPECIFICPURPOSE = "IsolateByAppId"; private string _applicationId; private string _applicationName; private CryptographicKey _autogenKeys; private CryptographicKey _encryptionKey; private KeyDerivationFunction _keyDerivationFunction; private readonly MachineKeySection _machineKeySection; private CryptographicKey _validationKey; // the only required parameter is 'machineKeySection'; other parameters are just used for unit testing internal MachineKeyMasterKeyProvider(MachineKeySection machineKeySection, string applicationId = null, string applicationName = null, CryptographicKey autogenKeys = null, KeyDerivationFunction keyDerivationFunction = null) { _machineKeySection = machineKeySection; _applicationId = applicationId; _applicationName = applicationName; _autogenKeys = autogenKeys; _keyDerivationFunction = keyDerivationFunction; } internal string ApplicationName { get { if (_applicationName == null) { _applicationName = HttpRuntime.AppDomainAppVirtualPath ?? Process.GetCurrentProcess().MainModule.ModuleName; } return _applicationName; } } internal string ApplicationId { get { if (_applicationId == null) { _applicationId = HttpRuntime.AppDomainAppId; } return _applicationId; } } internal CryptographicKey AutogenKeys { get { if (_autogenKeys == null) { _autogenKeys = new CryptographicKey(HttpRuntime.s_autogenKeys); } return _autogenKeys; } } internal KeyDerivationFunction KeyDerivationFunction { get { if (_keyDerivationFunction == null) { _keyDerivationFunction = SP800_108.DeriveKey; } return _keyDerivationFunction; } } private static void AddSpecificPurposeString(IList specificPurposes, string key, string value) { specificPurposes.Add(key + ": " + value); } // Generates 'cryptographicKey' from either the raw key material specified in config // or from the auto-generated key found in the system registry, optionally performing // subkey derivation. private CryptographicKey GenerateCryptographicKey(string configAttributeName, string configAttributeValue, int autogenKeyOffset, int autogenKeyCount, string errorResourceString) { byte[] keyMaterial = CryptoUtil.HexToBinary(configAttributeValue); // If contained a valid key, just use it verbatim. if (keyMaterial != null && keyMaterial.Length > 0) { return new CryptographicKey(keyMaterial); } // Otherwise, we need to generate it. bool autoGenerate = false; bool isolateApps = false; bool isolateByAppId = false; if (configAttributeValue != null) { foreach (string flag in configAttributeValue.Split(',')) { switch (flag) { case "AutoGenerate": autoGenerate = true; break; case "IsolateApps": isolateApps = true; break; case "IsolateByAppId": isolateByAppId = true; break; default: throw ConfigUtil.MakeConfigurationErrorsException( message: SR.GetString(errorResourceString), configProperty: _machineKeySection.ElementInformation.Properties[configAttributeName]); } } } if (!autoGenerate) { // at the absolute minimum, we must be configured to autogenerate throw ConfigUtil.MakeConfigurationErrorsException( message: SR.GetString(errorResourceString), configProperty: _machineKeySection.ElementInformation.Properties[configAttributeName]); } // The key should be a subset of the auto-generated key (which is a concatenation of several keys) CryptographicKey keyDerivationKey = AutogenKeys.ExtractBits(autogenKeyOffset, autogenKeyCount); List specificPurposes = new List(); if (isolateApps) { // Use the application name to derive a new cryptographic key AddSpecificPurposeString(specificPurposes, AUTOGEN_KEYDERIVATION_ISOLATEAPPS_SPECIFICPURPOSE, ApplicationName); } if (isolateByAppId) { // Use the application ID to derive a new cryptographic key AddSpecificPurposeString(specificPurposes, AUTOGEN_KEYDERIVATION_ISOLATEBYAPPID_SPECIFICPURPOSE, ApplicationId); } // Don't use the auto-gen key directly; derive a new one based on specified parameters. Purpose purpose = new Purpose(AUTOGEN_KEYDERIVATION_PRIMARYPURPOSE, specificPurposes.ToArray()); return KeyDerivationFunction(keyDerivationKey, purpose); } public CryptographicKey GetEncryptionKey() { if (_encryptionKey == null) { _encryptionKey = GenerateCryptographicKey( configAttributeName: "decryptionKey", configAttributeValue: _machineKeySection.DecryptionKey, autogenKeyOffset: AUTOGEN_ENCRYPTION_OFFSET, autogenKeyCount: AUTOGEN_ENCRYPTION_KEYLENGTH, errorResourceString: SR.Invalid_decryption_key); } return _encryptionKey; } public CryptographicKey GetValidationKey() { if (_validationKey == null) { _validationKey = GenerateCryptographicKey( configAttributeName: "validationKey", configAttributeValue: _machineKeySection.ValidationKey, autogenKeyOffset: AUTOGEN_VALIDATION_OFFSET, autogenKeyCount: AUTOGEN_VALIDATION_KEYLENGTH, errorResourceString: SR.Invalid_validation_key); } return _validationKey; } } }