.NET Framework 1.1 Security Guidelines - Cryptography

From Guidance Share

Jump to: navigation, search

- J.D. Meier, Alex Mackman, Michael Dunner, Srinath Vasireddy, Ray Escamilla and Anandha Murukan


Use Platform-provided Cryptographic Services

Do not create your own cryptographic implementations. It is extremely unlikely that these implementations will be as secure as the industry standard algorithms provided by the platform; that is, the operating system and the .NET Framework. Managed code should use the algorithms provided by the System.Security.Cryptography namespace for encryption, decryption, hashing, random number generating, and digital signatures.

Many of the types in this namespace wrap the operating system CryptoAPI, while others implement algorithms in managed code.

Generate Random Keys

If you need to generate encryption keys programmatically, use RNGCryptoServiceProvider for creating keys and initialization vectors and do not use the Random class. Unlike the Random class, RNGCryptoServiceProvider creates cryptographically strong random numbers which are FIPS-140 compliant. The following code shows how to use this function.

using System.Security.Cryptography;
. . .
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] key = new byte[keySize];

Use PasswordDeriveBytes for Password-Based Encryption

The System.Security.Cryptography.DeriveBytes namespace provides PasswordDeriveBytes for use when encrypting data based on a password the user supplies. To decrypt, the user must supply the same password used to encrypt.

Note that this approach is not for password authentication. Store a password verifier in the form of a hash value with a salt value order to authenticate a user's password. Use PasswordDeriveBytes to generate keys for password-based encryption.

PasswordDeriveBytes accepts a password, salt, an encryption algorithm, a hashing algorithm, key size (in bits), and initialization vector data to create a symmetric key to be used for encryption.

Note Although .NET 2.0 still supports PasswordDeriveBytes for backward compatibility with .NET 1.1, you should use Rfc2898DeriveBytes. It supports the RSA Password-Based Key Derivation Function version 2 (PBKDF2), which is an improved version of the PBKDF1 standard implementation used by PasswordDeriveBytes. For more information, see ".NET Framework Class Library - Rfc2898DeriveBytes Class." at http://msdn2.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx

After the key is used to encrypt the data, clear it from memory but persist the salt and initialization vector. These values should be protected and are needed to re-generate the key for decryption.

For more information about storing password hashes with salt, see Chapter 14, "Building Secure Data Access." at http://msdn.microsoft.com/library/en-us/dnnetsec/html/THCMCh14.asp

Prefer Large Keys

When generating an encryption key or key pair, use the largest key size possible for the algorithm. This does not necessarily make the algorithm more secure but dramatically increases the time needed to successfully perform a brute force attack on the key. The following code shows how to find the largest supported key size for a particular algorithm.

private int GetLargestSymKeySize(SymmetricAlgorithm symAlg)
 KeySizes[] sizes = symAlg.LegalKeySizes;
 return sizes[sizes.Length].MaxSize;

private int GetLargestAsymKeySize(AsymmetricAlgorithm asymAlg)
 KeySizes[] sizes = asymAlg.LegalKeySizes;
 return sizes[sizes.Length].MaxSize;

Use DPAPI to Avoid Key Management

DPAPI is a native encryption/decryption feature provided by Microsoft Windows 2000. One of the main advantages of using DPAPI is that the encryption key is managed by the operating system, because the key is derived from the password that is associated with the process account (or thread account if the thread is impersonating) that calls the DPAPI functions.

Note In .NET 2.0, you no longer need to use P/Invoke. Instead, you can use the new ProtectedData class, which contains two static methods: Protect and Unprotect. For more information, see ".NET Framework Class Library - ProtectedData Class." at http://msdn2.microsoft.com/en-us/library/system.security.cryptography.protecteddata.aspx

User Key vs. Machine Key

You can perform encryption with DPAPI using either the user key or the machine key. By default, DPAPI uses a user key. This means that only a thread that runs under the security context of the user account that encrypted the data can decrypt the data. You can instruct DPAPI to use the machine key by passing the CRYPTPROTECT_LOCAL_MACHINE flag to the CryptProtectData API. In this event, any user on the current computer can decrypt the data.

The user key option can be used only if the account used to perform the encryption has a loaded user profile. If you run code in an environment where the user profile is not loaded, you cannot easily use the user store and should opt for the machine store instead.

The .NET Framework loads the user profile for the ASPNET account on Windows 2000. On Windows Server 2003, the profile for this account is only loaded if the ASP.NET process model is used. It is not loaded explicitly by IIS 6 if the IIS 6 process model is used on Windows Server 2003.

If you use the machine key option, you should use an ACL to secure the encrypted data, for example in a registry key, and use this approach to limit which users have access to the encrypted data. For added security, you should also pass an optional entropy value to the DPAPI functions.

Note An entropy value is an additional random value that can be passed to the DPAPI CryptProtectData and CryptUnprotectData functions. The same value that is used to encrypt the data must be used to decrypt the data. The machine key option means that any user on the computer can decrypt the data. With added entropy, the user must also know the entropy value.

The drawback with using entropy is that you must manage the entropy value as you would manage a key. To avoid entropy management issues, use the machine store without entropy and validate users and code (using code access security) thoroughly before calling the DPAPI code.

For more information about using DPAPI from ASP.NET Web applications, see "How To: Create a DPAPI Library," in the How To section of "Building Secure ASP.NET Applications," at http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT07.asp.

Do Not Store Keys in Code

Do not store keys in code because hard-coded keys in your compiled assembly can be disassembled using tools similar to ILDASM, which will render your key in plaintext.

Restrict Access to Persisted Keys

When storing keys in persistent storage to be used at runtime, use appropriate ACLs and limit access to the key. Access to the key should be granted only to Administrators, SYSTEM, and the identity of the code at runtime, for example the ASPNET or Network Service account.

When backing up a key, do not store it in plain text, encrypt it using DPAPI or a strong password and place it on removable media.

Cycle Keys Periodically

You should change your encryption keys from time to time because a static secret is more likely to be discovered over time. Did you write it down somewhere? Did Bob the administrator with the secrets change positions in your company or leave the company? Are you using the same session key to encrypt communication for a long time? Do not overuse keys.

Key Compromise

Keys can be compromised in a number of ways. For example, you may lose the key or discover that an attacker has stolen or discovered the key.

If your private key used for asymmetric encryption and key exchange is compromised, do not continue to use it, and notify the users of the public key that the key has been compromised. If you used the key to sign documents, they need to be re-signed.

If the private key of your certificate is compromised, contact the issuing certification authority to have your certificate placed on a certificate revocation list. Also, change the way your keys are stored to avoid a future compromise.

Personal tools