0

I am working on a project where I will be encrypting a string of data using the AES module from PyCrypto and then decrypting it using Powershell.

I've written a simple encryption function to do what I need here:

import base64

from Crypto import Random
from Crypto.Cipher import AES

key = "SuperSecret" #Insecure and just for testing
plaintext = "Secret message please don't look"

BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)

def padKey(s): #Pad key to 32 bytes for AES256
    return (s * (int(32/len(s))+1))[:32]

class AESCipher:

    def __init__(self, key):
        self.key = key

    def encrypt(self, raw):
        raw = pad(raw)
        iv = Random.new().read( AES.block_size )
        cipher = AES.new( self.key, AES.MODE_CBC, iv )
        return base64.b64encode( iv + cipher.encrypt( raw ) )

paddedKey = padKey(key)
cipher = AESCipher(paddedKey)

encrypted = str(cipher.encrypt(plaintext))
encrypted = encrypted[2:-1]

print("Key:", paddedKey)
print("Plaintext:",plaintext)
print("Encrypted and B64:",encrypted)

I am having some issues with decrypting and decoding the output with Powershell and could use some help. I was able to find a simple decryption script that I've been working with online, but the output is all garbage:

function Create-AesManagedObject($key, $IV) {
    $aesManaged = New-Object "System.Security.Cryptography.AesManaged"
    $aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
    $aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
    $aesManaged.BlockSize = 128
    $aesManaged.KeySize = 256
    if ($IV) {
        if ($IV.getType().Name -eq "String") {
            $aesManaged.IV = [System.Convert]::FromBase64String($IV)
        }
        else {
            $aesManaged.IV = $IV
        }
    }
    if ($key) {
        if ($key.getType().Name -eq "String") {
            $aesManaged.Key = [System.Convert]::FromBase64String($key)
        }
        else {
            $aesManaged.Key = $key
        }
    }
    $aesManaged
}

function Decrypt-String($key, $encryptedStringWithIV) {
    $bytes = [System.Convert]::FromBase64String($encryptedStringWithIV)
    $IV = $bytes[0..15]
    $aesManaged = Create-AesManagedObject $key $IV
    $decryptor = $aesManaged.CreateDecryptor();
    $unencryptedData = $decryptor.TransformFinalBlock($bytes, 16, $bytes.Length - 16);
    $aesManaged.Dispose()
    [System.Text.Encoding]::UTF8.GetString($unencryptedData).Trim([char]0)
}

Sample output:

PS C:\> Decrypt-String 'SuperSecretSuperSecretSuperSecre' $encryptedString
���H�'G zM۞� �i�ZtCI���H~N�GG��A�Pc��aF��`)��GS�N�2{�[.

Related: Using PowerShell to decrypt a Python encrypted String

dosgatos
  • 43
  • 1
  • 9
  • See this line: `if ($key.getType().Name -eq "String") {`. So, either base64 encode your key (string) or use bytes. – t.m.adam May 14 '18 at 14:55
  • Hey there! I just tried with a B64-encoded key passed to the Powershell function and the output is still busted. – dosgatos May 14 '18 at 20:33
  • Sorry, I've tested your powershell code with `'U3VwZXJTZWNyZXRTdXBlclNlY3JldFN1cGVyU2VjcmU='` as key and I get your Python's `plaintext` string as result (plus some garbage because of different padding methods - see Maarten's [answer](https://stackoverflow.com/a/50323051/7811673)) – t.m.adam May 14 '18 at 20:49
  • That worked! Thank you so much! – dosgatos May 14 '18 at 23:14

2 Answers2

2

Closing this out with the solution (Thanks @Maarten and @t.m.adam). The issue was twofold. First, the key needs to be passed to Powershell in Base64 format, and the padding needed to me moved to PKCS7. The final code is as follows:

Python Encryption:

import base64

from Crypto import Random
from Crypto.Cipher import AES

key = "SuperSecret" #Insecure and just for testing
plaintext = "Secret message please don't look"

BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)

def padKey(s): #Pad key to 32 bytes for AES256
    return (s * (int(32/len(s))+1))[:32]

