Answered by:
Encryption and Decryption using AES 256 in Win RT

Question
-
Hi,
Is there a good code sample for doing encryption/decryption using AES 256 using a password and salt value?.
I am trying to bring a WP7 app to Windows 8. Below is a code sample used in WP7.5 to encrypt /decrypt using AES 256.
private static string Encrypt(string dataToEncrypt, string password, string salt) { AesManaged aes = null; MemoryStream memoryStream = null; CryptoStream cryptoStream = null; try { //Generate a Key based on a Password and HMACSHA1 pseudo-random number generator //Salt must be at least 8 bytes long //Use an iteration count of at least 1000 Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt), 1000); //Create AES algorithm aes = new AesManaged(); //Key derived from byte array with 32 pseudo-random key bytes aes.Key = rfc2898.GetBytes(32); //IV derived from byte array with 16 pseudo-random key bytes aes.IV = rfc2898.GetBytes(16); //Create Memory and Crypto Streams memoryStream = new MemoryStream(); cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write); //Encrypt Data byte[] data = Encoding.UTF8.GetBytes(dataToEncrypt); cryptoStream.Write(data, 0, data.Length); cryptoStream.FlushFinalBlock(); //Return Base 64 String return Convert.ToBase64String(memoryStream.ToArray()); } catch { MasterPasswordLib.CryptographyExceptionOccured = true; return ""; } finally { if (cryptoStream != null) cryptoStream.Close(); if (memoryStream != null) memoryStream.Close(); if (aes != null) aes.Clear(); } }
private static string Decrypt(string dataToDecrypt, string password, string salt) { AesManaged aes = null; MemoryStream memoryStream = null; try { //Generate a Key based on a Password and HMACSHA1 pseudo-random number generator //Salt must be at least 8 bytes long //Use an iteration count of at least 1000 Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt), 1000); //Create AES algorithm aes = new AesManaged(); //Key derived from byte array with 32 pseudo-random key bytes aes.Key = rfc2898.GetBytes(32); //IV derived from byte array with 16 pseudo-random key bytes aes.IV = rfc2898.GetBytes(16); //Create Memory and Crypto Streams memoryStream = new MemoryStream(); CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Write); //Decrypt Data byte[] data = Convert.FromBase64String(dataToDecrypt); cryptoStream.Write(data, 0, data.Length); cryptoStream.FlushFinalBlock(); //Return Decrypted String byte[] decryptBytes = memoryStream.ToArray(); //Dispose if (cryptoStream != null) cryptoStream.Dispose(); //Retval return Encoding.UTF8.GetString(decryptBytes, 0, decryptBytes.Length); } catch { MasterPasswordLib.CryptographyExceptionOccured = true; return ""; } finally { if (memoryStream != null) memoryStream.Dispose(); if (aes != null) aes.Clear(); } } }
- Edited by vbfnet Tuesday, October 2, 2012 10:15 AM
Tuesday, October 2, 2012 10:14 AM
Answers
-
The cryptographic operations exposed from WinRT are based out of the Windows.Security.Cryptography.Core.CryptographicEngine class. That class will let you use both PBKDF#2 for password based key derivation and AES for symmetric encryption. For example - to generate 2556 bit key and 128 bit IV from a string salt, string password, and iteration count, you could use CryptographicEngine this way:
private static void GenerateKeyMaterial(string password, string salt, uint iterationCount, out IBuffer keyMaterial, out IBuffer iv) { // Setup KDF parameters for the desired salt and iteration count IBuffer saltBuffer = CryptographicBuffer.ConvertStringToBinary(salt, BinaryStringEncoding.Utf8); KeyDerivationParameters kdfParameters = KeyDerivationParameters.BuildForPbkdf2(saltBuffer, iterationCount); // Get a KDF provider for PBKDF2, and store the source password in a Cryptographic Key KeyDerivationAlgorithmProvider kdf = KeyDerivationAlgorithmProvider.OpenAlgorithm(KeyDerivationAlgorithmNames.Pbkdf2Sha256); IBuffer passwordBuffer = CryptographicBuffer.ConvertStringToBinary(password, BinaryStringEncoding.Utf8); CryptographicKey passwordSourceKey = kdf.CreateKey(passwordBuffer); // Generate key material from the source password, salt, and iteration count. Only call DeriveKeyMaterial once, // since calling it twice will generate the same data for the key and IV. int keySize = 256 / 8; int ivSize = 128 / 8; uint totalDataNeeded = (uint)(keySize + ivSize); IBuffer keyAndIv = CryptographicEngine.DeriveKeyMaterial(passwordSourceKey, kdfParameters, totalDataNeeded); // Split the derived bytes into a seperate key and IV byte[] keyMaterialBytes = keyAndIv.ToArray(); keyMaterial = WindowsRuntimeBuffer.Create(keyMaterialBytes, 0, keySize, keySize); iv = WindowsRuntimeBuffer.Create(keyMaterialBytes, keySize, ivSize, ivSize); }
This makes use of the ToArray extension method for IBuffer which is in the System.Runtime.InteropServices.WindowsRuntime namespace, so for that code to work, you'll need to include that namespace.
Encryption and decryption are also done via CryptographicEngine. Encryption from a source string to a base64 ciphertext might look like this:
// Generate a key and IV from the password and salt IBuffer aesKeyMaterial; IBuffer iv; uint iterationCount = 10000; GenerateKeyMaterial(passwordString, saltString, iterationCount, out aesKeyMaterial, out iv); IBuffer plainText = CryptographicBuffer.ConvertStringToBinary(plaintextString, BinaryStringEncoding.Utf8); // Setup an AES key, using AES in CBC mode and applying PKCS#7 padding on the input SymmetricKeyAlgorithmProvider aesProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7); CryptographicKey aesKey = aesProvider.CreateSymmetricKey(aesKeyMaterial); // Encrypt the data and convert it to a Base64 string IBuffer encrypted = CryptographicEngine.Encrypt(aesKey, plainText, iv); string ciphertextString = CryptographicBuffer.EncodeToBase64String(encrypted);
And decryption should be very similar as well:
// Generate a key and IV from the password and salt IBuffer aesKeyMaterial; IBuffer iv; uint iterationCount = 10000; GenerateKeyMaterial(passwordString, saltString, iterationCount, out aesKeyMaterial, out iv); // Setup an AES key, using AES in CBC mode and applying PKCS#7 padding on the input SymmetricKeyAlgorithmProvider aesProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7); CryptographicKey aesKey = aesProvider.CreateSymmetricKey(aesKeyMaterial); // Convert the base64 input to an IBuffer for decryption IBuffer ciphertext = CryptographicBuffer.DecodeFromBase64String(ciphertextString); // Decrypt the data and convert it back to a string IBuffer decrypted = CryptographicEngine.Decrypt(aesKey, ciphertext, iv); byte[] decryptedArray = decrypted.ToArray(); string decryptedString = Encoding.UTF8.GetString(decryptedArray, 0, decryptedArray.Length);
- Proposed as answer by Shawn Farkas [MSFT]Microsoft employee Wednesday, October 3, 2012 1:49 AM
- Marked as answer by Jesse Jiang Thursday, October 4, 2012 6:25 AM
Wednesday, October 3, 2012 1:49 AM
All replies
-
The cryptographic operations exposed from WinRT are based out of the Windows.Security.Cryptography.Core.CryptographicEngine class. That class will let you use both PBKDF#2 for password based key derivation and AES for symmetric encryption. For example - to generate 2556 bit key and 128 bit IV from a string salt, string password, and iteration count, you could use CryptographicEngine this way:
private static void GenerateKeyMaterial(string password, string salt, uint iterationCount, out IBuffer keyMaterial, out IBuffer iv) { // Setup KDF parameters for the desired salt and iteration count IBuffer saltBuffer = CryptographicBuffer.ConvertStringToBinary(salt, BinaryStringEncoding.Utf8); KeyDerivationParameters kdfParameters = KeyDerivationParameters.BuildForPbkdf2(saltBuffer, iterationCount); // Get a KDF provider for PBKDF2, and store the source password in a Cryptographic Key KeyDerivationAlgorithmProvider kdf = KeyDerivationAlgorithmProvider.OpenAlgorithm(KeyDerivationAlgorithmNames.Pbkdf2Sha256); IBuffer passwordBuffer = CryptographicBuffer.ConvertStringToBinary(password, BinaryStringEncoding.Utf8); CryptographicKey passwordSourceKey = kdf.CreateKey(passwordBuffer); // Generate key material from the source password, salt, and iteration count. Only call DeriveKeyMaterial once, // since calling it twice will generate the same data for the key and IV. int keySize = 256 / 8; int ivSize = 128 / 8; uint totalDataNeeded = (uint)(keySize + ivSize); IBuffer keyAndIv = CryptographicEngine.DeriveKeyMaterial(passwordSourceKey, kdfParameters, totalDataNeeded); // Split the derived bytes into a seperate key and IV byte[] keyMaterialBytes = keyAndIv.ToArray(); keyMaterial = WindowsRuntimeBuffer.Create(keyMaterialBytes, 0, keySize, keySize); iv = WindowsRuntimeBuffer.Create(keyMaterialBytes, keySize, ivSize, ivSize); }
This makes use of the ToArray extension method for IBuffer which is in the System.Runtime.InteropServices.WindowsRuntime namespace, so for that code to work, you'll need to include that namespace.
Encryption and decryption are also done via CryptographicEngine. Encryption from a source string to a base64 ciphertext might look like this:
// Generate a key and IV from the password and salt IBuffer aesKeyMaterial; IBuffer iv; uint iterationCount = 10000; GenerateKeyMaterial(passwordString, saltString, iterationCount, out aesKeyMaterial, out iv); IBuffer plainText = CryptographicBuffer.ConvertStringToBinary(plaintextString, BinaryStringEncoding.Utf8); // Setup an AES key, using AES in CBC mode and applying PKCS#7 padding on the input SymmetricKeyAlgorithmProvider aesProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7); CryptographicKey aesKey = aesProvider.CreateSymmetricKey(aesKeyMaterial); // Encrypt the data and convert it to a Base64 string IBuffer encrypted = CryptographicEngine.Encrypt(aesKey, plainText, iv); string ciphertextString = CryptographicBuffer.EncodeToBase64String(encrypted);
And decryption should be very similar as well:
// Generate a key and IV from the password and salt IBuffer aesKeyMaterial; IBuffer iv; uint iterationCount = 10000; GenerateKeyMaterial(passwordString, saltString, iterationCount, out aesKeyMaterial, out iv); // Setup an AES key, using AES in CBC mode and applying PKCS#7 padding on the input SymmetricKeyAlgorithmProvider aesProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7); CryptographicKey aesKey = aesProvider.CreateSymmetricKey(aesKeyMaterial); // Convert the base64 input to an IBuffer for decryption IBuffer ciphertext = CryptographicBuffer.DecodeFromBase64String(ciphertextString); // Decrypt the data and convert it back to a string IBuffer decrypted = CryptographicEngine.Decrypt(aesKey, ciphertext, iv); byte[] decryptedArray = decrypted.ToArray(); string decryptedString = Encoding.UTF8.GetString(decryptedArray, 0, decryptedArray.Length);
- Proposed as answer by Shawn Farkas [MSFT]Microsoft employee Wednesday, October 3, 2012 1:49 AM
- Marked as answer by Jesse Jiang Thursday, October 4, 2012 6:25 AM
Wednesday, October 3, 2012 1:49 AM -
Hi Shawn,
Thanks a million.
Wednesday, October 3, 2012 7:36 AM -
I believe there is a bug in the code.
int keySize = 256 / 8;
Should be changed to
int keySize = 256;
Because CryptographicEngine.DeriveKeyMaterial expects key size in bits not in bytes. Otherwise, you will end up with a 32-bit key!
- Edited by yyu009 Saturday, December 15, 2012 7:49 PM
Saturday, December 15, 2012 7:48 PM -
Hi,
will this get the same results as the WP code?
Can I decode something that was encoded on WP8?
Thanks,
Thomas
Monday, March 30, 2015 4:38 PM -
The documentation says 'Requested size, in bytes, of the derived key.' however.
Edit: I verified in the debugger that bytes is correct. BTW, I finally managed to get the same key results as with WP8.1. Decryption, however, did not work, yet. Got an exception "The supplied user buffer is not valid for the requested operation. (Exception from HRESULT: 0x800706F8)". Working on that.
- Edited by t_s_b Tuesday, March 31, 2015 5:54 PM
Monday, March 30, 2015 4:57 PM