Skip to content

Commit

Permalink
[asp.net] Normalization of cryptographic uses in asp.net.
Browse files Browse the repository at this point in the history
  Backport of commit a22389f
      Normalization of cryptographic uses in asp.net

    * System.Web.Configuration_2.0/MachineKeyRegistryStorage.cs:
    Remove key length check and generic key generation.

    * System.Web.Configuration_2.0/MachineKeySection.cs: Add support for 4.0
    ValidationAlgorithm and the use of custom algorithms (validation and
    decryption). Allow the use of any, valid, key length (based on the
    algorithm). Let each algorithm creates its own key (e.g. special needs,
    default length...)

    * System.Web.Configuration_2.0/MachineKeySectionUtils.cs: Remove key
    generation (from random) code and 192bits key length hack (won't work
    with custom algorithms). Add support for new (4.0) algorithms, including
    custom ones. Provide uniform/shared code to Encrypt/Decrypt, Sign/Verify
    and EncryptSign/VerifyDecrypt using MachineKeySection data.

    * System.Web.Configuration_2.0/MachineKeyValidation.cs: Add new (4.0)
    values.

    * System.Web.Configuration_2.0/MachineKeyValidationConverter.cs: Add
    support for new (4.0) algorithms.

    * System.Web.Handlers/AssemblyResourceLoader.cs: Use the new common
    cryptographic code and base64 the encrypted data.

    * System.Web.Security/FormsAuthentication.cs: Use the new common
    cryptographic code and base64 the signed and/or encrypted data.

    * System.Web.Security/MembershipHelper.cs: Use the new common
    cryptographic code - this should be 100% compatible with existing data.

    * System.Web.Security/RolePrincipal.cs: Use the new common cryptographic
    code.

    * System.Web.Security/SqliteMembershipProvider.cs: Adapt code for
    internal API change.
  • Loading branch information