class AESCipher:

    def __init__(self, key):
        self.key = key

    def encrypt(self, raw):
        raw = pad(raw)
        iv = Random.new().read( AES.block_size )
        cipher = AES.new( self.key, AES.MODE_CBC, iv )
        return base64.b64encode( iv + cipher.encrypt( raw ) )

paddedKey = padKey(key)
cipher = AESCipher(paddedKey)

encrypted = str(cipher.encrypt(plaintext))
encrypted = encrypted[2:-1]

print("Key:", base64.b64encode(paddedKey))
print("Plaintext:",plaintext)
print("Encrypted and B64:",encrypted)

Powershell Decryption:

function Create-AesManagedObject($key, $IV) {
    $aesManaged = New-Object "System.Security.Cryptography.AesManaged"
    $aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
    $aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7
    $aesManaged.BlockSize = 128
    $aesManaged.KeySize = 256
    if ($IV) {
        if ($IV.getType().Name -eq "String") {
            $aesManaged.IV = [System.Convert]::FromBase64String($IV)
        }
        else {
            $aesManaged.IV = $IV
        }
    }
    if ($key) {
        if ($key.getType().Name -eq "String") {
            $aesManaged.Key = [System.Convert]::FromBase64String($key)
        }
        else {
            $aesManaged.Key = $key
        }
    }
    $aesManaged
}

function Decrypt-String($key, $encryptedStringWithIV) {
    $bytes = [System.Convert]::FromBase64String($encryptedStringWithIV)
    $IV = $bytes[0..15]
    $aesManaged = Create-AesManagedObject $key $IV
    $decryptor = $aesManaged.CreateDecryptor();
    $unencryptedData = $decryptor.TransformFinalBlock($bytes, 16, $bytes.Length - 16);
    $aesManaged.Dispose()
    [System.Text.Encoding]::UTF8.GetString($unencryptedData).Trim([char]0)
}

Usage:

PS C:> $key = 'U3VwZXJTZWNyZXRTdXBlclNlY3JldFN1cGVyU2VjcmU='
PS C:> $encryptedString = 'Opgtr8XEcvkcYT5UzsFjZR4Wt5DI++fU4Gm0dTM/22m+xyObjP162rFphIS/xkS4I7ErJfshwI7T4X1MNz
wMog=='
PS C:> Decrypt-String $key $encryptedString
Secret message please don't look
dosgatos
  • 43
  • 1
  • 9
0

In the Python code, the following is entirely unnecessary and should be removed:

encrypted = str(cipher.encrypt(plaintext))
encrypted = encrypted[2:-1]

In your PowerShell code, you need to use PKCS7 instead of Zeros for the padding.

In the PowerShell code you don't implement the key padding with zeros anywhere. This is required (I'm not sure how you get this to work at all without it).

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • Hey! Thanks for your reply. That slice is just to remove some Python formatting since the B64 string is outputted like `b'<...>'`. As for the PKCS7 padding, that throws a new error, `"Padding is invalid and cannot be removed."`. I'm also not sure what you mean by key padding. – dosgatos May 14 '18 at 13:58
  • Well, I didn't write the `padKey` method in the Python code which does the zero padding. And I don't see how you load / generate the key in the PowerShell code either. – Maarten Bodewes May 14 '18 at 14:09
  • The `padKey` function just repeats the key until it is 32 bytes long (i.e. 'secret' -> 'secretsecretsecretsecretsecretse'). The key is passed to the Powershell decrypter function via a command line argument along with the encoded and encrypted data. – dosgatos May 14 '18 at 20:31
  • As long as you're 100% sure that it is correct because otherwise you could get garbage back, like you're doing now. Print out the key in hexadecimals for both the Python and the PowerShell code and compare! Keys do generally not consist of characters, by the way, you'd use password based encryption instead (PBE). – Maarten Bodewes May 14 '18 at 20:34
  • So along with @t.m.adam comment, changing the padding seems to have done it! Thank you for your help! – dosgatos May 14 '18 at 23:15