locked
Encryption and Decryption using AES 256 in Win RT RRS feed

  • 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);

    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);

    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.

    https://msdn.microsoft.com/en-us/library/windows/apps/windows.security.cryptography.core.cryptographicengine.derivekeymaterial.aspx

    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