With AES, we can have a block cipher mode, a stream cipher mode, and AEAD (Authenicated Encryption With Additional Data). In this case we will implement GCM (AEAD), CFB (Stream) and CBC (Block), and will use PBKDF2 (as defined in RFC 2898) to generate an encryption key of a given size (128-bit, 192-bit and 256-bit).
AES GCM Encryption with PowerShell |
Outline
The workhorse of the cybersecurity industry is AES (Advanced Encryption Standard) and which is used to encrypt and decrypt data. The method comes from the winner of a NIST competition in 2001, and was previously known as the Rijndael (“Rain Dahl”) cipher.
AES is a symmetric key method, and where Bob and Alice have the same encryption key. In the following, Bob and Alice share an encryption key, and where Bob converts his plaintext into ciphertext, and then Alice converts the ciphertext back into plaintext using a shared secret key:
The problem with this setup is that the same plaintext will always result in the same ciphertext, so we typically add salt into the encryption process. We also need a way for Bob and Alice to generate the same secret key. This is either typically done through a key exchange method (such as with the Diffie-Hellman method) or by a KDF (Key Derivation Function). One of the most popular KDFs is PBKDF2 and which allows a password to be converted into an encryption key of a defined size:
The size of the key is typically either 128 bits or 256 bits. AES (Advanced Encryption Standard) can be applied into three modes:
- Block cipher. This can be implemented with CBC (Cipher Block Chaining) and which implements a block cipher. In the case of AES, the block size will be 16 bytes, and thus the ciphertext will be a multiple of the block size. As we use a block cipher, we need to pad the data for the cipher process, and then unpad the decrypted data. The most common method is PKCS7, and which pads with the value which equals the number of padding bytes.
- AEAD (Authenticated Encryption with Additional Data). This can be implemented with GCM (Galois Counter Mode) and which implements with a stream cipher with the addition of additional data. This additional data can include something related to the ciphertext, such as with a session ID or TCP port. There is no padding required for the plaintext text, and thus the ciphertext will have the same length as the plaintext.
- Stream cipher. This can be implemented with CFB (Cipher FeedBack) and which implements a stream cipher. As we use a stream cipher, we do not need to pad the data, and where the ciphertext size will be the same as the plaintext. Overall we use a simple XOR operation between the plaintext stream and a key stream (derived from the main encryption key). The decryption is performed by just XOR’ing the ciphertext stream with the same key stream.
$plaintext = $Args[0] $password=$Args[1] $keygen = [System.Security.Cryptography.Rfc2898DeriveBytes]::new($Args[1], 64, 10000) $key = $keygen.GetBytes(32) $aesGcm = [Security.Cryptography.AesGcm]::new($key) $plainBytes = [System.Text.Encoding]::UTF8.GetBytes($plaintext) $IV = $keygen.GetBytes(12) $tag = [byte[]]::new(16) $associatedData = [byte[]]::new(12) $cipherText = [byte[]]::new($plainBytes.Length) $aesGcm.Encrypt($IV, $plainBytes, $cipherText, $tag, $associatedData) $plain = [byte[]]::new($cipherText.Length) $aesGcm.Decrypt($IV, $cipherText, $tag, $plain, $associatedData) "== AES GCM Encryption (using RFC2898 with 10,000 rounds for key derivation) ==" "`nMessage: " + $plaintext "`nPassword: " + $password "`nSalt: " + [System.Convert]::ToBase64String($IV) "key: " + [System.Convert]::ToBase64String($key) "`nEncryption: " + [System.Convert]::ToBase64String($cipherText) "Decryption: " + [System.Text.Encoding]::UTF8.GetString($plain)
A sample run shows:
== AES GCM Encryption (using RFC2898 with 10,000 rounds for key derivation) == Message: Secret message Password: qwerty123 Salt: NxnEfWSMSGNt6pZN key: 9/5NVekYBhCTqxBf0835MAQGcJ0B8qZFzoQmGefZZWo= Encryption: 36kHK3Hw4p8d/URs3f0= Decryption: Secret message