grendello committed Nov 30, 2010
1 parent 8125ab9 commit 414ecd8
Show file tree
Hide file tree
Showing 25 changed files with 1,646 additions and 768 deletions.
198 changes: 126 additions & 72 deletions mcs/class/System.Web/System.Web.Configuration/MachineKeyConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,40 @@ namespace System.Web.Configuration
{
class MachineKeyConfig
{
byte [] validation_key;
//bool isolate_validation;
byte [] decryption_key;
byte [] decryption_key_192bits;
//bool isolate_decryption; // For us, this is always true by now.
byte[] decryption_key;
byte[] validation_key;
string decryption_key_name;
string validation_key_name;
SymmetricAlgorithm decryption_template;
KeyedHashAlgorithm validation_template;
MachineKeyValidation validation_type;

static byte [] autogenerated;
static byte [] autogenerated_decrypt;
internal static MachineKeyConfig Config {
get {
HttpContext context = HttpContext.Current;
if (context == null)
return null;

return context.GetConfig ("system.web/machineKey") as MachineKeyConfig;
}
}

static MachineKeyConfig ()
{
autogenerated = new byte [64];
RandomNumberGenerator rng = RandomNumberGenerator.Create ();
rng.GetBytes (autogenerated);
autogenerated_decrypt = new byte [64];
rng.GetBytes (autogenerated_decrypt);
// not to be reused outside algorithm and key validation purpose
SymmetricAlgorithm DecryptionTemplate {
get {
if (decryption_template == null)
decryption_template = GetDecryptionAlgorithm ();
return decryption_template;
}
}

// not to be reused outside algorithm and key validation purpose
KeyedHashAlgorithm ValidationTemplate {
get {
if (validation_template == null)
validation_template = GetValidationAlgorithm ();
return validation_template;
}
}

internal MachineKeyConfig (object parent)
Expand All @@ -68,75 +85,45 @@ internal MachineKeyConfig (object parent)
}
}

static byte ToHexValue (char c, bool high)
internal byte [] GetDecryptionKey ()
{
byte v;
if (c >= '0' && c <= '9')
v = (byte) (c - '0');
else if (c >= 'a' && c <= 'f')
v = (byte) (c - 'a' + 10);
else if (c >= 'A' && c <= 'F')
v = (byte) (c - 'A' + 10);
else
throw new ArgumentException ("Invalid hex character");

if (high)
v <<= 4;

return v;
}

internal static byte [] GetBytes (string key, int len)
{
byte [] result = new byte [len / 2];
for (int i = 0; i < len; i += 2)
result [i / 2] = (byte) (ToHexValue (key [i], true) + ToHexValue (key [i + 1], false));

return result;
if (decryption_key == null)
SetDecryptionKey (decryption_key_name);
return decryption_key;
}

static byte [] MakeKey (string key, bool decryption) //, out bool isolate)
internal void SetDecryptionKey (string key)
{
if (key == null || key.StartsWith ("AutoGenerate")){
//isolate = key.IndexOf ("IsolateApps") != 1;

return (decryption) ? autogenerated_decrypt : autogenerated;
if ((key == null) || key.StartsWith ("AutoGenerate")) {
decryption_key = AutoGenerate (MachineKeyRegistryStorage.KeyType.Encryption);
} else {
try {
decryption_key = MachineKeySectionUtils.GetBytes (key, key.Length);
DecryptionTemplate.Key = decryption_key;
}
catch {
decryption_key = null;
throw new ArgumentException ("Invalid key length");
}
}

//isolate = false;

int len = key.Length;
if (len < 40 || len > 128 || (len % 2) == 1)
throw new ArgumentException ("Invalid key length");

return GetBytes (key, len);
}
internal void SetValidationKey (string n)

internal KeyedHashAlgorithm GetValidationAlgorithm ()
{
validation_key = MakeKey (n, false); //, out isolate_validation);
}

internal byte [] ValidationKey {
get { return validation_key; }
// code location to help with unit testing the code
return MachineKeySectionUtils.GetValidationAlgorithm (this.ValidationType);
}

internal void SetDecryptionKey (string n)
internal SymmetricAlgorithm GetDecryptionAlgorithm ()
{
decryption_key = MakeKey (n, true); //, out isolate_decryption);
decryption_key_192bits = new byte [24];
int count = 24;
if (decryption_key.Length < 24)
count = decryption_key.Length;
Buffer.BlockCopy (decryption_key, 0, decryption_key_192bits, 0, count);
}

internal byte [] DecryptionKey {
get { return decryption_key; }
}
string name;

internal byte [] DecryptionKey192Bits {
get { return decryption_key_192bits; }
if (decryption_key_name == null || decryption_key_name.StartsWith ("AutoGenerate"))
name = "Auto";
else
name = decryption_key_name;

return MachineKeySectionUtils.GetDecryptionAlgorithm (name);
}

internal MachineKeyValidation ValidationType {
Expand All @@ -147,6 +134,73 @@ internal MachineKeyValidation ValidationType {
validation_type = value;
}
}

internal byte [] GetValidationKey ()
{
if (validation_key == null)
SetValidationKey (validation_key_name);
return validation_key;
}

// key can be expended for HMAC - i.e. a small key, e.g. 32 bytes, is still accepted as valid
// the HMAC class already deals with keys larger than what it can use (digested to right size)
internal void SetValidationKey (string key)
{
validation_key_name = key;
if ((key == null) || key.StartsWith ("AutoGenerate")) {
validation_key = AutoGenerate (MachineKeyRegistryStorage.KeyType.Validation);
} else {
try {
validation_key = MachineKeySectionUtils.GetBytes (key, key.Length);
ValidationTemplate.Key = validation_key;
}
catch (CryptographicException) {
// second chance, use the key length that the HMAC really wants
try {
byte[] expanded_key = new byte [ValidationTemplate.Key.Length];
Array.Copy (validation_key, 0, expanded_key, 0, validation_key.Length);
ValidationTemplate.Key = expanded_key;
validation_key = expanded_key;
}
catch {
validation_key = null;
throw new ArgumentException ("Invalid key length");
}
}
}
}

byte[] AutoGenerate (MachineKeyRegistryStorage.KeyType type)
{
byte[] key = null;
#if TARGET_J2EE
{
#else
try {
key = MachineKeyRegistryStorage.Retrieve (type);

// ensure the stored key is usable with the selection algorithm
if (type == MachineKeyRegistryStorage.KeyType.Encryption)
DecryptionTemplate.Key = key;
else if (type == MachineKeyRegistryStorage.KeyType.Validation)
ValidationTemplate.Key = key;
} catch (Exception) {
key = null;
}
#endif
// some algorithms have special needs for key (e.g. length, parity, weak keys...)
// so we better ask them to provide a default key (than to generate/use bad ones)
if (key == null) {
if (type == MachineKeyRegistryStorage.KeyType.Encryption)
key = DecryptionTemplate.Key;
else if (type == MachineKeyRegistryStorage.KeyType.Validation)
key = ValidationTemplate.Key;
#if !TARGET_J2EE
MachineKeyRegistryStorage.Store (key, type);
#endif
}
return key;
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;
using System.Security.Cryptography;
using Microsoft.Win32;

namespace System.Web.Configuration
Expand All @@ -42,9 +40,6 @@ public enum KeyType
Encryption
};

const int encryptionKeyLength = 64;
const int validationKeyLength = 64;

static string keyEncryption;
static string keyValidation;

Expand All @@ -62,31 +57,16 @@ static MachineKeyRegistryStorage ()
}

public static byte[] Retrieve (KeyType kt)
{
byte[] ret = GetKey (kt);
if (ret == null) {
ret = Generate (kt);
if (ret != null)
Store (ret, kt);
}

return ret;
}

static byte[] GetKey (KeyType kt)
{
string key = null;
int len;

switch (kt) {
case KeyType.Validation:
key = keyValidation;
len = validationKeyLength;
break;

case KeyType.Encryption:
key = keyEncryption;
len = validationKeyLength;
break;

default:
Expand All @@ -107,11 +87,7 @@ static byte[] GetKey (KeyType kt)

if (o == null || o.GetType () != typeof (byte[]))
return null;
byte[] ret = (byte[])o;
if (ret.Length != len)
return null;

return ret;
return (byte[]) o;
}

static RegistryKey OpenRegistryKey (string path, bool write)
Expand All @@ -134,23 +110,19 @@ static RegistryKey OpenRegistryKey (string path, bool write)
return ret;
}

static void Store (byte[] buf, KeyType kt)
public static void Store (byte[] buf, KeyType kt)
{
if (buf == null)
return;

string key = null;
int len;

switch (kt) {
case KeyType.Validation:
key = keyValidation;
len = validationKeyLength;
break;

case KeyType.Encryption:
key = keyEncryption;
len = validationKeyLength;
break;

default:
Expand All @@ -160,9 +132,6 @@ static void Store (byte[] buf, KeyType kt)
if (key == null)
return;

if (buf.Length != len)
throw new ArgumentException ("Key has invalid length");

try {
using (RegistryKey rk = OpenRegistryKey (key, true)) {
#if NET_2_0
Expand All @@ -180,27 +149,5 @@ static void Store (byte[] buf, KeyType kt)
throw new ApplicationException ("Failed to store encryption key in the registry.", ex);
}
}

static byte[] Generate (KeyType kt)
{
RandomNumberGenerator rng = RandomNumberGenerator.Create ();
byte[] ret = null;

switch (kt) {
case KeyType.Validation:
ret = new byte [validationKeyLength];
break;

case KeyType.Encryption:
ret = new byte [encryptionKeyLength];
break;

default:
throw new ArgumentException ("Unknown key type.");
}

rng.GetBytes (ret);
return ret;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
// Copyright (C) 2004, 2010 Novell, Inc (http://www.novell.com)
//

using System.Resources;

namespace System.Web.Configuration
{
#if NET_2_0
Expand All @@ -40,7 +38,13 @@ enum MachineKeyValidation
MD5 = 0,
SHA1 = 1,
TripleDES = 2,
AES = 3
AES = 3,
#if NET_4_0
HMACSHA256 = 4,
HMACSHA384 = 5,
HMACSHA512 = 6,
Custom = 7
#endif
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ static public PagesConfiguration GetInstance (HttpContext context)
{
if (context == null)
context = HttpContext.Current;
if (context == null)
return new PagesConfiguration (null);
return context.GetConfig ("system.web/pages") as PagesConfiguration;
}
}
Expand Down
Loading

0 comments on commit 414ecd8

Please sign in to comment.