I am writing an iPhone application that needs to encrypt a password using AES encryption. I have found many different examples for AES encryption but I'm finding that the implementation differs from sample to sample. This would be fine if I controlled the decryption process as well, but I do not - I need to send the encrypted password to a .NET API, which will decrypt the password using .NET code.
I am including the C# code below. Can someone point me in the right direction, or even better, provide some Objective-C code for encrypting an NSString which will work with this C# code?
The sharedSecret I have been provided with is 126 characters in length, so I'm assuming this is 128-bit encryption. Or should the sharedSecret then be 128 characters?
public class Crypto
{
private static byte[] _salt = Encoding.ASCII.GetBytes("SALT GOES HERE");
/// <summary>
/// Encrypt the given string using AES. The string can be decrypted using
/// DecryptStringAES(). The sharedSecret parameters must match.
/// </summary>
/// <param name="plainText">The text to encrypt.</param>
/// <param name="sharedSecret">A password used to generate a key for encryption.</param>
public static string EncryptStringAES(string plainText, string sharedSecret)
{
if (string.IsNullOrEmpty(plainText))
throw new ArgumentNullException("plainText");
if (string.IsNullOrEmpty(sharedSecret))
throw new ArgumentNullException("sharedSecret");
string outStr = null; // Encrypted string to return
RijndaelManaged aesAlg = null; // RijndaelManaged object used to encrypt the data.
try
{
// generate the key from the shared secret and the salt
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);
// Create a RijndaelManaged object
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
// prepend the IV
msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
}
outStr = Convert.ToBase64String(msEncrypt.ToArray());
}
}
finally
{
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
// Return the encrypted bytes from the memory stream.
return outStr;
}
/// <summary>
/// Decrypt the given string. Assumes the string was encrypted using
/// EncryptStringAES(), using an identical sharedSecret.
/// </summary>
/// <param name="cipherText">The text to decrypt.</param>
/// <param name="sharedSecret">A password used to generate a key for decryption.</param>
public static string DecryptStringAES(string cipherText, string sharedSecret)
{
if (string.IsNullOrEmpty(cipherText))
throw new ArgumentNullException("cipherText");
if (string.IsNullOrEmpty(sharedSecret))
throw new ArgumentNullException("sharedSecret");
// Declare the RijndaelManaged object
// used to decrypt the data.
RijndaelManaged aesAlg = null;
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
try
{
// generate the key from the shared secret and the salt
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);
// Create the streams used for decryption.
byte[] bytes = Convert.FromBase64String(cipherText);
using (MemoryStream msDecrypt = new MemoryStream(bytes))
{
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
// Get the initialization vector from the encrypted stream
aesAlg.IV = ReadByteArray(msDecrypt);
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
finally
{
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
return plaintext;
}
private static byte[] ReadByteArray(Stream s)
{
byte[] rawLength = new byte[sizeof(int)];
if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length)
{
throw new SystemException("Stream did not contain properly formatted byte array");
}
byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)];
if (s.Read(buffer, 0, buffer.Length) != buffer.Length)
{
throw new SystemException("Did not read byte array properly");
}
return buffer;
}
}
The shared secret's length is not relevant to the bit length of the key in this case. You can see here how the C# is deriving the key with Rfc2898DeriveBytes:
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);
RFC 2898 defines the PKCS5 standard (which means PBKDF2). Based on Microsoft's documentation it looks like the default iteration count is 1000, so you've got the shared secret, the salt, and the iteration count. If you plug that into another PBKDF2 implementation that will give you the raw key you need to use to encrypt.
It next creates a RijndaelManaged object (Rijndael was the name of AES before it was standardized) and gets the default key size in bits (which it then divides by 8 to get the bytes). It then gets that many bytes from the key variable. If you find out the default key size for this class then that's the size of the AES key.
(Incidentally, when creating one of these objects the documentation states that a random IV is generated and that it defaults to CBC so we can assume that from here on)
Next it writes the length of the IV, then the IV itself.
msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
After all that it writes the ciphertext and the entire blob is complete.
On the decrypt side it does mostly the same thing in reverse. First it derives the key, then it grabs the whole encrypted blob and feeds it to ReadByteArray, which extracts the IV. Then it uses the key + IV to decrypt.
Implementing this in Objective-C shouldn't be too difficult given a sample encrypted blob and the shared secret!
If you send passwords - you doing it wrong.
Never send a password even in encrypted form, it is a security vulnerability: you have to maintain client and server to use the latest encryption/decryption library. You must be sure that the key is not compromised, you need to update the key from time to time, and hence transfer it both to server and client. You must use different keys for different passwords. You have to be sure that server is secured and not compromised, you need to know that you actually speak to the server etc etc.
Instead, create a strong cryptographic hash (a one-way function) of a password and send that over a secured channel. It would also mean that on the server side you never store passwords at all.
Related
I'm trying to write some straight forward encryption routines. Here's what I've been able to come up with based on searching the Web.
public string Encrypt(string plainText)
{
byte[] encrypted;
// Create an AesCryptoServiceProvider object
// with the specified key and IV.
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
msEncrypt.WriteByte((byte)aesAlg.Key.Length);
msEncrypt.Write(aesAlg.Key, 0, aesAlg.Key.Length);
msEncrypt.WriteByte((byte)aesAlg.IV.Length);
msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
return Convert.ToBase64String(encrypted);
}
public string Decrypt(string cipherText)
{
string plaintext = null;
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(Convert.FromBase64String(cipherText)))
{
int l = msDecrypt.ReadByte();
byte[] key = new byte[l];
msDecrypt.Read(key, 0, l);
l = msDecrypt.ReadByte();
byte[] IV = new byte[l];
msDecrypt.Read(IV, 0, l);
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(key, IV);
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
return plaintext;
}
Two questions:
First, most of the examples I found hard coded the Key and IV. So what I'm doing is writing it to the encrypted bytes. This will make my encrypted data larger. Is there a better way?
Also, I'm not using any password. Would one use a password to generate a custom Key? And, if so, how would I know how long that key needed to be?
First, most of the examples I found hard coded the Key and IV. So what I'm doing is writing it to the encrypted bytes. This will make my encrypted data larger. Is there a better way?
Obviously you should not write the key to the unprotected stream, as the key needs to be shared or established in advance and remain secret. This sharing of the secret key can be performed in many ways, ranging from key agreement to key derivation, ratcheting, etc. etc.
Also, I'm not using any password. Would one use a password to generate a custom Key? And, if so, how would I know how long that key needed to be?
That's a possibility. However, remind yourself that passwords are often not that strong, so if password based encryption (PBE) can be avoided, it may be a good idea to do so.
If you derive a key from a password, you should use a Password Based Key Derivation Function (also sometimes called a password hash). In C# there is an implementation of PBKDF2 (badly) called Rfc2898DeriveBytes. By now that's not very state of the art either, but it should suffice - if you set a high enough iteration count anyway.
When you derive a key from a human remembered password then 128 bits is plenty. There is almost no way that the key can be found easier than the password that was used to derive it.
i have the following PHP decryption routine which is working flawlessly and need help converting it to c#. i have tried many approaches but none of them is working.
i have managed to match the hash function output between c# and php.
also matched the output of the conversion from and to base64.
PHP Code:
function decrypt($encrypted_txt, $secret_key, $secret_iv)
{
$encrypt_method = "AES-256-CBC";
// hash
$key = hash('sha256', $secret_key);
// iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
$iv = substr(hash('sha256', $secret_iv), 0, 16);
$output = openssl_decrypt(base64_decode($encrypted_txt), $encrypt_method, $key, 0, $iv);
return $output;
}
secret_key= "t-3zafRa";
secret_key_hash = "d03a4d94b29e7f55c80726f1152dcebc9f03f4c698470f72083af967cf786b6b";
the problem is that the key hash is a 64 bytes which is invalid for the AES-256 but i am not sure how it's working in php and how the openssl_decrypt php function is dealing with the keys.
i have also tried to pass the MD5 of the key hash but also failed to decrypt.
byte[] asciiBytes = ASCIIEncoding.ASCII.GetBytes(keyhash);
byte[] hashedBytes = MD5CryptoServiceProvider.Create().ComputeHash(asciiBytes);
string keymd5 = BitConverter.ToString(hashedBytes).Replace("-", "").ToLower(); //To match with PHP MD5 output
C# Hashing function:
static string sha256(string randomString)
{
var crypt = new System.Security.Cryptography.SHA256Managed();
var hash = new System.Text.StringBuilder();
byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(randomString));
foreach (byte theByte in crypto)
{
hash.Append(theByte.ToString("x2"));
}
return hash.ToString();
}
C# Decryption routine:
static string DecryptStringFromBytesAes(byte[] cipherText, byte[] key, byte[] iv)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (key == null || key.Length <= 0)
throw new ArgumentNullException("key");
if (iv == null || iv.Length <= 0)
throw new ArgumentNullException("iv");
// Declare the RijndaelManaged object
// used to decrypt the data.
RijndaelManaged aesAlg = null;
// Declare the string used to hold
// the decrypted text.
string plaintext;
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.None, KeySize = 256, BlockSize = 128, Key = key, IV = iv };
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
srDecrypt.Close();
}
}
}
return plaintext;
}
any help or ideas are highly appreciated.
openssl_decrypt simply takes as many bytes for the key as required for the algorithm. As your algorithm is "AES-256-CBC" is uses 32 bytes (256 bits), as AES-256 is defined to as AES with a 256 bit key (and 14 rounds, rather than 10 or 12).
The way PHP does this is either by adding 00 valued bytes to the right in case the key is too small, or - as in your case - by simply ignoring the bytes after the 32st one. That's not a good way to behave of any kind of cryptographic library, especially for a high level language like PHP, but the OpenSSL wrapper library does it anyway.
So you have to extract the first 32 bytes from the hex encoded key and use that as key in C# to be compatible. Using different hash functions is of course not going to work, MD5 and SHA-256 are entirely incompatible (by design). Of course, you now have 16 hex encoded bytes left, which means you are using AES-256 with 128 bit keys, leaving you with 128 bit security. And yes, you need to use PKCS#7 padding in C#.
Note that using CBC with a static IV is not secure. Using CBC mode for transport mode security is not secure. Using SHA-256 or any normal hash over a password or key with little entropy is not secure. Storing keys in strings is generally not secure.
Getting crypto working is hard enough; getting it secure is much harder and requires understanding what the heck you're doing in the first place. You need to start off with a good protocol for your specific use case (and that's skipping a few steps already).
I need to encrypt/decrypt some strings. I've build my wrapper class according to the msdn documentation but with some changes.
Since I want to encrypt/decrypt data with a given string/passphrase, I don't use AesManaged for creating a key. (The user should be able to encrypt/decrypt with a key he enters, and therefore I cannot use the key from AesManaged and I cannot save the key).
I instead create the key by using Rfc2898DeriveBytes (PBKDF2) with a given salt. The given salt is used since I do not store the key and I think because of this, the salt must be always the same.
I then create an IV, encrypt the given string and concatenate the IV and the encrypted string. This will then eventually got saved in a file. This means the IV gets save together with the encrypted data.
Questions:
Is it ok to store the IV together with the encrypted data?
Is there another way to create the key without using the same salt everytime(Based on a given passphrase)?
Is this encryption done using AES128 or AES256?
Will the IV be always 16 bytes, or can this change?
static void Main(string[] args)
{
const string stringToEncrypt = "String to be encrypted/decrypted. Encryption is done via AesManaged";
const string password = "m1Sup3rS3cre!Password";
string encrypted = EncryptString(stringToEncrypt, password);
string roundtrip = DecryptStringFromBytes_Aes(encrypted, password);
Console.WriteLine("Original: {0}", stringToEncrypt);
Console.WriteLine("Round Trip: {0}", roundtrip);
Console.ReadLine();
}
static string EncryptString(string plainText, string password)
{
string encryptedString;
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = PasswordAsByte(password);
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
var encrypted = msEncrypt.ToArray();
encryptedString = Encoding.Default.GetString(aesAlg.IV);
encryptedString += Encoding.Default.GetString(encrypted);
}
}
}
return encryptedString;
}
static string DecryptStringFromBytes_Aes(string cipherText, string password)
{
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = PasswordAsByte(password);
aesAlg.IV = Encoding.Default.GetBytes(cipherText).Take(16).ToArray();
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
var encryptedByteArray = Encoding.Default.GetBytes(cipherText).Skip(16).ToArray();
using (MemoryStream msDecrypt = new MemoryStream(encryptedByteArray))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
return srDecrypt.ReadToEnd();
}
}
}
}
}
private static byte[] PasswordAsByte(string password)
{
byte[] salt = Encoding.Default.GetBytes("foobar42");
Rfc2898DeriveBytes passwordBytes = new Rfc2898DeriveBytes(password, salt);
return passwordBytes.GetBytes(32);
}
No, this is not okay.
1) You're using Encoding.Default in various places. Don't do that - it means you're at the whim of the platform you're on. Always use an explicit encoding, ideally UTF-8 in most cases.
2) You're using Encoding.GetString / Encoding.GetBytes to convert arbitrary binary data to a string and back. That's almost bound to lose data. (It happened to succeed on my machine, but it really depends on the encoding - and it's fundamentally a bad idea.) Encoding is designed for data which is inherently text data, and you're just applying an encoding one way or the other. Your encrypted data is inherently binary data. Use Convert.ToBase64String and Convert.FromBase64String instead.
For your other questions:
Yes, it's okay to store the IV with the encrypted data, as far as I know.
You could use the same approach for the password: generate a different salt each time, and store that with the encrypted text. Not sure whether that's generally recommended or not, I'm afraid.
I believe you're controlling whether the key size is 128 or 256 bits, with your call to passwordBytes.GetBytes(32) - that's a 256-bit key, so it's AES256.
I believe the IV size for AES is always 16 bytes (128 bits)
Normally salt is used together with cryptographic hashing of say passwords to protect against dictionary attacks. To get the same kind of protection for symmetric encryption with AES you should use a random initialization vector. So when you encrypt create a random IV and prepend it to the message (in cleartext). When you decrypt get the IV from the encrypted message and use it to decrypt the message. Then the ciphertext of the same message encrypted with the same key will be different.
So, yes, it is OK to store the IV together with the encrypted data.
You do not need a different salt every time because the purpose of the random IV is similar in how salt makes dictionary attacks on hashes harder.
AES can use key sizes of 128, 192 or 256 bits so to use AES 256 you need a 256 bit key (32 bytes) which is what you use.
AES uses a 128 bit block which requires a 128 bit IV (or 16 bytes).
Is it ok to store the IV together with the encrypted data?
Yes, it is ok. Moreover, you're using AesManaged without explicit setting of Mode - it this case mode is CBC, and in CBC mode IV should preceed cyphertext.
Is there another way to create the key without using the same salt everytime(Based on a given passphrase)?
Rfc2898DeriveBytes is pretty standard way to derive key from text password. There is no need to reinvent way of deriving key from password, just use Rfc2898DeriveBytes as you're doing it now.
Is this encryption done using AES128 or AES256?
It is AES256 since you're using 32-byte password.
Will the IV be always 16byte, or can this change?
The size of the IV property must be the same as the BlockSize property divided by 8. So it is 16 for 128-bit blocks.
Somebody asked me how I would decrypt a given AES 256-bit encrypted string if I knew the secret key. I'm not very familiar with encryption, so I sat down to look into the problem.
I found this example on MSDN, and tried to modify it to do only the Decrypt:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
internal class AesExample
{
public static void Main()
{
var encryptedString = "U2FsdGVkX1/cHT8XuHCfpw0AV4jpaO8JfLqUeCRJqjY=";
var secret = "SPARKY";
// I know this is not the correct way to get my input byte arrays...
// Just illustrating that I DO need byte arrays.
var encryptedBytes = Encoding.UTF8.GetBytes(encryptedString);
var secretBytes = Encoding.UTF8.GetBytes(secret);
try
{
using (var aes = new AesManaged())
{
aes.Key = secretBytes;
// Decrypt the bytes to a string.
var decryptedString = Decrypt(encryptedBytes, aes.Key, aes.IV);
//Display the original data and the decrypted data.
Console.WriteLine("Encrypted: {0}", encryptedString);
Console.WriteLine("Decrypted: {0}", decryptedString);
}
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e.Message);
}
}
private static string Decrypt(byte[] cipherText, byte[] key, byte[] iv)
{
// Declare the string used to hold
// the decrypted text.
string plaintext;
// Create an AesManaged object
// with the specified key and IV.
using (var aes = new AesManaged())
{
aes.Key = key;
aes.IV = iv;
// Create a decrytor to perform the stream transform.
var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
// Create the streams used for decryption.
using (var msDecrypt = new MemoryStream(cipherText))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
}
Of course as soon as I hit the following line, a CryptographicExcetion is thrown with the message "Specified key is not a valid size for this algorithm."
==> aes.Key = secretBytes
Someone suggested taking a SHA1 hash of the secret and trimming that to 20 byes. I tried that, and I started getting a new CryptographicException with the message "Length of the data to decrypt is invalid."
So, I have a few questions:
1) Is this even possible given only the encrypted text and secret key?
2) If so, are them some base assumptions one would need to make, like the CipherMode? I was reading that the ECB mode doesn't have a initialization vector. That's why I ask.
3) What would I need to do to put the inputs (encrypted text and secret key) into the correct Byte[] format for the decryption to work?
Thanks!
You probably need more information to make this work. To answer your specific questions:
Yes, except that you don't have the secret key. "SPARKY" is not a valid AES key, as DavidH mentions, though passwords are routinely used to derive secret keys through what are called key derivation functions. You could try running your password through Rfc2898DeriveBytes (a popular KDF in .NET) to derive different AES keys that might work, but it too takes parameters that you apparently don't have. You could also try various SHA hash digests of your password, though again 20 bytes is not a valid AES key - you need a 16, 24 or 32 byte key.
If you don't have an IV, then yes, you'll have to assume the encryption uses ECB. (But note that in general you should never use ECB mode.)
Your encrypted string appears to be encoded using base64. Converting it to a byte array is simple enough in .NET using Convert.FromBase64String(encryptedString);.
This sounds like a fun exercise, but you're probably just going to end up frustrated without a bit more information.
AES key lengths are 128, 192, and 256 bit depending on the cipher you want to use. You must ensure that your string is the appropriate length of bytes.
I want to implement encryption as there are multiple people with access to the database and my company wants to store personnel (sensitive) information in my application. To create some separation and security I want to implement encryption ideally using a 'key' they pick to encrypt the data in one SQL table.
I know doing it myself, I am going to miss a trick and tried-and-tested is probably best, especially for a company our size where we don't need to worry too much about hackers as the DB is not externally accessible. Just enough to keep out interested parties.
I would like to know what level of security is appropriate and also I am bit lost as to what to even Google to find out what sort of encryption maybe in a third party plugin I need to use as they all want to sell their product they will all say their own is great?
Most other questions I could find and the suggested 'Similar Questions' talked about data transfer encryption, hashing or ASP.NET
All security is really about raising the bar and usability tradeoffs. With encryption, there are a lot of options here, but it really all comes down to key management.
Who has the keys to decrypt?
How are they stored?
Brute-force attacks on your application data (say they get a hold of a backup tape of the encrypted SQL Server database by intercepting your FedEx to Iron Mountain) are less likely than an internal attack on the key management system - for instance an employee or developer alters the program to decrypt and dump the data.
Because the application probably in general has to decrypt this data at any time to authorized users, I would probably concentrate on the visibility of the columns which are sensitive and the roles allowed to access them first, then worry about encrypting them.
SQL Server only offers transparent encryption for data as well as encryption on the connections. This is not useful if the users have SELECT * access to a table. Encrypting it yourself within a column without SQL Server's knowledge may be problematic. For instance, if one column is pay data and that is sensitive, if you encrypt it within a column, you can't just run SELECT Employee, SUM(Pay) GROUP BY Employee
So first I would start by ensuring you have identified users and roles in your application, reviewed what kind of access they have and ensure that all connections to the database use appropriate roles.
Personally I would recommend using AES as its very easy to implement and with it being sensitive personal data would provide enough encryption to keep people out unlike something like DES.
This article goes in depth into AES if you want to have a technical understanding of how it works: http://msdn.microsoft.com/en-us/magazine/cc164055.aspx
and the basic examples shipped with it : http://msdn.microsoft.com/en-us/magazine/cc164846.aspx
A very clean example of how to implement it is here: http://www.obviex.com/samples/Code.aspx?Source=EncryptionCS&Title=Symmetric%20Key%20Encryption&Lang=C%23
Example stripped down (To prevent link rott)
using System;
using System.IO;
using System.Security.Cryptography;
namespace RijndaelManaged_Examples
{
class RijndaelMemoryExample
{
public static void Main()
{
try
{
string original = "Here is some data to encrypt!";
// Create a new instance of the RijndaelManaged
// class. This generates a new key and initialization
// vector (IV).
RijndaelManaged myRijndael = new RijndaelManaged();
// Encrypt the string to an array of bytes.
byte[] encrypted = encryptStringToBytes_AES(original, myRijndael.Key, myRijndael.IV);
// Decrypt the bytes to a string.
string roundtrip = decryptStringFromBytes_AES(encrypted, myRijndael.Key, myRijndael.IV);
//Display the original data and the decrypted data.
Console.WriteLine("Original: {0}", original);
Console.WriteLine("Round Trip: {0}", roundtrip);
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e.Message);
}
}
static byte[] encryptStringToBytes_AES(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
// Declare the streams used
// to encrypt to an in memory
// array of bytes.
MemoryStream msEncrypt = null;
CryptoStream csEncrypt = null;
StreamWriter swEncrypt = null;
// Declare the RijndaelManaged object
// used to encrypt the data.
RijndaelManaged aesAlg = null;
try
{
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
msEncrypt = new MemoryStream();
csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
swEncrypt = new StreamWriter(csEncrypt);
//Write all data to the stream.
swEncrypt.Write(plainText);
}
finally
{
// Clean things up.
// Close the streams.
if(swEncrypt != null)
swEncrypt.Close();
if (csEncrypt != null)
csEncrypt.Close();
if (msEncrypt != null)
msEncrypt.Close();
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
// Return the encrypted bytes from the memory stream.
return msEncrypt.ToArray();
}
static string decryptStringFromBytes_AES(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
// TDeclare the streams used
// to decrypt to an in memory
// array of bytes.
MemoryStream msDecrypt = null;
CryptoStream csDecrypt = null;
StreamReader srDecrypt = null;
// Declare the RijndaelManaged object
// used to decrypt the data.
RijndaelManaged aesAlg = null;
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
try
{
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
msDecrypt = new MemoryStream(cipherText);
csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
srDecrypt = new StreamReader(csDecrypt);
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
finally
{
// Clean things up.
// Close the streams.
if (srDecrypt != null)
srDecrypt.Close();
if (csDecrypt != null)
csDecrypt.Close();
if (msDecrypt != null)
msDecrypt.Close();
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
return plaintext;
}
}
}
scramble information can be done with a Symmetric (Rijndael) Key but I don't know how much improve performance for an application that use SQL update.
Symmetric (Rijndael) Key