How to AES decrypt a string in Powershell with a given IV and key as string? - powershell

I need to create a powershell code which sould decode an AES-128-CBC-encrypted string.
It needs to work with an IV and key in string-format.
Here is a demo with sample dataset from a public webpage:
# test-data from https://www.coderstool.com/aes-decryption
# should return 'CodersTool secret message'
$data = "Y+egIFaYXtHdRKFVg5h80Bn/6dECi5iiPgr2L9Bd8LY="
$iv = "rwj76dfsotja10tk"
$key = "nszxnqbq1s"
$aes = [System.Security.Cryptography.Aes]::Create()
$aes.Key = [byte[]][char[]]$key
$aes.IV = [byte[]][char[]]$IV
$aes.Padding = [System.Security.Cryptography.PaddingMode]::None
$dec = $aes.CreateDecryptor()
$result = $dec.TransformFinalBlock($data, 0, $data.Length)
$dec.Dispose()
But this code throws an error, that the key does not have the correct length.
What needs to be fixed to make the above sample work in Powershell and return the expected result?

The ciphertext has been generated using AES-128 in CBC mode and PKCS#7 padding.
The following must be changed in the code:
the ciphertext must be Base64 decoded,
the key must be padded from right with 0x00 values to a length of 16 bytes (AES-128),
the padding must be PKCS#7 (since PKCS#7 is the default, the line can be omitted),
the result has to be decoded with UTF-8.
The mode is set correctly, because CBC is the default:
$data = [Convert]::FromBase64String("Y+egIFaYXtHdRKFVg5h80Bn/6dECi5iiPgr2L9Bd8LY=")
$iv = "rwj76dfsotja10tk"
$key = "nszxnqbq1s".PadRight(16, [char]0)
$aes = [System.Security.Cryptography.Aes]::Create()
$utf8 = [System.Text.Encoding]::Utf8
$aes.Key = $utf8.GetBytes($key)
$aes.IV = $utf8.GetBytes($iv)
#$aes.Padding = [System.Security.Cryptography.PaddingMode]::none # change to PKCS7 or omit line, since PKCS7 is the default
$dec = $aes.CreateDecryptor()
$result = $dec.TransformFinalBlock($data, 0, $data.Length)
$resultStr = $utf8.GetString($result)
Write-Output $resultStr
$dec.Dispose()
Output:
CodersTool secret message

Related

openssl_seal fails error:0E06D06C:configuration file routines:NCONF_get_string:no value

I am trying to seal/open a file. Encryption fails and the following error is generated.
error:0E06D06C:configuration file routines:NCONF_get_string:no value
Here's a code sample that can reproduce the issue
// Generate key pair and keep them safe...
$key = openssl_pkey_new([
'private_key_bits' => 4096,
'private_key_type' => OPENSSL_KEYTYPE_EC,
'curve_name' => 'prime256v1',
]);
$privKey = null;
openssl_pkey_export($key, $privKey);
$pubKeyDetails = openssl_pkey_get_details($key);
$pubKey = $pubKeyDetails['key'];
openssl_free_key($key);
// Load the pubkey to encrypt
$key = openssl_pkey_get_public($pubKey);
$data = file_get_contents('of-some-pretty-large-file');
// ----- This here fails -----
if (openssl_seal($data, $sealed, $eKeys, [$key]) === false) {
echo "Encryption failed\n";
echo openssl_error_string() . "\n";
exit;
}
openssl_free_key($key);
$key = openssl_pkey_get_private($privKey);
if (openssl_open($sealed, $decryptedData, $eKeys[0], $key)) {
echo ($decryptedData === $data ? "Matched\n" : "Trash\n");
}
openssl_free_key($key);
The error message
error:0E06D06C:configuration file routines:NCONF_get_string:no value
is not caused by openssl_seal, but by openssl_pkey_new. This does not affect the functionality, i.e. the key is generated successfully, see here and here. This also applies to the posted code, which generates a private EC key in SEC1 format and a public key in X.509 format.
The PHP method openssl_seal is based on the OpenSSL functions EVP_SealInit, EVP_SealUpdate and EVP_SealFinal, here. In the corresponding OpenSSL documentation the following is described in the Notes-section:
The public key must be RSA because it is the only OpenSSL public key algorithm that supports key transport.
This means that openssl_seal only works with RSA or an RSA key. If the following is used in the posted code:
$key = openssl_pkey_new([
'private_key_bits' => 4096,
'private_key_type' => OPENSSL_KEYTYPE_RSA
]);
decryption and encryption work as expected.

