AES-256-CBC in VBScript
This project provides VBScript functions to perform encryption and decryption with AES-256-CBC.
While performing encryption/decryption, it also computes/verifies a message authentication code (MAC) using HMAC-SHA-256 to maintain integrity and authenticity of initialization vector (IV) and ciphertext.
Note: This project does not implement the cryptographic primitives from
scratch. It is a wrapper around Microsoft's Common Object Runtime
- Demo Script
- Demo Output
- Example Ciphertexts
- Crypto Properties
- OpenSSL CLI Examples
- Questions and Answers
Here are some of the features of this project:
Works with Base64 encoded keys.
Exposes two simple functions named
Decrypt()that perform AES-256-CBC encryption and decryption along with computing and verifying MAC using HMAC-SHA-256 to ensure integrity and authenticity of IV and ciphertext.
Initialization vector (IV) is automatically generated. Caller does not need to worry about it.
Message authentication code (MAC) is computed by
Encrypt()and verified by
Decrypt()to ensure the integrity and authenticity of IV and ciphertext. HMAC-SHA-256 is used to generate MAC.
Encrypt()returns the MAC, IV, and ciphertext concatenated together as a single string which can then be fed directly to
Decrypt(). The three fields are joined by colons in the concatenated string. No need to worry about maintaining the MAC and IV yourself.
Can be used with Classic ASP. Just put the entire source code within the ASP
%>delimiters in a file named
aes.incand include it in ASP files with an
#includedirective like this:
<!-- #include file="aes.inc" -->
Note: This is not a crypto library. You can use it like one if all you want to use is AES-256-CBC with HMAC-SHA-256. It does not support anything else. If you want a different key size, cipher mode, or MAC algorithm, you'll have to dive into the source code and modify it as per your needs. If you do so, you might find one or more of the documentation links provided in the References section useful.
Okay, the real story behind writing this project involved a legacy application written in Classic ASP and VBScript. It used an outdated cipher that needed to be updated. That's what prompted me to create a wrapper for AES-256-CBC with HMAC-SHA-256 in VBScript. After writing it, I thought it would be good to share it on the Internet in case someone else is looking for something like this.
On a Windows system, enter the following command to see a quick demo:
The output from the demo script looks like this:
demoAESKey: CKkPfmeHzhuGf2WYY2CIo5C6aGCyM5JR8gTaaI0IRJg= demoMACKey: wDF4W9XQ6wy2DmI/7+ONF+mwCEr9tVgWGLGHUYnguh4= encrypted1: Ru7CEo3KJBMT9ati55ASO0xJOVw5+7crhL4RxQSVu1s=:dHTHWCy5sGu9z7gUAa0tpA==:sBbmFDtzkPU7kD4T1OSbvw== decrypted1: hello encrypted2: 7BnQ5trOLDk8cecEnVayfSW9Q2fA38FvFkDlwHxbAKA=:M1ipFnh884qcXYlX9NPjwA==:ANF8P0PfaUQwvcS2jiIpdQ== decrypted2: hello aes.BlockSize: 128 aes.FeedbackSize: 128 aes.KeySize: 256 aes.Mode: 1 aes.Padding: 2 mac.HashName: SHA256 mac.HashSize: 256 aesEnc.InputBlockSize: 16 aesEnc.OutputBlockSize: 16 aesDec.InputBlockSize: 16 aesDec.OutputBlockSize: 16 b64Enc.InputBlockSize: 3 b64Enc.OutputBlockSize: 4 b64Dec.InputBlockSize: 1 b64Dec.OutputBlockSize: 3
Only the third line of output (
encrypted1) changes on every run
because it depends on a dynamically generated initialization vector (IV)
is different each time. This is, in fact, an important security
requirement for achieving semantic security.
For example, if a database contains encrypted secrets from various users and if the same plaintext always encrypts to the same ciphertext (which would happen if the key and IV remain constant between encryptions), then we can tell that two users have the same plaintext secrets if their ciphertexts are equal in the database. Being able to do so violates the requirements of semantic security which requires that an adversary must not be able to compute any information about a plaintext from its ciphertext. This is why the ciphertext needs to be different for the same plaintext in different encryptions and this is why we need a random IV for each encryption.
There are two blocks of output shown above. The first block shows example keys and ciphertexts along with the plaintexts they decrypt to. The second block shows the default properties of the cryptography objects used in the VBScript code. Both blocks of output are explained in detail in the sections below.
The first block of output in the demo script looks like this:
demoAESKey: CKkPfmeHzhuGf2WYY2CIo5C6aGCyM5JR8gTaaI0IRJg= demoMACKey: wDF4W9XQ6wy2DmI/7+ONF+mwCEr9tVgWGLGHUYnguh4= encrypted1: Ru7CEo3KJBMT9ati55ASO0xJOVw5+7crhL4RxQSVu1s=:dHTHWCy5sGu9z7gUAa0tpA==:sBbmFDtzkPU7kD4T1OSbvw== decrypted1: hello encrypted2: 7BnQ5trOLDk8cecEnVayfSW9Q2fA38FvFkDlwHxbAKA=:M1ipFnh884qcXYlX9NPjwA==:ANF8P0PfaUQwvcS2jiIpdQ== decrypted2: hello
The third line of output (
encrypted1) changes on every run because it
depends on a dynamically generated initialization vector (IV) used in
the encryption. The fifth line of output (
encrypted2) remains the
same because it is hardcoded in the demo script.
Each encrypted value in the output is actually a concatenation of the
message authentication code (MAC), colon (
:), initialization vector
(IV), colon (
:), and ciphertext.
The ciphertext is generated with a function call like this:
demoPlaintext = "hello" demoAESKey = "CKkPfmeHzhuGf2WYY2CIo5C6aGCyM5JR8gTaaI0IRJg=" demoMACKey = "wDF4W9XQ6wy2DmI/7+ONF+mwCEr9tVgWGLGHUYnguh4=" encrypted1 = Encrypt(demoPlaintext, demoAESKey, demoMACKey)
The encrypted value (i.e., concatenated MAC, colon, IV, colon, and ciphertext) can be decrypted back to plaintext with a function call like this:
decrypted1 = Decrypt(encrypted1, demoAESKey, demoMACKey)
Decrypt functions are defined in
The second block of output from the demo script looks like this:
aes.BlockSize: 128 aes.FeedbackSize: 128 aes.KeySize: 256 aes.Mode: 1 aes.Padding: 2 mac.HashName: SHA256 mac.HashSize: 256 aesEnc.InputBlockSize: 16 aesEnc.OutputBlockSize: 16 aesDec.InputBlockSize: 16 aesDec.OutputBlockSize: 16 b64Enc.InputBlockSize: 3 b64Enc.OutputBlockSize: 4 b64Dec.InputBlockSize: 1 b64Dec.OutputBlockSize: 3
These are the values of the properties of various cryptography objects
aes.vbs. The output shows the following details:
aesobject (of class
RijndaelManaged) has a default key size of 256 bits.
- The block size is 128 bits. The mode is 1, i.e., CBC. See
CipherModedocumentation to confirm that the default mode is indeed CBC.
- The padding mode is 2, i.e., PKCS #7. See
PaddingModedocumentation to confirm that the default padding mode is indeed PKCS #7.
We do not change these defaults in
aes.vbs and we supply a
256-bit encryption key to
Decrypt functions to ensure
that we use AES-256-CBC for encryption.
OpenSSL CLI Examples
plaintext=PLAINTEXT aes_key=AES_KEY aes_iv=AES_IV mac_key=MAC_KEY sh encrypt ciphertext=CIPHERTEXT aes_key=AES_KEY mac_key=MAC_KEY sh decrypt
These scripts are merely wrappers around OpenSSL. They accept Base64 key and Base64 IV and convert them to hexadecimal for OpenSSL command line tool to consume.
To see example usage of the script we will use these three values from the demo output:
demoAESKey: CKkPfmeHzhuGf2WYY2CIo5C6aGCyM5JR8gTaaI0IRJg= demoMACKey: wDF4W9XQ6wy2DmI/7+ONF+mwCEr9tVgWGLGHUYnguh4= encrypted2: 7BnQ5trOLDk8cecEnVayfSW9Q2fA38FvFkDlwHxbAKA=:M1ipFnh884qcXYlX9NPjwA==:ANF8P0PfaUQwvcS2jiIpdQ== decrypted2: hello
Encrypt the plaintext
hellowith the demo AES key and IV, and compute MAC of the result with the demo MAC key:
plaintext=hello \ aes_key=CKkPfmeHzhuGf2WYY2CIo5C6aGCyM5JR8gTaaI0IRJg= \ aes_iv=M1ipFnh884qcXYlX9NPjwA== \ mac_key=wDF4W9XQ6wy2DmI/7+ONF+mwCEr9tVgWGLGHUYnguh4= \ sh encrypt
The output should match the string in the
Verify MAC and decrypt the ciphertext to obtain the plaintext AES keys and IV.
ciphertext=7BnQ5trOLDk8cecEnVayfSW9Q2fA38FvFkDlwHxbAKA=:M1ipFnh884qcXYlX9NPjwA==:ANF8P0PfaUQwvcS2jiIpdQ== \ aes_key=CKkPfmeHzhuGf2WYY2CIo5C6aGCyM5JR8gTaaI0IRJg= \ mac_key=wDF4W9XQ6wy2DmI/7+ONF+mwCEr9tVgWGLGHUYnguh4= \ sh decrypt
The output should match the plaintext in the
Questions and Answers
As a result, we get an error for this VBScript code:
Set aes = CreateObject("System.Security.Cryptography.AesManaged")
Here is the error that occurs:
ActiveX component can't create object: 'System.Security.Cryptography.AesManaged'
Here is a command that shows the DLLs in which the two classes are present:
C:\>powershell -Command [System.Reflection.Assembly]::GetAssembly('System.Security.Cryptography.RijndaelManaged') GAC Version Location --- ------- -------- True v4.0.30319 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorlib.dll C:\>powershell -Command [System.Reflection.Assembly]::GetAssembly('System.Security.Cryptography.AesManaged') GAC Version Location --- ------- -------- True v4.0.30319 C:\windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_22.214.171.124__b77a5c561934e089\System.Core.dll
This is why we use
RijndaelManagedin our VBScript code.
RijndaelManagedsuitable as AES cipher?
Yes, it is.
RijndaelManagedclass allows setting its
BlockSizeproperties independently to
256. Further, the key size need not match the block size.
AesManagedclass allows setting its
BlockSizeproperty must be
128only. These constraints match the key sizes and the block size defined in the AES standard (see section 5 of FIPS 197).
AesManagedclass is the preferred class to use because it is not possible to use it in a non-conformant manner. But since this class cannot be used in VBScript as explained in the previous point and since we stick to the default key size of 256 bits and the default block size of 128 bits while using
RijndaelManagedas explained in section Crypto Properties, it is a suitable substitute for
Further, the section OpenSSL CLI examples shows that the default properties of this cipher is compatible with
Instead of using a Base64 encoded 256-bit key, can we not use a password and derive a 256-bit key from it using a key derivative function (KDF)?
This is not easily possible in VBScript because there are only two key derivative functions available in
PasswordDeriveBytes: This is an implementation of an extension of the PBKDF1 algorithm.
Rfc2898DeriveBytes: This is an implementation of the PBKDF2 algorithm.
However, they are not registered in Windows registry by default. Therefore, like
AesManagedclass, these two classes too cannot be used in VBScript code without modifying the registry.
If we consider brute-force search of the key, then in case of a key derived from a password using a KDF, an attacker could either search the password which would be inefficient due to the use of KDF or an attacker could search the encryption key directly. Brute-force search of the key can be slower than brute-search of the password if the password is small and small number of iterations is used in the KDF. Therefore, using a 256-bit key directly is never worse than using a key derived from a password.
- Windows Scripting
- VBScript User's Guide
- Using Windows Script Files (.wsf)
- FIPS 197
This is free and open source software. You can use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of it, under the terms of the MIT License. See LICENSE.md for details.
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND, express or implied. See LICENSE.md for details.
To report bugs, suggest improvements, or ask questions, please create a new issue at http://github.com/susam/aes.vbs/issues.