|
//------------------------------------------------------------------------------
// <copyright file="MachineKeyMasterKeyProvider.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
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 <machineKey> 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<string> 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 <machineKey> 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<string> specificPurposes = new List<string>();
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;
}
}
}
|