Determining the hash type I am working with for use in hashcat

I am trying to crack some hashed information because the passcode was lost to us. I have the hashed information in the database, and the code that was used to encrypt it. It goes through cryptastic which appears to use rijndael-256 and pbkdf2, as far as my ignorant self can tell:
public function encrypt($msg, $k, $base64 = false)
{
# open cipher module (do not change cipher/mode)
if (!$td = mcrypt_module_open('rijndael-256', '', 'ctr', ''))
return false;
$msg = serialize($msg); # serialize
$iv = mcrypt_create_iv(32, MCRYPT_RAND); # create iv
if (mcrypt_generic_init($td, $k, $iv) !== 0) # initialize buffers
return false;
$msg = mcrypt_generic($td, $msg); # encrypt
$msg = $iv . $msg; # prepend iv
$mac = $this->pbkdf2($msg, $k, 1000, 32, 'sha256'); # create mac
$msg .= $mac; # append mac
mcrypt_generic_deinit($td); # clear buffers
mcrypt_module_close($td); # close cipher module
if ($base64)
$msg = base64_encode($msg);# base64 encode?
return $msg; # return iv+ciphertext+mac
}
And in the end looks like this: wWTWLPvXT9YRz2Zj+Og0EwTTSEiZGdjAQ1TRhycJA9jusjQ2mTpptw3hSM1XJ9yPw+4XvsvFASe08AbLr3BT0LFnvGsYPrq87yI= (I know this to be a 3 digit number if that helps at all)
So I am trying to use hashcat to recover our information and I am not certain I am using the correct hash-type. I am checking this page here: https://hashcat.net/wiki/doku.php?id=example_hashes and searching for 'pbkdf2' and looking at all the hits.
The best match as far as I can tell is 9200/Cisco-IOS $8$ (PBKDF2-SHA256) except that that seems to have a header of $8$, and none of my information has any headers at all, and no $ characters. Everything else with PBKDF2 in it doesn't seem to be a match either so I find myself kind of lost before I've even gotten started.
I also noticed my hashed info always had == on the end, but only for the longer information being encrypted, in the list Juniper IVE seems to fit that format but the name doesn't match anything I can see in cryptastic.
I'm mostly ready to go aside from this as far as I can tell, I have my custom rules set up since I know how we create the initial passcodes and the hashes are in a file to be read, it's just this hash-type selection that is blocking me.
Any help appreciated!

Coldfusion Encryption and Perl Decryption

