.NET Framework 2.0 Security Guidelines - Cryptography

From Guidance Share

Jump to: navigation, search

- J.D. Meier, Alex Mackman, Blaine Wastell, Prashant Bansode, Chaitanya Bijwe


Use platform-provided cryptographic services

Cryptography is notoriously difficult to develop. The Windows crypto APIs are proven to be effective. These APIs are implementations of algorithms derived from years of academic research and study. Some developers believe that a less well-known algorithm can provide more security, but this is not true. Cryptographic algorithms are mathematically proven; therefore, the more scrutiny they receive, the better. An obscure algorithm will not protect a flawed cryptographic implementation from a determined attacker.

  • For hashing, use SHA1. For integrity checking, use HMACSHA1 or a digital signature mechanism
  • Consider using the XMLEncryption mechanisms when you need to encrypt different parts of a document under different keys or if you only want to encrypt small sections of a document.
  • Use X509 and SMIME encryption if you are using an internal or external public key infrastructure (PKI) based on digital certificates.

Use appropriately sized keys

Choosing a key size represents a trade-off between performance and security. If you choose a key that is too small, the data that you thought was well protected can be vulnerable to attack. If you choose a key that is too large, your system will be subject to a performance impact without a commensurate real-world improvement in security. The appropriate key size changes based on the cryptographic algorithm in use, and also changes over time as machine processing speeds increase and attack techniques become more sophisticated. The following recommendations will give you a margin of safety without sacrificing too much performance:

  • When you use an asymmetric algorithm (RSA), choose a 2048-bit key
  • When you use a symmetric algorithm (AES), choose a 128-bit key

Use GenerateKey to generate random keys

When you use a default constructor to create a new instance of a managed symmetric cryptographic class, a new key and initialization vector are automatically created. You should call the GenerateKey method on the symmetric algorithm instance. GenerateKey creates a random strong key and sets it to the algorithm.

When you use symmetric algorithms, creating and managing keys is an important part of cryptographic process. If you use weak keys, you increase the likelihood that an attacker can compromise the key and access your encrypted data. The following example shows how to use the GenerateKey method.

using System.Security.Cryptography;
// create instance of AES encryption algorithm and set the key value
RijndaelManaged aesEncryptionAlgorithm = new RijndaelManaged();
//Generate IV and Key for the instance
// Retrieve the IV and Key and encrypt and safeguard it.

Consider using DPAPI to avoid key management

By using DPAPI to encrypt sensitive data—either in memory or in persistent stores, such as configuration files or the registry—you avoid having to manage and protect the encryption key. With DPAPI, the operating system manages and protects the key.

For sensitive data stored in ASP.NET Web.config files, you can use the Aspnet_regiis tool and the data protection feature provided with ASP.NET 2.0. For more information see, "How To: Encrypt Configuration Section in ASP.NET 2.0 using DPAPI" at http://msdn.microsoft.com/library/en-us/dnpag2/html/PAGHT000005.asp"

Note DPAPI encryption is not recommended for use in Web farm scenarios because of machine affinity. Instead, you should use RSA encryption, which is also supported by the Aspnet_regiis tool.

For sensitive data stored in memory, you can use the ProtectedMemory class which provides a managed wrapper for DPAPI to encrypt the data. For text-based data, consider using SecureString instead. SecureString uses the ProtectedMemory class to encrypt text in memory.

The following example shows how to use the ProtectedMemory class for encrypting and decrypting data in memory.

using System.Security.Cryptography;
using System.Text;
byte[] optionalEntropy = {7,5,4,9,0};
byte[] dataToBeEncrypted = Encoding.Unicode.GetBytes("Test String 1211");
ProtectedMemory.Protect(dataToBeEncrypted, MemoryProtectionScope.SameLogon);
ProtectedMemory.Unprotect(dataToBeEncrypted, MemoryProtectionScope.SameLogon);
string originalData = Encoding.Unicode.GetString(dataToBeEncrypted);

For sensitive data stored in any other data store, use the ProtectedData class as shown in the following example.

using System.Security.Cryptography;
using System.Text;
byte[] optionalEntropy = {7,5,4,9,0};
byte[] dataToBeEncrypted = Encoding.Unicode.GetBytes("Test String");
byte[] encryptedData = ProtectedData.Protect(dataToBeEncrypted, optionalEntropy, DataProtectionScope.CurrentUser);
byte[] decryptedData = ProtectedData.Unprotect(encryptedData, optionalEntropy, DataProtectionScope.CurrentUser);
           string originalData = Encoding.Unicode.GetString(decryptedData);

When you use DPAPI, you can use the machine key store or the user key store. Use machine-level key storage in the following situations:

  • Your application runs on its own dedicated server with no other applications.
  • You have multiple applications on the same server, and you want those applications to be able to share sensitive information.

Use user-level key storage if you run your application in a shared hosting environment and you want to make sure that your application's sensitive data is not accessible to other applications on the server. In this situation, each application should run under a separate identity, and the resources for the application—such as files and databases—should be restricted to that identity.

Use PasswordDeriveBytes for password-based encryption

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

Note that you should not use this approach for password authentication. Store a password verifier in the form of a hash value with a salt value 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. 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 regenerate the key for decryption.

The following code shows how to call PasswordDeriveBytes.

using System.Security.Cryptography;
// Get salt (random bytes) uisng RNGCryptoServiceProvider.
byte[] salt = new byte[8];
new RNGCryptoServiceProvider().GetBytes(salt);
// Get the  PasswordDeriveBytes using password and salt
PasswordDeriveBytes passwordBytes = new PasswordDeriveBytes("P@ssword!", salt);
// Create a TripleDESCryptoServiceProvider object.
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
// Create the key and add it to the Key property.
tdes.Key = passwordBytes.CryptDeriveKey("TripleDES", "SHA1", 192, tdes.IV);

Use the TripleDES object to encrypt and decrypt data.

Do not store keys in code

Do not store keys in code because an attacker can disassemble hard-coded keys in your compiled assembly by using tools similar to ILDASM, which will render your key in plain text. Instead use DPAPI to encrypt the encryption key and store it in a protected registry key. Create an ACL to protect the registry key that allows full control for administrators and read-only access for your application's process account.

If you need to encrypt data and decrypt data by using symmetric encryption, protect the symmetric encryption key. Use DPAPI to encrypt it, and then store the resulting cipher text in a protected registry key.

Restrict access to persisted keys

When you store keys to be used at run time in persistent storage, use appropriate ACLs and limit access to the keys. Grant access to the keys only to administrators, SYSTEM, and the identity of the code at run time (for example, the Network Service account in the case of ASP.NET applications that run in the default application pool).

When you back up a key, do not store it in plain text. Instead, use DPAPI to encrypt it or use 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 the administrator with access to 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? Also, do not overuse keys.

Protect exported private keys

If your private key used for asymmetric encryption and key exchange is compromised, do not continue to use it. Immediately notify the users of the public key that the key has been compromised. If you used the key to sign documents, use a new key to sign them again.

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