/aes.vbs

AES-256-CBC Encrypt and Decrypt Functions in VBScript

Primary LanguageVBScriptMIT LicenseMIT

AES-256-CBC in VBScript

This project provides VBScript functions to perform encryption and decryption with AES-256-CBC.

View Source MIT License

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 Library (mscorlib.dll).

Contents

Features

Here are some of the features of this project:

  • Works with Base64 encoded keys.

  • Exposes two simple functions named Encrypt() and 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 <% and %> delimiters in a file named aes.inc and include it in ASP files with an #include directive 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.

Why?

Why not?

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.

Demo Script

On a Windows system, enter the following command to see a quick demo:

cscript aesdemo.wsf

The Windows Script File (WSF) named aesdemo.wsf loads aes.vbs and executes the functions defined in it.

Demo Output

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) which is different each time it is generated. 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.

Example Ciphertexts

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)

The Encrypt and Decrypt functions are defined in aes.vbs.

Crypto Properties

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 used in aes.vbs. The output shows the following details:

  • The aes object (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 RijndaelManaged.Mode and CipherMode documentation to confirm that the default mode is indeed CBC.
  • The padding mode is 2, i.e., PKCS #7. See RijndaelManaged.Padding and PaddingMode documentation 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 Encrypt and Decrypt functions to ensure that we use AES-256-CBC for encryption.

OpenSSL CLI Examples

For troubleshooting purpose, there are two shell scripts named encrypt and decrypt present in the current directory. Here is the synopsis of these scripts:

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

Here is how to use the encrypt and decrypt scripts:

  1. Encrypt the plaintext hello with 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 encrypted2 value.

  2. 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 decrypted2 value.

Questions and Answers

  1. Can we not use AesManaged instead of RijndaelManaged in aes.vbs?

    No, we cannot use AesManaged in VBScript. We can use only those classes that are defined in mscorlib.dll. RijndaelManaged exists in mscorlib.dll but AesManaged does not.

    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_4.0.0.0__b77a5c561934e089\System.Core.dll

    This is why we use RijndaelManaged in our VBScript code.

  2. Is RijndaelManaged suitable as AES cipher?

    Yes, it is.

    The RijndaelManaged class allows setting its KeySize and BlockSize properties independently to 128, 160, 192, 224, or 256. Further, the key size need not match the block size.

    But the AesManaged class allows setting its KeySize to 128, 192, or 256 only. The BlockSize property must be 128 only. These constraints match the key sizes and the block size defined in the AES standard (see section 5 of FIPS 197).

    Therefore, AesManaged class 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 RijndaelManaged as explained in section Crypto Properties, it is a suitable substitute for AesManaged.

    Further, the section OpenSSL CLI examples shows that the default properties of this cipher is compatible with openssl aes-256-cbc.

  3. 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 mscorlib.dll:

    However, they are not registered in Windows registry by default. Therefore, like AesManaged class, 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.

References

License

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.

Support

To report bugs, suggest improvements, or ask questions, please create a new issue at http://github.com/susam/aes.vbs/issues.