I have a situation where I need to encrypt content in Coldfusion and then decrypt in Perl. Here's a sample Coldfusion code:
<cfscript>
input = "Amidst the roar of liberated Rome, Of nations freed, and the world overjoy'd";
encryptionKey = "8kbD1Cf8TIMvm8SRxNNfaQ==";
encryptedInput = encrypt( input, encryptionKey, "AES/ECB/PKCS5Padding", "hex" );
writeOutput( "Encrypted Input: #encryptedInput# <br />" );
</cfscript>
This produces:
27B0F3EB1286FFB462BDD3F14F5A41724DF1ED888F1BEFA7174CA981C7898ED2EF841A15CDE4332D030818B9923A2DBA0C68C8352E128A0744DF5F9FA955D3C72469FEFDAE2120DE5D74319ED666DDD0
And the Perl:
use 5.24.1;
use Crypt::ECB qw(encrypt_hex);
my $input = "Amidst the roar of liberated Rome, Of nations freed, and the world overjoy'd";
my $encryption_key = "8kbD1Cf8TIMvm8SRxNNfaQ==";
my $encrypted_input = encrypt_hex($encryption_key, 'Rijndael', $input);
say $encrypted_input;
This produces:
e220ff2efe5d41e92237622ba969f35158d20e2c9c44995d44136d928d517462980321d4d6193fe62dc942fd717128442972524207777366954e5ceb2d1812ac997e06767a27d6a0145176d717c3836b
Why is the encrypted content different? Does anyone have any insights into this?
Your encryption key is base64 encoded, but Crypt::ECB expects a raw byte string (this isn't clear from the docs, though).
use Convert::Base64;
...
my $encryption_key = decode_base64("8kbD1Cf8TIMvm8SRxNNfaQ==");
...
New output:
27b0f3eb1286ffb462bdd3f14f5a41724df1ed888f1befa7174ca981c7898ed2ef841a15cde4332d030818b9923a2dba0c68c8352e128a0744df5f9fa955d3c72469fefdae2120de5d74319ed666ddd0

How can I decrypt Blowfish ciphertext with a salted header?

I have some ciphertext that has been encrypted using Perl's Crypt::CBC module that I wish to decrypt elsewhere.
The ciphertext was generated using the 'simple' version of the Crypt::CBC constructor, that is:
use Crypt::CBC;
$cipher = Crypt::CBC->new( -key => 'my secret key',
-cipher => 'Blowfish'
);
From reading the MAN page, this method of construction will take the simple string key and random salt to generate an IV & literal key to use for encryption, as well as embed a header with the salt.
"salt" -- Combine the passphrase with
an 8-byte random value to
generate both the block cipher key and the IV from the
provided passphrase. The salt will be appended to the
beginning of the data stream allowing decryption to
regenerate both the key and IV given the correct passphrase.
This method is compatible with current versions of OpenSSL.
I now need to decrypt the ciphertext on another platform that only supports CBC decryption given the ciphertext, a literal key & IV. To attempt to generate the literal key, IV & salt, I used Crypt::CBC to generate the values like so:
my $crypt = new Crypt::CBC(-key => 'my secret key', -cipher => 'Blowfish');
my $out = $crypt->decrypt($ciphertext);
my $literal_key = $crypt->key();
my $iv = $crypt->iv();
my $salt = $crypt->salt();
The decryption here is correct, but I've been unable to use the generated literal key & IV to decrypt the cipher; this produces rubbish:
my $crypt2 = new Crypt::CBC(
-literal_key => 1,
-key => $literal_key,
-cipher => 'Blowfish',
-iv => $iv,
-header => 'none');
my $rubbish - $crypt2->decrypt($ciphertext);
I can't provide a literal key and use a salted header so I'm lost as to the next move.
How can I decrypt this text?
EDIT: The target system is not running Perl, but I have been able to generate the identical value as in $rubbish above, so I'm sure it's using the same algorithm (CBC, Blowfish) to decipher.
To decrypt the stream, you first need to remove the header added by Crypt::CBC's "salt" mode. The header consists of the 8 characters Salted__ followed by 8 bytes of salt data.
In perl, something like this should do it:
my $crypt2 = new Crypt::CBC(
-literal_key => 1,
-key => $literal_key,
-cipher => 'Blowfish',
-iv => $iv,
-header => 'none');
my $cleartext = $crypt2->decrypt(substr($ciphertext, 16));
This may work. Your key will have to be exactly 56 bytes in length and the iv will have to be exactly eight bytes long:
#!/usr/bin/perl
use strict;
use warnings;
use Crypt::CBC;
my $key = "x" x 56;
my $iv = "x" x 8;
my $plaintext = "this is just some normal text\n";
my $ciphertext = Crypt::CBC->new(
-cipher => 'Blowfish',
-header => 'none',
-literal_key => 1,
-key => $key,
-iv => $iv,
)->encrypt($plaintext);
print Crypt::CBC->new(
-cipher => 'Blowfish',
-header => 'none',
-literal_key => 1,
-key => $key,
-iv => $iv,
)->decrypt($ciphertext);
If anybody needs the _salted_key_and_iv function in PHP - here it is:
function _salted_key_and_iv ($pass, $salt) {
if(strlen($salt) != 8) {
die("Salt must be 8 bytes long");
}
$key_len = 56;
$iv_len = 8;
$desired_len = $key_len+$iv_len;
$data = '';
$d = '';
while (strlen($data) < $desired_len) {
$d = pack("H*", md5($d . $pass . $salt));
$data .= "$d";
}
return $data;
}

DbConnectionStringBuilder does not parse when used in PowerShell

I am trying to use capabilities of DbConnectionStringBuilder for parsing connection-string-like user input. This works just fine in C#:
using System;
using System.Data.Common;
class Program
{
static void Main(string[] args)
{
var sb = new DbConnectionStringBuilder();
sb.ConnectionString = "server = 'smtp.gmail.com'; port = 587; user = you#gmail.com";
Console.WriteLine(sb["server"]);
Console.WriteLine(sb["port"]);
Console.WriteLine(sb["user"]);
}
}
Output:
smtp.gmail.com
587
you#gmail.com
But it does not work in PowerShell. Namely, this literally translated code in PowerShell
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.ConnectionString = "server = 'smtp.gmail.com'; port = 587; user = you#gmail.com"
$sb["server"]
$sb["port"]
$sb["user"]
produces no output at all.
Any ideas why? How to make DbConnectionStringBuilder to work as a parser in PowerShell?
System.Data.Common.DbConnectionStringBuilder implements IDictionary. Powershell has a shorthand for dictionaries using . that allows retrieval and assignment of key/value pairs as if the key was a property:
$dict = #{}
$dict["key1"] = 'value1'
$dict.key2 = 'value2'
You can see that it is storing the entire connection string as a key/value pair instead of on the ConnectionString property this way:
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.ConnectionString = "server = 'smtp.gmail.com'; port = 587; user = you#gmail.com"
$sb #formatted table of key/value pairs
The easiest way to get around this is to call the set_/get_ methods generated for properties:
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.set_ConnectionString("server = 'smtp.gmail.com'; port = 587; user = you#gmail.com")
$sb["server"]
$sb["port"]
$sb["user"]
It is probably a bug (a gotcha anyway) and I will submit it soon. It looks like PowerShell does not call setters on properties of classes that implement IDictionary (as DbConnectionStringBuilder does, and it is the setter of ConnectionString that parses the string).
Here are two demos (the original and workaround):
# test 1 - does not work, presumably PowerShell invokes $sb["ConnectionString"] = .. instead of the setter
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.ConnectionString = "server = 'smtp.gmail.com'; port = 587; user = you#gmail.com"
# the original string
$sb.ConnectionString
# nothing at all
$sb["server"]
$sb["port"]
$sb["user"]
# test 2 - works fine, we make PowerShell to invoke the ConnectionString property setter in this way
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.PSObject.Properties['ConnectionString'].Value = "server = 'smtp.gmail.com'; port = 587; user = you#gmail.com"
# parsed and re-formatted string
$sb.ConnectionString
# parsed data
$sb["server"]
$sb["port"]
$sb["user"]
Output:
server = 'smtp.gmail.com'; port = 587; user = you#gmail.com
server=smtp.gmail.com;port=587;user=you#gmail.com
smtp.gmail.com
587
you#gmail.com
As far as the workaround is found we get for free a pretty powerful parser for connection-string-like data. Here is the demo that shows parsing of quite convoluted input:
# get the parser
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
# input the string to parse using this workaround way
$sb.PSObject.Properties['ConnectionString'].Value = #'
Apostrophes = "Some 'value'";
Quotations = 'Some "value"';
Semicolons = '1; 2; 3';
Multiline = Line1
Line2
Line3;
Name with spaces = Some value;
Name with == sign = Some value;
'#
# get the parsed results
# the string: not the original but parsed and re-formatted
$sb.ConnectionString
# the data: parsed key/values
$sb | Format-Table -AutoSize -Wrap
Output:
apostrophes="Some 'value'";quotations='Some "value"';semicolons="1; 2; 3";multiline="Line1
Line2
Line3";name with spaces="Some value";name with == sign="Some value"
Key Value
--- -----
apostrophes Some 'value'
quotations Some "value"
semicolons 1; 2; 3
multiline Line1
Line2
Line3
name with spaces Some value
name with = sign Some value
$sb = New-Object System.Data.Common.DbConnectionStringBuilder
$sb.Add("server","smtp.gmail.com")
$sb.Add("port",587)
$sb.Add("user","you#gmail.com")
$sb["server"]
$sb["port"]
$sb["user"]