Skip to content

Instantly share code, notes, and snippets.

@Darryl-G
Forked from mendel129/PoSh_OpenSSL_AES
Last active February 8, 2024 00:45
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Darryl-G/d1039c2407262cb6d735c3e7a730ee86 to your computer and use it in GitHub Desktop.
Save Darryl-G/d1039c2407262cb6d735c3e7a730ee86 to your computer and use it in GitHub Desktop.
Powershell Basic Routines for Encrypt and Decrypt AES 256 CBC using OpenSSL EVP
#PowerShell to create an gibberishaes(and openssl) compatible aes string with salt
#Salted__8bitsalt/aesstring
#thanks for .netcode -> http://stackoverflow.com/questions/5452422/openssl-using-only-net-classes
#
# This outputs the same ciphertext as: echo -n "SomePlainText"|/usr/bin/openssl enc -A -e -aes-256-cbc -a -pass pass:ThePassword
# For decrypt: echo "[cipherText]"|/usr/bin/openssl base64 -d|/usr/bin/openssl enc -A -d -aes-256-cbc -pass pass:ThePassword
function OpenSSLEncrypt($passphrase, $plainText)
{
# generate salt
[byte[]] $key
[byte[]] $iv;
[byte[]] $salt = RandomByteArray
$rng = (new-Object Security.Cryptography.RNGCryptoServiceProvider);
$res = DeriveKeyAndIV $passphrase $salt
$key = $res.key
$iv = $res.iv
# encrypt bytes
[byte[]] $encryptedBytes = EncryptStringToBytesAes $plainText $key $iv;
$encryptedBytes = $encryptedBytes[1..33] # Increase this if you enter longer plaintext e.g. 128, 256 etc.
# add salt as first 8 bytes
[byte[]] $encryptedBytesWithSalt
$encryptedBytesWithSalt = ([Text.Encoding]::ASCII.GetBytes("Salted__"))
$encryptedBytesWithSalt += $salt
$encryptedBytesWithSalt += $encryptedBytes
# base64 encode
return [Convert]::ToBase64String($encryptedBytesWithSalt)
}
function DeriveKeyAndIV($passphrase, $salt)
{
# generate key and iv
$concatenatedHashes
[byte[]] $password = [Text.Encoding]::UTF8.GetBytes($passphrase);
[byte[]] $currentHash =@()
$md5 = new-object System.Security.Cryptography.MD5CryptoServiceProvider
[bool] $enoughBytesForKey = $false;
# See http://www.openssl.org/docs/crypto/EVP_BytesToKey.html#KEY_DERIVATION_ALGORITHM
while (!$enoughBytesForKey)
{
[byte[]] $preHash = @()
$preHash = $currentHash
$preHash += $password
$preHash += $salt
$currentHash = $md5.ComputeHash($preHash);
$concatenatedHashes += $currentHash;
if ($concatenatedHashes.Count -ge 48)
{
$enoughBytesForKey = $true;
}
}
$key = $concatenatedHashes[0..31]
$iv = $concatenatedHashes[32..(32+15)]
$md5.Clear();
$md5 = $null;
$value = New-Object -TypeName PSObject -Property @{
key = $key
iv = $iv
}
$value
}
function EncryptStringToBytesAes($plainText, $key, $iv)
{
# Check arguments.
if ($plainText -eq $null -or $plainText.Length -le 0){
throw new-object ArgumentNullException("plainText");}
if ($key -eq $null -or $key.Length -le 0){
throw new-object ArgumentNullException("key");}
if ($iv -eq $null -or $iv.Length -le 0){
throw new-object ArgumentNullException("iv");}
# Declare the stream used to encrypt to an in memory
# array of bytes.
$msEncrypt;
# Declare the RijndaelManaged object
# used to encrypt the data.
$aesAlg = new-Object System.Security.Cryptography.RijndaelManaged
try
{
# Create a RijndaelManaged object
# with the specified key and IV.
$aesAlg = new-object System.Security.Cryptography.RijndaelManaged
$aesAlg.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesAlg.KeySize = 256
$aesAlg.BlockSize = 128
$aesAlg.key = $key
$aesAlg.IV = $iv
# Create an encryptor to perform the stream transform.
[System.Security.Cryptography.ICryptoTransform] $encryptor = $aesAlg.CreateEncryptor($aesAlg.Key, $aesAlg.IV);
# Create the streams used for encryption.
$msEncrypt = new-Object System.IO.MemoryStream
$csEncrypt = new-object System.Security.Cryptography.CryptoStream($msEncrypt, $encryptor, [System.Security.Cryptography.CryptoStreamMode]::Write)
$swEncrypt = new-object System.IO.StreamWriter($csEncrypt)
#Write all data to the stream.
$swEncrypt.Write($plainText);
$swEncrypt.Flush();
$swEncrypt.Close();
}
finally
{
# Clear the RijndaelManaged object.
if ($aesAlg -ne $null){
$aesAlg.Clear();}
}
# Return the encrypted bytes from the memory stream.
return $msEncrypt.ToArray();
}
function RandomByteArray([int] $length = 8)
{
$array = @()
for($i=0;$i -lt $length;$i++)
{
$array += [math]::Round($(Get-Random -Minimum 50.1 -Maximum 190.1))
}
return $array
}
function OpenSSLDecrypt([String] $passphrase, [String] $encrypted) {
# base 64 decode
[byte[]] $encryptedBytesWithSalt = [Convert]::FromBase64String($encrypted);
# extract salt (first 8 bytes of encrypted)
[byte[]] $salt = $encryptedBytesWithSalt[8..15]
[byte[]] $encryptedBytes = $encryptedBytesWithSalt[($salt.length + 8 )..($encryptedBytesWithSalt.Length)]
#$encryptedBytes+=0
# get key and iv
$res = DeriveKeyAndIV $passphrase $salt
[byte[]] $key = $res.key
[byte[]] $iv = $res.iv
return DecryptStringFromBytesAes $encryptedBytes $key $iv
}
function DecryptStringFromBytesAes([byte[]] $cipherText, [byte[]] $key, [byte[]] $iv) {
# Check arguments.
if ($cipherText -eq $null -or $cipherText.Length -le 0){
throw new-object ArgumentNullException("cipherText");}
if ($key -eq $null -or $key.Length -le 0){
throw new-object ArgumentNullException("key");}
if ($iv -eq $null -or $iv.Length -le 0){
throw new-object ArgumentNullException("iv");}
# Declare the stream used to encrypt to an in memory
# array of bytes.
[System.IO.MemoryStream] $msDecrypt
# Declare the RijndaelManaged object
# used to encrypt the data.
[System.Security.Cryptography.RijndaelManaged] $aesAlg = new-Object System.Security.Cryptography.RijndaelManaged
[String] $plainText=""
try {
# Create a RijndaelManaged object
# with the specified key and IV.
$aesAlg = new-object System.Security.Cryptography.RijndaelManaged
$aesAlg.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesAlg.KeySize = 256
$aesAlg.BlockSize = 128
$aesAlg.key = $key
$aesAlg.IV = $iv
# Create an encryptor to perform the stream transform.
[System.Security.Cryptography.ICryptoTransform] $decryptor = $aesAlg.CreateDecryptor($aesAlg.Key, $aesAlg.IV);
# Create the streams used for encryption.
$msDecrypt = new-Object System.IO.MemoryStream @(,$cipherText)
$csDecrypt = new-object System.Security.Cryptography.CryptoStream($msDecrypt, $decryptor, [System.Security.Cryptography.CryptoStreamMode]::Read)
$srDecrypt = new-object System.IO.StreamReader($csDecrypt)
#Write all data to the stream.
$plainText = $srDecrypt.ReadToEnd()
$srDecrypt.Close()
$csDecrypt.Close()
$msDecrypt.Close()
}
finally {
# Clear the RijndaelManaged object.
if ($aesAlg -ne $null){
$aesAlg.Clear()
}
}
# Return the Decrypted bytes from the memory stream.
return $plainText
}
###example###
$passphrase = "some password"
$plaintext = "A lot of dummy plaintext,"
$encryptedText="$(OpenSSLEncrypt $passphrase $plainText)".Trim()
$encryptedText
$(OpenSSLDecrypt $passphrase $encryptedText)
@Kyle-Jeynes
Copy link

Hi dude, using this with openssl_encrypt in PHP

openssl_encrypt('Write-Host "Test"', 'aes-256-cbc', 'SOME_KEY', $options=0, openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc')), $tag)
=> "W61oGlJYdkXhe6+12dLzZ4oVKb6g9gv+2lKsXulpStI="

Does not seem to work.

$response = (Invoke-RestMethod "http://$IPV4/test.json")
$decrypt  = $(OpenSSLDecrypt $response.key $response.text)
=> Exception calling "ReadToEnd" with "0" argument(s): "Padding is invalid and cannot be removed." 
At line:178 char:9  +
$plainText = $srDecrypt.ReadToEnd() 
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
  + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException  
       + FullyQualifiedErrorId : CryptographicException                                                                                                                                                                                                

Json:

{"key":"SOME_KEY","text":"W61oGlJYdkXhe6+12dLzZ4oVKb6g9gv+2lKsXulpStI="}

Any ideas? Thanks man.

@Kyle-Jeynes
Copy link

Kyle-Jeynes commented Aug 18, 2021

>>> $plaintext = "Salted__Write-Host 'Test'"
=> "Salted__Write-Host 'Test'"
>>> $ivlen = openssl_cipher_iv_length($cipher="AES-128-CBC")
=> 16
>>> $ciphertext_raw = openssl_encrypt($plaintext, $cipher, 'g3tR3kT', ($options=OPENSSL_RAW_DATA), $iv);
=> b"\x17E| *4╗´\vÀÏühCh3Jû_ú¶®‗\x04▄╩\x1F¡ıë@f"
>>> $hmac = hash_hmac('sha256', $ciphertext_raw, 'g3tR3kT', ($as_binary=true));
=> b"'─\võT\\Í$\*°Ûƶ\x05_Älù¤H\6\x13î·9ظ\x11\x15"
>>> $ciphertext = base64_encode( $iv.$hmac.$ciphertext_raw );
=> "3de9ftLKKcJr9e5P8KYnCyfEC+RUXFzWJFwq+OqS9AVfjmyXz0hcNhOM+jmd9xEVMPGJjqEAMMwxVNwpBw9il+GJjusefhIsMWkKQK2iCRE="

Json is now:

{"key":"g3tR3kT","text":"3de9ftLKKcJr9e5P8KYnCyfEC+RUXFzWJFwq+OqS9AVfjmyXz0hcNhOM+jmd9xEVMPGJjqEAMMwxVNwpBw9il+GJjusefhIsMWkKQK2iCRE="}

Error:

Error

PS:

$response = (Invoke-RestMethod "http://$IPV4/test.json")
Write-Host $response.key
Write-Host $response.text
$decrypt  = $(OpenSSLDecrypt $response.key $response.text)

Output:

output

@Darryl-G
Copy link
Author

Darryl-G commented Aug 19, 2021 via email

@sergeevabc
Copy link

Alas, PowerShell 7 throws errors:

InvalidOperation: C:\tmp\tmp.ps1:205
Line |
 205 |  $encryptedText=$(OpenSSLEncrypt $passphrase $plainText).Trim()
     |                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | You cannot call a method on a null-valued expression.
OperationStopped: C:\tmp\tmp.ps1:150
Line |
 150 |          throw  new-object ArgumentNullException("cipherText");}
     |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Value cannot be null. (Parameter 'cipherText')

@Darryl-G
Copy link
Author

Darryl-G commented Feb 7, 2024

Alas, PowerShell 7 throws errors:

InvalidOperation: C:\tmp\tmp.ps1:205
Line |
 205 |  $encryptedText=$(OpenSSLEncrypt $passphrase $plainText).Trim()
     |                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | You cannot call a method on a null-valued expression.
OperationStopped: C:\tmp\tmp.ps1:150
Line |
 150 |          throw  new-object ArgumentNullException("cipherText");}
     |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Value cannot be null. (Parameter 'cipherText')

Hey @sergeevabc

That's annoying.
Here's how it works in 7 from what I can see:

$encryptedText="$(OpenSSLEncrypt $passphrase $plainText)".Trim()

Just wrap the call to the function in double quotes.

Hopefully that's it. The Trim is just there to ensure that any whitespace is removed off the end.

Best

Darryl

@Darryl-G
Copy link
Author

Darryl-G commented Feb 7, 2024

Updated the code for Powershell 7 as per the previous comment.

@sergeevabc
Copy link

@Darryl-G, it works now, thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment